diff --git a/.travis.yml b/.travis.yml index 86d0f69a..52844c32 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,6 +9,7 @@ before_install: - git config --global user.name "BitPay, Inc." script: - _mocha -R spec integration/regtest.js + - _mocha -R spec integration/regtest-node.js - _mocha -R spec --recursive cache: directories: diff --git a/integration/data/bitcoin.conf b/integration/data/bitcoin.conf index 2ebd94ba..840c95a3 100644 --- a/integration/data/bitcoin.conf +++ b/integration/data/bitcoin.conf @@ -4,5 +4,3 @@ txindex=1 rpcallowip=127.0.0.1 rpcuser=bitcoin rpcpassword=local321 - - diff --git a/integration/regtest-node.js b/integration/regtest-node.js new file mode 100644 index 00000000..a12b7366 --- /dev/null +++ b/integration/regtest-node.js @@ -0,0 +1,157 @@ +'use strict'; + +// These tests require bitcoind.js Bitcoin Core bindings to be compiled with +// the environment variable BITCOINDJS_ENV=test. This enables the use of regtest +// functionality by including the wallet in the build. +// To run the tests: $ mocha -R spec integration/regtest.js + +var chainlib = require('chainlib'); +var log = chainlib.log; + +if (process.env.BITCORENODE_ENV !== 'test') { + log.info('Please set the environment variable BITCORENODE_ENV=test and make sure bindings are compiled for testing'); + process.exit(); +} + +var chai = require('chai'); +var bitcore = require('bitcore'); +var rimraf = require('rimraf'); +var node; + +var should = chai.should(); + +var BitcoinRPC = require('bitcoind-rpc'); +var BitcoreNode = require('..').Node; +var testWIF = 'cSdkPxkAjA4HDr5VHgsebAPDEh9Gyub4HK8UJr2DFGGqKKy4K5sG'; +var testKey; +var client; + +describe('Node Functionality', function() { + + before(function(done) { + this.timeout(30000); + + // Add the regtest network + bitcore.Networks.remove(bitcore.Networks.testnet); + bitcore.Networks.add({ + name: 'regtest', + alias: 'regtest', + pubkeyhash: 0x6f, + privatekey: 0xef, + scripthash: 0xc4, + xpubkey: 0x043587cf, + xprivkey: 0x04358394, + networkMagic: 0xfabfb5da, + port: 18444, + dnsSeeds: [ ] + }); + + var datadir = __dirname + '/data'; + + testKey = bitcore.PrivateKey(testWIF); + + rimraf(datadir + '/regtest', function(err) { + + if (err) { + throw err; + } + + var configuration = { + datadir: datadir, + network: 'regtest' + }; + + node = new BitcoreNode(configuration); + + node.on('error', function(err) { + log.error(err); + }); + + node.on('ready', function() { + + client = new BitcoinRPC({ + protocol: 'http', + host: '127.0.0.1', + port: 18332, + user: 'bitcoin', + pass: 'local321' + }); + + node.on('synced', function() { + //todo: refactor to remove the event listener + if (node.chain.tip.__height === 150) { + done(); + } + }); + + client.generate(150, function(err, response) { + if (err) { + throw err; + } + }); + }); + + + }); + }); + + after(function(done) { + this.timeout(20000); + node.db.bitcoind.stop(function(err, result) { + done(); + }); + }); + + describe('bitcoin core daemon reorgs', function() { + + before(function(done) { + client.getBlockCount(function(err, response) { + if (err) { + throw err; + } + var count = response.result; + client.getBlockHash(count, function(err, response) { + if (err) { + throw err; + } + var blockHash = response.result; + client.invalidateBlock(blockHash, function(err, response) { + if (err) { + throw err; + } + client.getBlockCount(function(err, response) { + if (err) { + throw err; + } + response.result.should.equal(count - 1); + done(); + }); + }); + }); + }); + }); + + it('will handle a reorganization', function(done) { + + node.db.bitcoind.on('tip', function(height) { + height.should.equal(151); + done(); + }); + + // We need to add a transaction to the mempool so that the next block will + // have a different hash as the hash has been invalidated. + client.sendToAddress(testKey.toAddress().toString(), 10, function(err) { + if (err) { + throw err; + } + client.generate(2, function(err, response) { + if (err) { + throw err; + } + }); + }); + + }); + }); + +}); diff --git a/lib/node.js b/lib/node.js index 3df1e614..cda20249 100644 --- a/lib/node.js +++ b/lib/node.js @@ -123,8 +123,6 @@ Node.prototype._syncBitcoindAncestor = function(block, done) { done(err); } - console.log('getHashes done'); - // Create a hash map for faster lookups var currentHashesMap = {}; var length = currentHashes.length; @@ -139,7 +137,6 @@ Node.prototype._syncBitcoindAncestor = function(block, done) { while(ancestorHash && !currentHashesMap[ancestorHash]) { var blockIndex = self.bitcoind.getBlockIndex(ancestorHash); - console.log(blockIndex); ancestorHash = blockIndex ? blockIndex.prevHash : null; } @@ -167,8 +164,9 @@ Node.prototype._syncBitcoindRewind = function(block, done) { var self = this; self._syncBitcoindAncestor(block, function(err, ancestorHash) { - console.log('ancestorHash', ancestorHash); - + if (err) { + return done(err); + } // Rewind the chain to the common ancestor async.whilst( function() { @@ -196,7 +194,6 @@ Node.prototype._syncBitcoindRewind = function(block, done) { self.chain.tip = previousTip; self.chain.saveMetadata(); self.chain.emit('removeblock', tip); - console.log('removeblock', tip.hash); removeDone(); }); @@ -216,8 +213,6 @@ Node.prototype._syncBitcoindRewind = function(block, done) { Node.prototype._syncBitcoind = function() { var self = this; - console.log('_syncBitcoind'); - if (self.bitcoindSyncing) { return; } @@ -232,8 +227,6 @@ Node.prototype._syncBitcoind = function() { var height; async.whilst(function() { - console.log('height', self.chain.tip.__height); - console.log('bitcoindHeight', self.bitcoindHeight); height = self.chain.tip.__height; return height < self.bitcoindHeight && !self.stopping; }, function(done) { @@ -243,6 +236,7 @@ Node.prototype._syncBitcoind = function() { } var block = self.Block.fromBuffer(blockBuffer); + if (block.prevHash === self.chain.tip.hash) { // This block appends to the current chain tip and we can @@ -374,6 +368,10 @@ Node.prototype._loadConsensus = function(config) { genesisBlock = config.genesis; } else if (config.network === 'testnet') { genesisBlock = genesis.testnet; + } else if (config.network === 'regtest') { + // initializeBitcoind will set the genesis block later in the chain + // todo: we should be able to refactor since the genesis is set later + genesisBlock = null; } else { genesisBlock = genesis.livenet; } @@ -404,6 +402,17 @@ Node.prototype._initialize = function() { // Set the current chain height var info = self.bitcoind.getInfo(); self.bitcoindHeight = info.blocks; + + // Get the genesis block + self.bitcoind.getBlock(0, function(err, block) { + self.chain.genesis = self.Block.fromBuffer(block); + self.db.initialize(); + }); + + }); + + this.bitcoind.on('open', function(status) { + log.info('Bitcoin Core Daemon Status:', status); }); // Notify that there is a new tip