diff --git a/lib/block/merkleblock.js b/lib/block/merkleblock.js index 60068f7..311adcf 100644 --- a/lib/block/merkleblock.js +++ b/lib/block/merkleblock.js @@ -6,6 +6,7 @@ var BufferReader = require('../encoding/bufferreader'); var BufferWriter = require('../encoding/bufferwriter'); var Hash = require('../crypto/hash'); var JSUtil = require('../util/js'); +var Transaction = require('../transaction'); var $ = require('../util/preconditions'); /** @@ -139,6 +140,8 @@ MerkleBlock.prototype.toJSON = function toJSON() { * @returns {Bool} - True/False whether this MerkleBlock is Valid */ MerkleBlock.prototype.validMerkleTree = function validMerkleTree() { + $.checkState(this.flags instanceof Array, 'MerkleBlock flags is not an array'); + $.checkState(this.hashes instanceof Array, 'MerkleBlock flags is not an array'); var self = this; if(this._validMerkleTree === true) { return true; @@ -207,6 +210,26 @@ MerkleBlock.prototype.validMerkleTree = function validMerkleTree() { } } +/** + * @param {Transaction|String} - Transaction or Transaction ID Hash + * @returns {Bool} - return true/false if this MerkleBlock has the TX or not + * @private + */ +MerkleBlock.prototype.hasTransaction = function hasTransaction(tx) { + $.checkArgument(!_.isUndefined(tx), 'No transaction given'); + $.checkArgument(tx instanceof Transaction + || typeof tx === 'string', 'No transaction given'); + + var hash = tx; + if(tx instanceof Transaction) { + hash = tx.id; + } + var revHash = BufferUtil.reverse(new Buffer(hash,'hex')).toString('hex'); + + return (this.hashes.indexOf(hash) !== -1 + || this.hashes.indexOf(revHash) !== -1); +} + /** * @param {Bool} - set the merkle tree validity * @returns {Bool} - return true/false diff --git a/test/data/merkleblocks.js b/test/data/merkleblocks.js index 33aadc8..b6bff4d 100644 --- a/test/data/merkleblocks.js +++ b/test/data/merkleblocks.js @@ -1,8 +1,14 @@ 'use strict'; module.exports = { + TXHEX: [ + [ // From Mainnet Block 100014 + // From: http://btc.blockr.io/api/v1/tx/raw/652b0aa4cf4f17bdb31f7a1d308331bba91f3b3cbf8f39c9cb5e19d4015b9f01 + "0100000001834537b2f1ce8ef9373a258e10545ce5a50b758df616cd4356e0032554ebd3c4000000008b483045022100e68f422dd7c34fdce11eeb4509ddae38201773dd62f284e8aa9d96f85099d0b002202243bd399ff96b649a0fad05fa759d6a882f0af8c90cf7632c2840c29070aec20141045e58067e815c2f464c6a2a15f987758374203895710c2d452442e28496ff38ba8f5fd901dc20e29e88477167fe4fc299bf818fd0d9e1632d467b2a3d9503b1aaffffffff0280d7e636030000001976a914f34c3e10eb387efe872acb614c89e78bfca7815d88ac404b4c00000000001976a914a84e272933aaf87e1715d7786c51dfaeb5b65a6f88ac00000000" + ], + ], HEX: [ - // Mainnet Block 100015 + // Mainnet Block 100014 "01000000" + // Version "82bb869cf3a793432a66e826e05a6fc37469f8efb7421dc88067010000000000" + // prevHash "7f16c5962e8bd963659c793ce370d95f093bc7e367117b3c30c1f8fdd0d97287" + // MerkleRoot @@ -19,7 +25,7 @@ module.exports = { "1d" // Flags ], JSON: [ - { // Mainnet Block 100015 + { // Mainnet Block 100014 header: { version: 1, prevHash: "82bb869cf3a793432a66e826e05a6fc37469f8efb7421dc88067010000000000", @@ -54,100 +60,13 @@ module.exports = { }, { // Mainnet Block 280472 flags : [ - 255, - 85, - 218, - 225, - 90, - 173, - 229, - 43, - 183, - 195, - 213, - 229, - 43, - 108, - 43, - 219, - 226, - 215, - 217, - 226, - 61, - 92, - 253, - 92, - 237, - 134, - 215, - 170, - 174, - 182, - 170, - 237, - 220, - 251, - 106, - 235, - 109, - 109, - 253, - 219, - 58, - 159, - 182, - 221, - 190, - 189, - 181, - 126, - 251, - 223, - 223, - 254, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 15 + 255, 85, 218, 225, 90, 173, 229, 43, 183, 195, 213, 229, 43, 108, 43, + 219, 226, 215, 217, 226, 61, 92, 253, 92, 237, 134, 215, 170, 174, 182, + 170, 237, 220, 251, 106, 235, 109, 109, 253, 219, 58, 159, 182, 221, + 190, 189, 181, 126, 251, 223, 223, 254, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 15 ], numTransactions : 1159, hashes : [ diff --git a/test/merkleblock.js b/test/merkleblock.js index d2069f7..8b1a506 100644 --- a/test/merkleblock.js +++ b/test/merkleblock.js @@ -1,10 +1,13 @@ 'use strict'; - var bitcore = require('..'); var MerkleBlock = bitcore.MerkleBlock; var BufferReader = bitcore.encoding.BufferReader; var BufferWriter = bitcore.encoding.BufferWriter; +var BufferUtil = bitcore.util.buffer; +var Transaction = bitcore.Transaction; var data = require('./data/merkleblocks.js'); +var transactionVector = require('./data/tx_creation'); + describe('MerkleBlock', function() { var blockhex = data.HEX[0]; @@ -139,8 +142,53 @@ describe('MerkleBlock', function() { }); }); + it('should not validate merkleblocks with too many hashes', function() { + var json = data.JSON[0]; + var b = MerkleBlock(JSON.stringify(data.JSON[0])); + // Add too many hashes + var i = 0; + while(i <= b.numTransactions) { + b.hashes.push('bad' + i++); + } + b.validMerkleTree().should.equal(false); + }); + + it('should not validate merkleblocks with too few bit flags', function() { + var json = JSON.stringify(data.JSON[0]); + var b = MerkleBlock(json); + b.flags.pop() + b.validMerkleTree().should.equal(false); + }); + }); + describe('#hasTransaction', function() { + + it('should find transactions via hash string', function() { + var json = data.JSON[0]; + var txId = BufferUtil.reverse(new Buffer(json.hashes[1],'hex')).toString('hex'); + var b = MerkleBlock(JSON.stringify(json)); + b.hasTransaction(txId).should.equal(true); + b.hasTransaction(txId + 'abcd').should.equal(false); + }); + + it('should find transactions via Transaction object', function() { + var json = data.JSON[0]; + var txBuf = new Buffer(data.TXHEX[0][0],'hex'); + var tx = new Transaction().fromBuffer(txBuf); + var b = MerkleBlock(JSON.stringify(json)); + b.hasTransaction(tx).should.equal(true); + }); + + it('should not find non-existant Transaction object', function() { + // Reuse another transaction already in data/ dir + var serialized = transactionVector[0][7]; + var tx = new Transaction().fromBuffer(new Buffer(serialized, 'hex')); + var b = MerkleBlock(JSON.stringify(data.JSON[0])); + b.hasTransaction(tx).should.equal(false); + }); + + }); });