diff --git a/channel/src/lib.rs b/channel/src/lib.rs index b9e6a64..9acfb4d 100644 --- a/channel/src/lib.rs +++ b/channel/src/lib.rs @@ -5,6 +5,7 @@ multiversx_sc::imports!(); pub mod channel_libs; pub mod client_interface; pub mod ibc_module_interface; +pub mod membership; pub mod packet_timeout; #[multiversx_sc::contract] @@ -12,6 +13,7 @@ pub trait Channel: channel_libs::ibc_channel_lib::IbcChannelLibModule + channel_libs::events::EventsModule + packet_timeout::PacketTimeoutModule + + membership::MembershipModule + host::commitment::CommitmentModule + host::host_config::HostConfigModule + host::host_views::HostViewsModule diff --git a/channel/src/membership.rs b/channel/src/membership.rs new file mode 100644 index 0000000..b97a36b --- /dev/null +++ b/channel/src/membership.rs @@ -0,0 +1,165 @@ +use client_common::{VerifyMembershipArgs, VerifyNonMembershipArgs}; +use common_types::{ + channel_types::{channel, channel_counterparty}, + connection_types::connection_end, + ConnectionHops, Timestamp, +}; + +use crate::{ + channel_libs::packet_types::{MsgTimeoutOnClose, TimeoutArgs}, + client_interface, +}; + +multiversx_sc::imports!(); + +#[multiversx_sc::module] +pub trait MembershipModule: + host::module_manager::ModuleManagerModule + + host::storage::StorageModule + + host::commitment::CommitmentModule + + common_modules::utils::UtilsModule +{ + fn check_channel_membership( + &self, + ordering: channel::Order, + client_impl: ManagedAddress, + connection_info: &connection_end::Data, + timeout_args: &dyn TimeoutArgs, + ) { + match ordering { + channel::Order::Ordered => { + self.check_channel_ordered_membership(client_impl, connection_info, timeout_args) + } + channel::Order::Unordered => { + self.check_channel_unordered_membership(client_impl, connection_info, timeout_args) + } + channel::Order::NoneUnspecified => sc_panic!("Unknown channel order"), + }; + } + + fn check_channel_ordered_membership( + &self, + client_impl: ManagedAddress, + connection_info: &connection_end::Data, + timeout_args: &dyn TimeoutArgs, + ) { + let packet = timeout_args.get_packet(); + require!( + packet.sequence >= timeout_args.get_next_seq_recv(), + "Packet may already be received" + ); + + let encoded_value = self.encode_to_buffer(&timeout_args.get_next_seq_recv()); + let membership_args = VerifyMembershipArgs { + client_id: connection_info.client_id.clone(), + height: timeout_args.get_proof_height(), + delay_time_period: connection_info.delay_period, + delay_block_period: self.calculate_block_delay(connection_info.delay_period), + proof: timeout_args.get_proof().clone(), + prefix: connection_info.counterparty.prefix.key_prefix.clone(), + path: self.get_next_seq_recv_commitment_path(&packet.dest_port, &packet.dest_channel), + value: encoded_value, + }; + let membership_result: bool = self + .generic_client_proxy_impl_membership(client_impl) + .verify_membership(membership_args) + .execute_on_dest_context(); + require!(membership_result, "Failed to verify next seq receive"); + + self.channel_info(&packet.source_port, &packet.source_channel) + .update(|channel_info| channel_info.channel.state = channel::State::Closed); + } + + fn check_channel_unordered_membership( + &self, + client_impl: ManagedAddress, + connection_info: &connection_end::Data, + timeout_args: &dyn TimeoutArgs, + ) { + let packet = timeout_args.get_packet(); + let path = self.get_packet_receipt_commitment_path( + &packet.dest_port, + &packet.dest_channel, + packet.sequence, + ); + let non_membership_args = VerifyNonMembershipArgs { + client_id: connection_info.client_id.clone(), + height: timeout_args.get_proof_height(), + delay_time_period: connection_info.delay_period, + delay_block_period: self.calculate_block_delay(connection_info.delay_period), + proof: timeout_args.get_proof().clone(), + prefix: connection_info.counterparty.prefix.key_prefix.clone(), + path, + }; + let non_membership_result: bool = self + .generic_client_proxy_impl_membership(client_impl) + .verify_non_membership(non_membership_args) + .execute_on_dest_context(); + require!( + non_membership_result, + "Failed to verify packet receipt absence" + ); + } + + fn check_expected_channel_membership( + &self, + client_impl: ManagedAddress, + channel: &channel::Data, + connection_info: &connection_end::Data, + args: &MsgTimeoutOnClose, + ) { + let expected_channel = channel::Data { + state: channel::State::Closed, + ordering: channel.ordering, + counterparty: channel_counterparty::Data { + port_id: args.packet.source_port.clone(), + channel_id: args.packet.source_channel.clone(), + }, + connection_hops: ConnectionHops::from_single_item( + connection_info.counterparty.connection_id.clone(), + ), + version: channel.version.clone(), + upgrade_sequence: args.counterparty_upgrade_seq, + }; + + let encoded_value = self.encode_to_buffer(&expected_channel); + let membership_args = VerifyMembershipArgs { + client_id: connection_info.client_id.clone(), + height: args.proof_height, + delay_time_period: connection_info.delay_period, + delay_block_period: self.calculate_block_delay(connection_info.delay_period), + proof: args.proof_close.clone(), + prefix: connection_info.counterparty.prefix.key_prefix.clone(), + path: self.get_next_seq_recv_commitment_path( + &args.packet.dest_port, + &args.packet.dest_channel, + ), + value: encoded_value, + }; + let membership_result: bool = self + .generic_client_proxy_impl_membership(client_impl) + .verify_membership(membership_args) + .execute_on_dest_context(); + require!(membership_result, "Failed to verify channel state"); + } + + /// calculates the block delay based on the expected time per block + fn calculate_block_delay(&self, time_delay: Timestamp) -> Timestamp { + if time_delay == 0 { + return 0; + } + + let host_info = self.host_info().get(); + if host_info.expected_time_per_block == 0 { + return 0; + } + + (time_delay + host_info.expected_time_per_block - 1) / host_info.expected_time_per_block + } + + #[proxy] + fn generic_client_proxy_impl_membership( + &self, + sc_address: ManagedAddress, + ) -> client_interface::generic_client_proxy::GenericClientProxy; +} diff --git a/channel/src/packet_timeout.rs b/channel/src/packet_timeout.rs index 8665b3a..d64130b 100644 --- a/channel/src/packet_timeout.rs +++ b/channel/src/packet_timeout.rs @@ -1,15 +1,10 @@ -use client_common::{VerifyMembershipArgs, VerifyNonMembershipArgs}; use common_types::{ - channel_types::{ - channel::{self, Order}, - channel_counterparty, height, - }, - connection_types::connection_end, - ChannelId, ClientId, ConnectionHops, Hash, PortId, Sequence, Timestamp, + channel_types::{channel, height}, + ChannelId, ClientId, Hash, PortId, Sequence, Timestamp, }; use crate::{ - channel_libs::packet_types::{MsgTimeoutOnClose, MsgTimeoutPacket, Packet, TimeoutArgs}, + channel_libs::packet_types::{MsgTimeoutOnClose, MsgTimeoutPacket, Packet}, client_interface, ibc_module_interface, }; @@ -24,6 +19,7 @@ pub trait PacketTimeoutModule: + host::storage::StorageModule + host::commitment::CommitmentModule + common_modules::utils::UtilsModule + + crate::membership::MembershipModule + crate::channel_libs::events::EventsModule { #[endpoint(timeoutPacket)] @@ -172,130 +168,6 @@ pub trait PacketTimeoutModule: self.crypto().keccak256(first_hashing.as_managed_buffer()) } - fn check_channel_membership( - &self, - ordering: channel::Order, - client_impl: ManagedAddress, - connection_info: &connection_end::Data, - timeout_args: &dyn TimeoutArgs, - ) { - match ordering { - Order::Ordered => { - self.check_channel_ordered_membership(client_impl, connection_info, timeout_args) - } - Order::Unordered => { - self.check_channel_unordered_membership(client_impl, connection_info, timeout_args) - } - Order::NoneUnspecified => sc_panic!("Unknown channel order"), - }; - } - - fn check_channel_ordered_membership( - &self, - client_impl: ManagedAddress, - connection_info: &connection_end::Data, - timeout_args: &dyn TimeoutArgs, - ) { - let packet = timeout_args.get_packet(); - require!( - packet.sequence >= timeout_args.get_next_seq_recv(), - "Packet may already be received" - ); - - let encoded_value = self.encode_to_buffer(&timeout_args.get_next_seq_recv()); - let membership_args = VerifyMembershipArgs { - client_id: connection_info.client_id.clone(), - height: timeout_args.get_proof_height(), - delay_time_period: connection_info.delay_period, - delay_block_period: self.calculate_block_delay(connection_info.delay_period), - proof: timeout_args.get_proof().clone(), - prefix: connection_info.counterparty.prefix.key_prefix.clone(), - path: self.get_next_seq_recv_commitment_path(&packet.dest_port, &packet.dest_channel), - value: encoded_value, - }; - let membership_result: bool = self - .generic_client_proxy_impl(client_impl) - .verify_membership(membership_args) - .execute_on_dest_context(); - require!(membership_result, "Failed to verify next seq receive"); - - self.channel_info(&packet.source_port, &packet.source_channel) - .update(|channel_info| channel_info.channel.state = channel::State::Closed); - } - - fn check_channel_unordered_membership( - &self, - client_impl: ManagedAddress, - connection_info: &connection_end::Data, - timeout_args: &dyn TimeoutArgs, - ) { - let packet = timeout_args.get_packet(); - let path = self.get_packet_receipt_commitment_path( - &packet.dest_port, - &packet.dest_channel, - packet.sequence, - ); - let non_membership_args = VerifyNonMembershipArgs { - client_id: connection_info.client_id.clone(), - height: timeout_args.get_proof_height(), - delay_time_period: connection_info.delay_period, - delay_block_period: self.calculate_block_delay(connection_info.delay_period), - proof: timeout_args.get_proof().clone(), - prefix: connection_info.counterparty.prefix.key_prefix.clone(), - path, - }; - let non_membership_result: bool = self - .generic_client_proxy_impl(client_impl) - .verify_non_membership(non_membership_args) - .execute_on_dest_context(); - require!( - non_membership_result, - "Failed to verify packet receipt absence" - ); - } - - fn check_expected_channel_membership( - &self, - client_impl: ManagedAddress, - channel: &channel::Data, - connection_info: &connection_end::Data, - args: &MsgTimeoutOnClose, - ) { - let expected_channel = channel::Data { - state: channel::State::Closed, - ordering: channel.ordering, - counterparty: channel_counterparty::Data { - port_id: args.packet.source_port.clone(), - channel_id: args.packet.source_channel.clone(), - }, - connection_hops: ConnectionHops::from_single_item( - connection_info.counterparty.connection_id.clone(), - ), - version: channel.version.clone(), - upgrade_sequence: args.counterparty_upgrade_seq, - }; - - let encoded_value = self.encode_to_buffer(&expected_channel); - let membership_args = VerifyMembershipArgs { - client_id: connection_info.client_id.clone(), - height: args.proof_height, - delay_time_period: connection_info.delay_period, - delay_block_period: self.calculate_block_delay(connection_info.delay_period), - proof: args.proof_close.clone(), - prefix: connection_info.counterparty.prefix.key_prefix.clone(), - path: self.get_next_seq_recv_commitment_path( - &args.packet.dest_port, - &args.packet.dest_channel, - ), - value: encoded_value, - }; - let membership_result: bool = self - .generic_client_proxy_impl(client_impl) - .verify_membership(membership_args) - .execute_on_dest_context(); - require!(membership_result, "Failed to verify channel state"); - } - fn timeout_packet_final(&self, packet: Packet) { let caller = self.blockchain().get_caller(); let ibc_module = self.lookup_module_by_channel(&packet.source_port, &packet.source_channel); @@ -307,20 +179,6 @@ pub trait PacketTimeoutModule: self.timeout_packet_event(&packet); } - /// calculates the block delay based on the expected time per block - fn calculate_block_delay(&self, time_delay: Timestamp) -> Timestamp { - if time_delay == 0 { - return 0; - } - - let host_info = self.host_info().get(); - if host_info.expected_time_per_block == 0 { - return 0; - } - - (time_delay + host_info.expected_time_per_block - 1) / host_info.expected_time_per_block - } - #[proxy] fn generic_client_proxy_impl( &self,