diff --git a/README.md b/README.md index e1b0b1e6..75fb957d 100644 --- a/README.md +++ b/README.md @@ -293,7 +293,7 @@ inherits(MyModule, Node.Module); * @param {Function} callback - call with the leveldb database operations to perform */ MyModule.prototype.blockHandler = function(block, add, callback) { - var transactions = this.db.getTransactionsFromBlock(block); + var transactions = block.transactions; // loop through transactions and outputs // call the callback with leveldb database operations var operations = []; @@ -353,7 +353,7 @@ MyModule.prototype.subscribeCustom = function(emitter, param) { MyModule.prototype.getData = function(arg1, callback) { // You can query the data by reading from the leveldb store on db - this.db.store.get(arg1, callback); + this.node.db.store.get(arg1, callback); }; module.exports = MyModule; diff --git a/index.js b/index.js index 7129c70e..ec446e0c 100644 --- a/index.js +++ b/index.js @@ -3,7 +3,6 @@ module.exports = require('./lib'); module.exports.daemon = require('./lib/daemon'); module.exports.Node = require('./lib/node'); -module.exports.Block = require('./lib/block'); module.exports.Chain = require('./lib/chain'); module.exports.DB = require('./lib/db'); module.exports.Transaction = require('./lib/transaction'); diff --git a/integration/regtest-node.js b/integration/regtest-node.js index b7f580f3..ebd94108 100644 --- a/integration/regtest-node.js +++ b/integration/regtest-node.js @@ -102,7 +102,7 @@ describe('Node Functionality', function() { after(function(done) { this.timeout(20000); - node.db.bitcoind.stop(function(err, result) { + node.bitcoind.stop(function(err, result) { done(); }); }); diff --git a/lib/block.js b/lib/block.js deleted file mode 100644 index 7a5b39a6..00000000 --- a/lib/block.js +++ /dev/null @@ -1,168 +0,0 @@ -'use strict'; - -var bitcore = require('bitcore'); -var BufferReader = bitcore.encoding.BufferReader; -var BufferWriter = bitcore.encoding.BufferWriter; -var Hash = bitcore.crypto.Hash; - -//TODO: use bitcore.Block - -function Block(obj) { - /* jshint maxstatements: 25 */ - if (!(this instanceof Block)) { - return new Block(obj); - } - - this.version = obj.version || 1; - this.prevHash = obj.prevHash; - - if (!obj.hasOwnProperty('prevHash')) { - throw new TypeError('"prevHash" is expected'); - } - if (!obj.timestamp) { - throw new TypeError('"timestamp" is expected'); - } - this.timestamp = obj.timestamp; - if (typeof this.timestamp === 'string') { - this.timestamp = new Date(obj.timestamp); - } - - this.merkleRoot = obj.merkleRoot; - - if (obj.data) { - if (!Buffer.isBuffer(obj.data)) { - throw new TypeError('"data" is expected to be a buffer'); - } - this.data = obj.data; - } else { - this.data = new Buffer(0); - } - - var hashProperty = { - configurable: false, - enumerable: true, - get: function() { - return this.getHash(); - }, - set: function() {} - }; - - Object.defineProperty(this, 'hash', hashProperty); - - this.bits = obj.bits; - this.nonce = obj.nonce || 0; - - return this; -} - -Block.fromBuffer = function(buffer) { - var br = new BufferReader(buffer); - return Block.fromBufferReader(br); -}; - -Block.fromBufferReader = function(br) { - var obj = {}; - obj.version = br.readUInt32LE(); - obj.prevHash = BufferReader(br.read(32)).readReverse().toString('hex'); - var nullHash = new Buffer(Array(32)).toString('hex'); - if (obj.prevHash === nullHash) { - obj.prevHash = null; - } - obj.merkleRoot = BufferReader(br.read(32)).readReverse().toString('hex'); - var timestamp = br.readUInt32LE(); - obj.timestamp = new Date(timestamp * 1000); - obj.bits = br.readUInt32LE(); - obj.nonce = br.readUInt32LE(); - obj.data = br.readAll(); - return new Block(obj); -}; - -Block.prototype.validate = function(chain, callback) { - // bitcoind does all validation - setImmediate(callback); -}; - -Block.prototype.headerToBuffer = function() { - var bw = new BufferWriter(); - this.headerToBufferWriter(bw); - return bw.concat(); -}; - -Block.prototype.headerToBufferWriter = function(bw) { - /* jshint maxstatements: 20 */ - - // version - bw.writeUInt32LE(this.version); - - // prevhash - if (!this.prevHash) { - bw.write(new Buffer(Array(32))); - } else { - var prevHashBuffer = new Buffer(this.prevHash, 'hex'); - prevHashBuffer = BufferReader(prevHashBuffer).readReverse(); - if (prevHashBuffer.length !== 32) { - throw new Error('"prevHash" is expected to be 32 bytes'); - } - bw.write(prevHashBuffer); - } - - // merkleroot - if (!this.merkleRoot) { - bw.write(new Buffer(Array(32))); - } else { - var merkleRoot = new Buffer(this.merkleRoot, 'hex'); - merkleRoot = BufferReader(merkleRoot).readReverse(); - if (merkleRoot.length !== 32) { - throw new Error('"merkleRoot" is expected to be 32 bytes'); - } - bw.write(merkleRoot); - } - - // timestamp - bw.writeUInt32LE(Math.floor(this.timestamp.getTime() / 1000)); - - // bits - bw.writeUInt32LE(this.bits); - - // nonce - bw.writeUInt32LE(this.nonce); - - return bw; - -}; - -Block.prototype.toObject = Block.prototype.toJSON = function() { - return { - hash: this.hash, - version: this.version, - prevHash: this.prevHash, - merkleRoot: this.merkleRoot, - timestamp: this.timestamp.toISOString(), - bits: this.bits, - nonce: this.nonce, - data: this.data.toString('hex') - }; -}; - -Block.prototype.toBufferWriter = function(bw) { - // header - this.headerToBufferWriter(bw); - - // transaction data - bw.write(this.data); - return bw; -}; - -Block.prototype.toBuffer = function() { - var bw = new BufferWriter(); - this.toBufferWriter(bw); - return bw.concat(); -}; - -Block.prototype.getHash = function() { - var hashBuffer = BufferReader(Hash.sha256sha256(this.headerToBuffer())).readReverse(); - var hash = hashBuffer.toString('hex'); - return hash; -}; - -module.exports = Block; diff --git a/lib/chain.js b/lib/chain.js index 1daf29b6..66a47777 100644 --- a/lib/chain.js +++ b/lib/chain.js @@ -5,7 +5,7 @@ var EventEmitter = require('events').EventEmitter; var bitcore = require('bitcore'); var BN = bitcore.crypto.BN; var $ = bitcore.util.preconditions; -var Block = require('./block'); +var Block = bitcore.Block; var index = require('./index'); var log = index.log; var utils = require('./utils'); @@ -34,9 +34,6 @@ function Chain(opts) { opts = {}; } - this.db = opts.db; - this.p2p = opts.p2p; - this.genesis = opts.genesis; this.genesisOptions = opts.genesisOptions; this.genesisWeight = new BN(0); @@ -102,18 +99,18 @@ Chain.prototype.initialize = function() { var self = this; // Does our database already have a tip? - self.db.getMetadata(function getMetadataCallback(err, metadata) { + self.node.db.getMetadata(function getMetadataCallback(err, metadata) { if(err) { return self.emit('error', err); } else if(!metadata || !metadata.tip) { self.tip = self.genesis; self.tip.__height = 0; self.tip.__weight = self.genesisWeight; - self.db.putBlock(self.genesis, function putBlockCallback(err) { + self.node.db.putBlock(self.genesis, function putBlockCallback(err) { if(err) { return self.emit('error', err); } - self.db._onChainAddBlock(self.genesis, function(err) { + self.node.db._onChainAddBlock(self.genesis, function(err) { if(err) { return self.emit('error', err); } @@ -125,7 +122,7 @@ Chain.prototype.initialize = function() { }); } else { metadata.tip = metadata.tip; - self.db.getBlock(metadata.tip, function getBlockCallback(err, tip) { + self.node.db.getBlock(metadata.tip, function getBlockCallback(err, tip) { if(err) { return self.emit('error', err); } @@ -144,13 +141,6 @@ Chain.prototype.stop = function(callback) { setImmediate(callback); }; -Chain.prototype._writeBlock = function(block, callback) { - // Update hashes - this.cache.hashes[block.hash] = block.prevHash; - // call db.putBlock to update prevHash index, but it won't write the block to disk - this.db.putBlock(block, callback); -}; - Chain.prototype._validateBlock = function(block, callback) { // All validation is done by bitcoind setImmediate(callback); @@ -162,7 +152,7 @@ Chain.prototype.startBuilder = function() { Chain.prototype.getWeight = function getWeight(blockHash, callback) { var self = this; - var blockIndex = self.db.bitcoind.getBlockIndex(blockHash); + var blockIndex = self.node.bitcoind.getBlockIndex(blockHash); setImmediate(function() { if (blockIndex) { @@ -225,7 +215,7 @@ Chain.prototype.getHashes = function getHashes(tipHash, callback) { } } else { // do a db call if we don't have it - self.db.getPrevHash(hash, function(err, prevHash) { + self.node.db.getPrevHash(hash, function(err, prevHash) { if(err) { return callback(err); } @@ -256,7 +246,7 @@ Chain.prototype.saveMetadata = function saveMetadata(callback) { self.lastSavedMetadata = new Date(); - self.db.putMetadata(metadata, callback); + self.node.db.putMetadata(metadata, callback); }; module.exports = Chain; diff --git a/lib/db.js b/lib/db.js index 723ebf13..acbbe019 100644 --- a/lib/db.js +++ b/lib/db.js @@ -6,9 +6,8 @@ var async = require('async'); var levelup = require('levelup'); var leveldown = require('leveldown'); var bitcore = require('bitcore'); +var Block = bitcore.Block; var $ = bitcore.util.preconditions; -var BufferReader = bitcore.encoding.BufferReader; -var BufferWriter = bitcore.encoding.BufferWriter; var index = require('./'); var errors = index.errors; var log = index.log; @@ -38,8 +37,6 @@ function DB(options) { } this.store = levelup(options.path, { db: levelupStore }); - this.chain = options.chain; - this.Block = options.Block || require('./block'); this.txPrefix = options.txPrefix || DB.PREFIXES.TX; this.prevHashPrefix = options.prevHashPrefix || DB.PREFIXES.PREV_HASH; this.blockPrefix = options.blockPrefix || DB.PREFIXES.BLOCK; @@ -88,7 +85,7 @@ DB.prototype.start = function(callback) { this.addModule(this._modules[i]); } } - this.bitcoind.on('tx', this.transactionHandler.bind(this)); + this.node.bitcoind.on('tx', this.transactionHandler.bind(this)); this.emit('ready'); setImmediate(callback); }; @@ -102,16 +99,16 @@ DB.prototype.getBlock = function(hash, callback) { var self = this; // get block from bitcoind - this.bitcoind.getBlock(hash, function(err, blockData) { + this.node.bitcoind.getBlock(hash, function(err, blockData) { if(err) { return callback(err); } - callback(null, self.Block.fromBuffer(blockData)); + callback(null, Block.fromBuffer(blockData)); }); }; DB.prototype.getPrevHash = function(blockHash, callback) { - var blockIndex = this.bitcoind.getBlockIndex(blockHash); + var blockIndex = this.node.bitcoind.getBlockIndex(blockHash); setImmediate(function() { if (blockIndex) { callback(null, blockIndex.prevHash); @@ -127,7 +124,7 @@ DB.prototype.putBlock = function(block, callback) { }; DB.prototype.getTransaction = function(txid, queryMempool, callback) { - this.bitcoind.getTransaction(txid, queryMempool, function(err, txBuffer) { + this.node.bitcoind.getTransaction(txid, queryMempool, function(err, txBuffer) { if(err) { return callback(err); } @@ -140,7 +137,7 @@ DB.prototype.getTransaction = function(txid, queryMempool, callback) { }; DB.prototype.getTransactionWithBlockInfo = function(txid, queryMempool, callback) { - this.bitcoind.getTransactionWithBlockInfo(txid, queryMempool, function(err, obj) { + this.node.bitcoind.getTransactionWithBlockInfo(txid, queryMempool, function(err, obj) { if(err) { return callback(err); } @@ -160,7 +157,7 @@ DB.prototype.sendTransaction = function(tx, callback) { $.checkArgument(typeof tx === 'string', 'Argument must be a hex string or Transaction'); try { - var txid = this.bitcoind.sendTransaction(tx); + var txid = this.node.bitcoind.sendTransaction(tx); return callback(null, txid); } catch(err) { return callback(err); @@ -171,7 +168,7 @@ DB.prototype.estimateFee = function(blocks, callback) { var self = this; setImmediate(function() { - callback(null, self.bitcoind.estimateFee(blocks)); + callback(null, self.node.bitcoind.estimateFee(blocks)); }); }; @@ -232,43 +229,6 @@ DB.prototype.close = function(callback) { this.store.close(callback); }; -DB.prototype.addTransactionsToBlock = function addTransactionsToBlock(block, transactions) { - var txs = this.getTransactionsFromBlock(block); - txs = txs.concat(transactions); - var txsBuffer = this.Transaction.manyToBuffer(txs); - var bw = new BufferWriter(); - bw.writeVarintNum(txs.length); - bw.write(txsBuffer); - block.merkleRoot = this.getMerkleRoot(txs); - block.data = bw.concat(); - block.__transactions = txs; -}; - -DB.prototype.getTransactionsFromBlock = function getTransactionsFromBlock(block) { - var self = this; - if (block.data.length === 0) { - return []; - } else if(block.__transactions) { - return block.__transactions; - } - - var br = new BufferReader(block.data); - var count = br.readVarintNum(); - var transactions = []; - for (var i = 0; i < count; i++) { - var tx; - if (self.Transaction.prototype.fromBufferReader) { - tx = self.Transaction().fromBufferReader(br); - } else { - tx = self.Transaction.fromBufferReader(br); - } - transactions.push(tx); - } - - block.__transactions = transactions; - return transactions; -}; - DB.prototype.getOutputTotal = function(transactions, excludeCoinbase) { var totals = transactions.map(function(tx) { if(tx.isCoinbase() && excludeCoinbase) { @@ -375,7 +335,7 @@ DB.prototype.getPublishEvents = function() { DB.prototype.addModule = function(Module) { var module = new Module({ - db: this + node: this.node }); $.checkArgumentType(module, BaseModule); this.modules.push(module); diff --git a/lib/module.js b/lib/module.js index 0f15fb3b..82471327 100644 --- a/lib/module.js +++ b/lib/module.js @@ -1,7 +1,7 @@ 'use strict'; var Module = function(options) { - this.db = options.db; + this.node = options.node; }; /** diff --git a/lib/modules/address.js b/lib/modules/address.js index 73a3899c..c333c6eb 100644 --- a/lib/modules/address.js +++ b/lib/modules/address.js @@ -21,7 +21,7 @@ var AddressModule = function(options) { this.subscriptions['address/transaction'] = {}; this.subscriptions['address/balance'] = {}; - this.db.bitcoind.on('tx', this.transactionHandler.bind(this)); + this.node.bitcoind.on('tx', this.transactionHandler.bind(this)); }; @@ -77,10 +77,10 @@ AddressModule.prototype.transactionOutputHandler = function(messages, tx, output } // Find the address for the output - var address = script.toAddress(this.db.network); + var address = script.toAddress(this.node.db.network); if (!address && script.isPublicKeyOut()) { var pubkey = script.chunks[0].buf; - address = Address.fromPublicKey(new PublicKey(pubkey), this.db.network); + address = Address.fromPublicKey(new PublicKey(pubkey), this.node.db.network); } else if (!address){ return; } @@ -126,7 +126,7 @@ AddressModule.prototype.transactionHandler = function(txInfo) { }; AddressModule.prototype.blockHandler = function(block, addOutput, callback) { - var txs = this.db.getTransactionsFromBlock(block); + var txs = block.transactions; var action = 'put'; if (!addOutput) { @@ -157,16 +157,17 @@ AddressModule.prototype.blockHandler = function(block, addOutput, callback) { continue; } - var address = script.toAddress(this.db.network); + var address = script.toAddress(this.node.db.network); if (!address && script.isPublicKeyOut()) { var pubkey = script.chunks[0].buf; - address = Address.fromPublicKey(new PublicKey(pubkey), this.db.network); + address = Address.fromPublicKey(new PublicKey(pubkey), this.node.db.network); } else if (!address){ continue; } var outputIndex = j; - var timestamp = block.timestamp.getTime(); + // TODO: expose block timestamp as a date object in bitcore? + var timestamp = block.header.timestamp; var height = block.__height; var addressStr = address.toString(); @@ -190,7 +191,7 @@ AddressModule.prototype.blockHandler = function(block, addOutput, callback) { height: block.__height, outputIndexes: [outputIndex], address: addressStr, - timestamp: block.timestamp + timestamp: block.header.timestamp }; } @@ -323,7 +324,7 @@ AddressModule.prototype.getOutputs = function(addressStr, queryMempool, callback var outputs = []; var key = [AddressModule.PREFIXES.OUTPUTS, addressStr].join('-'); - var stream = this.db.store.createReadStream({ + var stream = this.node.db.store.createReadStream({ start: key, end: key + '~' }); @@ -341,7 +342,7 @@ AddressModule.prototype.getOutputs = function(addressStr, queryMempool, callback satoshis: Number(value[0]), script: value[1], blockHeight: Number(value[2]), - confirmations: self.db.chain.tip.__height - Number(value[2]) + 1 + confirmations: self.node.chain.tip.__height - Number(value[2]) + 1 }; outputs.push(output); @@ -362,7 +363,7 @@ AddressModule.prototype.getOutputs = function(addressStr, queryMempool, callback } if(queryMempool) { - outputs = outputs.concat(self.db.bitcoind.getMempoolOutputs(addressStr)); + outputs = outputs.concat(self.node.bitcoind.getMempoolOutputs(addressStr)); } callback(null, outputs); @@ -429,7 +430,7 @@ AddressModule.prototype.isSpent = function(output, queryMempool, callback) { var txid = output.prevTxId ? output.prevTxId.toString('hex') : output.txid; setImmediate(function() { - callback(self.db.bitcoind.isSpent(txid, output.outputIndex)); + callback(self.node.bitcoind.isSpent(txid, output.outputIndex)); }); }; @@ -437,7 +438,7 @@ AddressModule.prototype.getSpendInfoForOutput = function(txid, outputIndex, call var self = this; var key = [AddressModule.PREFIXES.SPENTS, txid, outputIndex].join('-'); - this.db.store.get(key, function(err, value) { + this.node.db.store.get(key, function(err, value) { if(err) { return callback(err); } @@ -486,19 +487,19 @@ AddressModule.prototype.getAddressHistoryForAddress = function(address, queryMem return callback(null, txinfos[txid]); } - self.db.getTransactionWithBlockInfo(txid, queryMempool, function(err, transaction) { + self.node.db.getTransactionWithBlockInfo(txid, queryMempool, function(err, transaction) { if(err) { return callback(err); } - transaction.populateInputs(self.db, [], function(err) { + transaction.populateInputs(self.node.db, [], function(err) { if(err) { return callback(err); } var confirmations = 0; if(transaction.__height >= 0) { - confirmations = self.db.chain.tip.__height - transaction.__height; + confirmations = self.node.chain.tip.__height - transaction.__height; } txinfos[transaction.hash] = { diff --git a/lib/node.js b/lib/node.js index f44dc616..7f39ab9b 100644 --- a/lib/node.js +++ b/lib/node.js @@ -6,11 +6,12 @@ var EventEmitter = require('events').EventEmitter; var async = require('async'); var mkdirp = require('mkdirp'); var bitcore = require('bitcore'); +var BufferUtil = bitcore.util.buffer; var Networks = bitcore.Networks; var _ = bitcore.deps._; var $ = bitcore.util.preconditions; +var Block = bitcore.Block; var Chain = require('./chain'); -var Block = require('./block'); var DB = require('./db'); var index = require('./'); var log = index.log; @@ -24,7 +25,6 @@ function Node(config) { this.db = null; this.chain = null; - this.p2p = null; this.network = null; this._loadConfiguration(config); @@ -136,7 +136,8 @@ Node.prototype._syncBitcoindAncestor = function(block, done) { currentHashesMap[currentHashes[i]] = true; } - var ancestorHash = block.prevHash; + // TODO: expose prevHash as a string from bitcore + var ancestorHash = BufferUtil.reverse(block.header.prevHash).toString('hex'); // We only need to go back until we meet the main chain for the forked block // and thus don't need to find the entire chain of hashes. @@ -183,7 +184,10 @@ Node.prototype._syncBitcoindRewind = function(block, done) { var tip = self.chain.tip; - self.getBlock(tip.prevHash, function(err, previousTip) { + // TODO: expose prevHash as a string from bitcore + var prevHash = BufferUtil.reverse(tip.header.prevHash).toString('hex'); + + self.getBlock(prevHash, function(err, previousTip) { if (err) { removeDone(err); } @@ -195,7 +199,6 @@ Node.prototype._syncBitcoindRewind = function(block, done) { } // Set the new tip - delete self.chain.tip.__transactions; previousTip.__height = self.chain.tip.__height - 1; self.chain.tip = previousTip; self.chain.saveMetadata(); @@ -241,9 +244,12 @@ Node.prototype._syncBitcoind = function() { return done(err); } - var block = self.Block.fromBuffer(blockBuffer); + var block = Block.fromBuffer(blockBuffer); - if (block.prevHash === self.chain.tip.hash) { + // TODO: expose prevHash as a string from bitcore + var prevHash = BufferUtil.reverse(block.header.prevHash).toString('hex'); + + if (prevHash === self.chain.tip.hash) { // This block appends to the current chain tip and we can // immediately add it to the chain and create indexes. @@ -252,7 +258,7 @@ Node.prototype._syncBitcoind = function() { block.__height = self.chain.tip.__height + 1; // Update chain.cache.hashes - self.chain.cache.hashes[block.hash] = block.prevHash; + self.chain.cache.hashes[block.hash] = prevHash; // Update chain.cache.chainHashes self.chain.getHashes(block.hash, function(err, hashes) { @@ -264,7 +270,6 @@ Node.prototype._syncBitcoind = function() { if (err) { return done(err); } - delete self.chain.tip.__transactions; self.chain.tip = block; log.debug('Saving metadata'); self.chain.saveMetadata(); @@ -369,7 +374,6 @@ Node.prototype._loadConsensus = function(config) { options = _.clone(config.consensus || {}); } options.node = this; - this.Block = Block; this.chain = new Chain(options); }; @@ -391,14 +395,6 @@ Node.prototype._loadAPI = function() { Node.prototype._initialize = function() { var self = this; - // DB References - this.db.chain = this.chain; - this.db.Block = this.Block; - this.db.bitcoind = this.bitcoind; - - // Chain References - this.chain.db = this.db; - this._initializeBitcoind(); this._initializeDatabase(); this._initializeChain(); diff --git a/test/block.unit.js b/test/block.unit.js deleted file mode 100644 index c8869cec..00000000 --- a/test/block.unit.js +++ /dev/null @@ -1,83 +0,0 @@ -'use strict'; - -var chai = require('chai'); -var should = chai.should(); -var sinon = require('sinon'); -var bitcore = require('bitcore'); -var BN = bitcore.crypto.BN; -var BufferWriter = bitcore.encoding.BufferWriter; -var BufferReader = bitcore.encoding.BufferReader; -var bitcoindjs = require('../'); -var Block = bitcoindjs.Block; -var chainData = require('./data/pow-chain.json'); - -describe('Bitcoin Block', function() { - - describe('@constructor', function() { - it('set bits and nonce', function() { - var block = new Block(chainData[1]); - should.exist(block.bits); - block.bits.should.equal(chainData[1].bits); - should.exist(block.nonce); - block.nonce.should.equal(chainData[1].nonce); - }); - }); - - describe('#fromBuffer', function() { - var buffer = new Buffer('010000004404c1ff5f300e5ed830b45ec9f68fbe9a0c51c4b4eaa4ce09a03ac4ddde01750000000000000000000000000000000000000000000000000000000000000000b134de547fcc071f4a020000abcdef', 'hex'); - - it('deserializes correctly', function() { - var block = Block.fromBuffer(buffer); - block.version.should.equal(1); - block.prevHash.should.equal('7501deddc43aa009cea4eab4c4510c9abe8ff6c95eb430d85e0e305fffc10444'); - block.merkleRoot.should.equal(new Buffer(Array(32)).toString('hex')); - block.timestamp.should.be.instanceof(Date); - block.timestamp.toISOString().should.equal('2015-02-13T17:30:25.000Z'); - block.bits.should.equal(520604799); - block.nonce.should.equal(586); - block.data.should.deep.equal(new Buffer('abcdef', 'hex')); - }); - it('roundtrip serialization', function() { - var actual = Block.fromBuffer(buffer).toBuffer(); - actual.should.deep.equal(buffer); - }); - it('set null prevHash if null hash buffer', function() { - var blockBuffer = new Buffer('0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d4d5fd834b0100007fcc071f4a020000abcdef', 'hex'); - var block = Block.fromBuffer(blockBuffer); - block.hasOwnProperty('prevHash').should.equal(true); - should.equal(block.prevHash, null); - }); - }); - - describe('#headerToBufferWriter', function() { - it('serializes correctly', function() { - var block = new Block(chainData[1]); - var bw = new BufferWriter(); - block.headerToBufferWriter(bw); - bw.bufs[0].toString('hex').should.equal('01000000'); // version - BufferReader(bw.bufs[1]).readReverse().toString('hex').should.equal(chainData[1].prevHash); // prevhash - Number(bw.bufs[2].toString('hex')).should.equal(0); // merkle root - should.exist(bw.bufs[3]); // time - bw.bufs[3].length.should.equal(4); - should.exist(bw.bufs[4]); // bits - bw.bufs[4].length.should.equal(4); - should.exist(bw.bufs[5]); // nonce - bw.bufs[5].length.should.equal(4); - }); - }); - - describe('Bitcoin Block', function() { - it('should load and serialize the Bitcoin testnet genesis block correctly', function() { - var blockBuffer = new Buffer('0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4adae5494dffff001d1aa4ae180101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000', 'hex'); - var block = Block.fromBuffer(blockBuffer); - block.hash.should.equal('000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943'); - }); - it('should load and serialize Bitcoin testnet #1 block correctly', function() { - var blockBuffer = new Buffer('0100000043497fd7f826957108f4a30fd9cec3aeba79972084e90ead01ea330900000000bac8b0fa927c0ac8234287e33c5f74d38d354820e24756ad709d7038fc5f31f020e7494dffff001d03e4b6720101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0e0420e7494d017f062f503253482fffffffff0100f2052a010000002321021aeaf2f8638a129a3156fbe7e5ef635226b0bafd495ff03afe2c843d7e3a4b51ac00000000', 'hex'); - var block = Block.fromBuffer(blockBuffer); - block.hash.should.equal('00000000b873e79784647a6c82962c70d228557d24a747ea4d1b8bbe878e1206'); - block.toBuffer().should.deep.equal(blockBuffer); - }); - }); - -}); diff --git a/test/chain.unit.js b/test/chain.unit.js index d20b91da..5864eca3 100644 --- a/test/chain.unit.js +++ b/test/chain.unit.js @@ -8,8 +8,9 @@ var memdown = require('memdown'); var index = require('../'); var DB = index.DB; var Chain = index.Chain; -var Block = index.Block; var bitcore = require('bitcore'); +var BufferUtil = bitcore.util.buffer; +var Block = bitcore.Block; var BN = bitcore.crypto.BN; var chainData = require('./data/testnet-blocks.json'); @@ -51,7 +52,10 @@ describe('Bitcoin Chain', function() { db.mempool = { on: sinon.spy() }; - var chain = new Chain({db: db, genesis: {hash: 'genesis'}}); + var node = { + db: db + }; + var chain = new Chain({node: node, genesis: {hash: 'genesis'}}); chain.on('ready', function() { should.exist(chain.tip); @@ -78,7 +82,10 @@ describe('Bitcoin Chain', function() { db.mempool = { on: sinon.spy() }; - var chain = new Chain({db: db, genesis: {hash: 'genesis'}}); + var node = { + db: db + }; + var chain = new Chain({node: node, genesis: {hash: 'genesis'}}); chain.getHeightForBlock = sinon.stub().callsArgWith(1, null, 10); chain.getWeight = sinon.stub().callsArgWith(1, null, new BN(50)); chain.on('ready', function() { @@ -104,7 +111,10 @@ describe('Bitcoin Chain', function() { db.mempool = { on: sinon.spy() }; - var chain = new Chain({db: db, genesis: {hash: 'genesis'}}); + var node = { + db: db + }; + var chain = new Chain({node: node, genesis: {hash: 'genesis'}}); chain.on('error', function(error) { should.exist(error); error.message.should.equal('getMetadataError'); @@ -126,7 +136,10 @@ describe('Bitcoin Chain', function() { db.mempool = { on: sinon.spy() }; - var chain = new Chain({db: db, genesis: {hash: 'genesis'}}); + var node = { + db: db + }; + var chain = new Chain({node: node, genesis: {hash: 'genesis'}}); chain.on('error', function(error) { should.exist(error); error.message.should.equal('putBlockError'); @@ -148,7 +161,10 @@ describe('Bitcoin Chain', function() { db.mempool = { on: sinon.spy() }; - var chain = new Chain({db: db, genesis: {hash: 'genesis'}}); + var node = { + db: db + }; + var chain = new Chain({node: node, genesis: {hash: 'genesis'}}); chain.on('error', function(error) { should.exist(error); error.message.should.equal('getBlockError'); @@ -165,21 +181,6 @@ describe('Bitcoin Chain', function() { }); }); - describe('#_writeBlock', function() { - it('should update hashes and call putBlock', function(done) { - var chain = new Chain(); - chain.db = { - putBlock: sinon.stub().callsArg(1) - }; - chain._writeBlock({hash: 'hash', prevHash: 'prevhash'}, function(err) { - should.not.exist(err); - chain.db.putBlock.callCount.should.equal(1); - chain.cache.hashes.hash.should.equal('prevhash'); - done(); - }); - }); - }); - describe('#_validateBlock', function() { it('should call the callback', function(done) { var chain = new Chain(); @@ -193,12 +194,12 @@ describe('Bitcoin Chain', function() { describe('#getWeight', function() { var work = '000000000000000000000000000000000000000000005a7b3c42ea8b844374e9'; var chain = new Chain(); - chain.db = { - bitcoind: { - getBlockIndex: sinon.stub().returns({ - chainWork: work - }) - } + chain.node = {}; + chain.node.db = {}; + chain.node.bitcoind = { + getBlockIndex: sinon.stub().returns({ + chainWork: work + }) }; it('should give the weight as a BN', function(done) { @@ -210,7 +211,7 @@ describe('Bitcoin Chain', function() { }); it('should give an error if the weight is undefined', function(done) { - chain.db.bitcoind.getBlockIndex = sinon.stub().returns(undefined); + chain.node.bitcoind.getBlockIndex = sinon.stub().returns(undefined); chain.getWeight('hash2', function(err, weight) { should.exist(err); done(); @@ -232,11 +233,17 @@ describe('Bitcoin Chain', function() { var db = new DB({store: memdown}); db.getPrevHash = function(blockHash, cb) { - cb(null, blocks[blockHash].prevHash); + // TODO: expose prevHash as a string from bitcore + var prevHash = BufferUtil.reverse(blocks[blockHash].header.prevHash).toString('hex'); + cb(null, prevHash); + }; + + var node = { + db: db }; var chain = new Chain({ - db: db, + node: node, genesis: genesisBlock }); diff --git a/test/data/pow-chain.json b/test/data/pow-chain.json deleted file mode 100644 index 41f9af21..00000000 --- a/test/data/pow-chain.json +++ /dev/null @@ -1,58 +0,0 @@ -[ - { - "version": 1, - "prevHash": null, - "timestamp": "2015-04-16T20:02:24.777Z", - "bits": 520617984, - "nonce": 0, - "data": "" - }, - { - "version": 1, - "prevHash": "bab3003201bdf327ac03735e70a5f02968bc1e8cf74cc9045ae960c074139386", - "timestamp": "2015-04-16T20:02:26.650Z", - "bits": 520617984, - "nonce": 6256, - "data": "" - }, - { - "version": 1, - "prevHash": "0002cbf2807997765971f14bdd7c748e93c315c2d3af35b85c6604126c788fa8", - "timestamp": "2015-04-16T20:02:27.885Z", - "bits": 520617984, - "nonce": 12232, - "data": "" - }, - { - "version": 1, - "prevHash": "0007b3fec55496a3741caa992aac55395921a965e8cec6192659f266eec39f62", - "timestamp": "2015-04-16T20:02:28.559Z", - "bits": 520355840, - "nonce": 6492, - "data": "" - }, - { - "version": 1, - "prevHash": "000002de06d8fdd4d7036fc99ddc8c9b432bfa910e0968756b988e25f7f43d8e", - "timestamp": "2015-04-16T20:02:29.593Z", - "bits": 520355840, - "nonce": 9502, - "data": "" - }, - { - "version": 1, - "prevHash": "00016a5c727ef18b406b16c188af017c101f5a32c2d881e92dd7aa1746b185ea", - "timestamp": "2015-04-16T20:02:30.713Z", - "bits": 520355840, - "nonce": 10586, - "data": "" - }, - { - "version": 1, - "prevHash": "00023ac90a33ce9faf07f407b525f186180c902fbb2e71c48cd73ffb25a8dc5b", - "timestamp": "22015-04-16T20:02:31.440Z", - "bits": 520181077, - "nonce": 6983, - "data": "" - } -] diff --git a/test/db.unit.js b/test/db.unit.js index 21a8ec1c..c93f5d3f 100644 --- a/test/db.unit.js +++ b/test/db.unit.js @@ -2,11 +2,13 @@ var should = require('chai').should(); var sinon = require('sinon'); -var bitcoindjs = require('../'); -var DB = bitcoindjs.DB; +var index = require('../'); +var DB = index.DB; var blockData = require('./data/livenet-345003.json'); +var bitcore = require('bitcore'); +var Block = bitcore.Block; var transactionData = require('./data/bitcoin-transactions.json'); -var errors = bitcoindjs.errors; +var errors = index.errors; var memdown = require('memdown'); var inherits = require('util').inherits; var BaseModule = require('../lib/module'); @@ -20,7 +22,8 @@ describe('Bitcoin DB', function() { it('should emit ready', function(done) { var db = new DB({store: memdown}); db._modules = ['mod1', 'mod2']; - db.bitcoind = { + db.node = {}; + db.node.bitcoind = { on: sinon.spy() }; db.addModule = sinon.spy(); @@ -49,7 +52,8 @@ describe('Bitcoin DB', function() { describe('#getTransaction', function() { it('will return a NotFound error', function(done) { var db = new DB({store: memdown}); - db.bitcoind = { + db.node = {}; + db.node.bitcoind = { getTransaction: sinon.stub().callsArgWith(2, null, null) }; var txid = '7426c707d0e9705bdd8158e60983e37d0f5d63529086d6672b07d9238d5aa623'; @@ -60,7 +64,8 @@ describe('Bitcoin DB', function() { }); it('will return an error from bitcoind', function(done) { var db = new DB({store: memdown}); - db.bitcoind = { + db.node = {}; + db.node.bitcoind = { getTransaction: sinon.stub().callsArgWith(2, new Error('test error')) }; var txid = '7426c707d0e9705bdd8158e60983e37d0f5d63529086d6672b07d9238d5aa623'; @@ -71,7 +76,8 @@ describe('Bitcoin DB', function() { }); it('will return an error from bitcoind', function(done) { var db = new DB({store: memdown}); - db.bitcoind = { + db.node = {}; + db.node.bitcoind = { getTransaction: sinon.stub().callsArgWith(2, null, new Buffer(transactionData[0].hex, 'hex')) }; var txid = '7426c707d0e9705bdd8158e60983e37d0f5d63529086d6672b07d9238d5aa623'; @@ -87,22 +93,24 @@ describe('Bitcoin DB', function() { describe('#getBlock', function() { var db = new DB({store: memdown}); - db.bitcoind = { - getBlock: sinon.stub().callsArgWith(1, null, new Buffer(blockData, 'hex')) - }; - db.Block = { - fromBuffer: sinon.stub().returns('block') + var blockBuffer = new Buffer(blockData, 'hex'); + var expectedBlock = Block.fromBuffer(blockBuffer); + db.node = {}; + db.node.bitcoind = { + getBlock: sinon.stub().callsArgWith(1, null, blockBuffer) }; - it('should get the block from bitcoind.js', function(done) { + it('should get the block from bitcoin daemon', function(done) { db.getBlock('00000000000000000593b60d8b4f40fd1ec080bdb0817d475dae47b5f5b1f735', function(err, block) { should.not.exist(err); - block.should.equal('block'); + block.hash.should.equal(expectedBlock.hash); done(); }); }); it('should give an error when bitcoind.js gives an error', function(done) { - db.bitcoind.getBlock = sinon.stub().callsArgWith(1, new Error('error')); + db.node = {}; + db.node.bitcoind = {}; + db.node.bitcoind.getBlock = sinon.stub().callsArgWith(1, new Error('error')); db.getBlock('00000000000000000593b60d8b4f40fd1ec080bdb0817d475dae47b5f5b1f735', function(err, block) { should.exist(err); err.message.should.equal('error'); @@ -124,7 +132,8 @@ describe('Bitcoin DB', function() { describe('#getPrevHash', function() { it('should return prevHash from bitcoind', function(done) { var db = new DB({store: memdown}); - db.bitcoind = { + db.node = {}; + db.node.bitcoind = { getBlockIndex: sinon.stub().returns({ prevHash: 'prevhash' }) @@ -139,7 +148,8 @@ describe('Bitcoin DB', function() { it('should give an error if bitcoind could not find it', function(done) { var db = new DB({store: memdown}); - db.bitcoind = { + db.node = {}; + db.node.bitcoind = { getBlockIndex: sinon.stub().returns(null) }; @@ -160,7 +170,8 @@ describe('Bitcoin DB', function() { }; var db = new DB({store: memdown}); - db.bitcoind = { + db.node = {}; + db.node.bitcoind = { getTransactionWithBlockInfo: sinon.stub().callsArgWith(2, null, info) }; @@ -173,7 +184,8 @@ describe('Bitcoin DB', function() { }); it('should give an error if one occurred', function(done) { var db = new DB({store: memdown}); - db.bitcoind = { + db.node = {}; + db.node.bitcoind = { getTransactionWithBlockInfo: sinon.stub().callsArgWith(2, new Error('error')) }; @@ -187,7 +199,8 @@ describe('Bitcoin DB', function() { describe('#sendTransaction', function() { it('should give the txid on success', function(done) { var db = new DB({store: memdown}); - db.bitcoind = { + db.node = {}; + db.node.bitcoind = { sendTransaction: sinon.stub().returns('txid') }; @@ -200,7 +213,8 @@ describe('Bitcoin DB', function() { }); it('should give an error if bitcoind threw an error', function(done) { var db = new DB({store: memdown}); - db.bitcoind = { + db.node = {}; + db.node.bitcoind = { sendTransaction: sinon.stub().throws(new Error('error')) }; @@ -215,14 +229,15 @@ describe('Bitcoin DB', function() { describe("#estimateFee", function() { it('should pass along the fee from bitcoind', function(done) { var db = new DB({store: memdown}); - db.bitcoind = { + db.node = {}; + db.node.bitcoind = { estimateFee: sinon.stub().returns(1000) }; db.estimateFee(5, function(err, fee) { should.not.exist(err); fee.should.equal(1000); - db.bitcoind.estimateFee.args[0][0].should.equal(5); + db.node.bitcoind.estimateFee.args[0][0].should.equal(5); done(); }); }); @@ -390,11 +405,14 @@ describe('Bitcoin DB', function() { inherits(Module1, BaseModule); var db = new DB({store: memdown}); + var node = {}; + db.node = node; db.modules = []; db.addModule(Module1); db.modules.length.should.equal(1); - should.exist(db.modules[0].db); + should.exist(db.modules[0].node); + db.modules[0].node.should.equal(node); }); it('should throw an error if module is not an instance of BaseModule', function() { diff --git a/test/modules/address.unit.js b/test/modules/address.unit.js index c65b9334..6031de9b 100644 --- a/test/modules/address.unit.js +++ b/test/modules/address.unit.js @@ -16,11 +16,18 @@ var mockdb = { } }; +var mocknode = { + db: mockdb, + bitcoind: { + on: sinon.stub() + } +}; + describe('AddressModule', function() { describe('#getAPIMethods', function() { it('should return the correct methods', function() { - var am = new AddressModule({db: mockdb}); + var am = new AddressModule({node: mocknode}); var methods = am.getAPIMethods(); methods.length.should.equal(5); }); @@ -28,7 +35,7 @@ describe('AddressModule', function() { describe('#getPublishEvents', function() { it('will return an array of publish event objects', function() { - var am = new AddressModule({db: mockdb}); + var am = new AddressModule({node: mocknode}); am.subscribe = sinon.spy(); am.unsubscribe = sinon.spy(); var events = am.getPublishEvents(); @@ -64,7 +71,7 @@ describe('AddressModule', function() { it('create a message for an address', function() { var txBuf = new Buffer('01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704ffff001d0104ffffffff0100f2052a0100000043410496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52da7589379515d4e0a604f8141781e62294721166bf621e73a82cbf2342c858eeac00000000', 'hex'); var tx = bitcore.Transaction().fromBuffer(txBuf); - var am = new AddressModule({db: mockdb}); + var am = new AddressModule({node: mocknode}); var address = '12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX'; var messages = {}; am.transactionOutputHandler(messages, tx, 0, true); @@ -80,7 +87,7 @@ describe('AddressModule', function() { describe('#transactionHandler', function() { it('will pass outputs to transactionOutputHandler and call transactionEventHandler', function() { var txBuf = new Buffer('01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704ffff001d0104ffffffff0100f2052a0100000043410496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52da7589379515d4e0a604f8141781e62294721166bf621e73a82cbf2342c858eeac00000000', 'hex'); - var am = new AddressModule({db: mockdb}); + var am = new AddressModule({node: mocknode}); var address = '12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX'; var message = {}; am.transactionOutputHandler = function(messages) { @@ -97,21 +104,18 @@ describe('AddressModule', function() { describe('#blockHandler', function() { var am; var db = { - getTransactionsFromBlock: function() { - return block.transactions.slice(0, 8); - }, bitcoind: { on: sinon.stub() } }; - var block = bitcore.Block.fromString(blockData); + var testBlock = bitcore.Block.fromString(blockData); var data = [ { key: { address: '1F1MAvhTKg2VG29w8cXsiSN2PJ8gSsrJw', - timestamp: 1424836934000, + timestamp: 1424836934, txid: 'fdbefe0d064729d85556bd3ab13c3a889b685d042499c02b4aa2064fb1e16923', outputIndex: 0 }, @@ -129,13 +133,13 @@ describe('AddressModule', function() { value: { txid: '5780f3ee54889a0717152a01abee9a32cec1b0cdf8d5537a08c7bd9eeb6bfbca', inputIndex: 0, - timestamp: 1424836934000 + timestamp: 1424836934 } }, { key: { address: '1Ep5LA4T6Y7zaBPiwruUJurjGFvCJHzJhm', - timestamp: 1424836934000, + timestamp: 1424836934, txid: 'e66f3b989c790178de2fc1a5329f94c0d8905d0d3df4e7ecf0115e7f90a6283d', outputIndex: 1 }, @@ -154,11 +158,19 @@ describe('AddressModule', function() { var value64 = data[2].value; before(function() { - am = new AddressModule({db: db, network: 'livenet'}); + am = new AddressModule({node: mocknode, network: 'livenet'}); }); it('should create the correct operations when updating/adding outputs', function(done) { - am.blockHandler({__height: 345003, timestamp: new Date(1424836934000)}, true, function(err, operations) { + var block = { + __height: 345003, + header: { + timestamp: 1424836934 + }, + transactions: testBlock.transactions.slice(0, 8) + }; + + am.blockHandler(block, true, function(err, operations) { should.not.exist(err); operations.length.should.equal(81); operations[0].type.should.equal('put'); @@ -177,7 +189,14 @@ describe('AddressModule', function() { }); }); it('should create the correct operations when removing outputs', function(done) { - am.blockHandler({__height: 345003, timestamp: new Date(1424836934000)}, false, function(err, operations) { + var block = { + __height: 345003, + header: { + timestamp: 1424836934 + }, + transactions: testBlock.transactions.slice(0, 8) + }; + am.blockHandler(block, false, function(err, operations) { should.not.exist(err); operations.length.should.equal(81); operations[0].type.should.equal('del'); @@ -193,53 +212,66 @@ describe('AddressModule', function() { }); }); it('should continue if output script is null', function(done) { - var transactions = [ - { - inputs: [], - outputs: [ - { - script: null, - satoshis: 1000, - } - ], - isCoinbase: sinon.stub().returns(false) - } - ]; var db = { - getTransactionsFromBlock: function() { - return transactions; - }, bitcoind: { on: sinon.stub() } }; - var am = new AddressModule({db: db, network: 'livenet'}); + var am = new AddressModule({node: mocknode, network: 'livenet'}); - am.blockHandler({__height: 345003, timestamp: new Date(1424836934000)}, false, function(err, operations) { + var block = { + __height: 345003, + header: { + timestamp: 1424836934 + }, + transactions: [ + { + inputs: [], + outputs: [ + { + script: null, + satoshis: 1000, + } + ], + isCoinbase: sinon.stub().returns(false) + } + ] + }; + + am.blockHandler(block, false, function(err, operations) { should.not.exist(err); operations.length.should.equal(0); done(); }); }); it('will call event handlers', function() { - var block = bitcore.Block.fromString(blockData); + var testBlock = bitcore.Block.fromString(blockData); var db = { - getTransactionsFromBlock: function() { - return block.transactions.slice(0, 8); - }, + bitcoind: { + on: sinon.stub() + } + }; + var testnode = { + db: db, bitcoind: { on: sinon.stub() } }; - var am = new AddressModule({db: db, network: 'livenet'}); + var am = new AddressModule({node: testnode, network: 'livenet'}); am.transactionEventHandler = sinon.spy(); am.balanceEventHandler = sinon.spy(); - am.blockHandler( - { - __height: 345003, - timestamp: new Date(1424836934000) + + var block = { + __height: 345003, + header: { + timestamp: 1424836934 }, + transactions: testBlock.transactions.slice(0, 8) + }; + + am.blockHandler( + block, true, function(err) { if (err) { @@ -254,7 +286,7 @@ describe('AddressModule', function() { describe('#transactionEventHandler', function() { it('will emit a transaction if there is a subscriber', function(done) { - var am = new AddressModule({db: mockdb}); + var am = new AddressModule({node: mocknode}); var emitter = new EventEmitter(); am.subscriptions['address/transaction'] = { '1DzjESe6SLmAKVPLFMj6Sx1sWki3qt5i8N': [emitter] @@ -284,7 +316,7 @@ describe('AddressModule', function() { describe('#balanceEventHandler', function() { it('will emit a balance if there is a subscriber', function(done) { - var am = new AddressModule({db: mockdb}); + var am = new AddressModule({node: mocknode}); var emitter = new EventEmitter(); am.subscriptions['address/balance'] = { '1DzjESe6SLmAKVPLFMj6Sx1sWki3qt5i8N': [emitter] @@ -304,7 +336,7 @@ describe('AddressModule', function() { describe('#subscribe', function() { it('will add emitters to the subscribers array (transaction)', function() { - var am = new AddressModule({db: mockdb}); + var am = new AddressModule({node: mocknode}); var emitter = new EventEmitter(); var address = '1DzjESe6SLmAKVPLFMj6Sx1sWki3qt5i8N'; @@ -321,7 +353,7 @@ describe('AddressModule', function() { am.subscriptions['address/transaction'][address].should.deep.equal([emitter, emitter2]); }); it('will add an emitter to the subscribers array (balance)', function() { - var am = new AddressModule({db: mockdb}); + var am = new AddressModule({node: mocknode}); var emitter = new EventEmitter(); var name = 'address/balance'; var address = '1DzjESe6SLmAKVPLFMj6Sx1sWki3qt5i8N'; @@ -340,7 +372,7 @@ describe('AddressModule', function() { describe('#unsubscribe', function() { it('will remove emitter from subscribers array (transaction)', function() { - var am = new AddressModule({db: mockdb}); + var am = new AddressModule({node: mocknode}); var emitter = new EventEmitter(); var emitter2 = new EventEmitter(); var address = '1DzjESe6SLmAKVPLFMj6Sx1sWki3qt5i8N'; @@ -350,7 +382,7 @@ describe('AddressModule', function() { am.subscriptions['address/transaction'][address].should.deep.equal([emitter2]); }); it('will remove emitter from subscribers array (balance)', function() { - var am = new AddressModule({db: mockdb}); + var am = new AddressModule({node: mocknode}); var emitter = new EventEmitter(); var emitter2 = new EventEmitter(); var address = '1DzjESe6SLmAKVPLFMj6Sx1sWki3qt5i8N'; @@ -360,7 +392,7 @@ describe('AddressModule', function() { am.subscriptions['address/balance'][address].should.deep.equal([emitter2]); }); it('should unsubscribe from all addresses if no addresses are specified', function() { - var am = new AddressModule({db: mockdb}); + var am = new AddressModule({node: mocknode}); var emitter = new EventEmitter(); var emitter2 = new EventEmitter(); am.subscriptions['address/balance'] = { @@ -377,7 +409,7 @@ describe('AddressModule', function() { describe('#getBalance', function() { it('should sum up the unspent outputs', function(done) { - var am = new AddressModule({db: mockdb}); + var am = new AddressModule({node: mocknode}); var outputs = [ {satoshis: 1000}, {satoshis: 2000}, {satoshis: 3000} ]; @@ -390,7 +422,7 @@ describe('AddressModule', function() { }); it('will handle error from unspent outputs', function(done) { - var am = new AddressModule({db: mockdb}); + var am = new AddressModule({node: mocknode}); am.getUnspentOutputs = sinon.stub().callsArgWith(2, new Error('error')); am.getBalance('someaddress', false, function(err) { should.exist(err); @@ -404,24 +436,26 @@ describe('AddressModule', function() { describe('#getOutputs', function() { var am; var address = '1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W'; - var db = { - bitcoind: { - on: sinon.stub() - }, + var db = {}; + var testnode = { chain: { tip: { __height: 1 } + }, + db: db, + bitcoind: { + on: sinon.stub() } }; before(function() { - am = new AddressModule({db: db}); + am = new AddressModule({node: testnode}); }); it('should get outputs for an address', function(done) { var readStream1 = new EventEmitter(); - am.db.store = { + am.node.db.store = { createReadStream: sinon.stub().returns(readStream1) }; var mempoolOutputs = [ @@ -433,7 +467,7 @@ describe('AddressModule', function() { blockHeight: 352532 } ]; - am.db.bitcoind = { + am.node.bitcoind = { getMempoolOutputs: sinon.stub().returns(mempoolOutputs) }; @@ -476,7 +510,7 @@ describe('AddressModule', function() { it('should give an error if the readstream has an error', function(done) { var readStream2 = new EventEmitter(); - am.db.store = { + am.node.db.store = { createReadStream: sinon.stub().returns(readStream2) }; @@ -506,7 +540,13 @@ describe('AddressModule', function() { on: sinon.spy() } }; - var am = new AddressModule({db: db}); + var testnode = { + db: db, + bitcoind: { + on: sinon.stub() + } + }; + var am = new AddressModule({node: testnode}); am.getUnspentOutputsForAddress = function(address, queryMempool, callback) { var result = addresses[address]; if(result instanceof Error) { @@ -534,7 +574,13 @@ describe('AddressModule', function() { on: sinon.spy() } }; - var am = new AddressModule({db: db}); + var testnode = { + db: db, + bitcoind: { + on: sinon.stub() + } + }; + var am = new AddressModule({node: testnode}); am.getUnspentOutputsForAddress = function(address, queryMempool, callback) { var result = addresses[address]; if(result instanceof Error) { @@ -563,7 +609,13 @@ describe('AddressModule', function() { on: sinon.spy() } }; - var am = new AddressModule({db: db}); + var testnode = { + db: db, + bitcoind: { + on: sinon.stub() + } + }; + var am = new AddressModule({node: testnode}); am.getUnspentOutputsForAddress = function(address, queryMempool, callback) { var result = addresses[address]; if(result instanceof Error) { @@ -599,7 +651,7 @@ describe('AddressModule', function() { ]; var i = 0; - var am = new AddressModule({db: mockdb}); + var am = new AddressModule({node: mocknode}); am.getOutputs = sinon.stub().callsArgWith(2, null, outputs); am.isUnspent = function(output, queryMempool, callback) { callback(!outputs[i].spent); @@ -615,7 +667,7 @@ describe('AddressModule', function() { }); }); it('should handle an error from getOutputs', function(done) { - var am = new AddressModule({db: mockdb}); + var am = new AddressModule({node: mocknode}); am.getOutputs = sinon.stub().callsArgWith(2, new Error('error')); am.getUnspentOutputsForAddress('1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W', false, function(err, outputs) { should.exist(err); @@ -624,7 +676,7 @@ describe('AddressModule', function() { }); }); it('should handle when there are no outputs', function(done) { - var am = new AddressModule({db: mockdb}); + var am = new AddressModule({node: mocknode}); am.getOutputs = sinon.stub().callsArgWith(2, null, []); am.getUnspentOutputsForAddress('1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W', false, function(err, outputs) { should.exist(err); @@ -639,7 +691,7 @@ describe('AddressModule', function() { var am; before(function() { - am = new AddressModule({db: mockdb}); + am = new AddressModule({node: mocknode}); }); it('should give true when isSpent() gives false', function(done) { @@ -674,9 +726,15 @@ describe('AddressModule', function() { on: sinon.stub() } }; + var testnode = { + db: db, + bitcoind: { + on: sinon.stub() + } + }; before(function() { - am = new AddressModule({db: db}); - am.db.bitcoind = { + am = new AddressModule({node: testnode}); + am.node.bitcoind = { isSpent: sinon.stub().returns(true), on: sinon.stub() }; @@ -695,12 +753,15 @@ describe('AddressModule', function() { var db = { store: { get: sinon.stub().callsArgWith(1, null, 'spendtxid:1') - }, + } + }; + var testnode = { + db: db, bitcoind: { on: sinon.stub() } }; - var am = new AddressModule({db: db}); + var am = new AddressModule({node: testnode}); am.getSpendInfoForOutput('txid', 3, function(err, info) { should.not.exist(err); info.txid.should.equal('spendtxid'); @@ -805,17 +866,20 @@ describe('AddressModule', function() { } } callback(new Error('tx ' + txid + ' not found')); - }, - bitcoind: { - on: sinon.stub() - }, + } + }; + var testnode = { chain: { tip: { __height: 1 } + }, + db: db, + bitcoind: { + on: sinon.stub() } }; - var am = new AddressModule({db: db}); + var am = new AddressModule({node: testnode}); am.getOutputs = sinon.stub().callsArgWith(2, null, incoming); am.getSpendInfoForOutput = function(txid, outputIndex, callback) { diff --git a/test/node.unit.js b/test/node.unit.js index d410cb2b..35476fdf 100644 --- a/test/node.unit.js +++ b/test/node.unit.js @@ -5,8 +5,9 @@ var sinon = require('sinon'); var EventEmitter = require('events').EventEmitter; var bitcore = require('bitcore'); var Networks = bitcore.Networks; +var BufferUtil = bitcore.util.buffer; +var Block = bitcore.Block; var blockData = require('./data/livenet-345003.json'); -var Block = require('../lib/block'); var proxyquire = require('proxyquire'); var index = require('..'); var fs = require('fs'); @@ -18,6 +19,14 @@ describe('Bitcoind Node', function() { var Node; var BadNode; + function hexlebuf(hexString){ + return BufferUtil.reverse(new Buffer(hexString, 'hex')); + } + + function lebufhex(buf) { + return BufferUtil.reverse(buf).toString('hex'); + } + before(function() { BadNode = proxyquire('../lib/node', { @@ -154,29 +163,45 @@ describe('Bitcoind Node', function() { } }; var expectedAncestor = chainHashes[chainHashes.length - 6]; + var forkedBlocks = { 'd7fa6f3d5b2fe35d711e6aca5530d311b8c6e45f588a65c642b8baf4b4441d82': { - prevHash: '76d920dbd83beca9fa8b2f346d5c5a81fe4a350f4b355873008229b1e6f8701a' + header: { + prevHash: hexlebuf('76d920dbd83beca9fa8b2f346d5c5a81fe4a350f4b355873008229b1e6f8701a') + } }, '76d920dbd83beca9fa8b2f346d5c5a81fe4a350f4b355873008229b1e6f8701a': { - prevHash: 'f0a0d76a628525243c8af7606ee364741ccd5881f0191bbe646c8a4b2853e60c' + header: { + prevHash: hexlebuf('f0a0d76a628525243c8af7606ee364741ccd5881f0191bbe646c8a4b2853e60c') + } }, 'f0a0d76a628525243c8af7606ee364741ccd5881f0191bbe646c8a4b2853e60c': { - prevHash: '2f72b809d5ccb750c501abfdfa8c4c4fad46b0b66c088f0568d4870d6f509c31' + header: { + prevHash: hexlebuf('2f72b809d5ccb750c501abfdfa8c4c4fad46b0b66c088f0568d4870d6f509c31') + } }, '2f72b809d5ccb750c501abfdfa8c4c4fad46b0b66c088f0568d4870d6f509c31': { - prevHash: 'adf66e6ae10bc28fc22bc963bf43e6b53ef4429269bdb65038927acfe66c5453' + header: { + prevHash: hexlebuf('adf66e6ae10bc28fc22bc963bf43e6b53ef4429269bdb65038927acfe66c5453') + } }, 'adf66e6ae10bc28fc22bc963bf43e6b53ef4429269bdb65038927acfe66c5453': { - prevHash: '3ea12707e92eed024acf97c6680918acc72560ec7112cf70ac213fb8bb4fa618' + header: { + prevHash: hexlebuf('3ea12707e92eed024acf97c6680918acc72560ec7112cf70ac213fb8bb4fa618') + } }, '3ea12707e92eed024acf97c6680918acc72560ec7112cf70ac213fb8bb4fa618': { - prevHash: expectedAncestor + header: { + prevHash: hexlebuf(expectedAncestor) + } }, }; node.bitcoind = { getBlockIndex: function(hash) { - return forkedBlocks[hash]; + var block = forkedBlocks[hash]; + return { + prevHash: BufferUtil.reverse(block.header.prevHash).toString('hex') + }; } }; var block = forkedBlocks['d7fa6f3d5b2fe35d711e6aca5530d311b8c6e45f588a65c642b8baf4b4441d82']; @@ -196,7 +221,9 @@ describe('Bitcoind Node', function() { tip: { __height: 10, hash: chainHashes[chainHashes.length], - prevHash: chainHashes[chainHashes.length - 1] + header: { + prevHash: hexlebuf(chainHashes[chainHashes.length - 1]) + } }, saveMetadata: sinon.stub(), emit: sinon.stub() @@ -204,11 +231,14 @@ describe('Bitcoind Node', function() { node.getBlock = function(hash, callback) { setImmediate(function() { for(var i = chainHashes.length; i > 0; i--) { + var block = { + hash: chainHashes[i], + header: { + prevHash: hexlebuf(chainHashes[i - 1]) + } + }; if (chainHashes[i] === hash) { - callback(null, { - hash: chainHashes[i], - prevHash: chainHashes[i - 1] - }); + callback(null, block); } } }); @@ -236,8 +266,7 @@ describe('Bitcoind Node', function() { describe('#_syncBitcoind', function() { it('will get and add block up to the tip height', function(done) { var node = new Node({}); - node.Block = Block; - var blockBuffer = new Buffer(blockData); + var blockBuffer = new Buffer(blockData, 'hex'); var block = Block.fromBuffer(blockBuffer); node.bitcoind = { getBlock: sinon.stub().callsArgWith(1, null, blockBuffer), @@ -247,7 +276,7 @@ describe('Bitcoind Node', function() { node.chain = { tip: { __height: 0, - hash: block.prevHash + hash: lebufhex(block.header.prevHash) }, getHashes: sinon.stub().callsArgWith(1, null), saveMetadata: sinon.stub(), @@ -286,8 +315,7 @@ describe('Bitcoind Node', function() { }); it('will stop syncing when the node is stopping', function(done) { var node = new Node({}); - node.Block = Block; - var blockBuffer = new Buffer(blockData); + var blockBuffer = new Buffer(blockData, 'hex'); var block = Block.fromBuffer(blockBuffer); node.bitcoind = { getBlock: sinon.stub().callsArgWith(1, null, blockBuffer), @@ -451,7 +479,6 @@ describe('Bitcoind Node', function() { it('will set properties', function() { node._loadConsensus(); - should.exist(node.Block); should.exist(node.chain); }); @@ -501,13 +528,6 @@ describe('Bitcoind Node', function() { node._initialize(); - // references - node.db.chain.should.equal(node.chain); - node.db.Block.should.equal(node.Block); - node.db.bitcoind.should.equal(node.bitcoind); - node.chain.db.should.equal(node.db); - node.chain.db.should.equal(node.db); - // event handlers node._initializeBitcoind.callCount.should.equal(1); node._initializeDatabase.callCount.should.equal(1);