Skip to content

Instantly share code, notes, and snippets.

@ordishs
Last active March 15, 2024 00:30
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save ordishs/7c4d2bf77b9fe74eb044aaa5a6867fc2 to your computer and use it in GitHub Desktop.
Save ordishs/7c4d2bf77b9fe74eb044aaa5a6867fc2 to your computer and use it in GitHub Desktop.
Small example of the steps needed to verify the merkle proofs of a transaction provided by WhatsOnChain.
const crypto = require('crypto')
const assert = require('assert')
function sha256 (buffer) {
assert(Buffer.isBuffer(buffer), `Argument must be a buffer, received ${typeof buffer}`)
const hash = crypto.createHash('sha256')
hash.update(buffer)
return hash.digest()
}
function sha256d (buffer) {
assert(Buffer.isBuffer(buffer), `Argument must be a buffer, received ${typeof buffer}`)
return sha256(sha256(buffer))
}
function reverseBuffer (buffer) {
assert(Buffer.isBuffer(buffer), `Argument must be a buffer, received ${typeof buffer}`)
const reversed = Buffer.alloc(buffer.length)
for (let i = buffer.length - 1; i >= 0; i--) {
reversed[buffer.length - i - 1] = buffer[i]
}
return reversed
}
function calculateMerkleRoot (transactionHash, branches) {
assert(Buffer.isBuffer(transactionHash), `Argument must be a buffer, received ${typeof transactionHash}`)
assert(Array.isArray(branches), `Argument must be an array, received ${typeof branches}`)
return branches.reduce((hash, branch) => {
const h = Buffer.from(branch.hash, 'hex')
if (branch.pos === 'L') {
hash = Buffer.concat([reverseBuffer(h), reverseBuffer(hash)])
} else {
hash = Buffer.concat([reverseBuffer(hash), reverseBuffer(h)])
}
return reverseBuffer(sha256d(hash))
}, transactionHash)
}
// EXAMPLE ----------------------------------------------------
// curl https://api.whatsonchain.com/v1/bsv/main/tx/3640323d29f022315cfba9fee90adfac4a2caac886aa3077c8e723a8b8dbf040/proof
const proofs = [{
blockHash: '0000000000000000020e4673ffebc7fa5d04b9728dfdde94e15cc07e0da0f064',
branches: [
{ hash: '8dcba0e634a4028bf905db5f34610a7163320469e77bf044e1aa96985177c630', pos: 'L' },
{ hash: '018a486a925d9185d91b485f5b12423ad73b80ef160fe6d28dab494916a52f0a', pos: 'R' },
{ hash: 'f0a4c8401882c6295edbed3ad4e5daf0bc4f9b004dca5478824f28cb8b62cdd6', pos: 'R' },
{ hash: 'aa58aa291e5965f0d96db472e3eb8cbca6ab747dc63f7b81946adc7ac22de5fe', pos: 'R' },
{ hash: 'f200e2036ee54ddf39d9121011a2703adea6db40d8b75f2424e13306f49c7d9c', pos: 'R' },
{ hash: '111330e6adc23cf1ffc14d61e9768e9ce4b7014e6cff3c71ae9ea9124a71cc58', pos: 'R' },
{ hash: '97c821db7f4b7ae477c6175c2b1d7d6a23937a911f3ddd11546d8d6866e9b54f', pos: 'R' },
{ hash: '38edc4f4ae31b619652b5fb3a28913c611e55c1448a098994b67f2c57fa49cb1', pos: 'R' },
{ hash: '29629285e0594928a8a2254fb0fc9e39ceb0ed49d148ff43dfd28cdd5430f0b9', pos: 'R' },
{ hash: 'b816f852462b8914c07fd58aba59b749d3b1a9b644795e50bfb608a6045a2ec3', pos: 'R' },
{ hash: 'b7a1bd0fc12540dcbbfd18fc0a33ca52ec0aa453a185e747bbf71c3a1740ecbb', pos: 'R' }
],
hash: '3640323d29f022315cfba9fee90adfac4a2caac886aa3077c8e723a8b8dbf040',
merkleRoot: '9fac7ee268b01bfcb80821c0a7f35c979aad380eb16674d63483ac8ec8de9ef0'
}]
const merkleRoot = calculateMerkleRoot(Buffer.from(proofs[0].hash, 'hex'), proofs[0].branches)
console.log(merkleRoot.toString('hex'))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment