Skip to content

Commit fb08588

Browse files
authored
Merge pull request #1646 from CosmWasm/ibc_packet_receive-erros
Add docs on Error handling for ibc_packet_receive
2 parents 9a23983 + 0508f1c commit fb08588

File tree

1 file changed

+130
-0
lines changed

1 file changed

+130
-0
lines changed

docs/ERROR_HANDLING.md

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
# Error handling for various entry points
2+
3+
In this document we discuss how different types of errors during contract
4+
execution are handled by wasmd and the blockchain.
5+
6+
## Two levels of errors
7+
8+
When cosmwasm-vm executes a contract, the caller receives a nested result type:
9+
`VmResult<ContractResult<R>>` with some success response `R`. The outer
10+
`VmResult` is created by the host environment and the inner `ContractResult` is
11+
created inside of the contract. Most application specific error should go into
12+
`ContractResult` errors. This is what happens when you use `?` inside of your
13+
contract implementations. The `VmResult`
14+
[error cases](https://github.com/CosmWasm/cosmwasm/blob/v1.2.3/packages/vm/src/errors/vm_error.rs#L11-L148)
15+
include e.g.
16+
17+
- Caching errors such as a missing Wasm file or corrupted module
18+
- Serialization problems in the contract-host communication
19+
- Panics from panic handler in contract
20+
- Errors in crypto API calls
21+
- Out of gas
22+
- Unreachable statements in the Wasm bytecode
23+
24+
## Error handling
25+
26+
Before version 2.0 those two error types were merged into one in wasmvm and
27+
handled as one thing in the caller (wasmd). See for example
28+
[Instantiate](https://github.com/CosmWasm/wasmvm/blob/v1.2.0/lib.go#L144-L151).
29+
However, there was one exception to this:
30+
[IBCPacketReceive](https://github.com/CosmWasm/wasmvm/blob/v1.2.0/lib.go#L535-L539).
31+
Instead of returning only the contents of the `Ok` case, the whole
32+
`IBCReceiveResult` is returned. This allows the caller to handle the two layers
33+
of errors differently.
34+
35+
As pointed out by our auditors from Oak Security, this
36+
[was inconsistent](https://github.com/CosmWasm/wasmvm/issues/398). Historically
37+
merging the two error types was the desired behaviour. When `IBCPacketReceive`
38+
came in, we needed the differentiation to be available in wasmd, which is why
39+
the API was different than the others.
40+
41+
In wasmvm >= 2.0 (wasmd >= 0.51), we
42+
[always return the contract result](https://github.com/CosmWasm/wasmvm/blob/v2.0.0-rc.2/lib.go#L132)
43+
and let wasmd handle it. Apart from making everything more consistent, this also
44+
allows wasmd to handle contract errors differently from VM errors.
45+
46+
Most errors returned by sub-messages are
47+
[redacted](https://github.com/CosmWasm/wasmd/blob/v0.51.0-rc.1/x/wasm/keeper/msg_dispatcher.go#L205)
48+
by wasmd before passing them back into the contract. The reason for this is the
49+
possible non-determinism of error messages. However, as contract errors come
50+
from the contract, they have to be deterministic. With the new separation, wasmd
51+
now passes the full contract error message back into the calling contract,
52+
massively improving the debugging experience.
53+
54+
## Handing ibc_packet_receive errors
55+
56+
From wasmd 0.22 to 0.31 (inclusive), contract errors and VM errors were handled
57+
the same. They got the special treatment of reverting state changes, writing an
58+
error acknowledgement but don't let the transaction fail.
59+
60+
For wasmd >= 0.32, the special treatment only applies to contract errors. VM
61+
errors in `IBCPacketReceive` let the transaction fail just like the `Execute`
62+
case would. This has two major implications:
63+
64+
1. Application specific errors (especially those which can be triggered by
65+
untrusted users) should create contract errors and no panics. This ensures
66+
that error acknowledgements are written and relayer transactions don't fail.
67+
2. Using panics allow the contract developer to make the transaction fail
68+
without writing an acknowledgement. This can be handy e.g. for allowlisting
69+
relayer addresses.
70+
71+
The following table shows the new handling logic.
72+
73+
| Entry point | Contract error | VM error |
74+
| --------------------- | -------------------------------------------------- | --------------------------------------------- |
75+
| `instantiate` | ⏮️ state reverted<br>❌ tx fails | ⏮️ state reverted<br>❌ tx fails |
76+
| `execute` | ⏮️ state reverted<br>❌ tx fails | ⏮️ state reverted<br>❌ tx fails |
77+
| `migrate` | ⏮️ state reverted<br>❌ tx fails | ⏮️ state reverted<br>❌ tx fails |
78+
| `sudo` | ⏮️ state reverted<br>❌ tx fails | ⏮️ state reverted<br>❌ tx fails |
79+
| `reply` | ⏮️ state reverted<br>❔ depends on `reply_on` | ⏮️ state reverted<br>❔ depends on `reply_on` |
80+
| `ibc_channel_open` | ⏮️ state reverted<br>❌ tx fails | ⏮️ state reverted<br>❌ tx fails |
81+
| `ibc_channel_connect` | ⏮️ state reverted<br>❌ tx fails | ⏮️ state reverted<br>❌ tx fails |
82+
| `ibc_channel_close` | ⏮️ state reverted<br>❌ tx fails | ⏮️ state reverted<br>❌ tx fails |
83+
| `ibc_packet_receive` | ⏮️ state reverted<br>✅ tx succeeds with error ack | ⏮️ state reverted<br>❌ tx fails |
84+
| `ibc_packet_ack` | ⏮️ state reverted<br>❌ tx fails | ⏮️ state reverted<br>❌ tx fails |
85+
| `ibc_packet_timeout` | ⏮️ state reverted<br>❌ tx fails | ⏮️ state reverted<br>❌ tx fails |
86+
87+
## Error acknowledgement formatting
88+
89+
In case of a contract error in `ibc_packet_receive`, wasmd creates an error
90+
acknowledgement. The format used is a JSON object with a single top level
91+
`error` string such as `{"error":"some error text"}`. This format is the JSON
92+
serialization of the ibc-go
93+
[Acknowledgement](https://github.com/cosmos/ibc-go/blob/v7.0.0/proto/ibc/core/channel/v1/channel.proto#L156-L162)
94+
type and compatible with ICS-20.
95+
96+
If you are using the acknowledgement types shipped with cosmwasm-std
97+
([#1512](https://github.com/CosmWasm/cosmwasm/issues/1512)), your protocol's
98+
acknowledgement is compatible with that.
99+
100+
If you are using a customized acknowledgement type, you need to convert contract
101+
errors to error acks yourself in `ibc_packet_receive`. The `Never` type provides
102+
type-safety for that. See:
103+
104+
```rust
105+
// The error type Never ensures you handle all contract errors inside the function body
106+
pub fn ibc_packet_receive(
107+
deps: DepsMut,
108+
_env: Env,
109+
msg: IbcPacketReceiveMsg,
110+
) -> Result<IbcReceiveResponse, Never> {
111+
// put this in a closure so we can convert all error responses into acknowledgements
112+
(|| {
113+
let packet = msg.packet;
114+
let caller = packet.dest.channel_id;
115+
let msg: PacketMsg = from_slice(&packet.data)?;
116+
match msg {
117+
// Some packet receive implementations which return results
118+
PacketMsg::Dispatch { msgs } => receive_dispatch(deps, caller, msgs),
119+
PacketMsg::WhoAmI {} => receive_who_am_i(deps, caller),
120+
PacketMsg::Balances {} => receive_balances(deps, caller),
121+
}
122+
})()
123+
.or_else(|e| {
124+
// Here we encode the error to our own fancy ack type
125+
let acknowledgement: Binary = make_my_ack(e);
126+
Ok(IbcReceiveResponse::new()
127+
.set_ack(acknowledgement))
128+
})
129+
}
130+
```

0 commit comments

Comments
 (0)