|
|
@ -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 |
|
|
|