"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 config_1 = __importDefault(require("../../src/config"));
const block_1 = require("../../src/models/block");
const coin_1 = require("../../src/models/coin");
const transaction_1 = require("../../src/models/transaction");
const p2p_1 = require("../../src/modules/bitcoin/p2p");
const VerificationPeer_1 = require("../../src/modules/bitcoin/VerificationPeer");
const rpc_1 = require("../../src/rpc");
const config_2 = require("../../src/services/config");
const utils_1 = require("../../src/utils");
const helpers_1 = require("../helpers");
const integration_1 = require("../helpers/integration");
describe('VerificationPeer', function () {
    const chain = 'BTC';
    const network = 'regtest';
    const address = '2MuYKLUaKCenkEpwPkWUwYpBoDBNA2dgY3t';
    let chainConfig;
    let creds;
    let rpc;
    async function sendBitcoin() {
        try {
            await rpc.sendtoaddress(address, 0.1);
            console.log('Sending');
        }
        catch (e) {
            // Handle insufficent balance issues
            console.log('Generating blocks');
            const ourAddress = await rpc.getnewaddress('');
            await rpc.call('generatetoaddress', [130, ourAddress]);
            await (0, utils_1.wait)(5000);
            console.log('Sending after generating');
            await rpc.sendtoaddress(address, 0.1);
        }
    }
    function addBlock1() {
        return block_1.BitcoinBlockStorage.collection.insertOne({
            chain,
            network,
            height: 8976158,
            hash: '0x9997699519d116dfa89256d0f6ebd1737db13adca583dc80dbd533d90083961c',
            version: 100,
            merkleRoot: 'a2262b524615b6d2f409784ceff898fd46bdde6a584269788c41f26ac4b4919e',
            time: new Date(1526326784),
            timeNormalized: new Date(1526326784),
            transactionCount: 1,
            reward: 50,
            nonce: 3,
            previousBlockHash: '64bfb3eda276ae4ae5b64d9e36c9c0b629bc767fb7ae66f9d55d2c5c8103a929',
            nextBlockHash: '0xddae2bf21fb5836dec837671afd7bea1cc49d7111462e803ed3efc10570f1858',
            size: 264,
            bits: parseInt('207fffff', 16),
            processed: true
        });
    }
    function addTx() {
        return transaction_1.TransactionStorage.collection.insertOne({
            chain,
            network,
            txid: '0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098',
            blockHash: '0x9997699519d116dfa89256d0f6ebd1737db13adca583dc80dbd533d90083961c',
            blockHeight: 8976158,
            blockTime: new Date('2009-01-09T02:54:25.000Z'),
            blockTimeNormalized: new Date('2009-01-09T02:54:25.000Z'),
            coinbase: true,
            fee: 0,
            inputCount: 1,
            locktime: 0,
            outputCount: 1,
            size: 134,
            value: 5000000000.0,
            wallets: []
        });
    }
    function addCoin() {
        return coin_1.CoinStorage.collection.insertOne({
            chain,
            network,
            mintIndex: 0,
            mintTxid: '0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098',
            address: '12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX',
            coinbase: true,
            mintHeight: 1,
            script: Buffer.from('QQSWtTjoU1GccmoskeYewRYArhOQgTpifGb7i+eUe+Y8Utp1iTeVFdTgpgT4FBeB5iKUchFmv2Iec6gsvyNCyFjurA=='),
            spentHeight: -2,
            spentTxid: '',
            value: 5000000000.0,
            wallets: []
        });
    }
    // eslint-disable-next-line @typescript-eslint/no-this-alias
    const suite = this;
    this.timeout(500000);
    before(async function () {
        chainConfig = config_1.default.chains[chain][network];
        creds = chainConfig.rpc;
        rpc = new rpc_1.AsyncRPC(creds.username, creds.password, creds.host, creds.port);
        await (0, integration_1.intBeforeHelper)();
    });
    after(async () => (0, integration_1.intAfterHelper)(suite));
    beforeEach(async () => {
        await (0, helpers_1.resetDatabase)();
    });
    it('should not save any mempoool transactions', async () => {
        const chainConfig = config_2.Config.chainConfig({ chain, network });
        const worker = new VerificationPeer_1.VerificationPeer({ chain, network, chainConfig });
        await worker.connect();
        const sawTx = new Promise(r => {
            worker.events.on('transaction', r);
        });
        await sendBitcoin();
        await sawTx;
        const txCount = await transaction_1.TransactionStorage.collection.countDocuments({ chain, network });
        (0, chai_1.expect)(txCount).to.be.eq(0);
        worker.disconnect();
    });
    it('should save any mempoool transactions', async () => {
        const chainConfig = config_2.Config.chainConfig({ chain, network });
        const worker = new p2p_1.BitcoinP2PWorker({ chain, network, chainConfig });
        worker.isSyncingNode = true;
        worker.isSyncing = true;
        await worker.connect();
        const sawTx = new Promise(r => {
            worker.events.on('transaction', r);
        });
        await sendBitcoin();
        await sawTx;
        const txCount = await transaction_1.TransactionStorage.collection.countDocuments({ chain, network });
        (0, chai_1.expect)(txCount).to.be.gt(0);
        worker.disconnect();
    });
    it('should detect a fault in a series of blocks', async () => {
        const worker = new VerificationPeer_1.VerificationPeer({ chain, network, chainConfig });
        await worker.connect();
        await addBlock1();
        await addTx();
        await addCoin();
        await block_1.BitcoinBlockStorage.collection.updateOne({ hash: '0x9997699519d116dfa89256d0f6ebd1737db13adca583dc80dbd533d90083961c' }, { $unset: { nextBlockHash: '' } });
        const tip = 8976159;
        const verified = await worker.validateDataForBlock(8976158, tip, true);
        (0, chai_1.expect)(verified.success == false);
        (0, chai_1.expect)(verified.errors.length).to.eq(1);
        (0, chai_1.expect)(verified.errors[0].type).to.eq('CORRUPTED_BLOCK');
        (0, chai_1.expect)(verified.errors[0].payload.txCount).to.eq(1);
        (0, chai_1.expect)(verified.errors[0].payload.foundTxs).to.eq(1);
        await worker.disconnect();
    });
    it('should detect a missing transaction', async () => {
        const worker = new VerificationPeer_1.VerificationPeer({ chain, network, chainConfig });
        await worker.connect();
        await addBlock1();
        await addCoin();
        const tip = 8976159;
        const verified = await worker.validateDataForBlock(8976158, tip, true);
        (0, chai_1.expect)(verified.success == false);
        (0, chai_1.expect)(verified.errors.length).to.eq(1);
        (0, chai_1.expect)(verified.errors[0].type).to.eq('CORRUPTED_BLOCK');
        (0, chai_1.expect)(verified.errors[0].payload.txCount).to.eq(1);
        (0, chai_1.expect)(verified.errors[0].payload.foundTxs).to.eq(0);
        await worker.disconnect();
    });
    it('should detect a dupe transaction', async () => {
        const worker = new VerificationPeer_1.VerificationPeer({ chain, network, chainConfig });
        await worker.connect();
        await addBlock1();
        await addTx();
        await addTx();
        await addCoin();
        const tip = 8976159;
        const verified = await worker.validateDataForBlock(8976158, tip, true);
        (0, chai_1.expect)(verified.success == false);
        (0, chai_1.expect)(verified.errors.length).to.eq(2);
        (0, chai_1.expect)(verified.errors[0].type).to.eq('CORRUPTED_BLOCK');
        (0, chai_1.expect)(verified.errors[0].payload.txCount).to.eq(1);
        (0, chai_1.expect)(verified.errors[0].payload.foundTxs).to.eq(2);
        (0, chai_1.expect)(verified.errors[1].type).to.eq('DUPE_TRANSACTION');
        (0, chai_1.expect)(verified.errors[1].payload.tx.txid).to.eq('0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098');
        await worker.disconnect();
    });
    it('should detect a missing transaction output', async () => {
        const worker = new VerificationPeer_1.VerificationPeer({ chain, network, chainConfig });
        await worker.connect();
        await addBlock1();
        await addTx();
        const tip = 8976159;
        const verified = await worker.validateDataForBlock(8976158, tip, true);
        (0, chai_1.expect)(verified.success == false);
        (0, chai_1.expect)(verified.errors.length).to.eq(1);
        (0, chai_1.expect)(verified.errors[0].type).to.eq('MISSING_COIN_FOR_TXID');
        (0, chai_1.expect)(verified.errors[0].payload.txid).to.eq('0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098');
        await worker.disconnect();
    });
    it('should detect a dupe transaction output', async () => {
        const worker = new VerificationPeer_1.VerificationPeer({ chain, network, chainConfig });
        await worker.connect();
        await addBlock1();
        await addTx();
        await addCoin();
        await addCoin();
        const tip = 8976159;
        const verified = await worker.validateDataForBlock(8976158, tip, true);
        (0, chai_1.expect)(verified.success == false);
        (0, chai_1.expect)(verified.errors.length).to.eq(1);
        (0, chai_1.expect)(verified.errors[0].type).to.eq('DUPE_COIN');
        (0, chai_1.expect)(verified.errors[0].payload.coin.mintTxid).to.eq('0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098');
        await worker.disconnect();
    });
    it('should detect a missing block', async () => {
        const worker = new VerificationPeer_1.VerificationPeer({ chain, network, chainConfig });
        await worker.connect();
        await addTx();
        await addCoin();
        const tip = 8976159;
        const verified = await worker.validateDataForBlock(8976158, tip, true);
        (0, chai_1.expect)(verified.success == false);
        (0, chai_1.expect)(verified.errors.length).to.eq(1);
        (0, chai_1.expect)(verified.errors[0].type).to.eq('MISSING_BLOCK');
        (0, chai_1.expect)(verified.errors[0].payload.blockNum).to.eq(8976158);
        await worker.disconnect();
    });
    it('should detect a dupe block', async () => {
        const worker = new VerificationPeer_1.VerificationPeer({ chain, network, chainConfig });
        await worker.connect();
        await addBlock1();
        await addBlock1();
        await addTx();
        await addCoin();
        const tip = 8976159;
        const verified = await worker.validateDataForBlock(8976158, tip, true);
        (0, chai_1.expect)(verified.success == false);
        (0, chai_1.expect)(verified.errors.length).to.eq(2);
        (0, chai_1.expect)(verified.errors[0].type).to.eq('DUPE_BLOCKHEIGHT');
        (0, chai_1.expect)(verified.errors[0].payload.blockNum).to.eq(8976158);
        (0, chai_1.expect)(verified.errors[1].type).to.eq('DUPE_BLOCKHASH');
        (0, chai_1.expect)(verified.errors[1].payload.blockNum).to.eq(8976158);
        await worker.disconnect();
    });
    it('should handle non-linear scanning by ignoring previousBlockHash', async () => {
        const worker = new VerificationPeer_1.VerificationPeer({ chain, network, chainConfig });
        await worker.connect();
        await addBlock1();
        await addTx();
        await addCoin();
        await block_1.BitcoinBlockStorage.collection.updateOne({ hash: '0x9997699519d116dfa89256d0f6ebd1737db13adca583dc80dbd533d90083961c' }, { $set: { previousBlockHash: 'abcdefg' } });
        const tip = 8976159;
        await worker.validateDataForBlock(8976154, tip, true);
        const verified = await worker.validateDataForBlock(8976158, tip, true);
        (0, chai_1.expect)(verified.success == true);
        await worker.disconnect();
    });
});
//# sourceMappingURL=verification.test.js.map