diff --git a/example/node.js b/example/node.js index 3ab2d5a4..446dbb0c 100644 --- a/example/node.js +++ b/example/node.js @@ -4,22 +4,22 @@ var BitcoindJS = require('..'); var BitcoinNode = BitcoindJS.Node; var chainlib = require('chainlib'); var log = chainlib.log; -//log.debug = function() {}; +log.debug = function() {}; var configuration = { - datadir: process.env.BITCOINDJS_DIR || '~/.bitcoin', - network: 'testnet' + datadir: process.env.BITCOINDJS_DIR || '~/.bitcoin' }; var node = new BitcoinNode(configuration); -var startHeight; -var count = 100; -var times = new Array(count); +var count = 0; +var interval; node.on('ready', function() { - times[node.chain.tip.__height % count] = Date.now(); - startHeight = node.chain.tip.__height; + interval = setInterval(function() { + log.info('Sync Status: Tip:', node.chain.tip.hash, 'Height:', node.chain.tip.__height, 'Rate:', count/10, 'blocks per second'); + count = 0; + }, 10000); }); node.on('error', function(err) { @@ -27,13 +27,5 @@ node.on('error', function(err) { }); node.chain.on('addblock', function(block) { - console.log('New Best Tip:', block.hash); - var startTime = times[node.chain.tip.__height % count]; - - if(startTime) { - var timeElapsed = (Date.now() - startTime) / 1000; - console.log(Math.round(count / timeElapsed) + ' blocks per second'); - } - - times[node.chain.tip.__height % count] = Date.now(); + count++; }); diff --git a/integration/regtest.js b/integration/regtest.js index d71010b4..9ee7bae6 100644 --- a/integration/regtest.js +++ b/integration/regtest.js @@ -12,6 +12,7 @@ if (process.env.BITCOINDJS_ENV !== 'test') { var chai = require('chai'); var bitcore = require('bitcore'); +var BN = bitcore.crypto.BN; var rimraf = require('rimraf'); var bitcoind; @@ -21,7 +22,7 @@ var assert = chai.assert; var sinon = require('sinon'); var BitcoinRPC = require('bitcoind-rpc'); var blockHashes = []; -var unspentTransactions = []; +var utxo; var coinbasePrivateKey; var privateKey = bitcore.PrivateKey(); var destKey = bitcore.PrivateKey(); @@ -118,9 +119,22 @@ describe('Daemon Binding Functionality', function() { throw err; } - var tx = bitcore.Transaction(); - tx.fromString(response.result.hex); - unspentTransactions.push(tx); + var unspentTransaction = bitcore.Transaction(); + var outputIndex; + unspentTransaction.fromString(response.result.hex); + for (var i = 0; i < unspentTransaction.outputs.length; i++) { + var output = unspentTransaction.outputs[i]; + if (output.script.toAddress(network).toString() === address.toString(network)) { + outputIndex = i; + } + } + + utxo = { + txid: unspentTransaction.hash, + outputIndex: outputIndex, + script: unspentTransaction.outputs[outputIndex].script, + satoshis: unspentTransaction.outputs[outputIndex].satoshis + }; // Include this transaction in a block so that it can // be spent in tests @@ -150,7 +164,7 @@ describe('Daemon Binding Functionality', function() { describe('mempool functionality', function() { var fromAddress = 'mszYqVnqKoQx4jcTdJXxwKAissE3Jbrrc1'; - var utxo = { + var utxo1 = { address: fromAddress, txId: 'a477af6b2667c29670467e4e0728b685ee07b240235771862318e29ddbe58458', outputIndex: 0, @@ -160,14 +174,14 @@ describe('Daemon Binding Functionality', function() { var toAddress = 'mrU9pEmAx26HcbKVrABvgL7AwA5fjNFoDc'; var changeAddress = 'mgBCJAsvzgT2qNNeXsoECg2uPKrUsZ76up'; var changeAddressP2SH = '2N7T3TAetJrSCruQ39aNrJvYLhG1LJosujf'; - var privateKey = 'cSBnVM4xvxarwGQuAfQFwqDg9k5tErHUHzgWsEfD4zdwUasvqRVY'; + var privateKey1 = 'cSBnVM4xvxarwGQuAfQFwqDg9k5tErHUHzgWsEfD4zdwUasvqRVY'; var private1 = '6ce7e97e317d2af16c33db0b9270ec047a91bff3eff8558afb5014afb2bb5976'; var private2 = 'c9b26b0f771a0d2dad88a44de90f05f416b3b385ff1d989343005546a0032890'; var tx = new bitcore.Transaction(); - tx.from(utxo); + tx.from(utxo1); tx.to(toAddress, 50000); tx.change(changeAddress); - tx.sign(privateKey); + tx.sign(privateKey1); it('will add an unchecked transaction', function() { var added = bitcoind.addMempoolUncheckedTransaction(tx.serialize()); @@ -233,19 +247,26 @@ describe('Daemon Binding Functionality', function() { }); }); + describe('get block index', function() { + var expectedWork = new BN(6); + [1,2,3,4,5,6,7,8,9].forEach(function(i) { + it('generate block ' + i, function() { + var blockIndex = bitcoind.getBlockIndex(blockHashes[i]); + should.exist(blockIndex); + should.exist(blockIndex.chainWork); + var work = new BN(blockIndex.chainWork, 'hex'); + work.cmp(expectedWork).should.equal(0); + expectedWork = expectedWork.add(new BN(2)); + should.exist(blockIndex.prevHash); + blockIndex.prevHash.should.equal(blockHashes[i - 1]); + }); + }); + }); + describe('send transaction functionality', function() { it('will not error and return the transaction hash', function() { - var unspentTx = unspentTransactions.shift(); - - var utxo = { - txid: unspentTx.hash, - outputIndex: 1, - script: unspentTx.outputs[1].script, - satoshis: unspentTx.outputs[1].satoshis - }; - // create and sign the transaction var tx = bitcore.Transaction(); tx.from(utxo); diff --git a/lib/chain.js b/lib/chain.js index b8cb942d..f9e7c200 100644 --- a/lib/chain.js +++ b/lib/chain.js @@ -85,13 +85,14 @@ Chain.prototype.buildGenesisBlock = function buildGenesisBlock(options) { Chain.prototype.getWeight = function getWeight(blockHash, callback) { var self = this; + var blockIndex = self.db.bitcoind.getBlockIndex(blockHash); setImmediate(function() { - var weight = self.db.bitcoind.getChainWork(blockHash); - if(weight === undefined) { + if (blockIndex) { + callback(null, new BN(blockIndex.chainWork, 'hex')); + } else { return callback(new Error('Weight not found for ' + blockHash)); } - callback(null, new BN(weight, 'hex')); }); }; diff --git a/lib/daemon.js b/lib/daemon.js index e5487e40..b2aaa3f2 100644 --- a/lib/daemon.js +++ b/lib/daemon.js @@ -340,8 +340,8 @@ Daemon.prototype.isSpent = function(txid, outputIndex) { return bitcoindjs.isSpent(txid, outputIndex); }; -Daemon.prototype.getChainWork = function(blockHash) { - return bitcoindjs.getChainWork(blockHash); +Daemon.prototype.getBlockIndex = function(blockHash) { + return bitcoindjs.getBlockIndex(blockHash); }; Daemon.prototype.sendTransaction = function(transaction, allowAbsurdFees) { diff --git a/lib/db.js b/lib/db.js index de7939fc..f950ca78 100644 --- a/lib/db.js +++ b/lib/db.js @@ -46,10 +46,20 @@ DB.prototype.getBlock = function(hash, callback) { }); }; +DB.prototype.getPrevHash = function(blockHash, callback) { + var blockIndex = this.bitcoind.getBlockIndex(blockHash); + setImmediate(function() { + if (blockIndex) { + callback(null, blockIndex.prevHash); + } else { + callback(new Error('Could not get prevHash, block not found')); + } + }); +}; + DB.prototype.putBlock = function(block, callback) { - // block is already stored in bitcoind, but we need to update - // our prevhash index still - this._updatePrevHashIndex(block, callback); + // block is already stored in bitcoind + setImmediate(callback); }; DB.prototype.getTransaction = function(txid, queryMempool, callback) { @@ -67,6 +77,11 @@ DB.prototype.validateBlockData = function(block, callback) { setImmediate(callback); }; +DB.prototype._updatePrevHashIndex = function(block, callback) { + // bitcoind has the previous hash for each block + setImmediate(callback); +}; + DB.prototype._updateWeight = function(hash, weight, callback) { // bitcoind has all work for each block setImmediate(callback); diff --git a/lib/node.js b/lib/node.js index 1c0ef8dd..831c2a8f 100644 --- a/lib/node.js +++ b/lib/node.js @@ -209,7 +209,7 @@ Node.prototype._loadConsensus = function(config) { var genesisBlock; if (config.genesis) { genesisBlock = config.genesis; - } else if (config.testnet) { + } else if (config.network === 'testnet') { genesisBlock = genesis.testnet; } else { genesisBlock = genesis.livenet; diff --git a/src/bitcoindjs.cc b/src/bitcoindjs.cc index c569d54b..454c61e5 100644 --- a/src/bitcoindjs.cc +++ b/src/bitcoindjs.cc @@ -980,12 +980,14 @@ NAN_METHOD(IsSpent) { }; /** - * GetChainWork() - * bitcoindjs.getChainWork() - * Get the total amount of work (expected number of hashes) in the chain up to - * and including this block. + * GetBlockIndex() + * bitcoindjs.getBlockIndex() + * Get index information about a block by hash including: + * - the total amount of work (expected number of hashes) in the chain up to + * and including this block. + * - the previous hash of the block */ -NAN_METHOD(GetChainWork) { +NAN_METHOD(GetBlockIndex) { Isolate* isolate = Isolate::GetCurrent(); HandleScope scope(isolate); @@ -1000,7 +1002,15 @@ NAN_METHOD(GetChainWork) { } else { blockIndex = mapBlockIndex[hash]; arith_uint256 cw = blockIndex->nChainWork; - NanReturnValue(Local::New(isolate, NanNew(cw.GetHex()))); + CBlockIndex* prevBlockIndex = blockIndex->pprev; + const uint256* prevHash = prevBlockIndex->phashBlock; + + Local obj = NanNew(); + + obj->Set(NanNew("chainWork"), NanNew(cw.GetHex())); + obj->Set(NanNew("prevHash"), NanNew(prevHash->GetHex())); + + NanReturnValue(obj); } }; @@ -1229,7 +1239,7 @@ init(Handle target) { NODE_SET_METHOD(target, "getTransaction", GetTransaction); NODE_SET_METHOD(target, "getInfo", GetInfo); NODE_SET_METHOD(target, "isSpent", IsSpent); - NODE_SET_METHOD(target, "getChainWork", GetChainWork); + NODE_SET_METHOD(target, "getBlockIndex", GetBlockIndex); NODE_SET_METHOD(target, "getMempoolOutputs", GetMempoolOutputs); NODE_SET_METHOD(target, "addMempoolUncheckedTransaction", AddMempoolUncheckedTransaction); NODE_SET_METHOD(target, "verifyScript", VerifyScript); diff --git a/src/bitcoindjs.h b/src/bitcoindjs.h index 050d6830..c863e226 100644 --- a/src/bitcoindjs.h +++ b/src/bitcoindjs.h @@ -23,7 +23,7 @@ NAN_METHOD(GetBlock); NAN_METHOD(GetTransaction); NAN_METHOD(GetInfo); NAN_METHOD(IsSpent); -NAN_METHOD(GetChainWork); +NAN_METHOD(GetBlockIndex); NAN_METHOD(GetMempoolOutputs); NAN_METHOD(AddMempoolUncheckedTransaction); NAN_METHOD(VerifyScript); diff --git a/test/chain.unit.js b/test/chain.unit.js index 78ea3097..9d463005 100644 --- a/test/chain.unit.js +++ b/test/chain.unit.js @@ -93,20 +93,22 @@ describe('Bitcoin Chain', function() { var chain = new Chain(); chain.db = { bitcoind: { - getChainWork: sinon.stub().returns(work) + getBlockIndex: sinon.stub().returns({ + chainWork: work + }) } }; it('should give the weight as a BN', function(done) { chain.getWeight('hash', function(err, weight) { should.not.exist(err); - weight.toString(16).should.equal('5a7b3c42ea8b844374e9'); + weight.toString(16, 64).should.equal(work); done(); }); }); it('should give an error if the weight is undefined', function(done) { - chain.db.bitcoind.getChainWork = sinon.stub().returns(undefined); + chain.db.bitcoind.getBlockIndex = sinon.stub().returns(undefined); chain.getWeight('hash2', function(err, weight) { should.exist(err); done(); diff --git a/test/db.unit.js b/test/db.unit.js index d4334fb5..580f5292 100644 --- a/test/db.unit.js +++ b/test/db.unit.js @@ -42,12 +42,10 @@ describe('Bitcoin DB', function() { }); describe('#putBlock', function() { - it('should call _updatePrevHashIndex', function(done) { + it('should call callback', function(done) { var db = new DB({store: memdown}); - db._updatePrevHashIndex = sinon.stub().callsArg(1); db.putBlock('block', function(err) { should.not.exist(err); - db._updatePrevHashIndex.called.should.equal(true); done(); }); }); diff --git a/test/node.unit.js b/test/node.unit.js index 1660836e..f482f1dc 100644 --- a/test/node.unit.js +++ b/test/node.unit.js @@ -309,7 +309,7 @@ describe('Bitcoind Node', function() { }); it('should use the testnet genesis if testnet is specified', function() { var config = { - testnet: true + network: 'testnet' }; node._loadConsensus(config); should.exist(node.chain);