Merge pull request #938 from braydonf/tests/block
Fixed an issue with block deserialization
This commit is contained in:
commit
92b2e1e614
68
lib/block.js
68
lib/block.js
|
@ -9,7 +9,6 @@ var BufferWriter = require('./encoding/bufferwriter');
|
||||||
var Hash = require('./crypto/hash');
|
var Hash = require('./crypto/hash');
|
||||||
var JSUtil = require('./util/js');
|
var JSUtil = require('./util/js');
|
||||||
var Transaction = require('./transaction');
|
var Transaction = require('./transaction');
|
||||||
var Varint = require('./encoding/varint');
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instantiate a Block from a Buffer, JSON object, or Object with
|
* Instantiate a Block from a Buffer, JSON object, or Object with
|
||||||
|
@ -44,27 +43,16 @@ Block._from = function _from(arg) {
|
||||||
info = Block._fromJSON(arg);
|
info = Block._fromJSON(arg);
|
||||||
} else if (_.isObject(arg)) {
|
} else if (_.isObject(arg)) {
|
||||||
info = {
|
info = {
|
||||||
/**
|
|
||||||
* @name Block#magicnum
|
|
||||||
* @type number
|
|
||||||
*/
|
|
||||||
magicnum: arg.magicnum,
|
|
||||||
/**
|
|
||||||
* @name Block#size
|
|
||||||
* @type number
|
|
||||||
*/
|
|
||||||
size: arg.size,
|
|
||||||
/**
|
/**
|
||||||
* @name Block#header
|
* @name Block#header
|
||||||
* @type {BlockHeader}
|
* @type {BlockHeader}
|
||||||
*/
|
*/
|
||||||
header: arg.header,
|
header: arg.header,
|
||||||
txsvi: arg.txsvi,
|
|
||||||
/**
|
/**
|
||||||
* @name Block#txs
|
* @name Block#transactions
|
||||||
* @type {Transaction[]}
|
* @type {Transaction[]}
|
||||||
*/
|
*/
|
||||||
txs: arg.txs
|
transactions: arg.transactions
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
throw new TypeError('Unrecognized argument for Block');
|
throw new TypeError('Unrecognized argument for Block');
|
||||||
|
@ -81,16 +69,13 @@ Block._fromJSON = function _fromJSON(data) {
|
||||||
if (JSUtil.isValidJSON(data)) {
|
if (JSUtil.isValidJSON(data)) {
|
||||||
data = JSON.parse(data);
|
data = JSON.parse(data);
|
||||||
}
|
}
|
||||||
var txs = [];
|
var transactions = [];
|
||||||
data.txs.forEach(function(tx) {
|
data.transactions.forEach(function(data) {
|
||||||
txs.push(Transaction().fromJSON(tx));
|
transactions.push(Transaction().fromJSON(data));
|
||||||
});
|
});
|
||||||
var info = {
|
var info = {
|
||||||
magicnum: data.magicnum,
|
|
||||||
size: data.size,
|
|
||||||
header: BlockHeader.fromJSON(data.header),
|
header: BlockHeader.fromJSON(data.header),
|
||||||
txsvi: Varint().fromString(data.txsvi),
|
transactions: transactions
|
||||||
txs: txs
|
|
||||||
};
|
};
|
||||||
return info;
|
return info;
|
||||||
};
|
};
|
||||||
|
@ -111,14 +96,11 @@ Block.fromJSON = function fromJSON(json) {
|
||||||
*/
|
*/
|
||||||
Block._fromBufferReader = function _fromBufferReader(br) {
|
Block._fromBufferReader = function _fromBufferReader(br) {
|
||||||
var info = {};
|
var info = {};
|
||||||
info.magicnum = br.readUInt32LE();
|
|
||||||
info.size = br.readUInt32LE();
|
|
||||||
info.header = BlockHeader.fromBufferReader(br);
|
info.header = BlockHeader.fromBufferReader(br);
|
||||||
info.txsvi = Varint(br.readVarintBuf());
|
var transactions = br.readVarintNum();
|
||||||
var txslen = info.txsvi.toNumber();
|
info.transactions = [];
|
||||||
info.txs = [];
|
for (var i = 0; i < transactions; i++) {
|
||||||
for (var i = 0; i < txslen; i++) {
|
info.transactions.push(Transaction().fromBufferReader(br));
|
||||||
info.txs.push(Transaction().fromBufferReader(br));
|
|
||||||
}
|
}
|
||||||
return info;
|
return info;
|
||||||
};
|
};
|
||||||
|
@ -158,6 +140,7 @@ Block.fromRawBlock = function fromRawBlock(data) {
|
||||||
data = new Buffer(data, 'binary');
|
data = new Buffer(data, 'binary');
|
||||||
}
|
}
|
||||||
var br = BufferReader(data);
|
var br = BufferReader(data);
|
||||||
|
br.pos = Block.Values.START_OF_BLOCK;
|
||||||
var info = Block._fromBufferReader(br);
|
var info = Block._fromBufferReader(br);
|
||||||
return new Block(info);
|
return new Block(info);
|
||||||
};
|
};
|
||||||
|
@ -166,16 +149,13 @@ Block.fromRawBlock = function fromRawBlock(data) {
|
||||||
* @returns {Object} - A plain object with the block properties
|
* @returns {Object} - A plain object with the block properties
|
||||||
*/
|
*/
|
||||||
Block.prototype.toObject = function toObject() {
|
Block.prototype.toObject = function toObject() {
|
||||||
var txs = [];
|
var transactions = [];
|
||||||
this.txs.forEach(function(tx) {
|
this.transactions.forEach(function(tx) {
|
||||||
txs.push(tx.toObject());
|
transactions.push(tx.toObject());
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
magicnum: this.magicnum,
|
|
||||||
size: this.size,
|
|
||||||
header: this.header.toObject(),
|
header: this.header.toObject(),
|
||||||
txsvi: this.txsvi.toString(),
|
transactions: transactions
|
||||||
txs: txs
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -208,13 +188,10 @@ Block.prototype.toBufferWriter = function toBufferWriter(bw) {
|
||||||
if (!bw) {
|
if (!bw) {
|
||||||
bw = new BufferWriter();
|
bw = new BufferWriter();
|
||||||
}
|
}
|
||||||
bw.writeUInt32LE(this.magicnum);
|
|
||||||
bw.writeUInt32LE(this.size);
|
|
||||||
bw.write(this.header.toBuffer());
|
bw.write(this.header.toBuffer());
|
||||||
bw.write(this.txsvi.buf);
|
bw.writeVarintNum(this.transactions.length);
|
||||||
var txslen = this.txsvi.toNumber();
|
for (var i = 0; i < this.transactions.length; i++) {
|
||||||
for (var i = 0; i < txslen; i++) {
|
this.transactions[i].toBufferWriter(bw);
|
||||||
this.txs[i].toBufferWriter(bw);
|
|
||||||
}
|
}
|
||||||
return bw;
|
return bw;
|
||||||
};
|
};
|
||||||
|
@ -225,11 +202,11 @@ Block.prototype.toBufferWriter = function toBufferWriter(bw) {
|
||||||
*/
|
*/
|
||||||
Block.prototype.getTransactionHashes = function getTransactionHashes() {
|
Block.prototype.getTransactionHashes = function getTransactionHashes() {
|
||||||
var hashes = [];
|
var hashes = [];
|
||||||
if (this.txs.length === 0) {
|
if (this.transactions.length === 0) {
|
||||||
return [Block.Values.NULL_HASH];
|
return [Block.Values.NULL_HASH];
|
||||||
}
|
}
|
||||||
for (var t = 0; t < this.txs.length; t++) {
|
for (var t = 0; t < this.transactions.length; t++) {
|
||||||
hashes.push(this.txs[t]._getHash());
|
hashes.push(this.transactions[t]._getHash());
|
||||||
}
|
}
|
||||||
return hashes;
|
return hashes;
|
||||||
};
|
};
|
||||||
|
@ -245,7 +222,7 @@ Block.prototype.getMerkleTree = function getMerkleTree() {
|
||||||
var tree = this.getTransactionHashes();
|
var tree = this.getTransactionHashes();
|
||||||
|
|
||||||
var j = 0;
|
var j = 0;
|
||||||
for (var size = this.txs.length; size > 1; size = Math.floor((size + 1) / 2)) {
|
for (var size = this.transactions.length; size > 1; size = Math.floor((size + 1) / 2)) {
|
||||||
for (var i = 0; i < size; i += 2) {
|
for (var i = 0; i < size; i += 2) {
|
||||||
var i2 = Math.min(i + 1, size - 1);
|
var i2 = Math.min(i + 1, size - 1);
|
||||||
var buf = Buffer.concat([tree[j + i], tree[j + i2]]);
|
var buf = Buffer.concat([tree[j + i], tree[j + i2]]);
|
||||||
|
@ -314,6 +291,7 @@ Block.prototype.inspect = function inspect() {
|
||||||
};
|
};
|
||||||
|
|
||||||
Block.Values = {
|
Block.Values = {
|
||||||
|
START_OF_BLOCK: 8, // Start of block in raw block data
|
||||||
NULL_HASH: new Buffer('0000000000000000000000000000000000000000000000000000000000000000', 'hex')
|
NULL_HASH: new Buffer('0000000000000000000000000000000000000000000000000000000000000000', 'hex')
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -119,14 +119,13 @@ BlockHeader.fromString = function fromString(str) {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
BlockHeader._fromBufferReader = function _fromBufferReader(br) {
|
BlockHeader._fromBufferReader = function _fromBufferReader(br) {
|
||||||
var info = {
|
var info = {};
|
||||||
version: br.readUInt32LE(),
|
info.version = br.readUInt32LE();
|
||||||
prevHash: br.read(32),
|
info.prevHash = br.read(32);
|
||||||
merkleRoot: br.read(32),
|
info.merkleRoot = br.read(32);
|
||||||
time: br.readUInt32LE(),
|
info.time = br.readUInt32LE();
|
||||||
bits: br.readUInt32LE(),
|
info.bits = br.readUInt32LE();
|
||||||
nonce: br.readUInt32LE()
|
info.nonce = br.readUInt32LE();
|
||||||
};
|
|
||||||
return info;
|
return info;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -10,29 +10,26 @@ var chai = require('chai');
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var should = chai.should();
|
var should = chai.should();
|
||||||
var Transaction = bitcore.Transaction;
|
var Transaction = bitcore.Transaction;
|
||||||
var Varint = bitcore.encoding.Varint;
|
|
||||||
|
|
||||||
// https://test-insight.bitpay.com/block/000000000b99b16390660d79fcc138d2ad0c89a0d044c4201a02bdf1f61ffa11
|
// https://test-insight.bitpay.com/block/000000000b99b16390660d79fcc138d2ad0c89a0d044c4201a02bdf1f61ffa11
|
||||||
var dataRawBlockBuffer = fs.readFileSync('test/data/blk86756-testnet.dat');
|
var dataRawBlockBuffer = fs.readFileSync('test/data/blk86756-testnet.dat');
|
||||||
var dataRawBlockBinary = fs.readFileSync('test/data/blk86756-testnet.dat', 'binary');
|
var dataRawBlockBinary = fs.readFileSync('test/data/blk86756-testnet.dat', 'binary');
|
||||||
var dataJson = fs.readFileSync('test/data/blk86756-testnet.json').toString();
|
var dataJson = fs.readFileSync('test/data/blk86756-testnet.json').toString();
|
||||||
var data = require('./data/blk86756-testnet');
|
var data = require('./data/blk86756-testnet');
|
||||||
|
var dataBlocks = require('./data/bitcoind/blocks');
|
||||||
|
|
||||||
describe('Block', function() {
|
describe('Block', function() {
|
||||||
|
|
||||||
var magicnum = data.magicnum;
|
|
||||||
var blockhex = data.blockhex;
|
var blockhex = data.blockhex;
|
||||||
var blockbuf = new Buffer(blockhex, 'hex');
|
var blockbuf = new Buffer(blockhex, 'hex');
|
||||||
var size = data.blocksize;
|
|
||||||
var bh = BlockHeader.fromBuffer(new Buffer(data.blockheaderhex, 'hex'));
|
var bh = BlockHeader.fromBuffer(new Buffer(data.blockheaderhex, 'hex'));
|
||||||
var txsvi = Varint().fromNumber(data.txsvi);
|
|
||||||
var txs = [];
|
var txs = [];
|
||||||
JSON.parse(dataJson).txs.forEach(function(tx){
|
JSON.parse(dataJson).transactions.forEach(function(tx){
|
||||||
txs.push(new Transaction().fromJSON(tx));
|
txs.push(new Transaction().fromJSON(tx));
|
||||||
});
|
});
|
||||||
var json = dataJson;
|
var json = dataJson;
|
||||||
|
|
||||||
var genesishex = 'f9beb4d91d0100000100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f49ffff001d1dac2b7c0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000';
|
var genesishex = '0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f49ffff001d1dac2b7c0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000';
|
||||||
var genesisbuf = new Buffer(genesishex, 'hex');
|
var genesisbuf = new Buffer(genesishex, 'hex');
|
||||||
var genesisidhex = '000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f';
|
var genesisidhex = '000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f';
|
||||||
|
|
||||||
|
@ -43,7 +40,7 @@ describe('Block', function() {
|
||||||
|
|
||||||
it('should not make an empty block', function() {
|
it('should not make an empty block', function() {
|
||||||
(function() {
|
(function() {
|
||||||
var b = new Block();
|
return new Block();
|
||||||
}).should.throw('Unrecognized argument for Block');
|
}).should.throw('Unrecognized argument for Block');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -51,17 +48,18 @@ describe('Block', function() {
|
||||||
|
|
||||||
it('should set these known values', function() {
|
it('should set these known values', function() {
|
||||||
var b = new Block({
|
var b = new Block({
|
||||||
magicnum: magicnum,
|
|
||||||
size: size,
|
|
||||||
header: bh,
|
header: bh,
|
||||||
txsvi: txsvi,
|
transactions: txs
|
||||||
txs: txs
|
|
||||||
});
|
});
|
||||||
should.exist(b.magicnum);
|
|
||||||
should.exist(b.size);
|
|
||||||
should.exist(b.txsvi);
|
|
||||||
should.exist(b.header);
|
should.exist(b.header);
|
||||||
should.exist(b.txs);
|
should.exist(b.transactions);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should properly deserialize blocks', function() {
|
||||||
|
dataBlocks.forEach(function(block){
|
||||||
|
var b = Block.fromBuffer(new Buffer(block.data, 'hex'));
|
||||||
|
b.transactions.length.should.equal(block.transactions);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -86,21 +84,15 @@ describe('Block', function() {
|
||||||
|
|
||||||
it('should set these known values', function() {
|
it('should set these known values', function() {
|
||||||
var block = Block.fromJSON(json);
|
var block = Block.fromJSON(json);
|
||||||
should.exist(block.magicnum);
|
|
||||||
should.exist(block.size);
|
|
||||||
should.exist(block.header);
|
should.exist(block.header);
|
||||||
should.exist(block.txsvi);
|
should.exist(block.transactions);
|
||||||
should.exist(block.txs);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set these known values', function() {
|
it('should set these known values', function() {
|
||||||
|
|
||||||
var block = Block(json);
|
var block = Block(json);
|
||||||
should.exist(block.magicnum);
|
|
||||||
should.exist(block.size);
|
|
||||||
should.exist(block.header);
|
should.exist(block.header);
|
||||||
should.exist(block.txsvi);
|
should.exist(block.transactions);
|
||||||
should.exist(block.txs);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('accepts an object as argument', function() {
|
it('accepts an object as argument', function() {
|
||||||
|
@ -115,11 +107,8 @@ describe('Block', function() {
|
||||||
it('should recover these known values', function() {
|
it('should recover these known values', function() {
|
||||||
var block = Block.fromJSON(json);
|
var block = Block.fromJSON(json);
|
||||||
var b = JSON.parse(block.toJSON());
|
var b = JSON.parse(block.toJSON());
|
||||||
should.exist(b.magicnum);
|
|
||||||
should.exist(b.size);
|
|
||||||
should.exist(b.header);
|
should.exist(b.header);
|
||||||
should.exist(b.txsvi);
|
should.exist(b.transactions);
|
||||||
should.exist(b.txs);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -216,14 +205,14 @@ describe('Block', function() {
|
||||||
|
|
||||||
it('should describe as invalid merkle root', function() {
|
it('should describe as invalid merkle root', function() {
|
||||||
var x = Block.fromRawBlock(dataRawBlockBinary);
|
var x = Block.fromRawBlock(dataRawBlockBinary);
|
||||||
x.txs.push(new Transaction());
|
x.transactions.push(new Transaction());
|
||||||
var valid = x.validMerkleRoot();
|
var valid = x.validMerkleRoot();
|
||||||
valid.should.equal(false);
|
valid.should.equal(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should get a null hash merkle root', function() {
|
it('should get a null hash merkle root', function() {
|
||||||
var x = Block.fromRawBlock(dataRawBlockBinary);
|
var x = Block.fromRawBlock(dataRawBlockBinary);
|
||||||
x.txs = []; // empty the txs
|
x.transactions = []; // empty the txs
|
||||||
var mr = x.getMerkleRoot();
|
var mr = x.getMerkleRoot();
|
||||||
mr.should.deep.equal(Block.Values.NULL_HASH);
|
mr.should.deep.equal(Block.Values.NULL_HASH);
|
||||||
});
|
});
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1,6 +1,4 @@
|
||||||
{
|
{
|
||||||
"magicnum": 118034699,
|
|
||||||
"size": 8003,
|
|
||||||
"header": {
|
"header": {
|
||||||
"version": 2,
|
"version": 2,
|
||||||
"prevHash": "4baaa9507c3b27908397ea7bc177a998e9f4fe38b9d5130be7b5353c00000000",
|
"prevHash": "4baaa9507c3b27908397ea7bc177a998e9f4fe38b9d5130be7b5353c00000000",
|
||||||
|
@ -9,8 +7,7 @@
|
||||||
"bits": 473956288,
|
"bits": 473956288,
|
||||||
"nonce": 3594009557
|
"nonce": 3594009557
|
||||||
},
|
},
|
||||||
"txsvi": "16",
|
"transactions": [{
|
||||||
"txs": [{
|
|
||||||
"version": 1,
|
"version": 1,
|
||||||
"txinsvi": "01",
|
"txinsvi": "01",
|
||||||
"txins": [{
|
"txins": [{
|
||||||
|
|
Loading…
Reference in New Issue