|
|
@ -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 |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|