|
| 1 | +## Merkle |
| 2 | + |
| 3 | +| File | Notes | |
| 4 | +| -------- | -------- | |
| 5 | +| [`Merkle.sol`](../../../src/contracts/libraries/Merkle.sol) | Core Merkle tree library | |
| 6 | + |
| 7 | +## Overview |
| 8 | + |
| 9 | +The `Merkle` library provides cryptographically secure Merkle tree functionality for the EigenLayer protocol. It supports both Keccak256 and SHA-256 hash functions for different use cases across the system. The library enables efficient verification of data inclusion in large datasets without requiring the full dataset, which is essential for scalable proof systems in EigenLayer. |
| 10 | + |
| 11 | +Key capabilities include: |
| 12 | +- **Proof verification**: Verify that a leaf exists in a Merkle tree given a root and proof |
| 13 | +- **Tree construction**: Build Merkle trees from arrays of leaves |
| 14 | +- **Proof generation**: Generate inclusion proofs for specific leaves |
| 15 | +- **Dual hash function support**: Both Keccak256 (for EVM compatibility) and SHA-256 (for beacon chain compatibility) |
| 16 | + |
| 17 | +## Prior Reading |
| 18 | + |
| 19 | +* Understanding of [Merkle trees](https://en.wikipedia.org/wiki/Merkle_tree) and cryptographic hash functions |
| 20 | +* [EIP-197](https://eips.ethereum.org/EIPS/eip-197) for understanding precompiled contracts |
| 21 | + |
| 22 | +## Usage in EigenLayer |
| 23 | + |
| 24 | +The Merkle library is used extensively throughout EigenLayer for various proof systems: |
| 25 | + |
| 26 | +- **[`BN254CertificateVerifier`](../../../src/contracts/multichain/BN254CertificateVerifier.sol)**: Verifies operator information inclusion in certificate Merkle trees (see [CertificateVerifier.md](../../multichain/destination/CertificateVerifier.md)) |
| 27 | +- **[`OperatorTableUpdater`](../../../src/contracts/multichain/OperatorTableUpdater.sol)**: Manages operator set proofs for multichain operations (see [OperatorTableUpdater.md](../../multichain/destination/OperatorTableUpdater.md)) |
| 28 | +- **[`RewardsCoordinator`](../../../src/contracts/core/RewardsCoordinator.sol)**: Verifies reward distribution claims (see [RewardsCoordinator.md](../RewardsCoordinator.md)) |
| 29 | +- **[`BeaconChainProofs`](../../../src/contracts/libraries/BeaconChainProofs.sol)**: Processes beacon chain state proofs (see [EigenPod.md](../EigenPod.md) for beacon chain proof usage) |
| 30 | + |
| 31 | +## Security Considerations |
| 32 | + |
| 33 | +### **Critical Security Warning** |
| 34 | + |
| 35 | +**You should avoid using leaf values that are 64 bytes long prior to hashing, salt the leaves, or hash the leaves with a hash function other than what is used for the Merkle tree's internal nodes.** This prevents potential collision attacks where the concatenation of a sorted pair of internal nodes could be reinterpreted as a leaf value. |
| 36 | + |
| 37 | +### **Zero Hash Padding** |
| 38 | + |
| 39 | +When trees are not perfect powers of 2, the library pads with `bytes32(0)` values. For security-critical applications, consider using unique filler values to prevent potential collision attacks with legitimate zero-valued leaves. |
| 40 | + |
| 41 | +--- |
| 42 | + |
| 43 | +## Proof Verification |
| 44 | + |
| 45 | +### **Keccak256 Proof Verification** |
| 46 | + |
| 47 | +#### `verifyInclusionKeccak` |
| 48 | + |
| 49 | +```solidity |
| 50 | +function verifyInclusionKeccak( |
| 51 | + bytes memory proof, |
| 52 | + bytes32 root, |
| 53 | + bytes32 leaf, |
| 54 | + uint256 index |
| 55 | +) internal pure returns (bool) |
| 56 | +``` |
| 57 | + |
| 58 | +Verifies that a given leaf is included in a Merkle tree using Keccak256. |
| 59 | + |
| 60 | +*Effects:* |
| 61 | +* Computes the root hash by traversing the Merkle proof path |
| 62 | +* Compares computed root with expected root for verification |
| 63 | + |
| 64 | +*Used in:* |
| 65 | +* [`BN254CertificateVerifier.verifyOperatorInfoProof`](../../../src/contracts/multichain/BN254CertificateVerifier.sol) |
| 66 | +* [`OperatorTableUpdater.checkGlobalTableHash`](../../../src/contracts/multichain/OperatorTableUpdater.sol) |
| 67 | +* [`RewardsCoordinator`](../../../src/contracts/core/RewardsCoordinator.sol) for reward claim verification |
| 68 | + |
| 69 | +#### `processInclusionProofKeccak` |
| 70 | + |
| 71 | +```solidity |
| 72 | +function processInclusionProofKeccak( |
| 73 | + bytes memory proof, |
| 74 | + bytes32 leaf, |
| 75 | + uint256 index |
| 76 | +) internal pure returns (bytes32) |
| 77 | +``` |
| 78 | + |
| 79 | +Returns the computed root hash by traversing up the tree from the leaf. |
| 80 | + |
| 81 | +*Effects:* |
| 82 | +* Traverses the Merkle tree from leaf to root using provided proof |
| 83 | +* Computes hash at each level by combining current hash with proof siblings |
| 84 | +* Returns the final computed root hash |
| 85 | + |
| 86 | +*Requirements:* |
| 87 | +* Proof length MUST be a multiple of 32 bytes (reverts with `InvalidProofLength` otherwise) |
| 88 | +* Index MUST reach 0 after processing all proof elements (reverts with `InvalidIndex` if proof length mismatch) |
| 89 | + |
| 90 | +### **SHA-256 Proof Verification** |
| 91 | + |
| 92 | +#### `verifyInclusionSha256` |
| 93 | + |
| 94 | +```solidity |
| 95 | +function verifyInclusionSha256( |
| 96 | + bytes memory proof, |
| 97 | + bytes32 root, |
| 98 | + bytes32 leaf, |
| 99 | + uint256 index |
| 100 | +) internal pure returns (bool) |
| 101 | +``` |
| 102 | + |
| 103 | +Verifies inclusion using SHA-256 hash function via precompiled contract. |
| 104 | + |
| 105 | +*Effects:* |
| 106 | +* Computes the root hash using SHA-256 via precompiled contract |
| 107 | +* Compares computed root with expected root for verification |
| 108 | + |
| 109 | +*Used in:* |
| 110 | +* [`BeaconChainProofs`](../../../src/contracts/libraries/BeaconChainProofs.sol) for beacon chain state verification |
| 111 | + |
| 112 | +#### `processInclusionProofSha256` |
| 113 | + |
| 114 | +```solidity |
| 115 | +function processInclusionProofSha256( |
| 116 | + bytes memory proof, |
| 117 | + bytes32 leaf, |
| 118 | + uint256 index |
| 119 | +) internal view returns (bytes32) |
| 120 | +``` |
| 121 | + |
| 122 | +Returns the computed root hash by traversing up the tree from the leaf using SHA-256. |
| 123 | + |
| 124 | +*Effects:* |
| 125 | +* Traverses the Merkle tree from leaf to root using provided proof |
| 126 | +* Computes hash at each level combining current hash with proof siblings |
| 127 | +* Returns the final computed root hash |
| 128 | + |
| 129 | +*Requirements:* |
| 130 | +* Proof length MUST be non-zero and a multiple of 32 bytes (reverts with `InvalidProofLength` otherwise) |
| 131 | +* Index MUST reach 0 after processing all proof elements (reverts with `InvalidIndex` if proof length mismatch) |
| 132 | + |
| 133 | +--- |
| 134 | + |
| 135 | +## Tree Construction |
| 136 | + |
| 137 | +### **Keccak256 Tree Construction** |
| 138 | + |
| 139 | +#### `merkleizeKeccak` |
| 140 | + |
| 141 | +```solidity |
| 142 | +function merkleizeKeccak(bytes32[] memory leaves) internal pure returns (bytes32) |
| 143 | +``` |
| 144 | + |
| 145 | +Constructs a Merkle tree root from an array of leaves using Keccak256. Accepts any non-empty array of leaves, including single-leaf trees, and automatically pads to the next power of 2 using `bytes32(0)` values. |
| 146 | + |
| 147 | +*Effects:* |
| 148 | +* Pads input array to next power of 2 (if needed) using `bytes32(0)` values |
| 149 | +* Constructs binary Merkle tree bottom-up by hashing pairs using in-place array modification |
| 150 | +* For single-leaf arrays, returns the leaf itself as the root |
| 151 | +* Returns the single root hash representing the entire tree |
| 152 | + |
| 153 | +*Algorithm:* |
| 154 | +1. Pad leaves array to next power of 2 |
| 155 | +2. Iteratively hash pairs level by level until single root remains |
| 156 | +3. Uses in-place array modification for gas efficiency |
| 157 | + |
| 158 | +*Used in:* |
| 159 | +* [`BN254CertificateVerifier`](../../../src/contracts/multichain/BN254CertificateVerifier.sol) for operator info trees |
| 160 | +* [`OperatorTableUpdater`](../../../src/contracts/multichain/OperatorTableUpdater.sol) for operator set hashing |
| 161 | + |
| 162 | +### **SHA-256 Tree Construction** |
| 163 | + |
| 164 | +#### `merkleizeSha256` |
| 165 | + |
| 166 | +```solidity |
| 167 | +function merkleizeSha256(bytes32[] memory leaves) internal pure returns (bytes32) |
| 168 | +``` |
| 169 | + |
| 170 | +Constructs a Merkle tree root using SHA-256 hash function. |
| 171 | + |
| 172 | +*Effects:* |
| 173 | +* Validates input meets strict requirements (power of 2, minimum 2 leaves) |
| 174 | +* Constructs binary Merkle tree bottom-up using SHA-256 hashing |
| 175 | +* Returns the single root hash representing the entire tree |
| 176 | + |
| 177 | +*Requirements*: |
| 178 | +* Input array MUST contain at least 2 leaves (rejects single-leaf trees with `NotEnoughLeaves` error) |
| 179 | +* Input array length MUST be an exact power of 2 (validates with `LeavesNotPowerOfTwo` error) |
| 180 | +* No auto-padding available - stricter requirements for beacon chain compatibility |
| 181 | + |
| 182 | +*Used in:* |
| 183 | +* [`BeaconChainProofs`](../../../src/contracts/libraries/BeaconChainProofs.sol) for beacon chain compatibility |
| 184 | + |
| 185 | +--- |
| 186 | + |
| 187 | +## Proof Generation |
| 188 | + |
| 189 | +### **Keccak256 Proof Generation** |
| 190 | + |
| 191 | +#### `getProofKeccak` |
| 192 | + |
| 193 | +```solidity |
| 194 | +function getProofKeccak(bytes32[] memory leaves, uint256 index) internal pure returns (bytes memory proof) |
| 195 | +``` |
| 196 | + |
| 197 | +Generates an inclusion proof for a specific leaf in a Keccak256 tree. Supports single-leaf trees (returns empty proof) and automatically handles non-power-of-2 leaf arrays through padding. |
| 198 | + |
| 199 | +*Effects:* |
| 200 | +* Constructs a Merkle tree from the provided leaves with automatic padding |
| 201 | +* Traverses from specified leaf to root, collecting sibling hashes at each level |
| 202 | +* For single-leaf trees, returns empty proof since root equals leaf |
| 203 | +* Returns concatenated proof bytes for verification |
| 204 | + |
| 205 | +*Algorithm:* |
| 206 | +1. Pad leaves to next power of 2 |
| 207 | +2. For each tree level, find sibling of current index |
| 208 | +3. Append sibling to proof bytes |
| 209 | +4. Move up tree by dividing index by 2 |
| 210 | +5. Continue until reaching root |
| 211 | + |
| 212 | +*Used in:* |
| 213 | +* Test frameworks for generating proofs |
| 214 | +* Off-chain proof generation systems |
| 215 | + |
| 216 | +### **SHA-256 Proof Generation** |
| 217 | + |
| 218 | +#### `getProofSha256` |
| 219 | + |
| 220 | +```solidity |
| 221 | +function getProofSha256(bytes32[] memory leaves, uint256 index) internal pure returns (bytes memory proof) |
| 222 | +``` |
| 223 | + |
| 224 | +Generates SHA-256 inclusion proof with same algorithm as Keccak version but using SHA-256 hashing. |
| 225 | + |
| 226 | +*Effects:* |
| 227 | +* Validates input meets SHA-256 requirements (minimum 2 leaves) |
| 228 | +* Constructs a Merkle tree using SHA-256 hashing |
| 229 | +* Traverses from specified leaf to root, collecting sibling hashes |
| 230 | +* Returns concatenated proof bytes for verification |
| 231 | + |
| 232 | +*Requirements*: |
| 233 | +* Input array MUST contain at least 2 leaves (rejects single-leaf trees with `NotEnoughLeaves` error) |
| 234 | +* Cannot generate proofs for single-element arrays |
| 235 | +* Follows stricter validation for beacon chain compatibility |
| 236 | + |
| 237 | +--- |
| 238 | + |
| 239 | +## Utility Functions |
| 240 | + |
| 241 | +### **Power of Two Check** |
| 242 | + |
| 243 | +#### `isPowerOfTwo` |
| 244 | + |
| 245 | +```solidity |
| 246 | +function isPowerOfTwo(uint256 value) internal pure returns (bool) |
| 247 | +``` |
| 248 | + |
| 249 | +Efficiently determines if a value is a power of 2 using bit manipulation. |
| 250 | + |
| 251 | +*Effects:* |
| 252 | +* Performs bitwise operations to check power-of-2 property |
| 253 | + |
| 254 | +*Used internally* for validation in `merkleizeSha256` and optimization paths. |
| 255 | + |
| 256 | +--- |
| 257 | + |
| 258 | +## Error Reference |
| 259 | + |
| 260 | +| Error | Code | Description | |
| 261 | +|-------|------|-------------| |
| 262 | +| `InvalidProofLength` | `0x4dc5f6a4` | Proof length not multiple of 32 bytes | |
| 263 | +| `InvalidIndex` | `0x63df8171` | Index outside valid range for tree | |
| 264 | +| `LeavesNotPowerOfTwo` | `0xf6558f51` | Leaves array not power of 2 (SHA-256 only) | |
| 265 | +| `NoLeaves` | `0xbaec3d9a` | Empty leaves array provided | |
| 266 | +| `NotEnoughLeaves` | `0xf8ef0367` | Less than 2 leaves for SHA-256 operations | |
| 267 | + |
| 268 | +--- |
| 269 | + |
| 270 | +## Implementation Optimizations |
| 271 | + |
| 272 | +### **Assembly Usage** |
| 273 | +- Proof verification uses inline assembly for gas efficiency |
| 274 | +- Manual memory management avoids Solidity's safety overhead |
| 275 | +- Direct opcode usage (Keccak256) vs precompile calls (SHA-256) |
| 276 | + |
| 277 | +### **Memory Efficiency** |
| 278 | +- Tree construction reuses input array space |
| 279 | +- In-place modifications reduce memory allocation costs |
| 280 | +- Sibling calculation uses XOR instead of arithmetic operations |
| 281 | + |
| 282 | +### **Precompile Handling** |
| 283 | +- SHA-256 functions reserve gas before precompile calls |
| 284 | +- Static call pattern optimized for success case |
| 285 | +- Explicit revert handling for precompile failures |
| 286 | + |
| 287 | +--- |
| 288 | + |
| 289 | +## Implementation Notes |
| 290 | + |
| 291 | +### **Keccak256 vs SHA-256 Differences** |
| 292 | + |
| 293 | +The library provides two distinct implementations with different design philosophies: |
| 294 | + |
| 295 | +| Feature | Keccak256 Functions | SHA-256 Functions | |
| 296 | +|---------|-------------------|-------------------| |
| 297 | +| **Single-leaf trees** | ✅ Supported | ❌ Rejected (`NotEnoughLeaves`) | |
| 298 | +| **Input validation** | Flexible (any non-empty array) | Strict (≥2 leaves, power of 2) | |
| 299 | +| **Auto-padding** | ✅ Pads to next power of 2 | ❌ Requires exact power of 2 | |
| 300 | +| **Use case** | General EVM Merkle trees | Beacon chain compatibility | |
| 301 | +| **Error handling** | Permissive | Strict validation | |
| 302 | + |
| 303 | +### **Tree Padding Strategy** |
| 304 | +The library uses different padding strategies for different hash functions: |
| 305 | + |
| 306 | +- **Keccak256**: Pads with `bytes32(0)` to next power of 2 |
| 307 | +- **SHA-256**: Requires exact power of 2, no padding |
| 308 | + |
| 309 | +### **Index Validation** |
| 310 | +Index validation occurs during proof processing rather than upfront, allowing the tree traversal algorithm to naturally detect out-of-bounds conditions. |
| 311 | + |
| 312 | +### **Memory Layout** |
| 313 | +Proof bytes are laid out as concatenated 32-byte chunks: `[sibling₀, sibling₁, ..., siblingₙ]` where siblings are sequenced by tree depth, with `sibling₀` being the sibling at the leaf level, `sibling₁` at the next level up, and so on until reaching the root. |
0 commit comments