I am trying to verify a SPV/Merkle Inclusion Proof.
I have the TXid of the transaction, the intermediary nodes between the transaction and the root hash, and the merkle root itself.
I need to calculate the merkle root from the TXid and intermediary nodes and confirm that is the same as the known merkle root.
I would like an explanation for how exactly this can be achieved, using the example below as a reference.*
The following is a correct SPV proof.
{"version": "01000000","vin": "0168fe12339c9eb3ceaf21899b775a53dc10901f88f57ba7b360bbd285fe1b8731000000006a47304402206f33a57c88e473a26eca617c21aa9545101b18df83b127bc485bf0fea1a831b702204d56bc0e39fa14ff254d0677f46c3f7f26f6dea8e6a9c5ba90aace3c8476f1d401210396dd84815a4f121bf29b882c283ec1cd8b5bfa92773008a79cb44d981821a399ffffffff","vout": "0230182b00000000001976a91452ada19e1305964e80a1a1cbbafea97f6b632fae88aca7ec3703000000001976a914f9da3787e63ad9261517a6d4d9dabd2cf40fb80988ac","locktime": "00000000","tx_id": "fff0d18db9f52e6bff445c26cb8bb9658882c8045997a74be26003225713e762","tx_id_le": "62e71357220360e24ba7975904c8828865b98bcb265c44ff6b2ef5b98dd1f0ff","index": 6,"confirming_header": {"raw": "00e0002074859a363c5a885b262722d5c5c5cd912e2cd1a53d0c0c000000000000000000a0c7ea544dcc99af8af5415d4293856da7c07f9f1a3b145d2498b39bf5fa5e36fbd1d45dd1201617d64b8b4c","hash": "00000000000000000003e7124509796d4f15ef47fedc71c79cea60d1ff503410","hash_le": "103450ffd160ea9cc771dcfe47ef154f6d79094512e703000000000000000000","height": 604596,"prevhash": "0000000000000000000c0c3da5d12c2e91cdc5c5d52227265b885a3c369a8574","prevhash_le": "74859a363c5a885b262722d5c5c5cd912e2cd1a53d0c0c000000000000000000","merkle_root": "365efaf59bb398245d143b1a9f7fc0a76d8593425d41f58aaf99cc4d54eac7a0","merkle_root_le": "a0c7ea544dcc99af8af5415d4293856da7c07f9f1a3b145d2498b39bf5fa5e36" },"intermediate_nodes": "c7ca43c16c6a587c3554d45a1c0d56e801ef2dc929fae3cc95bb604b3f566de1f6e9750121695b84989ba819f6cea180315f8a3ae71d644f1bb90379577dbc6d7167faee2d76d172fcec41469346d3d731a901fa86e71528a8569a414ef42ed5585623d28015eb91eb1dda03615196918e49e5f1ff0ef229748389698c91c8a5c61d721352a53cea50a25cbff4002b96d0cd93f0fe48d4e7c8f27a9bd4c54ade1c12d25788981d287caac5ee10094957a15d5cc4f65885ff97e292f0512942eba083dcbcab960639f672f7d10e745da5e7b271abca32a9e2060ea8ddbf545db2b89a2ecad745853bab40a383f612ae5ab94815f55b9dd26aa199d39fb7cde85326bce3f94cdad64b6470397f2aa5ff57126a2f343c5d133510f96bda999ee69d99ced303a1076d415b9c404c8fb73a36ae142b359e7106ab73da4862064f472e1459b5d923cb387b83559c100195428443bb8211ab2b57a6dd28c8fd5a44e1a1"}
I have also implemented a bash script which accepts the TXid (little-endian) and Intermediary Nodes information (also little-endian, ordered from leaf to root), as specified in the above (correct) SPV proof. It does not work correctly, any comments as to what needs to be done to fix it would be greatly appreciated. The bash script is as follows:
#!/bin/bash# Accept TXid and Intermediary Nodes String as command-line arguments.TXID="$1";INTERMEDIARY_NODES_LE="$2";# Calculate the number of Intermediary Nodes from the length of the string.PROOF_LENGTH="${#INTERMEDIARY_NODES_LE}";INTERMEDIARY_NODE_COUNT=$(( PROOF_LENGTH / 64 ));echo "There are $INTERMEDIARY_NODE_COUNT intermediary nodes between the TXid and the Root Hash."# Initialise an empty array in which to store the Little-Endian encoded Merkle Intermediary NodesMERKLE_NODES_LE=();for i in $(seq $INTERMEDIARY_NODE_COUNT); do # Cuts a Node out from the string. START=$(( ( 64 * ( i - 1 ) ) + 1 )); END=$(( 64 * i )); MERKLE_NODE_LE=$(echo "${INTERMEDIARY_NODES_LE}" | cut -c ${START}-${END} ); # Adds the Node to the array. MERKLE_NODES_LE+=("$MERKLE_NODE_LE");done;echo "TXid:"echo "$TXID"echo "";echo "Intermediary Merkle Nodes:";echo "${MERKLE_NODES_LE[*]}";echo "";# Determines how many rounds of hashing needed to get to the merkle root.MERKLE_ITERATE_BY=$(( ${#MERKLE_NODES_LE[@]} ));MERKLE_HASH_ROUND_LE="${TXID}";for i in $(seq $MERKLE_ITERATE_BY); do # Concatenates the last node (calculated by hashing the two child nodes) with its sibling node, converting to Big-Endian. MERKLE_HASH_ROUND_BE=$( echo "${MERKLE_NODES_LE[i-1]}${MERKLE_HASH_ROUND_LE}" | fold -w 2 | tac | tr -d "\n" ) # Hashes the result of the last instruction to get the parent node. MERKLE_HASH_ROUND_BE=$( echo "$MERKLE_HASH_ROUND_BE" | xxd -p -r | openssl dgst -sha256 -binary | openssl dgst -sha256 -hex | sed 's/SHA2-256(stdin)= //g' ); # Returns result in Little-Endian MERKLE_HASH_ROUND_LE=$( echo "$MERKLE_HASH_ROUND_BE" | fold -w 2 | tac | tr -d "\n" )done;# Echoes the result of the last hash operation, which should be the merkle root, in Little-Endian.echo "$MERKLE_HASH_ROUND_LE";