Skip to content

Commit

Permalink
feat: implemented first half of recv_packet
Browse files Browse the repository at this point in the history
srdtrk committed Jun 1, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent 8e21f47 commit 8ff2d10
Showing 10 changed files with 853 additions and 15 deletions.
682 changes: 681 additions & 1 deletion Cargo.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -46,3 +46,4 @@ ibc-client-tendermint = { version = "0.53.0", default-features = false, features
cw-ibc-lite-shared = { version = "0.1.0", path = "./packages/shared/" }
cw-ibc-lite-ics02-client = { version = "0.1.0", path = "./contracts/ics02-client/", default-features = false }
sha2 = "0.10.8"
ibc-proto = "0.44.0"
1 change: 1 addition & 0 deletions contracts/ics26-router/Cargo.toml
Original file line number Diff line number Diff line change
@@ -27,3 +27,4 @@ thiserror = { workspace = true }
ibc-core-host = { workspace = true }
cw-ibc-lite-shared = { workspace = true }
cw-ibc-lite-ics02-client = { workspace = true }
ibc-client-cw = { workspace = true }
88 changes: 77 additions & 11 deletions contracts/ics26-router/src/contract.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! This module handles the execution logic of the contract.
use cosmwasm_std::{Binary, Deps, DepsMut, Env, MessageInfo, Response};
use cosmwasm_std::{Binary, Deps, DepsMut, Env, MessageInfo, Reply, Response};

use cw_ibc_lite_ics02_client as ics02_client;

@@ -112,6 +112,19 @@ pub fn execute(
}
}

/// Handles the replies to the submessages.
///
/// # Errors
/// Will return an error if the handler returns an error.
#[cosmwasm_std::entry_point]
#[allow(clippy::needless_pass_by_value)]
pub fn reply(_deps: DepsMut, _env: Env, msg: Reply) -> Result<Response, ContractError> {
match msg.id {
keys::reply::ON_RECV_PACKET => todo!(),
_ => Err(ContractError::UnknownReplyId(msg.id)),
}
}

