feat(bridge): add destination claim flow#127
Conversation
Add the destination side of the one-way bridge: a chain anchors an attested
foreign state root, then claims a transfer by proving its record against that
root and minting the mapped asset to the recipient.
- AnchorForeignRoot: the genesis-configured attestor pins a foreign state root,
keyed by (source_chain_id, view) with a strictly monotonic view per source
chain. This is a trusted/authority-attested anchor for the MVP, not trustless
finalization verification.
- Claim { source_chain_id, source_view, record, proof }: verifies the record
targets this chain, looks up the anchored root for the view, checks the
embedded StateProof with verify_state_update, and marks the record consumed
exactly once. The proof decodes with fixed bridge-internal bounds so the
operation stays Read<Cfg = ()>.
- Settlement is lock-and-mint, wired in the coins-chain integration layer: a
genesis-configured AssetId -> CoinId mapping selects the local wrapped coin,
and a new coins Ledger::bridge_mint credits the recipient by increasing that
coin's supply. The claim's consume marker and the mint share one inner overlay,
and the claim's events are buffered and emitted only after settlement commits,
so a missing mapping or a mint failure reverts both state and events. Source
and destination are separate states; the claim never touches the source escrow.
A dedicated test proves a real BridgeOperation::Lock writes a record that
verify_state_update accepts, so the lock -> proof -> claim chain holds.
Adds Clone/Debug/PartialEq + manual Eq to common's StateProof so it can be
embedded in the claim operation.
Out of scope (follow-ups): cryptographic finalization verification, relayer
proof-generation from full production blocks, multisig claims, reverse
redemption, relayer/RPC wiring, and genesis JSON configuration.
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
…cessors Adds focused tests for logic branches and state accessors introduced by the destination claim flow: - anchor rejected when no attestor is configured - claim rejected on source-chain mismatch - ForeignRootAnchored event round-trips (topic + fields) - foreign-root, latest-view, and attestor accessors round-trip through state
distractedm1nd
left a comment
There was a problem hiding this comment.
I think we should take a step back and use the existing consensus certificate flow that bridging was supposed to use. Right now the attestor design is (probably) a bit less code, but operationally very complex.
The original bridge:
- Receives a foreign Finalization certificate.
- Verifies it against a configured foreign consensus scheme.
- Keeps the newest verified certificate.
- Includes that certificate in the destination chain’s consensus extension payload.
So originally just:
foreign certificate -> verify -> cache/include in blocks
But this PR:
attestor signature -> store raw state root -> verify claims
Theres not any connection rn.
Can we maybe try to restrict the MVP to one configured foreign chain and use:
Source Lock
↓
Finalized source checkpoint
↓
Destination stores certified root
↓
Claim proves record
↓
Atomic mint
?
I'll write some more details in sync to not pollute the PR
Summary
Adds the destination side of the bridge: a chain anchors an attested foreign
state root, then claims a transfer by proving its record against that root
and minting the mapped asset to the recipient. Together with the source lock
(#126), this establishes the core trusted-anchor destination claim path:
source lock → anchor root → claim (proof) → recipient mintedWhat's added
AnchorForeignRoot— the genesis-configured attestor pins a foreign stateroot, keyed by
(source_chain_id, view)with a strictly monotonic view persource chain (distinct source chains never block each other). This is a
trusted/authority-attested anchor for the MVP, deterministic and
replay-safe — not trustless finalization verification.
Claim { source_chain_id, source_view, record, proof }— verifies therecord targets this chain, looks up the anchored root for
source_view,checks the embedded
StateProofwithverify_state_update, and marks therecord consumed exactly once (double-claims rejected). The embedded proof
decodes with fixed bridge-internal bounds so the operation stays
Read<Cfg = ()>.genesis-configured
AssetId -> CoinIdmapping selects the local wrapped coin,and a new, narrowly-scoped
coins::Ledger::bridge_mintcredits the recipientby increasing that coin's supply (backed 1:1 by the source escrow). A missing
mapping fails before anything is consumed or minted.
one inner overlay, and the claim's events are buffered and emitted only after
settlement commits — a mint failure reverts state and drops the events.
common::StateProofgainsClone/Debug/PartialEq+ manualEqso it can beembedded in the claim operation (
QmdbOperationhas no upstreamEq;structural equality is reflexive here).
Note on proof generation
A dedicated test proves that a record written by a real
BridgeOperation::Lockverifies with
verify_state_update— so the lock → proof → claim chain holds.However, naive full-range proof generation over a state that also contains coins
writes (escrow balance overwrites advance the QMDB inactivity floor) does not
verify; a relayer must select the correct proof window over a production state
(via
historical_proofand the block's state range). That relayer-side work isdeliberately out of scope: the claim path is agnostic to how the proof was
produced.
Testing
rejected; monotonicity is per source chain; claim succeeds and marks consumed;
double claim, missing anchor, wrong destination, and tampered record rejected;
a real lock's record is provable; codec round-trips (incl. embedded proof).
coins-chain): claim verifies and mints the mapped wrapped cointo the recipient; unmapped asset fails with no consumption; a settlement
failure (max-supply cap) leaves no event and no consumption.
Out of scope (follow-ups)
Validation
All green:
cargo fmt --all -- --checkcargo test -p nunchi-bridge -p nunchi-coins -p nunchi-common -p nunchi-coins-chaincargo clippy --locked --all --all-targets -- -D warnings