Skip to content

Commit e1e4207

Browse files
committed
Address PR feedback
1 parent 22bcaf6 commit e1e4207

File tree

2 files changed

+82
-74
lines changed

2 files changed

+82
-74
lines changed

IBC.md

Lines changed: 81 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -266,8 +266,8 @@ pub fn ibc_packet_receive(
266266
```
267267

268268
This is a very special entry point as it has a unique workflow. (Please see the
269-
[Acknowledgement Processing section](#Acknowledgement-Processing) below to
270-
understand it fully).
269+
[Acknowledging Errors section](#Acknowledging-Errors) below to understand it
270+
fully).
271271

272272
Also note the different return response here (`IbcReceiveResponse` rather than
273273
`IbcBasicResponse`). This is because it has an extra field
@@ -277,15 +277,23 @@ by the sending chain.
277277

278278
The
279279
[`IbcPacket` structure](https://github.com/CosmWasm/cosmwasm/blob/v0.14.0-beta4/packages/std/src/ibc.rs#L129-L146)
280-
contains all information needed to process the receipt. You can generally ignore
281-
timeout (this entry point is only called if it hasn't yet timed out) and
282-
sequence (which is used by the IBC framework to avoid duplicates). I generally
283-
use `dest.channel_id` like `info.sender` to authenticate the packet, and parse
284-
`data` into a `PacketMsg` structure, using the same encoding rules as we
285-
discussed in the last section.
286-
287-
After that you can process `PacketMsg` more or less like an `ExecuteMsg`,
288-
including calling into other contracts.
280+
contains all information needed to process the receipt. This info has already
281+
been verified by the core IBC modules via light client and merkle proofs. It
282+
guarantees all metadata in the `IbcPacket` structure is valid, and the `data`
283+
field was written on the remote chain. Furthermore, it guarantees that the
284+
packet is processed at most once (zero times if it times out). Fields like
285+
`dest.channel_id` and `sequence` have a similar trust level to `MessageInfo`,
286+
which we use to authorize normal transactions. The `data` field should be
287+
treated like the `ExecuteMsg` data, which is only as valid as the entity that
288+
signed it.
289+
290+
You can generally ignore `timeout_*` (this entry point is only called if it
291+
hasn't yet timed out) and `sequence` (which is used by the IBC framework to
292+
avoid duplicates). I generally use `dest.channel_id` like `info.sender` to
293+
authenticate the packet, and parse `data` into a `PacketMsg` structure, using
294+
the same encoding rules as we discussed in the last section. After that you can
295+
process `PacketMsg` more or less like an `ExecuteMsg`, including calling into
296+
other contracts.
289297

290298
```rust
291299
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
@@ -334,16 +342,16 @@ After quite some
334342
we struggled to map this idea to the CosmWasm model. However, we also discovered
335343
a deep similarity between these requirements and the
336344
[submessage semantics](./SEMANTICS.md#submessages). It just requires some
337-
careful coding on the contract developer's side to new throw errors. This
338-
produced 3 suggests on how to handle errors and rollbacks _inside
345+
careful coding on the contract developer's side to not throw errors. This
346+
produced 3 suggestions on how to handle errors and rollbacks _inside
339347
`ibc_packet_receive`_
340348

341349
1. If the message doesn't modify any state directly, you can simply put the
342350
logic in a closure, and capture errors, converting them into error
343351
acknowledgements. This would look something like the
344352
[main dispatch loop in `ibc-reflect`](https://github.com/CosmWasm/cosmwasm/blob/cd784cd1148ee395574f3e564f102d0d7b5adcc3/contracts/ibc-reflect/src/contract.rs#L217-L248):
345353

346-
```rust
354+
```rust
347355
(|| {
348356
// which local channel did this packet come on
349357
let caller = packet.dest.channel_id;
@@ -365,7 +373,7 @@ produced 3 suggests on how to handle errors and rollbacks _inside
365373
attributes: vec![],
366374
})
367375
})
368-
```
376+
```
369377

370378
2. If we modify state with an external call, we need to wrap it in a
371379
`submessage` and capture the error. This approach requires we use _exactly
@@ -377,52 +385,52 @@ produced 3 suggests on how to handle errors and rollbacks _inside
377385
[bottom of reply section](./SEMANTICS.md#handling-the-reply)). You can see a
378386
similar example in how
379387
[`ibc-reflect` handles `receive_dispatch`](https://github.com/CosmWasm/cosmwasm/blob/eebb9395ccf315320e3f2fcc526ee76788f89174/contracts/ibc-reflect/src/contract.rs#L307-L336).
380-
Note how we use a unique reply id for this and use that to catch any
388+
Note how we use a unique reply ID for this and use that to catch any
381389
execution failure and return an error acknowledgement instead:
382390

383-
```rust
384-
fn receive_dispatch(
385-
deps: DepsMut,
386-
caller: String,
387-
msgs: Vec<CosmosMsg>,
388-
) -> StdResult<IbcReceiveResponse> {
389-
// what is the reflect contract here
390-
let reflect_addr = accounts(deps.storage).load(caller.as_bytes())?;
391-
392-
// let them know we're fine
393-
let acknowledgement = to_binary(&AcknowledgementMsg::<DispatchResponse>::Ok(()))?;
394-
// create the message to re-dispatch to the reflect contract
395-
let reflect_msg = ReflectExecuteMsg::ReflectMsg { msgs };
396-
let wasm_msg = wasm_execute(reflect_addr, &reflect_msg, vec![])?;
397-
398-
// we wrap it in a submessage to properly report errors
399-
let sub_msg = SubMsg {
400-
id: RECEIVE_DISPATCH_ID,
401-
msg: wasm_msg.into(),
402-
gas_limit: None,
403-
reply_on: ReplyOn::Error,
404-
};
405-
406-
Ok(IbcReceiveResponse {
407-
acknowledgement,
408-
submessages: vec![sub_msg],
409-
messages: vec![],
410-
attributes: vec![attr("action", "receive_dispatch")],
411-
})
412-
}
391+
```rust
392+
fn receive_dispatch(
393+
deps: DepsMut,
394+
caller: String,
395+
msgs: Vec<CosmosMsg>,
396+
) -> StdResult<IbcReceiveResponse> {
397+
// what is the reflect contract here
398+
let reflect_addr = accounts(deps.storage).load(caller.as_bytes())?;
399+
400+
// let them know we're fine
401+
let acknowledgement = to_binary(&AcknowledgementMsg::<DispatchResponse>::Ok(()))?;
402+
// create the message to re-dispatch to the reflect contract
403+
let reflect_msg = ReflectExecuteMsg::ReflectMsg { msgs };
404+
let wasm_msg = wasm_execute(reflect_addr, &reflect_msg, vec![])?;
405+
406+
// we wrap it in a submessage to properly report errors
407+
let sub_msg = SubMsg {
408+
id: RECEIVE_DISPATCH_ID,
409+
msg: wasm_msg.into(),
410+
gas_limit: None,
411+
reply_on: ReplyOn::Error,
412+
};
413+
414+
Ok(IbcReceiveResponse {
415+
acknowledgement,
416+
submessages: vec![sub_msg],
417+
messages: vec![],
418+
attributes: vec![attr("action", "receive_dispatch")],
419+
})
420+
}
413421

414-
#[entry_point]
415-
pub fn reply(deps: DepsMut, _env: Env, reply: Reply) -> StdResult<Response> {
416-
match (reply.id, reply.result) {
417-
(RECEIVE_DISPATCH_ID, ContractResult::Err(err)) => Ok(Response {
418-
data: Some(encode_ibc_error(err)),
419-
..Response::default()
420-
}),
421-
(INIT_CALLBACK_ID, ContractResult::Ok(response)) => handle_init_callback(deps, response),
422-
_ => Err(StdError::generic_err("invalid reply id")),
422+
#[entry_point]
423+
pub fn reply(deps: DepsMut, _env: Env, reply: Reply) -> StdResult<Response> {
424+
match (reply.id, reply.result) {
425+
(RECEIVE_DISPATCH_ID, ContractResult::Err(err)) => Ok(Response {
426+
data: Some(encode_ibc_error(err)),
427+
..Response::default()
428+
}),
429+
(INIT_CALLBACK_ID, ContractResult::Ok(response)) => handle_init_callback(deps, response),
430+
_ => Err(StdError::generic_err("invalid reply id or result")),
431+
}
423432
}
424-
}
425-
```
433+
```
426434

427435
3. For a more complex case, where we are modifying local state and possibly
428436
sending multiple messages, we need to do a self-call via submessages. What I
@@ -441,22 +449,22 @@ pub fn reply(deps: DepsMut, _env: Env, reply: Reply) -> StdResult<Response> {
441449
pieces we already have. For clarity, the `reply` statement should look
442450
something like:
443451

444-
```rust
445-
#[entry_point]
446-
pub fn reply(_deps: DepsMut, _env: Env, reply: Reply) -> StdResult<Response> {
447-
if reply.id != DO_IBC_RECEIVE_ID {
448-
return Err(StdError::generic_err("invalid reply id"));
449-
}
450-
let data = match reply.result {
451-
ContractResult::Ok(response) => response.data,
452-
ContractResult::Err(err) => Some(encode_ibc_error(err)),
453-
};
454-
Ok(Response {
455-
data,
456-
..Response::default()
457-
})
458-
}
459-
```
452+
```rust
453+
#[entry_point]
454+
pub fn reply(_deps: DepsMut, _env: Env, reply: Reply) -> StdResult<Response> {
455+
if reply.id != DO_IBC_RECEIVE_ID {
456+
return Err(StdError::generic_err("invalid reply id"));
457+
}
458+
let data = match reply.result {
459+
ContractResult::Ok(response) => response.data,
460+
ContractResult::Err(err) => Some(encode_ibc_error(err)),
461+
};
462+
Ok(Response {
463+
data,
464+
..Response::default()
465+
})
466+
}
467+
```
460468

461469
##### Standard Acknowledgement Envelope
462470

contracts/ibc-reflect/src/contract.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ pub fn reply(deps: DepsMut, _env: Env, reply: Reply) -> StdResult<Response> {
4545
..Response::default()
4646
}),
4747
(INIT_CALLBACK_ID, ContractResult::Ok(response)) => handle_init_callback(deps, response),
48-
_ => Err(StdError::generic_err("invalid reply id")),
48+
_ => Err(StdError::generic_err("invalid reply id or result")),
4949
}
5050
}
5151

0 commit comments

Comments
 (0)