Search Apps Documentation Source Content File Folder Download Copy Actions Download

recover-client.md

6.46 Kb · 157 lines
  1# RecoverClient
  2
  3`core.RecoverClient` is the governance escape hatch that revives a client that
  4has become unusable — either **Frozen** (valid misbehaviour was submitted via
  5`UpdateClient`) or **Expired** (no valid header was submitted within the
  6`TrustingPeriod`) — by copying state from a healthy **substitute** client that
  7tracks the same counterparty chain.
  8
  9```gno
 10core.RecoverClient(cross, subjectClientID, substituteClientID string)
 11```
 12
 13Only the admin can call it (see `admin.gno`). In the long run this is expected
 14to be driven by a govDAO proposal callback (tracked in issue #36).
 15
 16## End-to-end flow
 17
 18### 1. A client becomes unusable
 19
 20- **Frozen**: a relayer submitted valid misbehaviour via `UpdateClient` (two
 21  conflicting signed headers for the same chain). The client's `FrozenHeight`
 22  becomes non-zero and `Status()` returns `Frozen`.
 23- **Expired**: no valid header was submitted within `TrustingPeriod`, so
 24  `Status()` returns `Expired` because the latest consensus state's timestamp
 25  is too old.
 26
 27From this point `SendPacket`, `RecvPacket`, `Acknowledgement`, `Timeout` and
 28`UpdateClient` all panic for this client. Any in-flight user packets are
 29stuck, and any inbound packets cannot be acknowledged. Channels using this
 30client are frozen on this side until the client is recovered.
 31
 32### 2. Off-chain coordination
 33
 34Stakeholders agree to recover rather than migrate to a brand-new client.
 35Recovery is preferable because it preserves the client ID, packet
 36commitments / receipts / acknowledgements, counterparty registration and
 37channel state — users don't need to migrate anything.
 38
 39### 3. Create a substitute client
 40
 41A relayer calls `core.CreateClient` with a fresh, **Active** client targeting
 42the *same counterparty chain*. The substitute must satisfy
 43`isMatchingClientState` with the subject, i.e. these fields must match:
 44
 45- `TrustLevel`
 46- `UnbondingPeriod`
 47- `MaxClockDrift`
 48- `ProofSpecs`
 49- `UpgradePath`
 50
 51The following are allowed to differ and are **adopted from the substitute** by
 52the subject during recovery:
 53
 54- `ChainID` (typically the same, but the code supports a change — for example
 55  a genesis-restart on a new chain ID tracking the same state). `ChainID` and
 56  `LatestHeight` are always adopted together, so their revision numbers stay
 57  aligned: `ClientState.ValidateBasic` requires
 58  `LatestHeight.RevisionNumber == ParseChainID(ChainID)`, and since both sides
 59  of that equality come from the substitute (which passed `ValidateBasic` at
 60  `CreateClient`), the invariant is preserved on the subject post-recovery.
 61- `LatestHeight`
 62- `TrustingPeriod` — this is the parameter-tweaking knob: if the original
 63  `TrustingPeriod` was set too aggressively (and partly caused the expiry),
 64  governance can choose a larger value on the substitute and that new value is
 65  copied into the subject. Same mechanism as ibc-go.
 66- `FrozenHeight` (always reset to zero)
 67
 68### 4. (Optional) Fast-forward the substitute
 69
 70Relayers call `UpdateClient(substituteID, header)` until the substitute's
 71`LatestHeight` is at the desired recovery height. The substitute must be
 72`Active` at the moment recovery executes.
 73
 74### 5. Governance proposal
 75
 76A proposal asks to run:
 77
 78```gno
 79core.RecoverClient(cross, subjectID, substituteID)
 80```
 81
 82Currently gated by `ensureAdminCaller()`; once govDAO integration lands the
 83proposal executor becomes the authorized caller.
 84
 85### 6. `RecoverClient` executes
 86
 87`r/aib/ibc/core/client.gno`:
 88
 891. `ensureAdminCaller()`.
 902. Subject and substitute IDs must differ; both must resolve; `typ` must match.
 913. Subject status ∈ {`Frozen`, `Expired`}; substitute status must be `Active`.
 924. Delegates to `subject.lightClient.RecoverClient(substitute.lightClient)`.
 93
 94`p/aib/ibc/lightclient/tendermint/tendermint.gno`:
 95
 961. Type-assert substitute to `*TMLightClient`.
 972. `isMatchingClientState` check.
 983. Fetch `substitute.GetConsensusState(substitute.LatestHeight)`.
 994. Copy into subject: `ChainID`, `LatestHeight`, `TrustingPeriod`; reset
100   `FrozenHeight`.
1015. Store the substitute's consensus state at the substitute's latest height in
102   the subject.
103
104### 7. Post-recovery state
105
106- Subject's `Status() == Active`. `UpdateClient`, packet verification, etc.
107  resume.
108- **Packet commitments, receipts, acknowledgements, `sendSeq`,
109  `counterpartyClientID`, `counterpartyMerklePrefix` are untouched** — that is
110  the point: channels keep working with their existing identifiers and
111  in-flight state.
112- Pre-recovery consensus states remain in the subject's tree but are below the
113  new `LatestHeight` and are not used to verify new packets.
114- The substitute client is **not** deleted and remains `Active`. It can be
115  reused for a future recovery or left idle.
116- `recover_client` event is emitted.
117
118### 8. Counterparty side (symmetric)
119
120If the counterparty chain's client tracking this chain is also Frozen/Expired
121(common when misbehaviour or a long halt affects both sides), the counterparty
122runs its own governance-level recovery. Packet relaying cannot resume on that
123path until both sides are `Active`.
124
125### 9. Relayer resumes
126
127Once both sides are `Active`, relayers submit headers via `UpdateClient` and
128the normal packet lifecycle resumes. No re-`RegisterCounterparty`, no new
129channel.
130
131## Changing parameters during recovery
132
133Because the substitute's `TrustingPeriod` and `ChainID` are adopted by the
134subject, creating the substitute is also the opportunity to adjust those
135parameters through the same governance action:
136
137- **Lengthening `TrustingPeriod`** to reduce the risk of future expiry — for
138  example after learning that the counterparty's block production is slower
139  than originally assumed.
140- **Adopting a new `ChainID`** after a counterparty genesis restart that kept
141  the same consensus state tree — the subject starts verifying headers signed
142  under the new chain ID without being migrated to a new client ID.
143
144Other parameters (`TrustLevel`, `UnbondingPeriod`, `MaxClockDrift`,
145`ProofSpecs`, `UpgradePath`) **cannot** be changed by recovery — the match
146check rejects the substitute. Changing those requires `UpgradeClient` (or a
147fresh client migration).
148
149## Caveats
150
151- The substitute's `LatestHeight` is not required to be greater than the
152  subject's. Same as ibc-go — nothing enforces a "forward" recovery, though in
153  practice the substitute is always ahead.
154- Only the substitute's consensus state **at its `LatestHeight`** is copied
155  into the subject. Earlier substitute consensus states are not migrated.
156- Recovery does not reset packet sequences or clear commitments — those are
157  packet-layer concerns and stay intact.