bitcore-node
Version:
A blockchain indexing node with extended capabilities using bitcore
286 lines (254 loc) • 9.97 kB
text/typescript
import { expect } from 'chai';
import config from '../../src/config';
import { BitcoinBlockStorage } from '../../src/models/block';
import { CoinStorage } from '../../src/models/coin';
import { TransactionStorage } from '../../src/models/transaction';
import { BitcoinP2PWorker } from '../../src/modules/bitcoin/p2p';
import { VerificationPeer } from '../../src/modules/bitcoin/VerificationPeer';
import { AsyncRPC } from '../../src/rpc';
import { Config } from '../../src/services/config';
import { IUtxoNetworkConfig } from '../../src/types/Config';
import { wait } from '../../src/utils';
import { resetDatabase } from '../helpers';
import { intAfterHelper, intBeforeHelper } from '../helpers/integration';
const chain = 'BTC';
const network = 'regtest';
const address = '2MuYKLUaKCenkEpwPkWUwYpBoDBNA2dgY3t';
const chainConfig = config.chains[chain][network] as IUtxoNetworkConfig;
const creds = chainConfig.rpc;
const rpc = new AsyncRPC(creds.username, creds.password, creds.host, creds.port);
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 wait(5000);
console.log('Sending after generating');
await rpc.sendtoaddress(address, 0.1);
}
}
function addBlock1() {
return 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 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 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: []
});
}
describe('VerificationPeer', function() {
const suite = this;
this.timeout(500000);
before(intBeforeHelper);
after(async () => intAfterHelper(suite));
beforeEach(async () => {
await resetDatabase();
});
it('should not save any mempoool transactions', async () => {
const chainConfig = Config.chainConfig({ chain, network });
const worker = new VerificationPeer({ chain, network, chainConfig });
await worker.connect();
const sawTx = new Promise(r => {
worker.events.on('transaction', r);
});
await sendBitcoin();
await sawTx;
const txCount = await TransactionStorage.collection.countDocuments({ chain, network });
expect(txCount).to.be.eq(0);
worker.disconnect();
});
it('should save any mempoool transactions', async () => {
const chainConfig = Config.chainConfig({ chain, network });
const worker = new 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 TransactionStorage.collection.countDocuments({ chain, network });
expect(txCount).to.be.gt(0);
worker.disconnect();
});
it('should detect a fault in a series of blocks', async () => {
const worker = new VerificationPeer({ chain, network, chainConfig });
await worker.connect();
await addBlock1();
await addTx();
await addCoin();
await BitcoinBlockStorage.collection.updateOne(
{ hash: '0x9997699519d116dfa89256d0f6ebd1737db13adca583dc80dbd533d90083961c' },
{ $unset: { nextBlockHash: '' } }
);
const tip = 8976159;
const verified = await worker.validateDataForBlock(8976158, tip, true);
expect(verified.success == false);
expect(verified.errors.length).to.eq(1);
expect(verified.errors[0].type).to.eq('CORRUPTED_BLOCK');
expect(verified.errors[0].payload.txCount).to.eq(1);
expect(verified.errors[0].payload.foundTxs).to.eq(1);
await worker.disconnect();
});
it('should detect a missing transaction', async () => {
const worker = new VerificationPeer({ chain, network, chainConfig });
await worker.connect();
await addBlock1();
await addCoin();
const tip = 8976159;
const verified = await worker.validateDataForBlock(8976158, tip, true);
expect(verified.success == false);
expect(verified.errors.length).to.eq(1);
expect(verified.errors[0].type).to.eq('CORRUPTED_BLOCK');
expect(verified.errors[0].payload.txCount).to.eq(1);
expect(verified.errors[0].payload.foundTxs).to.eq(0);
await worker.disconnect();
});
it('should detect a dupe transaction', async () => {
const worker = new 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);
expect(verified.success == false);
expect(verified.errors.length).to.eq(2);
expect(verified.errors[0].type).to.eq('CORRUPTED_BLOCK');
expect(verified.errors[0].payload.txCount).to.eq(1);
expect(verified.errors[0].payload.foundTxs).to.eq(2);
expect(verified.errors[1].type).to.eq('DUPE_TRANSACTION');
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({ chain, network, chainConfig });
await worker.connect();
await addBlock1();
await addTx();
const tip = 8976159;
const verified = await worker.validateDataForBlock(8976158, tip, true);
expect(verified.success == false);
expect(verified.errors.length).to.eq(1);
expect(verified.errors[0].type).to.eq('MISSING_COIN_FOR_TXID');
expect(verified.errors[0].payload.txid).to.eq('0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098');
await worker.disconnect();
});
it('should detect a dupe transaction output', async () => {
const worker = new 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);
expect(verified.success == false);
expect(verified.errors.length).to.eq(1);
expect(verified.errors[0].type).to.eq('DUPE_COIN');
expect(verified.errors[0].payload.coin.mintTxid).to.eq(
'0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098'
);
await worker.disconnect();
});
it('should detect a missing block', async () => {
const worker = new VerificationPeer({ chain, network, chainConfig });
await worker.connect();
await addTx();
await addCoin();
const tip = 8976159;
const verified = await worker.validateDataForBlock(8976158, tip, true);
expect(verified.success == false);
expect(verified.errors.length).to.eq(1);
expect(verified.errors[0].type).to.eq('MISSING_BLOCK');
expect(verified.errors[0].payload.blockNum).to.eq(8976158);
await worker.disconnect();
});
it('should detect a dupe block', async () => {
const worker = new 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);
expect(verified.success == false);
expect(verified.errors.length).to.eq(2);
expect(verified.errors[0].type).to.eq('DUPE_BLOCKHEIGHT');
expect(verified.errors[0].payload.blockNum).to.eq(8976158);
expect(verified.errors[1].type).to.eq('DUPE_BLOCKHASH');
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({ chain, network, chainConfig });
await worker.connect();
await addBlock1();
await addTx();
await addCoin();
await 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);
expect(verified.success == true);
await worker.disconnect();
});
});