fcoin/test/chain-test.js
Christopher Jeffrey 52c46e83ed
add scanning test.
2016-07-15 18:20:55 -07:00

241 lines
6.5 KiB
JavaScript

'use strict';
var bn = require('bn.js');
var bcoin = require('../').set('regtest');
var constants = bcoin.protocol.constants;
var utils = bcoin.utils;
var assert = require('assert');
var opcodes = constants.opcodes;
constants.tx.COINBASE_MATURITY = 0;
describe('Chain', function() {
var chain, wallet, miner, walletdb;
var competingTip, oldTip, ch1, ch2, cb1, cb2;
this.timeout(5000);
chain = new bcoin.chain({ name: 'chain-test', db: 'memory' });
walletdb = new bcoin.walletdb({ name: 'chain-test-wdb', db: 'memory' });
miner = new bcoin.miner({
chain: chain
});
chain.on('error', function() {});
miner.on('error', function() {});
function mineBlock(entry, tx, callback) {
var realTip;
if (entry) {
realTip = chain.tip;
chain.tip = entry;
}
miner.createBlock(function(err, attempt) {
if (realTip)
chain.tip = realTip;
assert.ifError(err);
if (tx) {
var redeemer = bcoin.mtx();
redeemer.addOutput({
address: wallet.getAddress(),
value: utils.satoshi('25.0')
});
redeemer.addOutput({
address: wallet.account.deriveAddress(false, 100).getAddress(),
value: utils.satoshi('5.0')
});
redeemer.addInput(tx, 0);
redeemer.setLocktime(chain.height);
return wallet.sign(redeemer, function(err) {
assert.ifError(err);
attempt.addTX(redeemer);
callback(null, attempt.mineSync());
});
}
callback(null, attempt.mineSync());
});
}
function deleteCoins(tx) {
if (tx.txs) {
deleteCoins(tx.txs);
return;
}
if (Array.isArray(tx)) {
tx.forEach(deleteCoins);
return;
}
tx.inputs.forEach(function(input) {
input.coin = null;
});
}
it('should open chain and miner', function(cb) {
miner.open(cb);
});
it('should open walletdb', function(cb) {
walletdb.open(function(err) {
assert.ifError(err);
walletdb.create({}, function(err, w) {
assert.ifError(err);
wallet = w;
miner.address = wallet.getAddress();
cb();
});
});
});
it('should mine a block', function(cb) {
miner.mineBlock(function(err, block) {
assert.ifError(err);
assert(block);
cb();
});
});
it('should mine competing chains', function(cb) {
utils.forRangeSerial(0, 10, function(i, next) {
mineBlock(ch1, cb1, function(err, chain1) {
assert.ifError(err);
cb1 = chain1.txs[0];
mineBlock(ch2, cb2, function(err, chain2) {
assert.ifError(err);
cb2 = chain2.txs[0];
deleteCoins(chain1);
chain.add(chain1, function(err) {
assert.ifError(err);
deleteCoins(chain2);
chain.add(chain2, function(err) {
assert.ifError(err);
assert(chain.tip.hash === chain1.hash('hex'));
competingTip = chain2.hash('hex');
chain.db.get(chain1.hash('hex'), function(err, entry1) {
assert.ifError(err);
chain.db.get(chain2.hash('hex'), function(err, entry2) {
assert.ifError(err);
assert(entry1);
assert(entry2);
ch1 = entry1;
ch2 = entry2;
chain.db.isMainChain(chain2.hash('hex'), function(err, result) {
assert.ifError(err);
assert(!result);
next();
});
});
});
});
});
});
});
}, cb);
});
it('should handle a reorg', function(cb) {
oldTip = chain.tip;
chain.db.get(competingTip, function(err, entry) {
assert.ifError(err);
assert(entry);
assert(chain.height === entry.height);
chain.tip = entry;
miner.mineBlock(function(err, reorg) {
assert.ifError(err);
assert(reorg);
chain.tip = oldTip;
var forked = false;
chain.once('reorganize', function() {
forked = true;
});
deleteCoins(reorg);
chain.add(reorg, function(err) {
assert.ifError(err);
assert(forked);
assert(chain.tip.hash === reorg.hash('hex'));
assert(chain.tip.chainwork.cmp(oldTip.chainwork) > 0);
cb();
});
});
});
});
it('should check main chain', function(cb) {
chain.db.isMainChain(oldTip, function(err, result) {
assert.ifError(err);
assert(!result);
cb();
});
});
it('should mine a block after a reorg', function(cb) {
mineBlock(null, cb2, function(err, block) {
assert.ifError(err);
deleteCoins(block);
chain.add(block, function(err) {
assert.ifError(err);
chain.db.get(block.hash('hex'), function(err, entry) {
assert.ifError(err);
assert(entry);
assert(chain.tip.hash === entry.hash);
chain.db.isMainChain(entry.hash, function(err, result) {
assert.ifError(err);
assert(result);
cb();
});
});
});
});
});
it('should fail to mine a block with coins on an alternate chain', function(cb) {
mineBlock(null, cb1, function(err, block) {
assert.ifError(err);
deleteCoins(block);
chain.add(block, function(err) {
assert(err);
cb();
});
});
});
it('should get coin', function(cb) {
mineBlock(null, null, function(err, block) {
assert.ifError(err);
chain.add(block, function(err) {
assert.ifError(err);
mineBlock(null, block.txs[0], function(err, block) {
assert.ifError(err);
chain.add(block, function(err) {
assert.ifError(err);
chain.db.getCoin(block.txs[1].hash('hex'), 1, function(err, coin) {
assert.ifError(err);
assert.deepEqual(coin.toRaw(), bcoin.coin.fromTX(block.txs[1], 1).toRaw());
cb();
});
});
});
});
});
});
it('should rescan for transactions', function(cb) {
var txs = [];
walletdb.getAddresses(function(err, hashes) {
assert.ifError(err);
chain.db.scan(null, hashes, function(tx, block, next) {
txs.push(tx);
next();
}, function(err) {
assert.ifError(err);
assert.equal(txs.length, 25);
cb();
});
});
});
it('should cleanup', function(cb) {
constants.tx.COINBASE_MATURITY = 100;
cb();
});
});