Use Bitcore Block/BlockHeader

This commit is contained in:
Braydon Fuller 2015-08-27 10:10:07 -04:00
parent bbc421a31e
commit ccab818d33
13 changed files with 136 additions and 451 deletions

View File

@ -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 = [];

View File

@ -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');

View File

@ -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;

View File

@ -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');
@ -144,13 +144,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);

View File

@ -6,6 +6,7 @@ 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;
@ -39,7 +40,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;
@ -106,7 +106,7 @@ DB.prototype.getBlock = function(hash, callback) {
if(err) {
return callback(err);
}
callback(null, self.Block.fromBuffer(blockData));
callback(null, Block.fromBuffer(blockData));
});
};
@ -232,43 +232,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) {

View File

@ -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) {
@ -166,7 +166,8 @@ AddressModule.prototype.blockHandler = function(block, addOutput, callback) {
}
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
};
}

View File

@ -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;
@ -136,7 +137,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 +185,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 +200,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 +245,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 +259,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 +271,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 +375,6 @@ Node.prototype._loadConsensus = function(config) {
options = _.clone(config.consensus || {});
}
options.node = this;
this.Block = Block;
this.chain = new Chain(options);
};
@ -393,7 +398,6 @@ Node.prototype._initialize = function() {
// DB References
this.db.chain = this.chain;
this.db.Block = this.Block;
this.db.bitcoind = this.bitcoind;
// Chain References

View File

@ -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);
});
});
});

View File

@ -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');
@ -165,21 +166,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();
@ -232,7 +218,9 @@ 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 chain = new Chain({

View File

@ -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": ""
}
]

View File

@ -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');
@ -87,17 +89,16 @@ describe('Bitcoin DB', function() {
describe('#getBlock', function() {
var db = new DB({store: memdown});
var blockBuffer = new Buffer(blockData, 'hex');
var expectedBlock = Block.fromBuffer(blockBuffer);
db.bitcoind = {
getBlock: sinon.stub().callsArgWith(1, null, new Buffer(blockData, 'hex'))
};
db.Block = {
fromBuffer: sinon.stub().returns('block')
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();
});
});

View File

@ -97,21 +97,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 +126,13 @@ describe('AddressModule', function() {
value: {
txid: '5780f3ee54889a0717152a01abee9a32cec1b0cdf8d5537a08c7bd9eeb6bfbca',
inputIndex: 0,
timestamp: 1424836934000
timestamp: 1424836934
}
},
{
key: {
address: '1Ep5LA4T6Y7zaBPiwruUJurjGFvCJHzJhm',
timestamp: 1424836934000,
timestamp: 1424836934,
txid: 'e66f3b989c790178de2fc1a5329f94c0d8905d0d3df4e7ecf0115e7f90a6283d',
outputIndex: 1
},
@ -158,7 +155,15 @@ describe('AddressModule', function() {
});
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 +182,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,22 +205,7 @@ 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()
}
@ -216,30 +213,52 @@ describe('AddressModule', function() {
var am = new AddressModule({db: db, 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: {
bitcoind: {
on: sinon.stub()
}
};
var am = new AddressModule({db: db, 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) {

View File

@ -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);
});
@ -503,7 +530,6 @@ describe('Bitcoind Node', function() {
// 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);