├── .zuul.yml ├── .gitignore ├── test ├── browser.sh ├── travis.sh ├── util.js ├── index.js └── fixtures.json ├── package.json ├── .travis.yml ├── README.md └── index.js /.zuul.yml: -------------------------------------------------------------------------------- 1 | name: bitc**n-merkle-proof 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .nyc_output 3 | npm-debug.log 4 | -------------------------------------------------------------------------------- /test/browser.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ $BROWSER ]; then 4 | zuul \ 5 | --browser-name $BROWSER \ 6 | --browser-version latest \ 7 | --ui tape \ 8 | --tunnel ngrok \ 9 | -- test/*.js 10 | else 11 | zuul --local --ui tape -- test/*.js 12 | fi 13 | -------------------------------------------------------------------------------- /test/travis.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [ -z $NODE ]; then 4 | export NODE=6 5 | fi 6 | 7 | echo Installing nvm... 8 | git clone https://github.com/creationix/nvm.git /tmp/.nvm 9 | source /tmp/.nvm/nvm.sh 10 | echo Installed nvm version `nvm --version` 11 | nvm install $NODE 12 | nvm use $NODE 13 | nvm alias default $NODE 14 | echo node version: `node --version` 15 | echo npm version: `npm --version` 16 | npm install 17 | echo Install completed 18 | 19 | if [ $BROWSER ]; then 20 | npm run test-browser 21 | else 22 | npm test 23 | fi 24 | -------------------------------------------------------------------------------- /test/util.js: -------------------------------------------------------------------------------- 1 | var reverse = require('buffer-reverse') 2 | 3 | /** 4 | * @param {string} s 5 | * @return {Buffer} 6 | */ 7 | module.exports.string2buffer = function (s) { 8 | return reverse(new Buffer(s, 'hex')) 9 | } 10 | 11 | /** 12 | * @param {Buffer} b 13 | * @return {string} 14 | */ 15 | module.exports.buffer2string = function (b) { 16 | return reverse(new Buffer(b)).toString('hex') 17 | } 18 | 19 | /** 20 | * @param {string[]} ss 21 | * @param {Buffer[]} 22 | */ 23 | module.exports.strings2buffers = function (ss) { 24 | return ss.map(function (s) { return module.exports.string2buffer(s) }) 25 | } 26 | 27 | /** 28 | * @param {Buffer[]} bs 29 | * @param {string[]} 30 | */ 31 | module.exports.buffers2string = function (bs) { 32 | return bs.map(function (b) { return module.exports.buffer2string(b) }) 33 | } 34 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bitcoin-merkle-proof", 3 | "version": "0.1.0", 4 | "description": "Build and verify Bitcoin Merkle proofs", 5 | "keywords": [ 6 | "bitcoin", 7 | "merkle", 8 | "tree", 9 | "proof", 10 | "bip37" 11 | ], 12 | "author": "Matt Bell ", 13 | "files": [ 14 | "lib", 15 | "package.json", 16 | "README.md" 17 | ], 18 | "main": "./index.js", 19 | "scripts": { 20 | "test-browser": "./test/browser.sh", 21 | "test": "standard && nyc tape test/index.js | faucet && nyc report" 22 | }, 23 | "license": "MIT", 24 | "dependencies": { 25 | "create-hash": "^1.1.2" 26 | }, 27 | "devDependencies": { 28 | "buffer-reverse": "^1.0.1", 29 | "faucet": "0.0.1", 30 | "nyc": "^6.4.4", 31 | "standard": "^7.0.0", 32 | "tape": "^4.5.1", 33 | "zuul": "^3.10.1", 34 | "zuul-ngrok": "^4.0.0" 35 | }, 36 | "engines": { 37 | "node": ">=4" 38 | }, 39 | "directories": { 40 | "test": "test" 41 | }, 42 | "repository": { 43 | "type": "git", 44 | "url": "git+https://github.com/mappum/bitcoin-merkle-proof.git" 45 | }, 46 | "bugs": { 47 | "url": "https://github.com/mappum/bitcoin-merkle-proof/issues" 48 | }, 49 | "homepage": "https://github.com/mappum/bitcoin-merkle-proof#readme" 50 | } 51 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | install: true 4 | script: ./test/travis.sh 5 | env: 6 | matrix: 7 | - NODE=6 8 | - NODE=5 9 | - NODE=4 10 | - BROWSER=chrome 11 | - BROWSER=firefox 12 | - BROWSER=safari 13 | - BROWSER=android 14 | - BROWSER=iphone 15 | global: 16 | - secure: CGlhIPC6HszUTCXrK2CemTgy+3+ZyF9cx2zFk+OORRoxc9bVVDP9uI4qK/NKfo4DpYejEn8s8mIpegf42O1KprFwPd33hYCJEmTw6/8WWaKOE5XkBCQO7xHWbdBRbyozX5p9a+3H8dBp5V3fOYGOVTvj6NKOWCDGyOEKgxt6pv7dNUsH1jS1LS5Xzo9eoF0UbZzWCbbXhVFJrLBxucizD3i3FXsa2ZrtnY6azulqLHonS+Ja4zPzCqTC+IX2SAY/8TpOwY9j25AzFUY2OltlAVd39Xoj/NVrvDJEhjsHjUs6eEQkUtyWMxzrOEbyPjtCJOq2yVu0tccB3V9s1ki2hNbxm8c+TXKT8agB1OI8Gd1ZNCJF3yPVA4mRA/MCX4RQHVHCcZPKAjPJhYr0aCiqnb+dnMwgkHAxeTPRnF+24n1cmcqlK5E+x8z0CAhNRYzTcy94iTsR3hzF0PetK3xtbRKTD3E3cOVTiPlPhTWIkeGLDQDRx1DXIhNeKt650tMML1IJvj/yYO8rQhDoZH6e5M7jfoG1c3U99iO0lf71jciBxahw3rQlibl98IcaLXQY4cSj2SJORhNAE/edbdQpaIiMvbWiVo+FuWU7vpnZuS/UAYMJ9OwOTWmLR1SbsBW+D80D4gxh+m+5gBIVpLy5qNoJzEv8QbICQFLTQdWm5Ho= 17 | - secure: Sypz+gpVYQEA58W7iBBF9x4TNncstN4MfsbdQO4/ozoxWwwQ4yDKHxj0YMfmJtixS52rrpufQJT1SDsBEj615w37G6VY2kScjk32EX8pLYhFKnz41AaVRTllcEDOfhFqkOkHMKTKKaI9u+sgbwmY/L2xAqT5DsACkCOGjJiYSYnzl7QrCnmAWgNk/GuIbSm+kwa0759qzpVwPMTa3Bntkzu3BrbhJSRxj5FimItrEyTzKyjJ2U9tWRkVK7OJQUIuROp9K4RCQGJstUzzw4mJsioeel4T2At2sPTX/bFYigQn1Iw7S6gzZDhEeBvusJwt6fuMz0Py1zLcj/MmkMummlgvLaOw+x+GmCPPg3osIGc6irgr9QY8kVS4ujJkEojlt+qdEdx5EbLowOGDoQZqhClAWNMYhaUS6Ew1iQ+v/GdyL8UOOka2KPHOjFg4FzDzE+m72F/zFGZRmC1y+Tui/yL8ccYfDSB2IebZnZPpaNYMGrSAreARWreIh+A5wLPjYYV6ESRp+amYXAppI+XvzxuBjL1+E9L8cMr3ocI+nMabuZKAcPBY/VTsktQ+DKFJk+MPsVNsPdgR2ZKsSMb74fyMIhDYQ+EwGSXLQT0wjLXTxCuAr2eGMnlYJuTMV7v0GbdGSdYJwdg6ZD3FW3YHj6sJY1Y0C2AVtgjo8R6Tp8s= 18 | notifications: 19 | email: false 20 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | var test = require('tape') 2 | var merkleTree = require('../') 3 | 4 | var util = require('./util') 5 | var fixtures = require('./fixtures.json') 6 | 7 | test('valid', function (t) { 8 | fixtures.valid.forEach(function (fixture) { 9 | t.test(fixture.name, function (t) { 10 | var data = { 11 | hashes: util.strings2buffers(fixture.txids), 12 | include: util.strings2buffers(fixture.include), 13 | merkleRoot: util.string2buffer(fixture.merkleRoot) 14 | } 15 | 16 | var pmt = merkleTree.build(data) 17 | var hashes = merkleTree.verify(pmt) 18 | 19 | t.deepEqual(fixture.include, util.buffers2string(hashes)) 20 | t.end() 21 | }) 22 | }) 23 | 24 | t.end() 25 | }) 26 | 27 | test('valid verify()', function (t) { 28 | fixtures.validVerify.forEach(function (data) { 29 | t.test(data.name, function (t) { 30 | data.hashes = util.strings2buffers(data.hashes) 31 | data.merkleRoot = util.string2buffer(data.merkleRoot) 32 | 33 | var hashes = merkleTree.verify(data) 34 | 35 | t.deepEqual(data.include, util.buffers2string(hashes)) 36 | t.end() 37 | }) 38 | }) 39 | 40 | t.end() 41 | }) 42 | 43 | test('invalid', function (t) { 44 | fixtures.invalid.forEach(function (fixture) { 45 | t.test(fixture.name, function (t) { 46 | var pmt = { 47 | flags: fixture.flags, 48 | hashes: fixture.hashes.map(function (s) { return new Buffer(s, 'hex') }), 49 | numTransactions: fixture.numTransactions, 50 | merkleRoot: new Buffer(fixture.merkleRoot, 'hex') 51 | } 52 | 53 | t.throws(function () { 54 | merkleTree.verify(pmt) 55 | }, new RegExp(fixture.message)) 56 | t.end() 57 | }) 58 | }) 59 | 60 | t.end() 61 | }) 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bitcoin-merkle-proof 2 | 3 | [![npm version](https://img.shields.io/npm/v/bitcoin-merkle-proof.svg)](https://www.npmjs.com/package/bitcoin-merkle-proof) 4 | [![Build Status](https://travis-ci.org/mappum/bitcoin-merkle-proof.svg?branch=master)](https://travis-ci.org/mappum/bitcoin-merkle-proof) 5 | [![Dependency Status](https://david-dm.org/mappum/bitcoin-merkle-proof.svg)](https://david-dm.org/mappum/bitcoin-merkle-proof) 6 | 7 | **Verify Bitcoin Merkle trees** 8 | 9 | Bitcoin [BIP37](https://github.com/bitcoin/bips/blob/master/bip-0037.mediawiki) adds support for `merkleblock` messages, which allow clients to download blocks that only include transactions relevant to them. The transactions are selected via a Bloom Filter. 10 | 11 | This module creates and verifies the Merkle proofs in a `merkleblock` message, and lists the included transactions which match the filter. 12 | 13 | ## Usage 14 | 15 | `npm install bitcoin-merkle-proof` 16 | 17 | ```js 18 | var bmp = require('bitcoin-merkle-proof') 19 | 20 | // build merkle proof object (block #681135 in testnet) 21 | var merkleProof = bmp.build({ 22 | hashes: [ 23 | new Buffer('52a893ef120d5e24aa38604ead9ada6628eea417df6d6096ef0dd7b73a89c0e9', 'hex'), 24 | new Buffer('a76a1e1bffbbb254bd897e379298549eb8ff4aa57a4bb4c06637b36d76833207', 'hex'), 25 | new Buffer('056b4e64697677788744a8ad23cc407cbc1c357ff889d9975edd431fb779466f', 'hex'), 26 | new Buffer('3c51bfb4f9cdd2b8e3a5c47cb1b3bdbc8879a1c1b238d4123dcb572a00b2b80e', 'hex'), 27 | new Buffer('d6d1f9ca0a4017050379a82ecccb050cf4218f2180087e9592110972a71e375c', 'hex') 28 | ], 29 | include: [ 30 | new Buffer('3c51bfb4f9cdd2b8e3a5c47cb1b3bdbc8879a1c1b238d4123dcb572a00b2b80e', 'hex'), 31 | new Buffer('d6d1f9ca0a4017050379a82ecccb050cf4218f2180087e9592110972a71e375c', 'hex') 32 | ] 33 | }) 34 | // { flags: [ 235, 1 ], 35 | // hashes: 36 | // [ , 37 | // , 38 | // , 39 | // ], 40 | // numTransactions: 5, 41 | // merkleRoot: } 42 | 43 | // verify proof and return matched tx hashes 44 | var hashes = bmp.verify(merkleProof) 45 | console.log('Matched transactions: ', hashes) 46 | ``` 47 | 48 | ##### `var merkleProof = bmp.build(block)` 49 | 50 | Construct proof object for transactions. Proof object: 51 | ```js 52 | { 53 | flags: number[], 54 | hashes: Buffer[], 55 | numTransactions: number, 56 | merkleRoot: Buffer 57 | } 58 | ``` 59 | 60 | ##### `var hashes = bmp.verify(merkleProof)` 61 | 62 | Verifies a Merkle proof object. An error will be thrown if the tree is not valid or does not match the expected Merkle root. Returns an array of txids (as `Buffer`s) which matched the Bloom filter. 63 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var createHash = require('create-hash') 2 | 3 | /** 4 | * @param {Buffer} buf1 5 | * @param {Buffer} buf2 6 | * @return {Buffer} 7 | */ 8 | function hash256 (buf1, buf2) { 9 | var buf = createHash('sha256').update(buf1).update(buf2).digest() 10 | return createHash('sha256').update(buf).digest() 11 | } 12 | 13 | /** 14 | * @param {number} numTransactions 15 | * @return {number} height 16 | */ 17 | function calcTreeWidth (numTransactions, height) { 18 | return (numTransactions + (1 << height) - 1) >> height 19 | } 20 | 21 | /** 22 | * @param {number[]} bits 23 | * @return {number[]} 24 | */ 25 | function bits2bytes (bits) { 26 | var bytes = [] 27 | for (var i = 0; 8 * i < bits.length; ++i) { 28 | for (var j = 0; j < 8; ++j) { 29 | bytes[i] |= bits[8 * i + j] << j 30 | } 31 | } 32 | 33 | return bytes 34 | } 35 | 36 | /** 37 | * @param {number[]} bytes 38 | * @return {number[]} 39 | */ 40 | function bytes2bits (bytes) { 41 | var bits = [] 42 | for (var i = 0; i < bytes.length; ++i) { 43 | for (var j = 0; j < 8; ++j) { 44 | bits.push((bytes[i] >>> j) & 0x01) 45 | } 46 | } 47 | 48 | return bits 49 | } 50 | 51 | /** 52 | * @typedef {Object} partialMerkleTree 53 | * @property {number[]} flags 54 | * @property {Buffer[]} hashes 55 | * @property {number} numTransactions 56 | */ 57 | 58 | /** 59 | * @param {{hashes: Buffer[], include: Buffer[], merkleRoot: Buffer}} data 60 | * @return {partialMerkleTree} 61 | */ 62 | module.exports.build = function (data) { 63 | var numTransactions = data.hashes.length 64 | var include = data.include.map(function (b) { return b.toString('hex') }) 65 | var match = new Array(numTransactions) 66 | for (var i = 0; i < match.length; ++i) { 67 | match[i] = include.indexOf(data.hashes[i].toString('hex')) === -1 ? 0 : 1 68 | } 69 | 70 | var bits = [] 71 | var hashes = [] 72 | 73 | /** 74 | * @param {number} height 75 | * @param {number} pos 76 | * @return {Buffer} 77 | */ 78 | function getHash (height, pos) { 79 | if (height === 0) { 80 | return data.hashes[pos] 81 | } 82 | 83 | var left = getHash(height - 1, pos * 2) 84 | if (pos * 2 + 1 < calcTreeWidth(numTransactions, height - 1)) { 85 | return hash256(left, getHash(height - 1, pos * 2 + 1)) 86 | } 87 | 88 | return hash256(left, left) 89 | } 90 | 91 | /** 92 | * @param {number} height 93 | * @param {number} pos 94 | */ 95 | function build (height, pos) { 96 | var parentOfMatch = 0 97 | for (var p = pos << height, m = (pos + 1) << height; p < m && p < numTransactions; ++p) { 98 | parentOfMatch |= match[p] 99 | } 100 | bits.push(parentOfMatch) 101 | 102 | if (height === 0 || parentOfMatch === 0) { 103 | return hashes.push(getHash(height, pos)) 104 | } 105 | 106 | build(height - 1, pos * 2) 107 | if (pos * 2 + 1 < calcTreeWidth(numTransactions, height - 1)) { 108 | build(height - 1, pos * 2 + 1) 109 | } 110 | } 111 | 112 | var height = Math.ceil(Math.log2(data.hashes.length)) 113 | build(height, 0) 114 | 115 | return { 116 | flags: bits2bytes(bits), 117 | hashes: hashes, 118 | numTransactions: numTransactions, 119 | merkleRoot: getHash(height, 0) 120 | } 121 | } 122 | 123 | /** 124 | * @param {partialMerkleTree} data 125 | * @return {Buffer[]} 126 | */ 127 | module.exports.verify = function (data) { 128 | var hashes = [] 129 | var bits = bytes2bits(data.flags) 130 | var bitsUsed = 0 131 | var hashUsed = 0 132 | 133 | /** 134 | * @param {number} height 135 | * @param {number} pos 136 | * @return {Buffer} 137 | */ 138 | function extract (height, pos) { 139 | var parentOfMatch = bits[bitsUsed++] 140 | if (height === 0 || parentOfMatch === 0) { 141 | var hash = data.hashes[hashUsed++] 142 | if (height === 0 && parentOfMatch) { 143 | hashes.push(hash) 144 | } 145 | 146 | return hash 147 | } 148 | 149 | var left = extract(height - 1, pos * 2) 150 | if (pos * 2 + 1 < calcTreeWidth(data.numTransactions, height - 1)) { 151 | var right = extract(height - 1, pos * 2 + 1) 152 | if (left.equals(right)) { 153 | throw new Error('Merkle child hashes are equivalent (' + 154 | left.toString('hex') + ')') 155 | } 156 | 157 | return hash256(left, right) 158 | } 159 | 160 | return hash256(left, left) 161 | } 162 | 163 | var merkleRoot = extract(Math.ceil(Math.log2(data.numTransactions)), 0) 164 | 165 | var flagByte = Math.floor(bitsUsed / 8) 166 | if (flagByte + 1 < data.flags.length || 167 | data.flags[flagByte] > (1 << bitsUsed % 8)) { 168 | throw new Error('Tree did not consume all flag bits') 169 | } 170 | 171 | if (hashUsed !== data.hashes.length) { 172 | throw new Error('Tree did not consume all hashes') 173 | } 174 | 175 | if (!merkleRoot.equals(data.merkleRoot)) { 176 | throw new Error('Calculated Merkle root does not match header, calculated: ' + 177 | merkleRoot.toString('hex') + ', header: ' + data.merkleRoot.toString('hex')) 178 | } 179 | 180 | return hashes 181 | } 182 | -------------------------------------------------------------------------------- /test/fixtures.json: -------------------------------------------------------------------------------- 1 | { 2 | "valid": [ 3 | { 4 | "name": "block #681271", 5 | "txids": [ 6 | "f07972bbd941ef3bccb8403089f792d8e482766950122a2182620e11dcd693c5", 7 | "3890af0214ad65cb4201acdee48c0dc85ce99f7a2f49cb68ffbc2b5c116b7845", 8 | "35bbb87c4723d65990dfb755204036d698c447062287e357c392e660b4c47e98", 9 | "87c0eff61ac360e76b7455bd5267b8181d60b632f51dae7e68fe60efaa1df926", 10 | "3e4bb67ecc75f42a35ecd31e553c3a2fd7b290386291c1ef82fc302667f92069", 11 | "8495cf9156cc7fe7a9b4c4406db128040dbd18d9da6873e8893572c03a462a54", 12 | "830e470c27aae62e77440f05f4e73474d5b1a1f01a1cd2839d5649196a7a950d" 13 | ], 14 | "include": [ 15 | "3e4bb67ecc75f42a35ecd31e553c3a2fd7b290386291c1ef82fc302667f92069" 16 | ], 17 | "merkleRoot": "c0816c05d821c38f3bf72499407eeb52f64fb8f65ae07d91c8ffb73659649268" 18 | }, 19 | { 20 | "name": "block #681372", 21 | "txids": [ 22 | "672c82c081569fe0104b1abbee4bfbdcf461e45fbb57db4de51c169d2776756e", 23 | "149e28b2d66a7feb8768518b1bf31c7d3d9fb90ccb646f396e67bef290364634", 24 | "ae151d493d96c45167fe33e6fb04ad743b8b0239e9ba72691b46ab5bfb1c867d", 25 | "43bc1b562f5ce8271491841a90dcfc377479bc2b1954162670329435442e3a1d", 26 | "e67d83bf5f62aafc196b4c267574485bc984a83d93271b4ca0a18aa607c33ee8", 27 | "3ea4d6a6ab3fdcc0a2b722c74376d274850007ed96a94c08a3ff2178161c6e2a", 28 | "493dc20009050d44042dca430aef87e9f5b42c7b830ee4c67a68890014408eb7", 29 | "b6a8744444b908c5d1de92d8c4f735220ea9206c1934b59b8840e739a2d635bd", 30 | "7864e0b6b439d7fe162492ce869317e8b7911fa3a48ce98c55511af6852e6923", 31 | "a19f107d5f52879022c6a10bc89e34cdcd56635cd21597f648b7a4843bd6509c", 32 | "e98d0f2a252d482b10e218164406773d98e156399c0ae50e7d2ede99a70d6d1a", 33 | "544bf7155bd9495cbe6ae5f8b1a374c11b324368ee2d612bc3360ea709f7aaad", 34 | "a724fb497abfc6cb6dee1b2bd4c5a955dcace79121335ac189b1483a0312320f", 35 | "0d916910a2885744259e1083644a32521201a5234f897f082efa7bc45da78cfb", 36 | "d931dca43adeb4b70699c1c9283ae56115daeb0e2463024fc4c30b6cb6ad1877", 37 | "daa1c6e197bd1e615336534bae4ac676b6d219ef2cb0cab9684df45ddee8c11a", 38 | "0e7cdfaf48bc59012b732dacce4d44d378bee98a2ea96d30d7764db0e2dbbcc1", 39 | "9705ac3b91c65ea0685381c9e3b520c65de7b8ff1b61591d971a2a04efcf8a08", 40 | "d2c13cc801a2ecb004f08ef57e4de02ea8286aaf8e1289aaa17cb8d9b74ffe52", 41 | "bcbd05f5569bfe43510c24895b89ee03ea3b2d78633158817dd207af7f1b21b6", 42 | "dbbd884aca8f8aa174213db34a0e9d1c7a815f316f5c78ee4b5745bb40bb4f02", 43 | "36c786ffe9e4dfbd80a5bc7a086e82e15a4ae8f7bfee3966c234884f1d84e726", 44 | "a27cc4c8fb262ab48d2913725b18461bb4b6f256f3bc81b90124059289456e66", 45 | "ea037462f10af9e2d9ca0c93821fbe3e04678fa094fa7c66a93fcbda1fa8ee09", 46 | "1bc87c0b150a8ff8118ff8a32bb527de7f721f81957ec16207d51272361f6ac5", 47 | "bd9b1206922c3b6388bf7526765ac5c28bc44114c017af25338cd917c58c3358", 48 | "d9b30c10229eb0ea386d16c68d018a36d21bf13720d3c273ca0d4f47f21e9868", 49 | "b8d1b9d75c10e805401d4910d941eb13bed56d7dfe09804e6551ad9ded9861d3", 50 | "b4c40bf906a47a82ce98444345d0ff4e2c4cb921c1a43e6b5f28e42c02cf562c", 51 | "4eed2e650e32baddb726d255a306d73c77389cdf1f0bcb01ed108dd3d1850d4c", 52 | "3aecc861718da68e736a56ee92e61e9379356779794404cbecd106701eac7050", 53 | "5d68b94dcc0b928b20a4f49672824cfe374767f72b352f5ad0c92387ccf1d4dd", 54 | "91ef9ec5e38227bf8e979bf6d2522b9c674bc8743f4a2459b2eeb45dcbdb3c84", 55 | "b96c5354a1dfaead0c6ffe7db22fc96806f269ff04bacd2f757884006a32bbff", 56 | "2b0304338e01f684629e005203052b865325cdcc105aa36bdf635b826308b698" 57 | ], 58 | "include": [ 59 | "149e28b2d66a7feb8768518b1bf31c7d3d9fb90ccb646f396e67bef290364634", 60 | "9705ac3b91c65ea0685381c9e3b520c65de7b8ff1b61591d971a2a04efcf8a08", 61 | "bd9b1206922c3b6388bf7526765ac5c28bc44114c017af25338cd917c58c3358", 62 | "91ef9ec5e38227bf8e979bf6d2522b9c674bc8743f4a2459b2eeb45dcbdb3c84" 63 | ], 64 | "merkleRoot": "105f3f2cb20768b9266ad39f6f30ab71fc702569561df59176c6cf9104118542" 65 | }, 66 | { 67 | "name": "block #681246", 68 | "txids": [ 69 | "79419c12d762545065abbd5e196141b4c5338a3e36dd6c389c1ead912a71faa4" 70 | ], 71 | "include": [ 72 | "79419c12d762545065abbd5e196141b4c5338a3e36dd6c389c1ead912a71faa4" 73 | ], 74 | "merkleRoot": "79419c12d762545065abbd5e196141b4c5338a3e36dd6c389c1ead912a71faa4" 75 | }, 76 | { 77 | "name": "block #681135", 78 | "txids": [ 79 | "e9c0893ab7d70def96606ddf17a4ee2866da9aad4e6038aa245e0d12ef93a852", 80 | "073283766db33766c0b44b7aa54affb89e549892377e89bd54b2bbff1b1e6aa7", 81 | "6f4679b71f43dd5e97d989f87f351cbc7c40cc23ada8448778777669644e6b05", 82 | "0eb8b2002a57cb3d12d438b2c1a17988bcbdb3b17cc4a5e3b8d2cdf9b4bf513c", 83 | "5c371ea772091192957e0880218f21f40c05cbcc2ea879030517400acaf9d1d6" 84 | ], 85 | "include": [ 86 | "5c371ea772091192957e0880218f21f40c05cbcc2ea879030517400acaf9d1d6" 87 | ], 88 | "merkleRoot": "582ee58304ee37ea1030d7b7be5296bf5f4787b529a932dc8784c1940250b4b9" 89 | } 90 | ], 91 | "validVerify": [ 92 | { 93 | "name": "block #278888", 94 | "flags": [ 251, 27, 240, 219, 248, 1 ], 95 | "hashes": [ 96 | "57b64c480e9bc3d2e168b545264f43d5f689b481f152ce69bd4629444e7b9cb4", 97 | "134cc15113955c40ee98557763dc7c7a22cc7ab820b26ab4c14c78ebdbab1104", 98 | "1c1702341b35138d05be18a81298848d494f5174bddeb6624dc5f95abc112038", 99 | "e555e721a272bca952cb941b0d5e5c4faf02d636e4445b3d814edaa99ed59b90", 100 | "cdd7a7d622eb8f6879452b2fe082df642f765a0b0b54d28169d4de5c27612316", 101 | "aa519f56815c58cfbbbc02ef154b7f3596ace448d530c2543b45b90fd12ad1bf", 102 | "f16e62d64efcc74c4bd631b1a0385d62fa68c4b47ca4c2cc5f53df3a26483141", 103 | "8e4e50d36b7490264da950e5fa4d1c75d085dbf2b0c1237c7657cef038da8ca5", 104 | "0a06bada47677a9d85403ca043742589a27120f2b6fb455aae293ce0ca24d8c8", 105 | "fe9d0a104c3756c0dab9434816cf6130dec33a31fbefe7200636855ae3f14a6b", 106 | "f4854aae7c2f71ca319e8c59d25fb24310ec8533f276c8ec1e0db283cbf115c9", 107 | "c7a47620527981be9f1ac55850e3a440d60c322257938a3d003c499c353b4735", 108 | "b1e1a5e0ac0abbabf329773e173a8fbf81c8883ed404a30caff45423147360fa", 109 | "c257bbdb47163799d40896d87a6c528e3918b25a0aa2a66c642da9f5c9cb07d0", 110 | "8d4a8a54a5f5e167f9cbab4bb758d0bb8ad2ed08185606ba2b1f5f3eec961930", 111 | "4cbb0d2d5852d2cdede9bc9fdfe2c64271402cfdb9aca5f7748c8608f6dacc79", 112 | "f30670774e4903003014cdbd028feac9b06df661140696c2753850a74e5a58dd", 113 | "489f97268a158cc7aab416f5a5c18b1dfa30c14ad6bec7de125000370daad4a5", 114 | "154cc50059e4b0a154d89904144600ed06c77bdfd654eb820e7c453549518266", 115 | "ae94a436344fb90ca7c4704e2f9d1a43a75bdaed604d9ca99fffe3acdbf478c2", 116 | "52e35d7514b896fac2fdbd2dba75e7c4147a16d4465e2e20058da7bfa0f3b933", 117 | "4c285980f00396506afe951660584b528288231224f7f5849bb41c5979623d0d", 118 | "e4381f5a5398ba440f8b2ef5ed0b965890c8db9233f48cc943d0629628ed182f", 119 | "e627671e5168c1bf214aa101b9112b675bc02d1b1126dd2c0f5162a49074195d" 120 | ], 121 | "numTransactions": 692, 122 | "merkleRoot": "9e96ee639ffa71438835f65b9d534187fcefa31d33090d5e568672085d784d0f", 123 | "include": [ 124 | "1c1702341b35138d05be18a81298848d494f5174bddeb6624dc5f95abc112038", 125 | "b1e1a5e0ac0abbabf329773e173a8fbf81c8883ed404a30caff45423147360fa", 126 | "f30670774e4903003014cdbd028feac9b06df661140696c2753850a74e5a58dd" 127 | ] 128 | } 129 | ], 130 | "invalid": [ 131 | { 132 | "name": "not all hashes used", 133 | "flags": [ 109, 117, 0 ], 134 | "hashes": [ 135 | "97929f984c3188642c9732ea6df79f669c00fc213eecbfe568167a4a633cefb4", 136 | "6c243e0cfb25519bfdbc9a46184ac637557fd16a469e0b1d6489df82bd43f98d", 137 | "e51ac3a2e43d1b3360913db73e8e7559a37c0a26cd47a4e0c2a7ee7a6af6394d", 138 | "b4232ddd34821c98fbe92d0a8994e8c3d6eda738ba6c01d3fe436e5afe17ec73", 139 | "ff6a1f2453a773dead88d5352a8f9876c58d099cdcae55cb2aa562bb202c4024", 140 | "52a88d05f0f4c46681bae3be39c35391db370d41a2197f72f616b2ee47698f6f", 141 | "9d5493b33f4042bccc0f0745315c69f7f39317964288a7fd56c4a4bf3b12bed4", 142 | "ed8ba481ad870d0c25fd8fd07a9346212434749eb5f031e70332347c8232e377", 143 | "eba009b7beb9b2215ef4e73627513251fc373a41838e58ff12fa45a46fb7025e", 144 | "9d5493b33f4042bccc0f0745315c69f7f39317964288a7fd56c4a4bf3b12bed4", 145 | "ed8ba481ad870d0c25fd8fd07a9346212434749eb5f031e70332347c8232e377", 146 | "eba009b7beb9b2215ef4e73627513251fc373a41838e58ff12fa45a46fb7025e" 147 | ], 148 | "numTransactions": 362, 149 | "merkleRoot": "fb2e2ca078055ef2d41ef23f957c3723c53e067a81ebe7d5686e2d88be7189cc", 150 | "message": "Tree did not consume all hashes" 151 | }, 152 | { 153 | "name": "unused flag byte", 154 | "flags": [ 109, 117, 0, 0 ], 155 | "hashes": [ 156 | "97929f984c3188642c9732ea6df79f669c00fc213eecbfe568167a4a633cefb4", 157 | "6c243e0cfb25519bfdbc9a46184ac637557fd16a469e0b1d6489df82bd43f98d", 158 | "e51ac3a2e43d1b3360913db73e8e7559a37c0a26cd47a4e0c2a7ee7a6af6394d", 159 | "b4232ddd34821c98fbe92d0a8994e8c3d6eda738ba6c01d3fe436e5afe17ec73", 160 | "ff6a1f2453a773dead88d5352a8f9876c58d099cdcae55cb2aa562bb202c4024", 161 | "52a88d05f0f4c46681bae3be39c35391db370d41a2197f72f616b2ee47698f6f", 162 | "9d5493b33f4042bccc0f0745315c69f7f39317964288a7fd56c4a4bf3b12bed4", 163 | "ed8ba481ad870d0c25fd8fd07a9346212434749eb5f031e70332347c8232e377", 164 | "eba009b7beb9b2215ef4e73627513251fc373a41838e58ff12fa45a46fb7025e" 165 | ], 166 | "numTransactions": 362, 167 | "merkleRoot": "fb2e2ca078055ef2d41ef23f957c3723c53e067a81ebe7d5686e2d88be7189cc", 168 | "message": "Tree did not consume all flag bits" 169 | }, 170 | { 171 | "name": "unused flag bit", 172 | "flags": [ 109, 117, 128 ], 173 | "hashes": [ 174 | "97929f984c3188642c9732ea6df79f669c00fc213eecbfe568167a4a633cefb4", 175 | "6c243e0cfb25519bfdbc9a46184ac637557fd16a469e0b1d6489df82bd43f98d", 176 | "e51ac3a2e43d1b3360913db73e8e7559a37c0a26cd47a4e0c2a7ee7a6af6394d", 177 | "b4232ddd34821c98fbe92d0a8994e8c3d6eda738ba6c01d3fe436e5afe17ec73", 178 | "ff6a1f2453a773dead88d5352a8f9876c58d099cdcae55cb2aa562bb202c4024", 179 | "52a88d05f0f4c46681bae3be39c35391db370d41a2197f72f616b2ee47698f6f", 180 | "9d5493b33f4042bccc0f0745315c69f7f39317964288a7fd56c4a4bf3b12bed4", 181 | "ed8ba481ad870d0c25fd8fd07a9346212434749eb5f031e70332347c8232e377", 182 | "eba009b7beb9b2215ef4e73627513251fc373a41838e58ff12fa45a46fb7025e" 183 | ], 184 | "numTransactions": 362, 185 | "merkleRoot": "fb2e2ca078055ef2d41ef23f957c3723c53e067a81ebe7d5686e2d88be7189cc", 186 | "message": "Tree did not consume all flag bits" 187 | }, 188 | { 189 | "name": "duplicate leaf hashes", 190 | "flags": [ 109, 117, 0 ], 191 | "hashes": [ 192 | "97929f984c3188642c9732ea6df79f669c00fc213eecbfe568167a4a633cefb4", 193 | "97929f984c3188642c9732ea6df79f669c00fc213eecbfe568167a4a633cefb4", 194 | "97929f984c3188642c9732ea6df79f669c00fc213eecbfe568167a4a633cefb4", 195 | "97929f984c3188642c9732ea6df79f669c00fc213eecbfe568167a4a633cefb4", 196 | "97929f984c3188642c9732ea6df79f669c00fc213eecbfe568167a4a633cefb4", 197 | "97929f984c3188642c9732ea6df79f669c00fc213eecbfe568167a4a633cefb4", 198 | "97929f984c3188642c9732ea6df79f669c00fc213eecbfe568167a4a633cefb4", 199 | "97929f984c3188642c9732ea6df79f669c00fc213eecbfe568167a4a633cefb4", 200 | "97929f984c3188642c9732ea6df79f669c00fc213eecbfe568167a4a633cefb4" 201 | ], 202 | "numTransactions": 362, 203 | "merkleRoot": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 204 | "message": "Merkle child hashes are equivalent \\(97929f984c3188642c9732ea6df79f669c00fc213eecbfe568167a4a633cefb4\\)" 205 | }, 206 | { 207 | "name": "invalid merkle root in header", 208 | "flags": [ 109, 117, 0 ], 209 | "hashes": [ 210 | "97929f984c3188642c9732ea6df79f669c00fc213eecbfe568167a4a633cefb4", 211 | "6c243e0cfb25519bfdbc9a46184ac637557fd16a469e0b1d6489df82bd43f98d", 212 | "e51ac3a2e43d1b3360913db73e8e7559a37c0a26cd47a4e0c2a7ee7a6af6394d", 213 | "b4232ddd34821c98fbe92d0a8994e8c3d6eda738ba6c01d3fe436e5afe17ec73", 214 | "ff6a1f2453a773dead88d5352a8f9876c58d099cdcae55cb2aa562bb202c4024", 215 | "52a88d05f0f4c46681bae3be39c35391db370d41a2197f72f616b2ee47698f6f", 216 | "9d5493b33f4042bccc0f0745315c69f7f39317964288a7fd56c4a4bf3b12bed4", 217 | "ed8ba481ad870d0c25fd8fd07a9346212434749eb5f031e70332347c8232e377", 218 | "eba009b7beb9b2215ef4e73627513251fc373a41838e58ff12fa45a46fb7025e" 219 | ], 220 | "numTransactions": 362, 221 | "merkleRoot": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 222 | "message": "Calculated Merkle root does not match header, calculated: fb2e2ca078055ef2d41ef23f957c3723c53e067a81ebe7d5686e2d88be7189cc, header: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" 223 | } 224 | 225 | ] 226 | } 227 | --------------------------------------------------------------------------------