|
|
@ -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); |
|
|
|
} |
|
|
|
if (checkForTxs){ |
|
|
|
return opts.txs; |
|
|
|
} else { |
|
|
|
return Hash.sha256sha256(new Buffer.concat([left, right])); |
|
|
|
}; |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|