Merge pull request #1 from braydonf/merkleblock

Added test to check that merkle nodes do not match, and misc jshint fixes
This commit is contained in:
William Wolf 2015-02-24 08:49:27 -08:00
commit ae5723515e
2 changed files with 48 additions and 22 deletions

View File

@ -1,4 +1,5 @@
'use strict';
var _ = require('lodash');
var BlockHeader = require('./blockheader');
var BufferUtil = require('../util/buffer');
@ -18,6 +19,8 @@ var $ = require('../util/preconditions');
* @constructor
*/
function MerkleBlock(arg) {
/* jshint maxstatements: 18 */
if (!(this instanceof MerkleBlock)) {
return new MerkleBlock(arg);
}
@ -30,7 +33,7 @@ function MerkleBlock(arg) {
} else if (_.isObject(arg)) {
var header;
if(arg.header instanceof BlockHeader) {
header = arg.header
header = arg.header;
} else {
header = BlockHeader.fromJSON(JSON.stringify(arg.header));
}
@ -71,7 +74,7 @@ function MerkleBlock(arg) {
*/
MerkleBlock.fromBuffer = function fromBuffer(buf) {
return MerkleBlock.fromBufferReader(BufferReader(buf));
}
};
/**
* @param {BufferReader} - MerkleBlock data in a BufferReader object
@ -79,7 +82,7 @@ MerkleBlock.fromBuffer = function fromBuffer(buf) {
*/
MerkleBlock.fromBufferReader = function fromBufferReader(br) {
return new MerkleBlock(MerkleBlock._fromBufferReader(br));
}
};
/**
* @param {String|Object} - A JSON String or Object
@ -87,7 +90,7 @@ MerkleBlock.fromBufferReader = function fromBufferReader(br) {
*/
MerkleBlock.fromJSON = function fromJSON(buf) {
return new MerkleBlock(MerkleBlock._fromJSON(buf));
}
};
/**
* @returns {Buffer} - A buffer of the block
@ -138,7 +141,7 @@ MerkleBlock.prototype.toJSON = function toJSON() {
/**
* Verify that the MerkleBlock is valid
* @returns {Bool} - True/False whether this MerkleBlock is Valid
* @returns {Boolean} - True/False whether this MerkleBlock is Valid
*/
MerkleBlock.prototype.validMerkleTree = function validMerkleTree() {
$.checkState(_.isArray(this.flags), 'MerkleBlock flags is not an array');
@ -161,20 +164,24 @@ MerkleBlock.prototype.validMerkleTree = function validMerkleTree() {
return false;
}
return BufferUtil.equals(root, this.header.merkleRoot);
}
};
/** Traverse a the tree in this MerkleBlock, validating it along the way
* Modeled after Bitcoin Core merkleblock.cpp TraverseAndExtract()
* @param {Number} - Current height
* @param {Number} - Current position in the tree
* @param {Object} - Object with values that need to be mutated throughout the traversal
* @param {flagBitsUsed} - Number of flag bits used, should start at 0
* @param {hashesUsed} - Number of hashes used, should start at 0
* @param {tx} - Will finish populated by transactions found during traversal
/**
* Traverse a the tree in this MerkleBlock, validating it along the way
* Modeled after Bitcoin Core merkleblock.cpp TraverseAndExtract()
* @param {Number} - depth - Current height
* @param {Number} - pos - Current position in the tree
* @param {Object} - opts - Object with values that need to be mutated throughout the traversal
* @param {Number} - opts.flagBitsUsed - Number of flag bits used, should start at 0
* @param {Number} - opts.hashesUsed - Number of hashes used, should start at 0
* @param {Array} - opts.txs - Will finish populated by transactions found during traversal
* @returns {Buffer|null} - Buffer containing the Merkle Hash for that height
* @private
*/
MerkleBlock.prototype._traverseMerkleTree = function traverseMerkleTree(depth, pos, opts) {
/* jshint maxcomplexity: 12*/
/* jshint maxstatements: 20 */
opts = opts || {};
opts.txs = opts.txs || [];
opts.flagBitsUsed = opts.flagBitsUsed || 0;
@ -195,13 +202,13 @@ MerkleBlock.prototype._traverseMerkleTree = function traverseMerkleTree(depth, p
return new Buffer(hash, 'hex');
} else {
var left = this._traverseMerkleTree(depth-1, pos*2, opts);
var right = left;;
var right = left;
if(pos*2+1 < this._calcTreeWidth(depth-1)) {
right = this._traverseMerkleTree(depth-1, pos*2+1, opts);
}
return Hash.sha256sha256(new Buffer.concat([left, right]));
}
}
};
/** Calculates the width of a merkle tree at a given height.
* Modeled after Bitcoin Core merkleblock.h CalcTreeWidth()
@ -211,7 +218,7 @@ MerkleBlock.prototype._traverseMerkleTree = function traverseMerkleTree(depth, p
*/
MerkleBlock.prototype._calcTreeWidth = function calcTreeWidth(height) {
return (this.numTransactions + (1 << height) - 1) >> height;
}
};
/** Calculates the height of the merkle tree in this MerkleBlock
* @param {Number} - Height at which we want the tree width
@ -224,11 +231,11 @@ MerkleBlock.prototype._calcTreeHeight = function calcTreeHeight() {
height++;
}
return height;
}
};
/**
* @param {Transaction|String} - Transaction or Transaction ID Hash
* @returns {Bool} - return true/false if this MerkleBlock has the TX or not
* @returns {Boolean} - return true/false if this MerkleBlock has the TX or not
* @private
*/
MerkleBlock.prototype.hasTransaction = function hasTransaction(tx) {
@ -245,8 +252,8 @@ MerkleBlock.prototype.hasTransaction = function hasTransaction(tx) {
var txs = [];
var height = this._calcTreeHeight();
this._traverseMerkleTree(height, 0, { txs: txs });
return txs.indexOf(hash) !== -1
}
return txs.indexOf(hash) !== -1;
};
/**
* @param {Buffer} - MerkleBlock data

View File

@ -1,4 +1,7 @@
'use strict';
var should = require('chai').should();
var bitcore = require('..');
var MerkleBlock = bitcore.MerkleBlock;
var BufferReader = bitcore.encoding.BufferReader;
@ -143,7 +146,7 @@ describe('MerkleBlock', function() {
it('should not validate merkleblocks with too few bit flags', function() {
var b = MerkleBlock(blockJSON);
b.flags.pop()
b.flags.pop();
b.validMerkleTree().should.equal(false);
});
@ -175,6 +178,22 @@ describe('MerkleBlock', function() {
b.hasTransaction(tx).should.equal(false);
});
it('should not match with merkle nodes', function() {
var b = MerkleBlock(JSON.stringify(data.JSON[0]));
var hashData = [
['3612262624047ee87660be1a707519a443b1c1ce3d248cbfc6c15870f6c5daa2', false],
['019f5b01d4195ecbc9398fbf3c3b1fa9bb3183301d7a1fb3bd174fcfa40a2b65', true],
['41ed70551dd7e841883ab8f0b16bf04176b7d1480e4f0af9f3d4c3595768d068', false],
['20d2a7bc994987302e5b1ac80fc425fe25f8b63169ea78e68fbaaefa59379bbf', false]
];
hashData.forEach(function check(d){
b.hasTransaction(d[0]).should.equal(d[1]);
});
});
});
});