"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const chai_1 = require("chai");
const logger_1 = __importDefault(require("../../../src/logger"));
const block_1 = require("../../../src/models/block");
const coin_1 = require("../../../src/models/coin");
const transaction_1 = require("../../../src/models/transaction");
const test_block_1 = require("../../data/test-block");
const helpers_1 = require("../../helpers");
const integration_1 = require("../../helpers/integration");
describe('Block Model', function () {
    // eslint-disable-next-line @typescript-eslint/no-this-alias
    const suite = this;
    this.timeout(30000);
    async function insertBlocks() {
        await block_1.BitcoinBlockStorage.collection.insertOne({
            chain: 'BTC',
            network: 'regtest',
            height: 5,
            hash: '528f01c17829622ed6a4af51b3b3f6c062f304fa60e66499c9cbb8622c8407f7',
            version: 100,
            merkleRoot: 'a2262b524615b6d2f409784ceff898fd46bdde6a584269788c41f26ac4b4919e',
            time: new Date(1526326784),
            timeNormalized: new Date(1526326784),
            transactionCount: 1,
            reward: 50,
            nonce: 3,
            previousBlockHash: '64bfb3eda276ae4ae5b64d9e36c9c0b629bc767fb7ae66f9d55d2c5c8103a929',
            nextBlockHash: '',
            size: 264,
            bits: parseInt('207fffff', 16),
            processed: true
        });
        await block_1.BitcoinBlockStorage.collection.insertOne({
            chain: 'BTC',
            network: 'regtest',
            height: 6,
            hash: '2a883ff89c7d6e9302bb4a4634cd580319a4fd59d69e979b344972b0ba042b86',
            version: 100,
            merkleRoot: 'a2262b524615b6d2f409784ceff898fd46bdde6a584269788c41f26ac4b4919e',
            time: new Date(1526326784),
            timeNormalized: new Date(1526326784),
            transactionCount: 1,
            reward: 50,
            nonce: 3,
            previousBlockHash: '64bfb3eda276ae4ae5b64d9e36c9c0b629bc767fb7ae66f9d55d2c5c8103a929',
            nextBlockHash: '',
            size: 264,
            bits: parseInt('207fffff', 16),
            processed: true
        });
        await block_1.BitcoinBlockStorage.collection.insertOne({
            chain: 'BTC',
            network: 'regtest',
            height: 7,
            hash: '3279069d22ce5af68ef38332d5b40e79e1964b154d466e7fa233015a34c27312',
            version: 100,
            merkleRoot: 'a2262b524615b6d2f409784ceff898fd46bdde6a584269788c41f26ac4b4919e',
            time: new Date(1526326784),
            timeNormalized: new Date(1526326784),
            transactionCount: 1,
            reward: 50,
            nonce: 3,
            previousBlockHash: '64bfb3eda276ae4ae5b64d9e36c9c0b629bc767fb7ae66f9d55d2c5c8103a929',
            nextBlockHash: '',
            size: 264,
            bits: parseInt('207fffff', 16),
            processed: true
        });
        await block_1.BitcoinBlockStorage.collection.insertOne({
            chain: 'BTC',
            network: 'regtest',
            height: 8,
            hash: '3420349f63d96f257d56dd970f6b9079af9cf2784c267a13b1ac339d47031fe9',
            version: 100,
            merkleRoot: 'a2262b524615b6d2f409784ceff898fd46bdde6a584269788c41f26ac4b4919e',
            time: new Date(1526326784),
            timeNormalized: new Date(1526326784),
            transactionCount: 1,
            reward: 50,
            nonce: 3,
            previousBlockHash: '64bfb3eda276ae4ae5b64d9e36c9c0b629bc767fb7ae66f9d55d2c5c8103a929',
            nextBlockHash: '',
            size: 264,
            bits: parseInt('207fffff', 16),
            processed: true
        });
    }
    before(integration_1.intBeforeHelper);
    after(() => (0, integration_1.intAfterHelper)(suite));
    beforeEach(async () => {
        await (0, helpers_1.resetDatabase)();
    });
    describe('addBlock', () => {
        it('should add a block when incoming block references previous block hash', async () => {
            await insertBlocks();
            await block_1.BitcoinBlockStorage.addBlock({
                block: test_block_1.TEST_BLOCK,
                chain: 'BTC',
                network: 'regtest',
                initialSyncComplete: false
            });
            const blocks = await block_1.BitcoinBlockStorage.collection
                .find({ chain: 'BTC', network: 'regtest' })
                .sort({ height: 1 })
                .toArray();
            (0, chai_1.expect)(blocks.length).to.equal(5);
            const ownBlock = blocks[4];
            (0, chai_1.expect)(ownBlock.chain).to.equal('BTC');
            (0, chai_1.expect)(ownBlock.hash).to.equal('64bfb3eda276ae4ae5b64d9e36c9c0b629bc767fb7ae66f9d55d2c5c8103a929');
            (0, chai_1.expect)(ownBlock.network).to.equal('regtest');
            (0, chai_1.expect)(ownBlock.bits).to.equal(545259519);
            (0, chai_1.expect)(ownBlock.height).to.equal(9);
            (0, chai_1.expect)(ownBlock.merkleRoot).to.equal('08e23107e8449f02568d37d37aa76e840e55bbb5f100ed8ad257af303db88c08');
            (0, chai_1.expect)(ownBlock.nonce).to.equal(2);
            (0, chai_1.expect)(ownBlock.previousBlockHash).to.equal('3420349f63d96f257d56dd970f6b9079af9cf2784c267a13b1ac339d47031fe9');
            (0, chai_1.expect)(ownBlock.reward).to.equal(0.09765625);
            (0, chai_1.expect)(ownBlock.size).to.equal(264);
            (0, chai_1.expect)(ownBlock.version).to.equal(536870912);
            // TODO: assertion for block times
            (0, chai_1.expect)(ownBlock.transactionCount).to.equal(1);
            (0, chai_1.expect)(ownBlock.processed).to.equal(true);
            logger_1.default.info('new block was successfully added with hash: %o', ownBlock.hash);
            const transaction = await transaction_1.TransactionStorage.collection
                .find({
                chain: 'BTC',
                network: 'regtest',
                blockHash: '64bfb3eda276ae4ae5b64d9e36c9c0b629bc767fb7ae66f9d55d2c5c8103a929'
            })
                .toArray();
            (0, chai_1.expect)(transaction.length).to.equal(1);
            (0, chai_1.expect)(transaction[0].chain).to.equal('BTC');
            (0, chai_1.expect)(transaction[0].network).to.equal('regtest');
            (0, chai_1.expect)(transaction[0].txid).to.equal('08e23107e8449f02568d37d37aa76e840e55bbb5f100ed8ad257af303db88c08');
            (0, chai_1.expect)(transaction[0].blockHash).to.equal('64bfb3eda276ae4ae5b64d9e36c9c0b629bc767fb7ae66f9d55d2c5c8103a929');
            (0, chai_1.expect)(transaction[0].blockHeight).to.equal(9);
            (0, chai_1.expect)(transaction[0].coinbase).to.equal(true);
            (0, chai_1.expect)(transaction[0].locktime).to.equal(0);
            (0, chai_1.expect)(transaction[0].size).to.equal(0);
            // TODO: assertion for block times
            (0, chai_1.expect)(transaction[0].wallets.length).to.equal(0);
            logger_1.default.info(`tx: ${transaction[0].txid} was successfully stored in the TX model`);
        });
    });
    describe('handleReorg', () => {
        it("should not reorg if the incoming block's prevHash matches the block hash of the current highest block", async () => {
            await block_1.BitcoinBlockStorage.collection.insertOne({
                chain: 'BTC',
                network: 'regtest',
                height: 1335,
                hash: '528f01c17829622ed6a4af51b3b3f6c062f304fa60e66499c9cbb8622c8407f7',
                version: 100,
                merkleRoot: 'a2262b524615b6d2f409784ceff898fd46bdde6a584269788c41f26ac4b4919e',
                time: new Date(1526326784),
                timeNormalized: new Date(1526326784),
                transactionCount: 1,
                reward: 50,
                nonce: 3,
                previousBlockHash: '64bfb3eda276ae4ae5b64d9e36c9c0b629bc767fb7ae66f9d55d2c5c8103a929',
                nextBlockHash: '',
                size: 264,
                bits: parseInt('207fffff', 16),
                processed: true
            });
            await block_1.BitcoinBlockStorage.collection.insertOne({
                chain: 'BTC',
                network: 'regtest',
                height: 1336,
                hash: '2a883ff89c7d6e9302bb4a4634cd580319a4fd59d69e979b344972b0ba042b86',
                version: 100,
                merkleRoot: 'a2262b524615b6d2f409784ceff898fd46bdde6a584269788c41f26ac4b4919e',
                time: new Date(1526326784),
                timeNormalized: new Date(1526326784),
                transactionCount: 1,
                reward: 50,
                nonce: 3,
                previousBlockHash: '64bfb3eda276ae4ae5b64d9e36c9c0b629bc767fb7ae66f9d55d2c5c8103a929',
                nextBlockHash: '',
                size: 264,
                bits: parseInt('207fffff', 16),
                processed: true
            });
            await block_1.BitcoinBlockStorage.collection.insertOne({
                chain: 'BTC',
                network: 'regtest',
                height: 1337,
                hash: '3279069d22ce5af68ef38332d5b40e79e1964b154d466e7fa233015a34c27312',
                version: 100,
                merkleRoot: 'a2262b524615b6d2f409784ceff898fd46bdde6a584269788c41f26ac4b4919e',
                time: new Date(1526326784),
                timeNormalized: new Date(1526326784),
                transactionCount: 1,
                reward: 50,
                nonce: 3,
                previousBlockHash: '64bfb3eda276ae4ae5b64d9e36c9c0b629bc767fb7ae66f9d55d2c5c8103a929',
                nextBlockHash: '',
                size: 264,
                bits: parseInt('207fffff', 16),
                processed: true
            });
            await block_1.BitcoinBlockStorage.handleReorg({
                header: {
                    prevHash: '3279069d22ce5af68ef38332d5b40e79e1964b154d466e7fa233015a34c27312',
                    hash: '12c719927ce18f9a61d7c5a7af08d3110cacfa43671aa700956c3c05ed38bdaa',
                    time: 1526326785,
                    version: 536870912,
                    merkleRoot: '8c29860888b915715878b21ce14707a17b43f6c51dfb62a1e736e35bc5d8093f',
                    bits: parseInt('207fffff', 16),
                    nonce: 3
                },
                chain: 'BTC',
                network: 'regtest'
            });
            const result = await block_1.BitcoinBlockStorage.collection.find({ chain: 'BTC', network: 'regtest' }).toArray();
            (0, chai_1.expect)(result.length).to.equal(3);
        });
        it('should not reorg if localTip height is zero', async () => {
            await block_1.BitcoinBlockStorage.handleReorg({
                header: {
                    prevHash: '12c719927ce18f9a61d7c5a7af08d3110cacfa43671aa700956c3c05ed38bdaa',
                    hash: '4c6872bf45ecab2fb8b38c8b8f50fc4a8309c6171d28d479b8226afcb1a99920',
                    time: 1526326785,
                    version: 536870912,
                    merkleRoot: '8c29860888b915715878b21ce14707a17b43f6c51dfb62a1e736e35bc5d8093f',
                    bits: parseInt('207fffff', 16),
                    nonce: 3
                },
                chain: 'BTC',
                network: 'regtest'
            });
            const result = await block_1.BitcoinBlockStorage.collection.find({ chain: 'BTC', network: 'regtest' }).toArray();
            (0, chai_1.expect)(result.length).to.equal(0);
        });
        it('should successfully handle reorg', async () => {
            // setting the Block model
            await block_1.BitcoinBlockStorage.collection.insertOne({
                chain: 'BTC',
                network: 'regtest',
                height: 5,
                hash: '528f01c17829622ed6a4af51b3b3f6c062f304fa60e66499c9cbb8622c8407f7',
                version: 100,
                merkleRoot: 'a2262b524615b6d2f409784ceff898fd46bdde6a584269788c41f26ac4b4919e',
                time: new Date(1526326784),
                timeNormalized: new Date(1526326784),
                transactionCount: 1,
                reward: 50,
                nonce: 3,
                previousBlockHash: '64bfb3eda276ae4ae5b64d9e36c9c0b629bc767fb7ae66f9d55d2c5c8103a929',
                nextBlockHash: '',
                size: 264,
                bits: parseInt('207fffff', 16),
                processed: true
            });
            await block_1.BitcoinBlockStorage.collection.insertOne({
                chain: 'BTC',
                network: 'regtest',
                height: 6,
                hash: '2a883ff89c7d6e9302bb4a4634cd580319a4fd59d69e979b344972b0ba042b86',
                version: 100,
                merkleRoot: 'a2262b524615b6d2f409784ceff898fd46bdde6a584269788c41f26ac4b4919e',
                time: new Date(1526326784),
                timeNormalized: new Date(1526326784),
                transactionCount: 1,
                reward: 50,
                nonce: 3,
                previousBlockHash: '528f01c17829622ed6a4af51b3b3f6c062f304fa60e66499c9cbb8622c8407f7',
                nextBlockHash: '',
                size: 264,
                bits: parseInt('207fffff', 16),
                processed: true
            });
            await block_1.BitcoinBlockStorage.collection.insertOne({
                chain: 'BTC',
                network: 'regtest',
                height: 7,
                hash: '3279069d22ce5af68ef38332d5b40e79e1964b154d466e7fa233015a34c27312',
                version: 100,
                merkleRoot: 'a2262b524615b6d2f409784ceff898fd46bdde6a584269788c41f26ac4b4919e',
                time: new Date(1526326784),
                timeNormalized: new Date(1526326784),
                transactionCount: 1,
                reward: 50,
                nonce: 3,
                previousBlockHash: '2a883ff89c7d6e9302bb4a4634cd580319a4fd59d69e979b344972b0ba042b86',
                nextBlockHash: '',
                size: 264,
                bits: parseInt('207fffff', 16),
                processed: true
            });
            // setting TX model
            await transaction_1.TransactionStorage.collection.insertOne({
                txid: 'a2262b524615b6d2f409784ceff898fd46bdde6a584269788c41f26ac4b4919g',
                chain: 'BTC',
                network: 'regtest',
                blockHash: '528f01c17829622ed6a4af51b3b3f6c062f304fa60e66499c9cbb8622c8407f7',
                blockTime: new Date(1526326784),
                value: 100000,
                fee: 100,
                coinbase: true,
                locktime: 0,
                size: 145,
                inputCount: 1,
                outputCount: 1,
                wallets: [],
                blockHeight: 5
            });
            await transaction_1.TransactionStorage.collection.insertOne({
                txid: '8c29860888b915715878b21ce14707a17b43f6c51dfb62a1e736e35bc5d8093f',
                chain: 'BTC',
                network: 'regtest',
                blockHash: '3279069d22ce5af68ef38332d5b40e79e1964b154d466e7fa233015a34c27312',
                blockTime: new Date(1526326785),
                value: 100000,
                fee: 100,
                coinbase: true,
                locktime: 0,
                size: 145,
                inputCount: 1,
                outputCount: 1,
                wallets: [],
                blockHeight: 6
            });
            await transaction_1.TransactionStorage.collection.insertOne({
                txid: 'a2262b524615b6d2f409784ceff898fd46bdde6a584269788c41f26ac4b4919e',
                chain: 'BTC',
                network: 'regtest',
                blockHash: '3279069d22ce5af68ef38332d5b40e79e1964b154d466e7fa233015a34c27312',
                blockTime: new Date(1526326784),
                value: 100000,
                fee: 100,
                coinbase: true,
                locktime: 0,
                size: 145,
                inputCount: 1,
                outputCount: 1,
                wallets: [],
                blockHeight: 7
            });
            await transaction_1.TransactionStorage.collection.insertOne({
                txid: '8a351fa9fc3fcd38066b4bf61a8b5f71f08aa224d7a86165557e6da7ee13a826',
                chain: 'BTC',
                network: 'regtest',
                blockHash: '3279069d22ce5af68ef38332d5b40e79e1964b154d466e7fa233015a34c27312',
                blockTime: new Date(1526326785),
                value: 100000,
                fee: 100,
                coinbase: true,
                locktime: 0,
                size: 145,
                inputCount: 1,
                outputCount: 1,
                wallets: [],
                blockHeight: 7
            });
            // setting the Coin model
            await coin_1.CoinStorage.collection.insertOne({
                network: 'regtest',
                chain: 'BTC',
                mintTxid: 'a2262b524615b6d2f409784ceff898fd46bdde6a584269788c41f26ac4b4919g',
                spentTxid: '',
                mintIndex: 0,
                spentHeight: -2 /* SpentHeightIndicators.unspent */,
                mintHeight: 5,
                coinbase: true,
                script: Buffer.from(''),
                wallets: [],
                value: 500.0,
                address: 'mkjB6LmjiNfJWgH4aP4v1GkFjRcQTfDSfj'
            });
            await coin_1.CoinStorage.collection.insertOne({
                network: 'regtest',
                chain: 'BTC',
                mintTxid: 'a2262b524615b6d2f409784ceff898fd46bdde6a584269788c41f26ac4b4919e',
                spentTxid: '',
                mintIndex: 0,
                spentHeight: -2 /* SpentHeightIndicators.unspent */,
                mintHeight: 7,
                coinbase: true,
                script: Buffer.from(''),
                wallets: [],
                value: 500.0,
                address: 'mkjB6LmjiNfJWgH4aP4v1GkFjRcQTfDSfj'
            });
            await coin_1.CoinStorage.collection.insertOne({
                network: 'regtest',
                chain: 'BTC',
                mintTxid: '8a351fa9fc3fcd38066b4bf61a8b5f71f08aa224d7a86165557e6da7ee13a826',
                spentTxid: '',
                mintIndex: 0,
                spentHeight: -2 /* SpentHeightIndicators.unspent */,
                mintHeight: 7,
                coinbase: true,
                script: Buffer.from(''),
                wallets: [],
                value: 500.0,
                address: 'mkjB6LmjiNfJWgH4aP4v1GkFjRcQTfDSfj'
            });
            await coin_1.CoinStorage.collection.insertOne({
                network: 'regtest',
                chain: 'BTC',
                mintTxid: '8c29860888b915715878b21ce14707a17b43f6c51dfb62a1e736e35bc5d8093f',
                mintIndex: 0,
                spentHeight: 8,
                mintHeight: 7,
                coinbase: true,
                script: Buffer.from(''),
                wallets: [],
                value: 500.0,
                address: 'mkjB6LmjiNfJWgH4aP4v1GkFjRcQTfDSfj',
                spentTxid: 'eec8570a0c960b19fa6c86c71a06ebda379b86b5fe0be0e64ba83b2e0a3d05a3'
            });
            await block_1.BitcoinBlockStorage.handleReorg({
                header: {
                    prevHash: '2a883ff89c7d6e9302bb4a4634cd580319a4fd59d69e979b344972b0ba042b86',
                    hash: '3279069d22ce5af68ef38332d5b40e79e1964b154d466e7fa233015a34c27312',
                    time: 1526326785,
                    version: 536870912,
                    merkleRoot: '8c29860888b915715878b21ce14707a17b43f6c51dfb62a1e736e35bc5d8093f',
                    bits: parseInt('207fffff', 16),
                    nonce: 3
                },
                chain: 'BTC',
                network: 'regtest'
            });
            // check for removed block after Reorg in db
            const blocks = await block_1.BitcoinBlockStorage.collection
                .find({
                chain: 'BTC',
                network: 'regtest'
            })
                .toArray();
            (0, chai_1.expect)(blocks.length).to.equal(1);
            const removedBlock = await block_1.BitcoinBlockStorage.collection
                .find({
                chain: 'BTC',
                network: 'regtest',
                height: {
                    $gte: 7
                }
            })
                .toArray();
            (0, chai_1.expect)(removedBlock.length).to.equal(0);
            // check for removed tx after Reorg in db
            const transaction = await transaction_1.TransactionStorage.collection
                .find({
                chain: 'BTC',
                network: 'regtest'
            })
                .toArray();
            (0, chai_1.expect)(transaction.length).to.equal(1);
            const removedTransaction = await transaction_1.TransactionStorage.collection
                .find({
                chain: 'BTC',
                network: 'regtest',
                blockHeight: {
                    $gte: 7
                }
            })
                .toArray();
            (0, chai_1.expect)(removedTransaction.length).to.equal(0);
            // check for removed coin after Reorg in db
            const coinModel = await coin_1.CoinStorage.collection
                .find({
                chain: 'BTC',
                network: 'regtest'
            })
                .toArray();
            (0, chai_1.expect)(coinModel.length).to.equal(1);
            const removedCoin = await coin_1.CoinStorage.collection
                .find({
                chain: 'BTC',
                network: 'regtest',
                mintHeight: {
                    $gte: 7
                }
            })
                .toArray();
            (0, chai_1.expect)(removedCoin.length).to.equal(0);
            // check for unspent coins in the db
            const unspentCoins = await coin_1.CoinStorage.collection
                .find({
                chain: 'BTC',
                network: 'regtest',
                spentHeight: -2 /* SpentHeightIndicators.unspent */
            })
                .toArray();
            (0, chai_1.expect)(unspentCoins.length).equal(1);
            (0, chai_1.expect)(unspentCoins[0].chain).to.equal('BTC');
            (0, chai_1.expect)(unspentCoins[0].network).to.equal('regtest');
            (0, chai_1.expect)(unspentCoins[0].mintTxid).to.equal('a2262b524615b6d2f409784ceff898fd46bdde6a584269788c41f26ac4b4919g');
            (0, chai_1.expect)(unspentCoins[0].mintIndex).to.equal(0);
            (0, chai_1.expect)(unspentCoins[0].mintHeight).to.equal(5);
            (0, chai_1.expect)(unspentCoins[0].coinbase).to.equal(true);
            (0, chai_1.expect)(unspentCoins[0].value).to.equal(500.0);
            (0, chai_1.expect)(unspentCoins[0].address).to.equal('mkjB6LmjiNfJWgH4aP4v1GkFjRcQTfDSfj');
            (0, chai_1.expect)(unspentCoins[0].spentTxid).to.equal('');
            (0, chai_1.expect)(unspentCoins[0].spentHeight).to.equal(-2 /* SpentHeightIndicators.unspent */);
        });
        it('should detect a fault in the block hashes', async () => {
            const chain = 'BTC';
            const network = 'regtest';
            await insertBlocks();
            const badHash = '3279069d22ce5af68ef38332d5b40e79e1964b154d466e7fa233015a34c27312';
            await block_1.BitcoinBlockStorage.collection.updateOne({ chain, network, hash: badHash }, { $set: { previousBlockHash: 'aaaaa' } });
            const invalidChain = await block_1.BitcoinBlockStorage.validateLocatorHashes({ chain, network });
            (0, chai_1.expect)(invalidChain[1].hash).to.eq(badHash);
        });
        it('should detect a missing block', async () => {
            const chain = 'BTC';
            const network = 'regtest';
            await insertBlocks();
            const badHash = '3279069d22ce5af68ef38332d5b40e79e1964b154d466e7fa233015a34c27312';
            const lastKnown = '2a883ff89c7d6e9302bb4a4634cd580319a4fd59d69e979b344972b0ba042b86';
            await block_1.BitcoinBlockStorage.collection.deleteOne({ chain, network, hash: badHash });
            const invalidChain = await block_1.BitcoinBlockStorage.validateLocatorHashes({ chain, network });
            (0, chai_1.expect)(invalidChain[1].hash).to.eq(lastKnown);
        });
    });
});
//# sourceMappingURL=block.test.js.map