|
| 1 | +// SPDX-License-Identifier: MIT |
| 2 | +pragma solidity ^0.8.28; |
| 3 | + |
| 4 | +/** |
| 5 | + * @notice A library for verifying transaction inclusion in Bitcoin block. |
| 6 | + * Provides functions for processing and verifying Merkle tree proofs |
| 7 | + */ |
| 8 | +library TxMerkleProof { |
| 9 | + /** |
| 10 | + * @notice Possible directions for hashing: |
| 11 | + * Left: computed hash is on the left, sibling hash is on the right. |
| 12 | + * Right: computed hash is on the right, sibling hash is on the left. |
| 13 | + * Self: node has no sibling and is hashed with itself |
| 14 | + * */ |
| 15 | + enum HashDirection { |
| 16 | + Left, |
| 17 | + Right, |
| 18 | + Self |
| 19 | + } |
| 20 | + |
| 21 | + /** |
| 22 | + * @notice Emitted when the proof and directions array are of different length. |
| 23 | + * This error ensures that only correctly sized proofs are processed |
| 24 | + */ |
| 25 | + error InvalidLengths(); |
| 26 | + |
| 27 | + /** |
| 28 | + * @notice Returns true if `leaf_` can be proven to be part of a Merkle tree |
| 29 | + * defined by `root_`. Requires a `proof_` containing the sibling hashes along |
| 30 | + * the path from the leaf to the root. Each element of `directions_` indicates |
| 31 | + * the hashing order for each pair. Uses double SHA-256 hashing |
| 32 | + */ |
| 33 | + function verify( |
| 34 | + bytes32[] calldata proof_, |
| 35 | + HashDirection[] calldata directions_, |
| 36 | + bytes32 root_, |
| 37 | + bytes32 leaf_ |
| 38 | + ) internal pure returns (bool) { |
| 39 | + require(directions_.length == proof_.length, InvalidLengths()); |
| 40 | + |
| 41 | + return processProof(proof_, directions_, leaf_) == root_; |
| 42 | + } |
| 43 | + |
| 44 | + /** |
| 45 | + * @notice Returns the rebuilt hash obtained by traversing the Merkle tree |
| 46 | + * from `leaf_` using `proof_`. A `proof_` is valid if and only if the rebuilt |
| 47 | + * hash matches the given tree root. The pre-images are hashed in the order |
| 48 | + * specified by the `directions_` elements. Uses double SHA-256 hashing |
| 49 | + */ |
| 50 | + function processProof( |
| 51 | + bytes32[] calldata proof_, |
| 52 | + HashDirection[] calldata directions_, |
| 53 | + bytes32 leaf_ |
| 54 | + ) internal pure returns (bytes32) { |
| 55 | + bytes32 computedHash_ = leaf_; |
| 56 | + uint256 proofLength_ = proof_.length; |
| 57 | + |
| 58 | + for (uint256 i = 0; i < proofLength_; ++i) { |
| 59 | + if (directions_[i] == HashDirection.Left) { |
| 60 | + computedHash_ = _doubleSHA256(computedHash_, proof_[i]); |
| 61 | + } else if (directions_[i] == HashDirection.Right) { |
| 62 | + computedHash_ = _doubleSHA256(proof_[i], computedHash_); |
| 63 | + } else { |
| 64 | + computedHash_ = _doubleSHA256(computedHash_, computedHash_); |
| 65 | + } |
| 66 | + } |
| 67 | + |
| 68 | + return computedHash_; |
| 69 | + } |
| 70 | + |
| 71 | + function _doubleSHA256(bytes32 left_, bytes32 right_) private pure returns (bytes32) { |
| 72 | + return sha256(abi.encodePacked(sha256(abi.encodePacked(left_, right_)))); |
| 73 | + } |
| 74 | +} |
0 commit comments