Skip to content

Commit

Permalink
Add storage to channel lifecycle
Browse files Browse the repository at this point in the history
  • Loading branch information
chipshort committed Jun 20, 2024
1 parent efba76a commit e7c23df
Showing 1 changed file with 54 additions and 8 deletions.
62 changes: 54 additions & 8 deletions src/pages/ibc/diy-protocol/channel-lifecycle.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ entrypoint is called on chain B with the `IbcChannelOpenMsg::OpenTry` variant.
See the following example and the [`IbcChannelOpenMsg`] documentation.

```rust filename="ibc.rs" template="core"
use cw_storage_plus::Item;

/// enforces ordering and versioning constraints
#[entry_point]
pub fn ibc_channel_open(
Expand All @@ -69,10 +71,15 @@ pub fn ibc_channel_open(
) -> StdResult<IbcChannelOpenResponse> {
let channel = msg.channel();

// here we should check if the channel is what we expect (e.g. the order)
// in this example, we only allow a single channel per contract instance
// you can do more complex checks here
ensure!(!CHANNEL.exists(deps.storage), StdError::generic_err("channel already exists"));

// we should check if the channel is what we expect (e.g. the order)
if channel.order != IbcOrder::Ordered {
return Err(StdError::generic_err("only ordered channels are supported"));
}

// the OpenTry variant (on chain B) also has the counterparty version
// we should check if it is what we expect
if let Some(counter_version) = msg.counterparty_version() {
Expand All @@ -83,12 +90,27 @@ pub fn ibc_channel_open(
}
}

// now, we save the channel ID to storage, so we can use it later
// this also prevents any further channel openings
CHANNEL.save(deps.storage, &ChannelInfo {
channel_id: channel.endpoint.channel_id.clone(),
finalized: false,
})?;

// return the channel version we support
Ok(Some(Ibc3ChannelOpenResponse {
version: IBC_APP_VERSION.to_string(),
}))
}

#[cw_serde]
struct ChannelInfo {
channel_id: String,
/// whether the channel is completely set up
finalized: bool,
}

const CHANNEL: Item<ChannelInfo> = Item::new("channel");
const IBC_APP_VERSION: &str = "my-protocol-v1";
```

Expand All @@ -104,11 +126,17 @@ counterparty version.
#### Permissions

Opening a channel is generally a permissionless process, so make sure to keep
that in mind when implementing the entrypoints. You can add additional checks to
ensure that the channel is connecting to the correct counterparty or have a
state item containing a `bool` that is checked here to explicitly disable new
channels. Depending on the protocol, you might also want to limit yourself to a
single channel per contract.
that in mind when implementing the entrypoints. In the examples above, we only
allow a single channel per contract instance and always make sure not to
overwrite an existing channel. Note that we already save the channel in
`ibc_channel_open`. This causes overlapping channel openings to fail the channel
handshake. The drawback is that the contract can only connect to that one
channel, so if the handshake fails, the contract cannot connect to another
channel.

You can add additional checks to ensure that the channel is connecting to the
correct counterparty or use a map to keep track of multiple channels connecting
to different counterparties.

You can also be more restrictive and only allow the contract itself to initiate
the channel creation handshake. This can be done by adding a state item to the
Expand Down Expand Up @@ -234,18 +262,36 @@ chain B. The full data this entrypoint receives can be seen in the
[`IbcChannelConnectMsg`] documentation. Here is more example code:

```rust filename="ibc.rs" template="core"
use cw_storage_plus::Item;

pub fn ibc_channel_connect(
deps: DepsMut,
env: Env,
msg: IbcChannelConnectMsg,
) -> StdResult<IbcBasicResponse> {
let channel = msg.channel();

// you probably want to save the `channel.endpoint.channel_id` to storage,
// so you can use it when sending packets
// in this example, we only allow a single channel per contract instance
// you can do more complex checks here
let mut channel_info = CHANNEL.load(deps.storage)?;
ensure!(!channel_info.finalized, StdError::generic_err("channel already finalized"));
debug_assert_eq!(channel_info.channel_id, channel.endpoint.channel_id, "channel ID mismatch");

// at this point, we are finished setting up the channel and can mark it as finalized
channel_info.finalized = true;
CHANNEL.save(deps.storage, &channel_info)?;

Ok(IbcBasicResponse::new())
}

#[cw_serde]
struct ChannelInfo {
channel_id: String,
/// whether the channel is completely set up
finalized: bool,
}

const CHANNEL: Item<ChannelInfo> = Item::new("channel");
```

[`IbcChannelConnectMsg`]:
Expand Down

0 comments on commit e7c23df

Please sign in to comment.