Core Concepts
The Sudo Handler Challenge
When smart contracts interact with IBC, they must handle acknowledgments from counterparty chains. These acknowledgments are delivered to contracts via sudo calls, which are privileged operations that bypass normal permission checks. This creates several potential issues:- IBC Channel Disruption: If a contract’s sudo handler fails when processing an acknowledgment on an ORDERED channel, it can disrupt the entire channel.
- Relayer Load: Even on UNORDERED channels, a failing sudo handler can force relayers to repeatedly attempt delivery, causing unnecessary load.
- Attack Vector: Malicious contracts could deliberately implement failing sudo handlers to attack the network infrastructure.
The Solution
To address these challenges, the Contract Manager module implements a pattern where:- Sudo calls are executed in a temporary context (CacheContext), isolating any state changes.
- If the sudo call succeeds, the state changes are committed.
- If the sudo call fails, the error is captured and stored rather than propagated, allowing the IBC workflow to continue.
- Contract owners can later query these stored failures and handle them appropriately.
Architecture
SudoLimitWrapper
The SudoLimitWrapper is a middleware that intercepts calls to the Wasm module’s Sudo method. It:- Enforces Gas Limits: Prevents contracts from using excessive gas during sudo calls.
- Captures Errors: Intercepts both explicit errors and out-of-gas panics from sudo handlers.
- Records Failures: Stores sudo call failures for later retrieval and potential resubmission.
WasmKeeper
interface:
Gas Limitation Mechanism
To prevent DoS attacks via recursive IBC messages that could deplete block gas, the module enforces a strict gas limit for sudo calls. This limit is defined by theSudoCallGasLimit
parameter.
The gas limitation has important implications:
- Complex operations shouldn’t be performed directly in sudo handlers since they may exceed the gas limit.
- As a best practice, sudo handlers should store the received data in the contract’s state and defer processing to a later explicit execution.
- If a contract exceeds the gas limit, the operation is terminated, and a failure record is created.
Failure Management
Failure Record Structure
When a sudo call fails, the module stores aFailure
record with:
- Address: The contract address that experienced the failure
- ID: A sequential identifier for failures of the specific contract
- SudoPayload: The serialized data that was sent to the sudo handler
- Error: A redacted version of the error for deterministic storage
Error Redaction
Since raw error messages may not be deterministic across nodes, the module redacts errors stored in the state to contain only the codespace and code ID. The full error text is emitted as an event rather than stored in state.Failure Resubmission
Contracts can resubmit their failures for processing outside the relayer context. This feature allows:- Contracts to retry processing of a failed sudo call with more gas available.
- Developers to fix contract bugs and then process previously failed acknowledgments.
State Consistency
To protect chain state, all sudo calls are executed in a temporary context (CacheContext):- A temporary context is created before executing the sudo call.
- If the sudo call succeeds, changes are written to the active state.
- If the sudo call fails, the context is discarded, and no changes are made to the state.