Bridge Proof-State Sentinel Separation and Default-Value Discipline
- Name: bridge proof-state sentinel separation and default-value discipline
- Category: bridge reference / verifier-state design note / initialization-safety checklist / default-value hardening
- Summary: Bridge verifiers should never let
uninitialized,unproven,legacy default, orbootstrap placeholderalias toacceptable proof state. Healthy designs keep runtime proof admission, initialization constants, default mapping values, and execution eligibility in disjoint domains, then force every bridge message to cross those domains explicitly. This note exists to preserve the positive design target behind Nomad and adjacent verifier-state failures: the bridge should not be able to turnnothing has been proven yetintothis message may release custodyjust because zero, empty, or legacy state was treated as convenient shorthand. - Why this note exists:
- bridge-verification-failure preserves the broad negative family where a bridge accepts forged bridge truth.
- nomad-bridge-invalid-proof-acceptance-2022-08-01 is the canonical case where
default storage valueandacceptable committed rootcollapsed into the same runtime meaning. - What deserves its own positive-design note is the narrower verifier-state lesson: how bridge code should represent
not proven,proven,acceptable root,legacy migration state, andbootstrap configurationso that one omitted write or one zero-valued placeholder cannot authorize withdrawals.
The core design rule
No bridge release path should be reachable through a state value that can also arise from absence, default initialization, legacy migration residue, or zero-filled storage.
A healthy bridge verifier should make all of the following independently legible:
- no proof has been recorded for this message
- a proof was recorded but is not yet acceptable for execution
- a root or header is configured as trusted bootstrap state
- a message is proven against that trusted state
- a message is actually executable right now
If two of those meanings can share the same runtime value, the bridge is already carrying counterfeit-release risk.
Three verifier-state promotions healthy bridges should not collapse
- Some verifier-shaped state exists
- A mapping slot, root field, status word, timestamp, or proof record-shaped value is present somewhere in storage.
- This is only the weakest fact:
some bytes or defaults can be read.
- That state counts as canonical proof existence for this exact message
- The harder question is whether the bridge has actually crossed from
storage has a valueintothis message was deliberately proven under one exact trusted object. - Nomad collapsed this boundary: a missing message entry inherited a default
bytes32(0)root, and the verifier treated that default-shaped value as if it were a recorded proof reference.
- The harder question is whether the bridge has actually crossed from
- That accepted proof state deserves custody-moving execution authority right now
- Even an explicitly recorded proof should still need separate checks for trust-anchor acceptance, freshness, replay status, challenge-window state, and route-local execution eligibility.
- Bridges fail catastrophically when they flatten
proof record existsandrelease may execute nowinto the same predicate.
Healthy review gets materially better once teams stop calling all three steps message was proven.
What this note means by sentinel separation
- Sentinel means a value used to signal a special state such as
unset,none,not proven yet,legacy, orbootstrap. - Default-value discipline means treating language and storage defaults —
0,bytes32(0), empty arrays, zero enum discriminants, false booleans, missing map entries — as adversarial inputs unless proven otherwise. - Proof-state separation means the bridge cannot infer
acceptable for executionfrom raw stored bytes alone without first crossing explicit state checks. - The analytical habit is simple: if the verifier would behave differently when an absent entry and a real proof artifact are confused, those states must not share representation.
The five separations healthy bridge verifiers should preserve
1. Uninitialized state versus proven state
Message has never been provenmust not serialize to the same runtime object asmessage was proven under root X.- Mapping absence should stay semantically empty, not double as a root, status flag, or hash placeholder that can later satisfy execution checks.
- If the bridge wants proof state by root, it should usually store
status + rootorexists + root, not onlyrootand hope zero means the right thing forever.
2. Bootstrap trust versus runtime proof admission
- Initialization often needs a trusted committed root, genesis header, checkpoint, or signer set.
- That bootstrap object should not live in the same acceptance lane as per-message runtime proof state unless the code can still distinguish
bridge starts with trusted anchor Afromthis specific message proved inclusion under A. - Healthy code keeps
trusted root inventoryseparate frommessage proof inventoryand crosses the boundary explicitly.
3. Stored artifact versus execution eligibility
- Even when a message stores a real root or proof reference, execution should depend on explicit predicates such as
exists,not spent,challenge window elapsed,root currently trusted, andmessage leaf bound to that root. - A raw equality or acceptance check over one field is usually too weak if that field can be stale, default, or migration-shaped.
- The bridge should ask
is this exact message executable now?, not merelydoes one field look acceptable?
4. Migration compatibility versus current safety invariants
- Upgrades often preserve storage layouts, legacy sentinels, or prior root semantics for compatibility.
- That is exactly where dangerous aliasing appears: old code interpreted zero as
not proven; new code interprets the same slot asroot pointeroraccepted root. - A safe migration proves that old-state values cannot accidentally satisfy new execution predicates.
5. Acceptable root inventory versus acceptable message inventory
- A bridge may quite reasonably track
trusted rootsin one table andmessage -> proven rootin another. - The dangerous bug appears when a missing message entry can still inherit a root from the trusted-root table because both maps use the same sentinel value or the execution path forgets to prove
message entry existsfirst. - Healthy code forces the verifier to prove both:
this message has a recorded rootandthat recorded root is currently acceptable.
What sound design looks like
- A healthy bridge keeps three different claims separate all the way through the verifier:
some storage value exists,this exact message has canonical proof existence, andthat accepted proof state is executable bridge truth right now. If those three promotions collapse into one convenience check, untouched storage and live custody authority are already too close together.
1. Explicit proof-state encoding
- Represent proof state with explicit enums, booleans, or structured records instead of overloading one raw field.
- Good shapes include
enum { None, Proven, Executed }, or{ exists, root, timestamp, spent }, rather thanbytes32 rootalone. - The target property is that absence cannot impersonate proof, and that recorded proof still cannot impersonate executable authority without crossing separate gates.
2. Separate stores for trusted checkpoints and message proofs
- Trusted roots, updater checkpoints, light-client headers, and guardian-set metadata should be stored separately from message-level proof records.
- The execution path should bridge those stores deliberately:
lookup message proof→assert proof exists→lookup referenced trusted root→assert root acceptable now. - If a missing message entry can skip the first step, the bridge is already half-broken.
3. Non-default executable states
- Executable proof states should require writes that cannot be synthesized by untouched storage.
- Domain-separated hashes, explicit nonzero discriminants, and impossible-by-default state tuples are healthier than
zero unless something wrote a real value. - If the happy path is observationally identical to untouched storage, auditing and monitoring both get weaker.
4. Migration assertions and shadow checks
- Upgrades should assert invariants over live storage before enabling new verifier semantics.
- Examples:
no trusted root equals sentinel,no legacy entry can decode as executable,message-proof store has explicit existence bits,new acceptance predicate rejects untouched slots. - The safest migrations shadow-validate old and new semantics on production snapshots before flipping execution rights.
5. Fail-closed execution predicates
process,execute,mint, orwithdrawpaths should fail closed when any part of the proof record is missing, stale, ambiguous, or default-shaped.- The bridge should prefer an explicit revert on ambiguous proof state to any attempt at graceful interpretation.
- Bridge liveness is recoverable; counterfeit release is not.
Negative exemplar cluster: what no sentinel separation looks like
- Nomad — the clean canonical case:
messages[_messageHash]defaulted tobytes32(0)for unproven messages,confirmAt[bytes32(0)]was initialized as acceptable, and the execution path asked only whether that root was acceptable. - bridge-verification-failure preserves the broader family where this kind of state aliasing becomes counterfeit bridge truth rather than a mere storage bug.
- The recurring lesson is not
zero is bad. It isspecial states must not inherit authority from storage defaults or bootstrap placeholders.
Red flags that usually mean the verifier is one refactor away from a Nomad-style failure
- Proof status represented only as
bytes32 rootoruint256 timestampwith no explicit existence bit - Initializers that pre-approve roots, headers, or checkpoints without asserting they cannot equal sentinel values
- Execution code that checks
acceptableRoot(messageRoot)before checking whethermessageRootwas ever recorded for this message - Upgrades that change the semantic meaning of an existing storage slot without a full-state migration assertion
- Legacy compatibility comments like
zero means old-style provenorempty value falls back to committed root - Tests that cover valid proofs and invalid proofs but not untouched storage, zero-filled slots, or historical state carried across upgrades
- Monitoring that watches accepted roots but not the impossible condition
missing message entry still passes executable predicate
Design questions worth asking first
- Which values can appear in untouched storage for each verifier map, and what would execution do if it saw them right now?
- Does the bridge represent
message provenexplicitly, or does it infer proof existence from one field that can also be empty or legacy-shaped? - Are trusted roots, checkpoints, or bootstrap anchors stored separately from per-message proof records?
- Could an initializer, migration, or rollback ever make a sentinel value look like an acceptable root or header?
- Does the execution predicate require both
message proof existsandreferenced proof artifact is acceptable, or only the latter? - What invariant proves that no storage value reachable without a successful proof write can authorize custody movement?
- During upgrades, who checks that old-state encodings cannot satisfy new proof semantics accidentally?
- Which tests and runtime alerts would catch
untouched slot became executablebefore attackers do?
Failure-prevention checklist
- Encode message proof existence explicitly. Make
slot has some value,message is actually proven, andmessage is executable nowseparate states; do not inferprovenfrom raw root, hash, timestamp, or status values that can arise from untouched storage. - Keep trusted-root inventory separate from message-proof inventory, and force execution to prove membership in both lanes independently.
- Reserve impossible-by-default executable states: nonzero discriminants, structured records, or domain-separated tags that untouched storage cannot imitate.
- Assert during initialization and upgrade that bootstrap roots, legacy values, and sentinel constants are disjoint from runtime proof-state sentinels.
- Review migrations against production snapshots, not only local fixtures, before enabling new verifier semantics.
- Test untouched storage, zero values, stale entries, rolled-back upgrades, partially initialized replicas, and empty mappings as hostile inputs.
- Require
process/executepaths to fail closed on absent proof records, ambiguous status, or default-valued artifacts even if a related trusted root is present elsewhere. - Monitor for impossible state combinations such as
message entry absent but execution predicate true,trusted root equals sentinel, or sudden execution success without preceding proof-write events. - Document verifier-state invariants in the codebase and incident runbooks so future refactors do not reintroduce aliasing through convenience shortcuts.
- Treat storage default semantics as part of the custody boundary. If auditors cannot explain how absence, legacy state, and executable proof differ, the bridge is not ready.
Audit prompts
- At which promotion boundary does each verifier check operate:
some verifier-shaped state exists,this exact message is proven, orthis message is executable right now? - For every verifier mapping, what concrete value does an untouched slot return, and could any execution predicate treat that as meaningful proof state?
- Are there any code paths where
acceptable root,known root, ortrusted checkpointis checked beforemessage proof exists? - Does initialization or migration ever write trust anchors that can collide with sentinel values, especially
0,bytes32(0), or default enum variants? - Can old storage written under prior verifier semantics decode into executable state after an upgrade?
- Which fields must be non-default before a message can execute, and are those constraints enforced structurally or only by convention?
- If the bridge stores
message -> root, what proves that a missing message cannot inherit authority from the root-acceptance table? - Are challenge-window timestamps, replay markers, or spent flags also vulnerable to default-value aliasing in the same flow?
- Do tests and monitors cover
absence pretending to be proof, or only malformed proofs pretending to be proof? - Can any emergency recovery or manual repair script accidentally set verifier storage to values that the execution path interprets as already acceptable?
- What single invariant would you show a reviewer to prove that untouched storage can never release custody?
Lessons that should already be standard
Some verifier-shaped state exists,that state counts as canonical proof existence, andthat accepted proof state may release custody noware three different claims.- Default storage is protocol state. If its semantics are not intentionally designed, the bridge is outsourcing custody policy to language defaults.
- Bootstrap trust and runtime proof trust are different things. Conflating them is how initialization data becomes withdrawal authority.
- Explicit existence beats clever sentinel reuse. Storage efficiency is not worth ambiguous authenticity semantics.
- Upgrade compatibility is not safety unless old values are proven unable to satisfy new execution predicates.
- A verifier should ask
was this message proven?before it asksis the referenced root acceptable? - If an untouched slot can look one branch away from executable, the bridge is living on refactor luck rather than design discipline.
How this note complements the exploit corpus
- Start with bridge-verification-failure when the sharper question is
how did forged bridge truth cross the destination-side verifier at all? - Use this note when the sharper design question becomes
how should a healthy bridge represent proof state so default, bootstrap, and legacy values cannot impersonate execution authority? - Pair with bridge-truth-security-object when the next question is what exact source-side object the bridge should authenticate once state representation is sound.
- Pair with bridge-bounded-release-design when the next question is how to keep one verifier-state mistake from turning into uncapped custody loss.
- Keep nomad-bridge-invalid-proof-acceptance-2022-08-01 as the canonical negative case study.
Internal linkages
- Parent exploit family: bridge-verification-failure.
- Canonical negative exemplar: nomad-bridge-invalid-proof-acceptance-2022-08-01.
- Broader cross-family sentinel companion: sentinel-lane-separation-and-default-value-deauthorization.
- Broader bridge-authenticity companion: bridge-truth-security-object.
- Blast-radius companion: bridge-bounded-release-design.
- Adjacent verifier-shape companion: proof-facing-field-domain-canonicalization-discipline — useful when the sharper problem is not storage default aliasing but input-domain ambiguity at the proof interface.
Control surface
-
The decisive control surface is verifier state representation: how the bridge stores proof existence, trusted roots, challenge timing, spent markers, and execution eligibility, and where it crosses from
state existstoproof existstoexecution allowed. -
Unsafe systems let storage defaults, bootstrap placeholders, or migration residue participate directly in custody authorization.
-
Healthy systems force every executable bridge message through explicit proof-existence state, explicit trusted-anchor state, and explicit execution gating, with those domains kept disjoint by construction.
-
Sources:
-
Last reviewed: 2026-08-01 UTC