_traverseMerkleTree state cleanup + improve hasTransactions()

This commit is contained in:
William Wolf 2015-02-23 23:06:17 -08:00
parent bb0efd2108
commit 3eb95b6576
2 changed files with 49 additions and 27 deletions

View File

@ -154,16 +154,11 @@ MerkleBlock.prototype.validMerkleTree = function validMerkleTree() {
return false;
}
var height = 0;
while (this._calcTreeWidth(height) > 1) {
height++;
}
this._flagBitsUsed = 0;
this._hashesUsed = 0;
var root = this._traverseMerkleTree(height, 0);
if(this._hashesUsed !== this.hashes.length) {
false;
var height = this._calcTreeHeight();
var opts = { hashesUsed: 0, flagBitsUsed: 0 };
var root = this._traverseMerkleTree(height, 0, opts);
if(opts.hashesUsed !== this.hashes.length) {
return false;
}
return BufferUtil.equals(root, this.header.merkleRoot);
}
@ -172,27 +167,37 @@ MerkleBlock.prototype.validMerkleTree = function validMerkleTree() {
* Modeled after Bitcoin Core merkleblock.cpp TraverseAndExtract()
* @param {Number} - Current height
* @param {Number} - Current position in the tree
* @returns {Buffer|null} - Buffer containing the Merkle Hash far that height
* @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
* @returns {Buffer|null} - Buffer containing the Merkle Hash for that height
* @private
*/
MerkleBlock.prototype._traverseMerkleTree = function traverseMerkleTree(depth, pos) {
if(this._flagBitsUsed > this.flags.length * 8) {
MerkleBlock.prototype._traverseMerkleTree = function traverseMerkleTree(depth, pos, opts) {
opts = opts || {};
opts.txs = opts.txs || [];
opts.flagBitsUsed = opts.flagBitsUsed || 0;
opts.hashesUsed = opts.hashesUsed || 0;
if(opts.flagBitsUsed > this.flags.length * 8) {
return null;
}
var isParentOfMatch = (this.flags[this._flagBitsUsed >> 3] >>> (this._flagBitsUsed++ & 7)) & 1;
var isParentOfMatch = (this.flags[opts.flagBitsUsed >> 3] >>> (opts.flagBitsUsed++ & 7)) & 1;
if(depth === 0 || !isParentOfMatch) {
if(this._hashesUsed >= this.hashes.length) {
if(opts.hashesUsed >= this.hashes.length) {
return null;
}
var hash = this.hashes[this._hashesUsed++];
var hash = this.hashes[opts.hashesUsed++];
if(depth === 0 && isParentOfMatch) {
opts.txs.push(hash);
}
return new Buffer(hash, 'hex');
} else {
var left = this._traverseMerkleTree(depth-1, pos*2);
var right;
var left = this._traverseMerkleTree(depth-1, pos*2, opts);
var right = left;;
if(pos*2+1 < this._calcTreeWidth(depth-1)) {
right = this._traverseMerkleTree(depth-1, pos*2+1);
} else {
right = left;
right = this._traverseMerkleTree(depth-1, pos*2+1, opts);
}
return Hash.sha256sha256(new Buffer.concat([left, right]));
}
@ -208,6 +213,19 @@ 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
* @returns {Number} - Height of the merkle tree in this MerkleBlock
* @private
*/
MerkleBlock.prototype._calcTreeHeight = function calcTreeHeight() {
var height = 0;
while (this._calcTreeWidth(height) > 1) {
height++;
}
return height;
}
/**
* @param {Transaction|String} - Transaction or Transaction ID Hash
* @returns {Bool} - return true/false if this MerkleBlock has the TX or not
@ -218,11 +236,16 @@ MerkleBlock.prototype.hasTransaction = function hasTransaction(tx) {
$.checkArgument(tx instanceof Transaction || typeof tx === 'string',
'Invalid tx given, tx must be a "string" or "Transaction"');
var hash = tx.id || tx;
var revHash = BufferUtil.reverse(new Buffer(hash,'hex')).toString('hex');
var hash = tx;
if(tx instanceof Transaction) {
// We need to reverse the id hash for the lookup
hash = BufferUtil.reverse(new Buffer(tx.id, 'hex')).toString('hex');
}
return (this.hashes.indexOf(hash) !== -1
|| this.hashes.indexOf(revHash) !== -1);
var txs = [];
var height = this._calcTreeHeight();
this._traverseMerkleTree(height, 0, { txs: txs });
return txs.indexOf(hash) !== -1
}
/**

View File

@ -3,7 +3,6 @@ var bitcore = require('..');
var MerkleBlock = bitcore.MerkleBlock;
var BufferReader = bitcore.encoding.BufferReader;
var BufferWriter = bitcore.encoding.BufferWriter;
var BufferUtil = bitcore.util.buffer;
var Transaction = bitcore.Transaction;
var data = require('./data/merkleblocks.js');
var transactionVector = require('./data/tx_creation');
@ -154,7 +153,7 @@ describe('MerkleBlock', function() {
it('should find transactions via hash string', function() {
var json = data.JSON[0];
var txId = BufferUtil.reverse(new Buffer(json.hashes[1],'hex')).toString('hex');
var txId = new Buffer(json.hashes[1],'hex').toString('hex');
var b = MerkleBlock(JSON.stringify(json));
b.hasTransaction(txId).should.equal(true);
b.hasTransaction(txId + 'abcd').should.equal(false);