/// Handles the query messages by routing them to the respective handlers.
///
/// # Errors
@@ -129,9 +142,9 @@ mod execute {

use super::{keys, state, ContractError, DepsMut, Env, MessageInfo, Response};

use cosmwasm_std::{Binary, IbcTimeout};
use cosmwasm_std::{Binary, IbcTimeout, SubMsg};

use cw_ibc_lite_ics02_client as client;
use cw_ibc_lite_ics02_client as ics02_client;
use cw_ibc_lite_shared::{
types::{
apps,
@@ -140,6 +153,8 @@ mod execute {
utils,
};

use ibc_client_cw::types::VerifyMembershipMsgRaw;

#[allow(clippy::too_many_arguments, clippy::needless_pass_by_value)]
pub fn send_packet(
deps: DepsMut,
@@ -153,15 +168,16 @@ mod execute {
timeout: IbcTimeout,
) -> Result<Response, ContractError> {
let ics02_address = state::ICS02_CLIENT_ADDRESS.load(deps.storage)?;
let ics02_contract = client::helpers::Ics02ClientContract::new(ics02_address);
let ics02_contract = ics02_client::helpers::Ics02ClientContract::new(ics02_address);

let ibc_app_address = state::IBC_APPS.load(deps.storage, &source_port)?;
let ibc_app_contract = apps::helpers::IbcApplicationContract::new(ibc_app_address);

// Ensure the counterparty is the destination channel.
let counterparty_id = ics02_contract
.query(&deps.querier)
.counterparty(&source_channel)?;
.counterparty(&source_channel)?
.client_id;
if counterparty_id != dest_channel {
return Err(ContractError::invalid_counterparty(
counterparty_id,
@@ -204,14 +220,63 @@ mod execute {

#[allow(clippy::needless_pass_by_value)]
pub fn recv_packet(
_deps: DepsMut,
deps: DepsMut,
_env: Env,
_info: MessageInfo,
_packet: Packet,
_proof_commitment: Binary,
_proof_height: Height,
info: MessageInfo,
packet: Packet,
proof_commitment: Binary,
proof_height: Height,
) -> Result<Response, ContractError> {
todo!()
let ics02_address = state::ICS02_CLIENT_ADDRESS.load(deps.storage)?;
let ics02_contract = ics02_client::helpers::Ics02ClientContract::new(ics02_address);

let ibc_app_address = state::IBC_APPS.load(deps.storage, &packet.destination_port)?;
let ibc_app_contract = apps::helpers::IbcApplicationContract::new(ibc_app_address);

// Verify the
let counterparty = ics02_contract
.query(&deps.querier)
.counterparty(&packet.destination_channel)?;
if counterparty.client_id != packet.source_channel {
return Err(ContractError::invalid_counterparty(
counterparty.client_id,
packet.source_channel,
));
}

// NOTE: Verify the packet commitment.
let counterparty_commitment_path = state::packet_commitment_item::new(
&packet.source_port,
&packet.source_channel,
packet.sequence,
)
.try_into()?;
let verify_membership_msg = VerifyMembershipMsgRaw {
proof: proof_commitment.into(),
path: counterparty_commitment_path,
value: packet.to_commitment_bytes(),
height: proof_height.into(),
delay_time_period: 0,
delay_block_period: 0,
};
let _ = ics02_contract
.query(&deps.querier)
.client_querier(&packet.destination_channel)?
.verify_membership(verify_membership_msg)?;

state::helpers::set_packet_receipt(deps.storage, &packet)?;

// NOTE: We must retreive a reply from the IBC app to set the acknowledgement.
let callback_msg = apps::callbacks::IbcAppCallbackMsg::OnRecvPacket {
packet,
relayer: info.sender.into(),
};
let recv_packet_callback = SubMsg::reply_on_success(
ibc_app_contract.call(callback_msg)?,
keys::reply::ON_RECV_PACKET,
);

Ok(Response::new().add_submessage(recv_packet_callback))
}

#[allow(clippy::needless_pass_by_value)]
@@ -251,6 +316,7 @@ mod execute {
let contract_address = deps.api.addr_validate(&contract_address)?;
let port_id = if let Some(port_id) = port_id {
// NOTE: Only the admin can register an IBC app with a custom port ID.
// TODO: Add restrictions to the custom port ID. Such as not using `/`.
state::admin::assert_admin(&env, &deps.querier, &info.sender)?;
port_id
} else {
4 changes: 2 additions & 2 deletions contracts/ics26-router/src/types/keys.rs
Original file line number Diff line number Diff line change
@@ -16,6 +16,6 @@ pub const ICS02_CLIENT_SALT: &str = "ics02_client";

/// Contains the reply ids for various `SubMsg` replies
pub mod reply {
/// `ON_SEND_PACKET` is the reply id for the `OnSendPacket` callback reply
pub const ON_SEND_PACKET: u64 = 1;
/// `ON_RECV_PACKET` is the reply id for the `on_recv_packet` reply
pub const ON_RECV_PACKET: u64 = 1;
}
25 changes: 25 additions & 0 deletions contracts/ics26-router/src/types/state.rs
Original file line number Diff line number Diff line change
@@ -161,4 +161,29 @@ pub mod helpers {
item.save(storage, &packet.to_commitment_bytes());
Ok(())
}

/// Sets the packet receipt in the provable packet receipt store.
/// This is used to prevent replay.
///
/// # Errors
/// Returns an error if the receipt has already been committed.
pub fn set_packet_receipt(
storage: &mut dyn Storage,
packet: &Packet,
) -> Result<(), ContractError> {
let item = super::packet_receipt_item::new(
&packet.destination_port,
&packet.destination_channel,
packet.sequence,
);

if item.exists(storage) {
return Err(ContractError::packet_already_commited(
item.as_slice().to_vec(),
));
}

item.save(storage, &[1]);
Ok(())
}
}
1 change: 1 addition & 0 deletions packages/shared/Cargo.toml
Original file line number Diff line number Diff line change
@@ -17,3 +17,4 @@ ibc-core-host = { workspace = true }
ibc-client-cw = { workspace = true }
cw-ownable = { workspace = true }
sha2 = { workspace = true }
ibc-proto = { workspace = true }
6 changes: 6 additions & 0 deletions packages/shared/src/types/error.rs
Original file line number Diff line number Diff line change
@@ -12,6 +12,10 @@ pub enum ContractError {
Std(#[from] StdError),
#[error("{0}")]
OwnershipError(#[from] cw_ownable::OwnershipError),
#[error("{0}")]
FromUTF8Error(#[from] std::string::FromUtf8Error),
#[error("{0}")]
UTF8Error(#[from] std::str::Utf8Error),

#[error("unauthorized")]
Unauthorized,
@@ -23,6 +27,8 @@ pub enum ContractError {
source_type: String,
target_type: String,
},
#[error("unknown reply id: {0}")]
UnknownReplyId(u64),

#[error("counterparty already provided")]
CounterpartyAlreadyProvided,
16 changes: 16 additions & 0 deletions packages/shared/src/types/ibc.rs
Original file line number Diff line number Diff line change
@@ -43,6 +43,13 @@ pub struct Height {
pub revision_height: u64,
}

// /// `MerklePath` is the path used to verify commitment proofs, which can be an
// /// arbitrary structured object (defined by a commitment type).
// /// `MerklePath` is represented from root-to-leaf
// pub struct MerklePath {
// pub key_path: Vec<String>,
// }

impl Packet {
/// `to_commitment_bytes` serializes the packet to commitment bytes as per [ibc-lite go implementation](https://github.com/cosmos/ibc-go/blob/2b40562bcd59ce820ddd7d6732940728487cf94e/modules/core/04-channel/types/packet.go#L38)
///
@@ -68,3 +75,12 @@ impl Packet {
sha2::Sha256::digest(&buf).to_vec()
}
}

impl From<Height> for ibc_proto::ibc::core::client::v1::Height {
fn from(height: Height) -> Self {
Self {
revision_number: height.revision_number,
revision_height: height.revision_height,
}
}
}
44 changes: 43 additions & 1 deletion packages/shared/src/types/storage.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! This module defines the `CosmWasm` storage helper types.
use cosmwasm_std::{Addr, CustomQuery, QuerierWrapper, StdResult, Storage};
use ibc_client_cw::types::MerklePath;

use super::error::ContractError;

@@ -56,7 +57,7 @@ impl PureItem {
}

/// If you import [`PureItem`] from the remote contract, this will let you read the data
/// from a remote contract using [`WasmQuery::Raw`]. Returns `Ok(None)` if no data is set.
/// from a remote contract using [`cosmwasm_std::WasmQuery::Raw`]. Returns `Ok(None)` if no data is set.
///
/// # Errors
/// It only returns error on some runtime issue, not on any data cases.
@@ -67,6 +68,47 @@ impl PureItem {
) -> StdResult<Option<Vec<u8>>> {
querier.query_wasm_raw(remote_contract, self.as_slice())
}

/// This will convert the [`PureItem`] into a [`MerklePath`].
/// This is useful when you want to use the key for remote proofs.
///
/// # Errors
/// It returns an error if the key is not valid UTF-8.
pub fn into_merkle_path_with_prefix(
self,
merkle_prefix: Option<MerklePath>,
) -> Result<MerklePath, ContractError> {
if let Some(mut prefix) = merkle_prefix {
let last_mut = prefix.key_path.last_mut();
if let Some(last) = last_mut {
let key = String::from_utf8(self.storage_key)?;
last.push_str(&key);
Ok(prefix)
} else {
self.try_into()
}
} else {
self.try_into()
}
}
}

impl TryFrom<PureItem> for MerklePath {
type Error = ContractError;

fn try_from(item: PureItem) -> Result<Self, Self::Error> {
Ok(Self {
key_path: vec![item.try_into()?],
})
}
}

impl TryFrom<PureItem> for String {
type Error = ContractError;

fn try_from(item: PureItem) -> Result<Self, Self::Error> {
Ok(Self::from_utf8(item.storage_key)?)
}
}

/// Includes the helpers for constructing a [`cosmwasm_std::DepsMut`] from an [`cosmwasm_std::Deps`].

0 comments on commit 8ff2d10

Please sign in to comment.