# Merkle hash collision and truncation bugs

Leaves hashed like nodes, raw values used as leaves, or truncated hashes weakening collision resistance

_Area: DeFi accounting and math bugs. Seen in 4 disclosed reports; latest 2023-10-12. Updated 2026-10-23._

## How it works
Merkle verification assumes a leaf can never equal an internal node, and that a hash's collision resistance matches its output width. Both assumptions break when the leaf is used raw (not hashed), when leaf and node preimages share the same length, or when the hash is truncated. In each case an attacker can present a subtree node (or a colliding value) as a valid leaf and pass verification for data the tree never authorized.

## Where it shows up
- Proof verification that starts `computedHash` from the supplied leaf value instead of hashing it first [E1](https://bugbountylibrary.com/r/c4%3A2022-05-opensea-seaport%3AM-01.md).
- Leaf preimage and internal-node preimage encoded to the same byte length, e.g. `abi.encode(address,uint)` vs `abi.encode(bytes32,bytes32)` [E2](https://bugbountylibrary.com/r/c4%3A2022-05-factorydao%3AM-04.md).
- Hashes stored or compared at reduced width (15/16 bytes) for packing or gas [E3](https://bugbountylibrary.com/r/c4%3A2022-09-party%3AM-03.md).
- Any `keccak256(...)` result sliced, cast, or truncated before comparison.
- Merkle roots used to gate payments, airdrops, or governance actions.

## How to review
Check the exact invariant: a leaf must be domain-separated from internal nodes, and hashes must be full width.
- Confirm leaves are hashed before use as the starting hash, and that leaf hashing differs from node hashing (prefix byte, distinct type, or different encoding) [E1](https://bugbountylibrary.com/r/c4%3A2022-05-opensea-seaport%3AM-01.md)[E2](https://bugbountylibrary.com/r/c4%3A2022-05-factorydao%3AM-04.md).
- Compare byte lengths of leaf preimages vs node preimages; equal length is unsafe [E2](https://bugbountylibrary.com/r/c4%3A2022-05-factorydao%3AM-04.md).
- Verify no hash is truncated below 32 bytes anywhere in verification or storage comparison [E3](https://bugbountylibrary.com/r/c4%3A2022-09-party%3AM-03.md).
- Confirm the root itself cannot be submitted as a leaf (empty-proof edge case) [E1](https://bugbountylibrary.com/r/c4%3A2022-05-opensea-seaport%3AM-01.md).

## Variants seen
- Raw tokenId used as leaf, so any intermediate node passes as a valid token [E1](https://bugbountylibrary.com/r/c4%3A2022-05-opensea-seaport%3AM-01.md).
- Same-length leaf/node preimages allowing subtree-as-leaf forgery [E2](https://bugbountylibrary.com/r/c4%3A2022-05-factorydao%3AM-04.md).
- Truncated keccak output enabling birthday collisions on governance/distribution hashes [E3](https://bugbountylibrary.com/r/c4%3A2022-09-party%3AM-03.md).

## Impact and severity
Led to trading an unrequested NFT and draining the offerer's payment [E1](https://bugbountylibrary.com/r/c4%3A2022-05-opensea-seaport%3AM-01.md), forged airdrop proofs [E2](https://bugbountylibrary.com/r/c4%3A2022-05-factorydao%3AM-04.md), and potential hijack of governance or distributor funds [E3](https://bugbountylibrary.com/r/c4%3A2022-09-party%3AM-03.md). Severity rises with value gated by the root and with attacker-controlled leaf inputs; truncation to 16 bytes (~2^64) is only safe for low-value targets [E3](https://bugbountylibrary.com/r/c4%3A2022-09-party%3AM-03.md).

## Evidence
- E1: https://bugbountylibrary.com/r/c4%3A2022-05-opensea-seaport%3AM-01.md
- E2: https://bugbountylibrary.com/r/c4%3A2022-05-factorydao%3AM-04.md
- E3: https://bugbountylibrary.com/r/c4%3A2022-09-party%3AM-03.md
