diff --git a/lib/block/merkleblock.js b/lib/block/merkleblock.js index 3ff541e..3a0ef64 100644 --- a/lib/block/merkleblock.js +++ b/lib/block/merkleblock.js @@ -8,6 +8,7 @@ var BufferWriter = require('../encoding/bufferwriter'); var Hash = require('../crypto/hash'); var JSUtil = require('../util/js'); var Transaction = require('../transaction'); +var errors = require('../errors'); var $ = require('../util/preconditions'); /** @@ -63,6 +64,7 @@ function MerkleBlock(arg) { _.extend(this,info); this._flagBitsUsed = 0; this._hashesUsed = 0; + return this; } @@ -149,19 +151,48 @@ MerkleBlock.prototype.validMerkleTree = function validMerkleTree() { return BufferUtil.equals(root, this.header.merkleRoot); }; +/** + * Return a list of all the txs hash that match the filter + * @returns {Array} - txs hash that match the filter + */ +MerkleBlock.prototype.filterdTxsHash = function validMerkleTree() { + $.checkState(_.isArray(this.flags), 'MerkleBlock flags is not an array'); + $.checkState(_.isArray(this.hashes), 'MerkleBlock hashes is not an array'); + + // Can't have more hashes than numTransactions + if(this.hashes.length > this.numTransactions) { + throw new errors.MerkleBlock.InvalidMerkleTree(); + } + + // Can't have more flag bits than num hashes + if(this.flags.length * 8 < this.hashes.length) { + throw new errors.MerkleBlock.InvalidMerkleTree(); + } + + var height = this._calcTreeHeight(); + var opts = { hashesUsed: 0, flagBitsUsed: 0 }; + var txs = this._traverseMerkleTree(height, 0, opts, true); + if(opts.hashesUsed !== this.hashes.length) { + throw new errors.MerkleBlock.InvalidMerkleTree(); + } + return txs; +}; + /** * 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 {Boolean} - checkForTxs - if true return opts.txs else return the Merkle Hash * @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 + * @param {Array} - opts.txs - Will finish populated by transactions found during traversal that match the filter * @returns {Buffer|null} - Buffer containing the Merkle Hash for that height + * @returns {Array} - transactions found during traversal that match the filter * @private */ -MerkleBlock.prototype._traverseMerkleTree = function traverseMerkleTree(depth, pos, opts) { +MerkleBlock.prototype._traverseMerkleTree = function traverseMerkleTree(depth, pos, opts, checkForTxs) { /* jshint maxcomplexity: 12*/ /* jshint maxstatements: 20 */ @@ -169,6 +200,7 @@ MerkleBlock.prototype._traverseMerkleTree = function traverseMerkleTree(depth, p opts.txs = opts.txs || []; opts.flagBitsUsed = opts.flagBitsUsed || 0; opts.hashesUsed = opts.hashesUsed || 0; + var checkForTxs = checkForTxs || false; if(opts.flagBitsUsed > this.flags.length * 8) { return null; @@ -189,7 +221,11 @@ MerkleBlock.prototype._traverseMerkleTree = function traverseMerkleTree(depth, p 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])); + if (checkForTxs){ + return opts.txs; + } else { + return Hash.sha256sha256(new Buffer.concat([left, right])); + }; } };