agents/skills/injectable/l1/light-client-proof-verification/SKILL.md
L1 trigger - audits light client and cross-chain proof verification: Merkle proof soundness, ICS-23 subkey handling (Dragonberry class), state root checks, message integrity.
npx skillsauth add plamentsv/plamen light-client-proof-verificationInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
L1 trigger:
L1_PATTERN=trueAND (light_client/ORics23/ORmerkle/ORproof/ORverify_proofORverify_rootORvalidate_pathORmerkle_treeORstate_rootORtrie_proofORibc/ORbeacon-api-client/detected in recon subsystem map) Inject Into:depth-consensus-invariantordepth-externalLanguage: Go and Rust Finding prefix:[LC-N]Status: v0.1 draft, Round 4 exemplars pending
Recon identifies light-client or cross-chain proof verification code. This skill is the most consequential for cross-chain bridges and IBC-adjacent code because a proof-verification bug can allow forged state to be accepted as canonical — the Dragonberry class.
Identify the exact proof format used:
| Format | Identifier | Notable pitfalls |
|---|---|---|
| Merkle-Patricia Trie (MPT) | Ethereum state proofs, eth_getProof | RLP decode edge cases; extension/branch collision |
| IAVL+ | Cosmos SDK x/store | Left-right ordering; ICS-23 subkey encoding |
| Sparse Merkle Tree (SMT) | Aptos, Sui, Celestia | Empty-leaf handling; default hash values |
| Verkle | Future Ethereum | KZG commitment; opening soundness |
| SSZ proofs | Ethereum Beacon chain | Generalized index encoding |
| ICS-23 | IBC light clients | Generic proof spec; multiple backends |
Write the format into the finding header.
Tag: [MPT:{defect}]
NonExistenceProof needs the left+right neighbors. Dragonberry root cause: the check did not forbid the subkey from being a prefix of a real key, allowing forgery.Tag: [ICS23:{defect}]
Known exemplar: Cosmos SDK Dragonberry (Oct 2022) — Verichains writeup. Subkey suffix forgery allowed forged membership proofs. Patched in Cosmos SDK 0.45.9 / 0.46.3.
validate_path, validate_chunk)For custom Merkle helpers, do not stop at "hashes are recomputed." Build a field-by-field proof-soundness table:
| Check | Required invariant | Evidence |
|---|---|---|
| Leaf binding | The supplied leaf/value hash is compared to the target leaf, not only folded into a path | |
| Target offset | target_offset / index selects the left-right walk and is range-checked | |
| Proof length | number of siblings equals expected tree depth, or the verifier rejects | |
| Operator correctness | all acceptance conditions use the intended boolean connective (&& vs ||) | |
| Root binding | final computed root equals the trusted root for the same block/header/context | |
| Empty/singleton tree | zero-node and one-node cases are explicitly defined | |
Mandatory procedure:
validate_path, verify_path, validate_chunk,
verify_chunk, calculate_root, and hash_leaf function.true.target_offset, empty proof,
and a proof where only one side of a compound condition holds.Tag: [MERKLE-PATH:{leaf|length|offset|operator|root}]
The proof is only as good as the root it verifies against.
Tag: [ROOT-BIND:{source}:{freshness-bound}]
For protocols that accept cross-chain messages (IBC, LayerZero, Wormhole, CCIP):
Tag: [X-CHAIN-MSG:{binding}:{gap}]
For light clients that track validator sets:
Tag: [VSET-TRUST:{param}:{issue}]
| State | Test | Expected | Observed | |---|---|---|---| | Empty proof | proof with no inner nodes | rejected | | | Proof for empty key | key = "" | rejected or spec-defined | | | Proof with root mismatch | correct proof, wrong root | rejected | | | Proof replay across chains | same proof, different src chain ID | rejected | | | Stale root | root from N+1 trust periods ago | rejected | | | Subkey prefix | query is a prefix of a real key | handled per spec (Dragonberry!) | | | Zero-length value | leaf value is empty | handled per spec | |
[CONFORMANCE-PASS] (test vectors from spec) > [DIFF-PASS] (against reference impl) > [LSP-TRACE]IBC Dragonberry — ICS-23 membership proof forgery (October 2022) — the ICS-23 spec lacked soundness guarantees for leaf/inner node prefix/suffix length validation. Verichains showed that a malicious user could forge a membership proof and double-spend assets across every IBC-enabled Cosmos chain. Verichains VSA-2022-103; Cosmos forum retrospective. Adjacent impact: The BNB Chain bridge lost >$100M in October 2022 to a nearby IAVL proof verification flaw in a non-maintained library. Halborn writeup. Skill catch point: Section 2b — every length field in the proof must be bounds-checked; subkey/partial-key attacks must not bypass the leaf check.
Ghost in the Block — SSZ deserialization ghost regions (September 2024, Asymmetric Research) — SignedBeaconBlockDeneb.UnmarshalSSZ in the fastssz library validated that offsets were mutually coherent but did NOT assert contiguity. Attacker could insert 8-byte ghost regions between objects without changing hash-tree-root. Lighthouse correctly rejected with OffsetSkipsVariableBytes(568); Prysm accepted. Ghost in the Block writeup (published Sept 19, 2024). Skill catch point: new methodology nuance below — contiguity is not coherence.
Tendermint lite-client bisection skip-verification safety gap — bisection is unsafe without counterfactual slashing. A malicious 1/3+ validator set in a skipped interval can forge a header the light client will accept. Mitigated by witness-comparison detector. tendermint/tendermint #3244. Skill catch point: Section 5 — for skip/jump verification, security depends on slashing-accountability, not honest majority at target height.
Cosmos IBC Dragonberry retrospective (October 2022) — patched in ibc-go v1.1.5 / v2.0.3 / v3.2.2 + cosmos-sdk 0.45.9 / 0.46.3 after coordinated disclosure. The ICS-23 library consumed by every IBC chain had a single shared bug. Cosmos forum retrospective. Skill catch point: dependency-audit-nodeclient + light-client intersection; shared crypto libraries create multi-chain blast radius.
Insert as new Section 2e: Contiguity ≠ coherence. For any structured deserializer with offset fields:
offset[i+1] == offset[i] + length[i] for every i (contiguity check)total_bytes_consumed == buffer_length at end of parse (no trailing bytes)For any Merkle proof verifier (regardless of format): every length field in the proof spec is a structural constraint. Enumerate all length fields in the proof spec; assert each is bounds-checked.
Tag: [PROOF-CONTIGUITY:{format}:{field}]
VerifyProof, verify_membership, check_proof functionsconsensus-safety-invariants, bls-aggregation-audit (validator set signing)depth-consensus-invariant, depth-externaldocs/l1-mode/severity-matrix.mddevelopment
Prepare Solidity projects for a security audit — test coverage, test quality, NatSpec docs, code hygiene, dependency health, best-practice enforcement, deployment readiness, and project documentation checks. Generates a scored Audit Readiness Report and optionally runs static analysis. Trigger on: "prepare for audit", "audit readiness", "pre-audit check", "audit prep", "NatSpec check", or any request to review a Solidity codebase before a security review.
development
Launch the Plamen deterministic Web3 security audit pipeline
development
Run the Plamen smart-contract audit wizard in Codex
testing
Launch the Plamen deterministic L1 infrastructure audit pipeline