Skip to content
This repository was archived by the owner on Apr 3, 2019. It is now read-only.

Commit eee3874

Browse files
authored
Merge pull request #92 from Fi3/feature/merkleblock-add-txsThatMatchFilter
Feature/merkleblock add txs that match filter
2 parents ee61039 + 528f07f commit eee3874

File tree

4 files changed

+109
-3
lines changed

4 files changed

+109
-3
lines changed

lib/block/merkleblock.js

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ var BufferWriter = require('../encoding/bufferwriter');
88
var Hash = require('../crypto/hash');
99
var JSUtil = require('../util/js');
1010
var Transaction = require('../transaction');
11+
var errors = require('../errors');
1112
var $ = require('../util/preconditions');
1213

1314
/**
@@ -63,6 +64,7 @@ function MerkleBlock(arg) {
6364
_.extend(this,info);
6465
this._flagBitsUsed = 0;
6566
this._hashesUsed = 0;
67+
6668
return this;
6769
}
6870

@@ -149,26 +151,61 @@ MerkleBlock.prototype.validMerkleTree = function validMerkleTree() {
149151
return BufferUtil.equals(root, this.header.merkleRoot);
150152
};
151153

154+
/**
155+
* Return a list of all the txs hash that match the filter
156+
* @returns {Array} - txs hash that match the filter
157+
*/
158+
MerkleBlock.prototype.filterdTxsHash = function filterdTxsHash() {
159+
$.checkState(_.isArray(this.flags), 'MerkleBlock flags is not an array');
160+
$.checkState(_.isArray(this.hashes), 'MerkleBlock hashes is not an array');
161+
162+
// Can't have more hashes than numTransactions
163+
if(this.hashes.length > this.numTransactions) {
164+
throw new errors.MerkleBlock.InvalidMerkleTree();
165+
}
166+
167+
// Can't have more flag bits than num hashes
168+
if(this.flags.length * 8 < this.hashes.length) {
169+
throw new errors.MerkleBlock.InvalidMerkleTree();
170+
}
171+
172+
// If there is only one hash the filter do not match any txs in the block
173+
if(this.hashes.length === 1) {
174+
return [];
175+
};
176+
177+
var height = this._calcTreeHeight();
178+
var opts = { hashesUsed: 0, flagBitsUsed: 0 };
179+
var txs = this._traverseMerkleTree(height, 0, opts, true);
180+
if(opts.hashesUsed !== this.hashes.length) {
181+
throw new errors.MerkleBlock.InvalidMerkleTree();
182+
}
183+
return txs;
184+
};
185+
152186
/**
153187
* Traverse a the tree in this MerkleBlock, validating it along the way
154188
* Modeled after Bitcoin Core merkleblock.cpp TraverseAndExtract()
155189
* @param {Number} - depth - Current height
156190
* @param {Number} - pos - Current position in the tree
157191
* @param {Object} - opts - Object with values that need to be mutated throughout the traversal
192+
* @param {Boolean} - checkForTxs - if true return opts.txs else return the Merkle Hash
158193
* @param {Number} - opts.flagBitsUsed - Number of flag bits used, should start at 0
159194
* @param {Number} - opts.hashesUsed - Number of hashes used, should start at 0
160-
* @param {Array} - opts.txs - Will finish populated by transactions found during traversal
195+
* @param {Array} - opts.txs - Will finish populated by transactions found during traversal that match the filter
161196
* @returns {Buffer|null} - Buffer containing the Merkle Hash for that height
197+
* @returns {Array} - transactions found during traversal that match the filter
162198
* @private
163199
*/
164-
MerkleBlock.prototype._traverseMerkleTree = function traverseMerkleTree(depth, pos, opts) {
200+
MerkleBlock.prototype._traverseMerkleTree = function traverseMerkleTree(depth, pos, opts, checkForTxs) {
165201
/* jshint maxcomplexity: 12*/
166202
/* jshint maxstatements: 20 */
167203

168204
opts = opts || {};
169205
opts.txs = opts.txs || [];
170206
opts.flagBitsUsed = opts.flagBitsUsed || 0;
171207
opts.hashesUsed = opts.hashesUsed || 0;
208+
var checkForTxs = checkForTxs || false;
172209

173210
if(opts.flagBitsUsed > this.flags.length * 8) {
174211
return null;
@@ -189,7 +226,11 @@ MerkleBlock.prototype._traverseMerkleTree = function traverseMerkleTree(depth, p
189226
if(pos*2+1 < this._calcTreeWidth(depth-1)) {
190227
right = this._traverseMerkleTree(depth-1, pos*2+1, opts);
191228
}
192-
return Hash.sha256sha256(new Buffer.concat([left, right]));
229+
if (checkForTxs){
230+
return opts.txs;
231+
} else {
232+
return Hash.sha256sha256(new Buffer.concat([left, right]));
233+
};
193234
}
194235
};
195236

lib/errors/spec.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,13 @@ module.exports = [{
4444
'name': 'InvalidRate',
4545
'message': 'Invalid exchange rate: {0}'
4646
}]
47+
}, {
48+
name: 'MerkleBlock',
49+
message: 'Internal Error on MerkleBlock {0}',
50+
errors: [{
51+
'name': 'InvalidMerkleTree',
52+
'message': 'This MerkleBlock contain an invalid Merkle Tree'
53+
}]
4754
}, {
4855
name: 'Transaction',
4956
message: 'Internal Error on Transaction {0}',

test/block/merkleblock.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,36 @@ describe('MerkleBlock', function() {
152152

153153
});
154154

155+
describe('#filterdTxsHash', function() {
156+
157+
it('should validate good merkleblocks', function() {
158+
var hashOfFilteredTx = '6f64fd5aa9dd01f74c03656d376625cf80328d83d9afebe60cc68b8f0e245bd9'
159+
var b = MerkleBlock(data.JSON[3]);
160+
b.filterdTxsHash()[0].should.equal(hashOfFilteredTx);
161+
});
162+
163+
it('should fail with merkleblocks with too many hashes', function() {
164+
var b = MerkleBlock(data.JSON[0]);
165+
// Add too many hashes
166+
var i = 0;
167+
while(i <= b.numTransactions) {
168+
b.hashes.push('bad' + i++);
169+
}
170+
(function() {
171+
b.filterdTxsHash();
172+
}).should.throw('This MerkleBlock contain an invalid Merkle Tree');
173+
});
174+
175+
it('should fail with merkleblocks with too few bit flags', function() {
176+
var b = MerkleBlock(JSON.parse(blockJSON));
177+
b.flags.pop();
178+
(function() {
179+
b.filterdTxsHash();
180+
}).should.throw('This MerkleBlock contain an invalid Merkle Tree');
181+
});
182+
183+
});
184+
155185
describe('#hasTransaction', function() {
156186

157187
it('should find transactions via hash string', function() {

test/data/merkleblocks.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,34 @@ module.exports = {
453453
nonce : 322045839,
454454
bits : 419587686,
455455
}
456+
},
457+
{ // Mainnet FilteredBlock 399775 with filter: 6f64fd5aa9dd01f74c03656d376625cf80328d83d9afebe60cc68b8f0e245bd9
458+
"header": {
459+
"hash": "0000000000000000011b04bc9f4f3856e299b53a335eb1c42be906237c860bb8",
460+
"version": 4,
461+
"prevHash": "0000000000000000015373947aa93c7cb16a308fb0a59644d4123072ad24ce5b",
462+
"merkleRoot": "ac1841eb3b7d380ee114270e3b1c7df349f1e27e2f0f7891138199bc07e006f8",
463+
"time": 1456274787,
464+
"bits": 403093919,
465+
"nonce": 736568686
466+
},
467+
"numTransactions": 3309,
468+
"hashes": [
469+
"adaf1e41a5349a7e2b27e6f2b5fc1186d576d21b47531a410a654439f49bd5a9",
470+
"0f64562b6d361757bfdc5926d28cafb7c45e4822b4ad5a14e65530d0c9d44cd2",
471+
"c34dec8d5954d8c151c3a594a5a6d1f3a1ec8a1a6c470b27aea879b7757328fb",
472+
"c044e5d998fe29cdbe88f87c97c3547f142f5b491cb909d9b0a8e9d3ab7fc984",
473+
"51ecd2d2b02b0ca1a3b439eae3adf87ab37dd87f7be0c5b3437cd22350079101",
474+
"684ebe37c6eaae2879249ff20b3769d5f2f7ad853a5363a4baee68c0c85777a5",
475+
"e561fbeb0faff42f95f2dc8d57d432d4d1a9f84483597a0602e1edc3390f1e33",
476+
"02c070afc743c885a6c8d94d15d6e9c3b991c636b4284258dd37686c8792fe44",
477+
"6f64fd5aa9dd01f74c03656d376625cf80328d83d9afebe60cc68b8f0e245bd9",
478+
"a296fef97b3da0825bac00c794ea234913421b7c1cbb8571978d8d5ba3b16de2",
479+
"691c6855c5da434a06cb39a7e47d9337da0e39112ad975cc70cecc882233920a",
480+
"6a9e7f8e6d1d8c326a774ff691bdf424252cee710f8e5d9da2c094e999c15efc",
481+
"0723ffc695fef989e86784fa2b47097d55f6aac244631536d8d901f5cd9bf170"
482+
],
483+
"flags": [171,86,23,0]
456484
}
457485
]
458486
};

0 commit comments

Comments
 (0)