diff --git a/cairo-contracts/packages/apps/src/transfer/components/transfer.cairo b/cairo-contracts/packages/apps/src/transfer/components/transfer.cairo index 02fe1778..3ee06473 100644 --- a/cairo-contracts/packages/apps/src/transfer/components/transfer.cairo +++ b/cairo-contracts/packages/apps/src/transfer/components/transfer.cairo @@ -45,7 +45,7 @@ pub mod TokenTransferComponent { RecvEvent: RecvEvent, AckEvent: AckEvent, AckStatusEvent: AckStatusEvent, - TimoutEvent: TimeoutEvent, + TimeoutEvent: TimeoutEvent, CreateTokenEvent: CreateTokenEvent, } diff --git a/cairo-contracts/packages/apps/src/transfer/types.cairo b/cairo-contracts/packages/apps/src/transfer/types.cairo index 3c87c892..e202198c 100644 --- a/cairo-contracts/packages/apps/src/transfer/types.cairo +++ b/cairo-contracts/packages/apps/src/transfer/types.cairo @@ -54,9 +54,9 @@ pub impl PacketDataJsonSerialize of Serialize { pub impl ArrayFelt252IntoPacketData of Into, PacketData> { fn into(self: Array) -> PacketData { - let mut pakcet_data_span = self.span(); + let mut packet_data_span = self.span(); - let maybe_packet_data: Option = Serde::deserialize(ref pakcet_data_span); + let maybe_packet_data: Option = Serde::deserialize(ref packet_data_span); match maybe_packet_data { Option::Some(packet_data) => packet_data, diff --git a/cairo-contracts/packages/clients/src/cometbft/component.cairo b/cairo-contracts/packages/clients/src/cometbft/component.cairo index 916baaf7..d5156826 100644 --- a/cairo-contracts/packages/clients/src/cometbft/component.cairo +++ b/cairo-contracts/packages/clients/src/cometbft/component.cairo @@ -3,11 +3,13 @@ pub mod CometClientComponent { use alexandria_data_structures::array_ext::ArrayTraitExt; use alexandria_sorting::MergeSort; use core::num::traits::Zero; + use openzeppelin_access::ownable::OwnableComponent; + use openzeppelin_access::ownable::interface::IOwnable; use starknet::storage::{ Map, StorageMapReadAccess, StorageMapWriteAccess, StoragePointerReadAccess, StoragePointerWriteAccess, }; - use starknet::{get_block_timestamp, get_block_number}; + use starknet::{ContractAddress, get_block_timestamp, get_block_number, get_caller_address}; use starknet_ibc_clients::cometbft::{ CometClientState, CometClientStateImpl, CometConsensusState, CometConsensusStateImpl, CometHeader, CometHeaderImpl, CometErrors @@ -42,11 +44,15 @@ pub mod CometClientComponent { #[embeddable_as(CometClientHandler)] impl ClientHandlerImpl< - TContractState, +HasComponent, +Drop + TContractState, + +HasComponent, + +Drop, + impl Ownable: OwnableComponent::HasComponent, > of IClientHandler> { fn create_client( ref self: ComponentState, msg: MsgCreateClient ) -> CreateResponse { + self.assert_owner(); let client_sequence = self.read_next_client_sequence(); self.create_validate(client_sequence, msg.clone()); self.create_execute(client_sequence, msg) @@ -55,13 +61,18 @@ pub mod CometClientComponent { fn update_client( ref self: ComponentState, msg: MsgUpdateClient ) -> UpdateResponse { + self.assert_owner(); self.update_validate(msg.clone()); self.update_execute(msg) } - fn recover_client(ref self: ComponentState, msg: MsgRecoverClient) {} + fn recover_client(ref self: ComponentState, msg: MsgRecoverClient) { + self.assert_owner(); + } - fn upgrade_client(ref self: ComponentState, msg: MsgUpgradeClient) {} + fn upgrade_client(ref self: ComponentState, msg: MsgUpgradeClient) { + self.assert_owner(); + } } // ----------------------------------------------------------- @@ -375,6 +386,26 @@ pub mod CometClientComponent { ) {} } + // ----------------------------------------------------------- + // Client Owner + // ----------------------------------------------------------- + + #[generate_trait] + pub(crate) impl ClientOwnerImpl< + TContractState, + +HasComponent, + +Drop, + impl Ownable: OwnableComponent::HasComponent, + > of ClientOwnerTrait { + fn owner(self: @ComponentState) -> ContractAddress { + get_dep_component!(self, Ownable).owner() + } + + fn assert_owner(self: @ComponentState) { + assert(self.owner() == get_caller_address(), CometErrors::INVALID_OWNER); + } + } + // ----------------------------------------------------------- // Client Internal // ----------------------------------------------------------- diff --git a/cairo-contracts/packages/clients/src/cometbft/errors.cairo b/cairo-contracts/packages/clients/src/cometbft/errors.cairo index 41d06519..fc0fdd0f 100644 --- a/cairo-contracts/packages/clients/src/cometbft/errors.cairo +++ b/cairo-contracts/packages/clients/src/cometbft/errors.cairo @@ -5,9 +5,11 @@ pub mod CometErrors { pub const INVALID_CONSENSUS_STATE: felt252 = 'ICS07: invalid consensus state'; pub const INVALID_HEADER: felt252 = 'ICS07: invalid header'; pub const INVALID_HEADER_TIMESTAMP: felt252 = 'ICS07: invalid header timestamp'; + pub const INVALID_OWNER: felt252 = 'ICS07: invalid owner'; pub const MISSING_CLIENT_STATE: felt252 = 'ICS07: missing client state'; pub const MISSING_CONSENSUS_STATE: felt252 = 'ICS07: missing consensus state'; pub const MISSING_CLIENT_PROCESSED_TIME: felt252 = 'ICS07: missing processed time'; pub const MISSING_CLIENT_PROCESSED_HEIGHT: felt252 = 'ICS07: missing processed height'; pub const ZERO_UPDATE_HEIGHTS: felt252 = 'ICS07: zero update heights'; + pub const ZERO_OWNER: felt252 = 'ICS07: zero owner'; } diff --git a/cairo-contracts/packages/clients/src/tests/cometbft.cairo b/cairo-contracts/packages/clients/src/tests/cometbft.cairo index ead382d7..e8b3e4df 100644 --- a/cairo-contracts/packages/clients/src/tests/cometbft.cairo +++ b/cairo-contracts/packages/clients/src/tests/cometbft.cairo @@ -52,10 +52,10 @@ fn test_update_client_ok() { let msg = cfg.dummy_msg_create_client(); let create_resp = state.create_client(msg); let updating_height = cfg.latest_height.clone() + HEIGHT(1); - let updating_timestmap = cfg.latest_timestamp + 1; + let updating_timestamp = cfg.latest_timestamp + 1; let msg = cfg .dummy_msg_update_client( - create_resp.client_id, create_resp.height, updating_height.clone(), updating_timestmap + create_resp.client_id, create_resp.height, updating_height.clone(), updating_timestamp ); state.update_client(msg); assert_eq!(state.client_type(), cfg.client_type); diff --git a/cairo-contracts/packages/contracts/src/apps/transfer.cairo b/cairo-contracts/packages/contracts/src/apps/transfer.cairo index dc80e403..30195915 100644 --- a/cairo-contracts/packages/contracts/src/apps/transfer.cairo +++ b/cairo-contracts/packages/contracts/src/apps/transfer.cairo @@ -26,9 +26,9 @@ pub mod TransferApp { // Transferrable #[abi(embed_v0)] - impl TokenTransferreableImpl = + impl TokenTransferrableImpl = TransferrableComponent::Transferrable; - impl TokenTransferreableInternal = + impl TokenTransferrableInternal = TransferrableComponent::TransferrableInternalImpl; // Token Transfer diff --git a/cairo-contracts/packages/contracts/src/clients/cometbft.cairo b/cairo-contracts/packages/contracts/src/clients/cometbft.cairo index 86fb248a..e7c09350 100644 --- a/cairo-contracts/packages/contracts/src/clients/cometbft.cairo +++ b/cairo-contracts/packages/contracts/src/clients/cometbft.cairo @@ -1,11 +1,19 @@ #[starknet::contract] pub mod CometClient { - use starknet_ibc_clients::cometbft::CometClientComponent; + use core::num::traits::Zero; + use openzeppelin_access::ownable::OwnableComponent; + use starknet::ContractAddress; + use starknet_ibc_clients::cometbft::{CometClientComponent, CometErrors}; use starknet_ibc_utils::governance::IBCGovernanceComponent; + component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); component!(path: IBCGovernanceComponent, storage: governance, event: IBCGovernanceEvent); component!(path: CometClientComponent, storage: client, event: CometClientEvent); + #[abi(embed_v0)] + impl OwnableMixinImpl = OwnableComponent::OwnableMixinImpl; + impl OwnableInternalImpl = OwnableComponent::InternalImpl; + #[abi(embed_v0)] impl IBCGovernanceImpl = IBCGovernanceComponent::Governance; impl IBCGovernanceInternalImpl = IBCGovernanceComponent::GovernanceInternalImpl; @@ -18,7 +26,7 @@ pub mod CometClient { CometClientComponent::CometClientQuery; // NOTE: The client state validation interface is exposed for public use. - // However, only the handler contract can invoke the execution methods. + // However, only the IBC core contract (owner) can invoke the execution methods. #[abi(embed_v0)] impl CometClientValidationImpl = @@ -27,6 +35,8 @@ pub mod CometClient { #[storage] struct Storage { + #[substorage(v0)] + ownable: OwnableComponent::Storage, #[substorage(v0)] governance: IBCGovernanceComponent::Storage, #[substorage(v0)] @@ -34,8 +44,10 @@ pub mod CometClient { } #[event] - #[derive(Debug, Drop, starknet::Event)] + #[derive(Drop, starknet::Event)] pub enum Event { + #[flat] + OwnableEvent: OwnableComponent::Event, #[flat] IBCGovernanceEvent: IBCGovernanceComponent::Event, #[flat] @@ -43,7 +55,9 @@ pub mod CometClient { } #[constructor] - fn constructor(ref self: ContractState) { + fn constructor(ref self: ContractState, owner: ContractAddress) { + assert(owner.is_non_zero(), CometErrors::ZERO_OWNER); + self.ownable.initializer(owner); self.governance.initializer(); } } diff --git a/cairo-contracts/packages/contracts/src/core.cairo b/cairo-contracts/packages/contracts/src/core.cairo index ec50d109..5f2e71de 100644 --- a/cairo-contracts/packages/contracts/src/core.cairo +++ b/cairo-contracts/packages/contracts/src/core.cairo @@ -7,6 +7,17 @@ pub mod IBCCore { use starknet_ibc_core::connection::ConnectionEventEmitterComponent; use starknet_ibc_core::connection::ConnectionHandlerComponent; use starknet_ibc_core::router::RouterHandlerComponent; + use starknet_ibc_utils::governance::IBCGovernanceComponent; + + // ----------------------------------------------------------- + // Setup Governance Component + // ----------------------------------------------------------- + + component!(path: IBCGovernanceComponent, storage: governance, event: IBCGovernanceEvent); + + #[abi(embed_v0)] + impl IBCGovernanceImpl = IBCGovernanceComponent::Governance; + impl IBCGovernanceInternalImpl = IBCGovernanceComponent::GovernanceInternalImpl; // ----------------------------------------------------------- // Setup Client Components @@ -23,6 +34,9 @@ pub mod IBCCore { #[abi(embed_v0)] impl CoreRegisterClientImpl = ClientHandlerComponent::CoreRegisterClient; + #[abi(embed_v0)] + impl CoreRegisterRelayerImpl = + ClientHandlerComponent::CoreRegisterRelayer; impl ClientInitializerImpl = ClientHandlerComponent::ClientInitializerImpl; // ----------------------------------------------------------- @@ -57,7 +71,7 @@ pub mod IBCCore { component!(path: ChannelHandlerComponent, storage: channel_handler, event: ChannelHandlerEvent); #[abi(embed_v0)] - impl CoreChannelHanderImpl = + impl CoreChannelHandlerImpl = ChannelHandlerComponent::CoreChannelHandler; #[abi(embed_v0)] impl CoreChannelQueryImpl = @@ -70,13 +84,15 @@ pub mod IBCCore { component!(path: RouterHandlerComponent, storage: router_handler, event: RouterHandlerEvent); #[abi(embed_v0)] - impl CoreRouterHanderImpl = + impl CoreRouterHandlerImpl = RouterHandlerComponent::CoreRouterHandler; impl RouterInitializerImpl = RouterHandlerComponent::RouterInitializerImpl; #[storage] struct Storage { + #[substorage(v0)] + governance: IBCGovernanceComponent::Storage, #[substorage(v0)] client_emitter: ClientEventEmitterComponent::Storage, #[substorage(v0)] @@ -96,6 +112,8 @@ pub mod IBCCore { #[event] #[derive(Debug, Drop, starknet::Event)] pub enum Event { + #[flat] + IBCGovernanceEvent: IBCGovernanceComponent::Event, #[flat] ClientEventEmitterEvent: ClientEventEmitterComponent::Event, #[flat] @@ -114,6 +132,7 @@ pub mod IBCCore { #[constructor] fn constructor(ref self: ContractState) { + self.governance.initializer(); self.client_handler.initializer(); self.router_handler.initializer(); } diff --git a/cairo-contracts/packages/contracts/src/tests/channel.cairo b/cairo-contracts/packages/contracts/src/tests/channel.cairo index c0d175bb..a302d4ba 100644 --- a/cairo-contracts/packages/contracts/src/tests/channel.cairo +++ b/cairo-contracts/packages/contracts/src/tests/channel.cairo @@ -7,7 +7,7 @@ use starknet_ibc_testkit::configs::{ }; use starknet_ibc_testkit::dummies::{ OWNER, HEIGHT, TIMESTAMP, COSMOS, STARKNET, CLIENT_ID, CONNECTION_ID, CHANNEL_ID, PORT_ID, - SUPPLY, PACKET_COMMITMENT_ON_SN, + SUPPLY, PACKET_COMMITMENT_ON_SN, RELAYER }; use starknet_ibc_testkit::event_spy::{TransferEventSpyExt, ChannelEventSpyExt}; use starknet_ibc_testkit::handles::{CoreHandle, AppHandle, ERC20Handle}; @@ -453,6 +453,8 @@ fn try_timeout_packet(timeout_height: Height, timeout_timestamp: Timestamp) { // Update Client // ----------------------------------------------------------- + core.register_relayer(RELAYER()); + let msg = comet_cfg .dummy_msg_update_client( CLIENT_ID(), comet_cfg.latest_height, updating_height.clone(), updating_timestamp, diff --git a/cairo-contracts/packages/contracts/src/tests/client.cairo b/cairo-contracts/packages/contracts/src/tests/client.cairo index f45b385e..d9bef6e5 100644 --- a/cairo-contracts/packages/contracts/src/tests/client.cairo +++ b/cairo-contracts/packages/contracts/src/tests/client.cairo @@ -1,7 +1,7 @@ use snforge_std::spy_events; use starknet_ibc_core::client::{UpdateResponse, StatusTrait, ClientContractTrait}; use starknet_ibc_testkit::configs::CometClientConfigTrait; -use starknet_ibc_testkit::dummies::HEIGHT; +use starknet_ibc_testkit::dummies::{HEIGHT, RELAYER}; use starknet_ibc_testkit::event_spy::ClientEventSpyExt; use starknet_ibc_testkit::handles::CoreHandle; use starknet_ibc_testkit::setup::SetupImpl; @@ -58,6 +58,8 @@ fn test_update_comet_client_ok() { // Update Client // ----------------------------------------------------------- + core.register_relayer(RELAYER()); + // Update the client to a new height and time. let updating_height = cfg.latest_height.clone() + HEIGHT(1); let updating_time = cfg.latest_timestamp.clone() + 1; diff --git a/cairo-contracts/packages/contracts/src/tests/transfer.cairo b/cairo-contracts/packages/contracts/src/tests/transfer.cairo index 0af93a71..cc50e023 100644 --- a/cairo-contracts/packages/contracts/src/tests/transfer.cairo +++ b/cairo-contracts/packages/contracts/src/tests/transfer.cairo @@ -127,6 +127,6 @@ fn test_mint_burn_roundtrip() { // Check the balance of the `TransferApp` contract. erc20.assert_balance(ics20.address, 0); - // Chekck the total supply of the ERC20 contract. + // Check the total supply of the ERC20 contract. erc20.assert_total_supply(0); } diff --git a/cairo-contracts/packages/core/src/channel/components/handler.cairo b/cairo-contracts/packages/core/src/channel/components/handler.cairo index f9add0e0..84719d4b 100644 --- a/cairo-contracts/packages/core/src/channel/components/handler.cairo +++ b/cairo-contracts/packages/core/src/channel/components/handler.cairo @@ -646,7 +646,7 @@ pub mod ChannelHandlerComponent { // If the packet sequence matches the expected next // sequence, we check if the ack not exists. As the - // existance means the packet was already relayed. + // existence means the packet was already relayed. if next_sequence_recv == msg.packet.seq_on_a { let ack_exists = self .packet_ack_exists( diff --git a/cairo-contracts/packages/core/src/client/components/handler.cairo b/cairo-contracts/packages/core/src/client/components/handler.cairo index 0c2d48e7..eef45fca 100644 --- a/cairo-contracts/packages/core/src/client/components/handler.cairo +++ b/cairo-contracts/packages/core/src/client/components/handler.cairo @@ -1,19 +1,27 @@ #[starknet::component] pub mod ClientHandlerComponent { use core::num::traits::Zero; - use starknet::ContractAddress; - use starknet::storage::{Map, StorageMapReadAccess, StorageMapWriteAccess}; + use starknet::storage::{ + Map, StorageMapReadAccess, StorageMapWriteAccess, Vec, VecTrait, MutableVecTrait, + StoragePointerReadAccess, StoragePointerWriteAccess + }; + use starknet::{ContractAddress, get_caller_address, get_tx_info}; use starknet_ibc_core::client::ClientEventEmitterComponent::ClientEventEmitterTrait; use starknet_ibc_core::client::ClientEventEmitterComponent; - use starknet_ibc_core::client::interface::{IClientHandler, IRegisterClient}; + use starknet_ibc_core::client::interface::{IClientHandler, IRegisterClient, IRegisterRelayer}; use starknet_ibc_core::client::{ MsgCreateClient, MsgUpdateClient, MsgRecoverClient, MsgUpgradeClient, Height, CreateResponse, UpdateResponse, ClientErrors, ClientContract, ClientContractHandlerTrait }; use starknet_ibc_core::host::{ClientId, ClientIdImpl}; + use starknet_ibc_utils::governance::IBCGovernanceComponent::GovernanceInternalTrait; + use starknet_ibc_utils::governance::IBCGovernanceComponent; #[storage] pub struct Storage { + // NOTE: Temporary relayer whitelist for phase two, + // to be replaced after Comet client contract is implemented. + allowed_relayers: Vec, supported_clients: Map, } @@ -29,7 +37,12 @@ pub mod ClientHandlerComponent { pub impl ClientInitializerImpl< TContractState, +HasComponent, +Drop > of ClientInitializerTrait { - fn initializer(ref self: ComponentState) {} + fn initializer(ref self: ComponentState) { + // NOTE: authorizing the contract's deployer as a relayer to + // simplify the process. This avoids an additional registration step + // for the relayer, as this setup is temporary. + self.write_allowed_relayer(get_tx_info().deref().account_contract_address); + } } // ----------------------------------------------------------- @@ -41,7 +54,7 @@ pub mod ClientHandlerComponent { TContractState, +HasComponent, +Drop, - impl EventEmitter: ClientEventEmitterComponent::HasComponent + impl EventEmitter: ClientEventEmitterComponent::HasComponent, > of IClientHandler> { fn create_client( ref self: ComponentState, msg: MsgCreateClient @@ -58,6 +71,10 @@ pub mod ClientHandlerComponent { fn update_client( ref self: ComponentState, msg: MsgUpdateClient ) -> UpdateResponse { + assert( + self.in_allowed_relayers(get_caller_address()), ClientErrors::UNAUTHORIZED_RELAYER + ); + let mut client = self.get_client(msg.client_id.client_type); let update_result = client.update(msg.clone()); @@ -105,6 +122,37 @@ pub mod ClientHandlerComponent { } } + // ----------------------------------------------------------- + // Allowed Relayers + // ----------------------------------------------------------- + + #[embeddable_as(CoreRegisterRelayer)] + pub impl CoreRegisterRelayerImpl< + TContractState, + +HasComponent, + +Drop, + impl Governance: IBCGovernanceComponent::HasComponent + > of IRegisterRelayer> { + fn register_relayer( + ref self: ComponentState, relayer_address: ContractAddress + ) { + assert(relayer_address.is_non_zero(), ClientErrors::ZERO_RELAYER_ADDRESS); + + assert( + !self.in_allowed_relayers(relayer_address), ClientErrors::RELAYER_ALREADY_REGISTERED + ); + + let governor = get_dep_component!(@self, Governance).governor(); + + assert( + governor.is_zero() || governor == get_caller_address(), + ClientErrors::INVALID_GOVERNOR + ); + + self.write_allowed_relayer(relayer_address); + } + } + // ----------------------------------------------------------- // Client Internal // ----------------------------------------------------------- @@ -128,6 +176,21 @@ pub mod ClientHandlerComponent { pub(crate) impl ClientReaderImpl< TContractState, +HasComponent, +Drop > of ClientReaderTrait { + fn in_allowed_relayers( + self: @ComponentState, caller: ContractAddress + ) -> bool { + let mut allowed = false; + let mut i = 0; + while i < self.allowed_relayers.len() { + if self.allowed_relayers.at(i).read() == caller { + allowed = true; + break; + } + i += 1; + }; + allowed + } + fn read_supported_client( self: @ComponentState, client_type: felt252 ) -> ContractAddress { @@ -143,6 +206,12 @@ pub mod ClientHandlerComponent { pub(crate) impl ClientWriterImpl< TContractState, +HasComponent, +Drop > of ClientWriterTrait { + fn write_allowed_relayer( + ref self: ComponentState, relayer_address: ContractAddress + ) { + self.allowed_relayers.append().write(relayer_address); + } + fn write_supported_client( ref self: ComponentState, client_type: felt252, diff --git a/cairo-contracts/packages/core/src/client/errors.cairo b/cairo-contracts/packages/core/src/client/errors.cairo index 7e56364d..c5a63855 100644 --- a/cairo-contracts/packages/core/src/client/errors.cairo +++ b/cairo-contracts/packages/core/src/client/errors.cairo @@ -1,12 +1,16 @@ pub mod ClientErrors { pub const ZERO_CLIENT_TYPE: felt252 = 'ICS02: client type is 0'; pub const ZERO_CLIENT_ADDRESS: felt252 = 'ICS02: client address is 0'; + pub const ZERO_RELAYER_ADDRESS: felt252 = 'ICS02: relayer address is 0'; pub const EMPTY_CLIENT_STATE: felt252 = 'ICS02: empty client state'; pub const EMPTY_CONSENSUS_STATE: felt252 = 'ICS02: empty consensus state'; pub const EMPTY_CLIENT_MESSAGE: felt252 = 'ICS02: empty client message'; pub const INACTIVE_CLIENT: felt252 = 'ICS02: inactive client'; pub const INVALID_PROOF_HEIGHT: felt252 = 'ICS04: invalid proof height'; + pub const INVALID_GOVERNOR: felt252 = 'ICS02: invalid governor'; pub const INVALID_SUBSTITUTE_CLIENT_ID: felt252 = 'ICS02: invalid subs client id'; pub const OVERFLOWED_HEIGHT: felt252 = 'ICS02: overflowed height'; pub const OVERFLOWED_TIMESTAMP: felt252 = 'ICS02: overflowed timestamp'; + pub const RELAYER_ALREADY_REGISTERED: felt252 = 'ICS02: rly already registered'; + pub const UNAUTHORIZED_RELAYER: felt252 = 'ICS02: unauthorized relayer'; } diff --git a/cairo-contracts/packages/core/src/client/interface.cairo b/cairo-contracts/packages/core/src/client/interface.cairo index 56d4d676..cfe25490 100644 --- a/cairo-contracts/packages/core/src/client/interface.cairo +++ b/cairo-contracts/packages/core/src/client/interface.cairo @@ -23,6 +23,11 @@ pub trait IRegisterClient { ); } +#[starknet::interface] +pub trait IRegisterRelayer { + fn register_relayer(ref self: TContractState, relayer_address: ContractAddress); +} + #[starknet::interface] pub trait IClientStateValidation { fn verify_membership( diff --git a/cairo-contracts/packages/core/src/connection/components/handler.cairo b/cairo-contracts/packages/core/src/connection/components/handler.cairo index 374a6353..bddcdd5c 100644 --- a/cairo-contracts/packages/core/src/connection/components/handler.cairo +++ b/cairo-contracts/packages/core/src/connection/components/handler.cairo @@ -289,7 +289,7 @@ pub mod ConnectionHandlerComponent { +Drop, impl EventEmitter: ConnectionEventEmitterComponent::HasComponent, impl ClientHandler: ClientHandlerComponent::HasComponent, - > of ConnOpenConfitmTrait { + > of ConnOpenConfirmTrait { fn conn_open_confirm_validate( self: @ComponentState, conn_end_on_b: ConnectionEnd, diff --git a/cairo-contracts/packages/core/src/host/paths.cairo b/cairo-contracts/packages/core/src/host/paths.cairo index d3d80cb0..12b6579b 100644 --- a/cairo-contracts/packages/core/src/host/paths.cairo +++ b/cairo-contracts/packages/core/src/host/paths.cairo @@ -70,23 +70,23 @@ pub fn next_sequence_recv_path( builder.path() } -pub fn append_prefix(ref path_builer: RemotePathBuilder, prefix: ByteArray) { - path_builer.append(prefix); +pub fn append_prefix(ref path_builder: RemotePathBuilder, prefix: ByteArray) { + path_builder.append(prefix); } -pub fn append_port(ref path_builer: RemotePathBuilder, port_id: PortId) { - path_builer.append(PORTS_PREFIX()); - path_builer.append(port_id); +pub fn append_port(ref path_builder: RemotePathBuilder, port_id: PortId) { + path_builder.append(PORTS_PREFIX()); + path_builder.append(port_id); } -pub fn append_channel(ref path_builer: RemotePathBuilder, channel_id: ChannelId) { - path_builer.append(CHANNELS_PREFIX()); - path_builer.append(channel_id); +pub fn append_channel(ref path_builder: RemotePathBuilder, channel_id: ChannelId) { + path_builder.append(CHANNELS_PREFIX()); + path_builder.append(channel_id); } -pub fn append_sequence(ref path_builer: RemotePathBuilder, sequence: Sequence) { - path_builer.append(SEQUENCES_PREFIX()); - path_builer.append(sequence); +pub fn append_sequence(ref path_builder: RemotePathBuilder, sequence: Sequence) { + path_builder.append(SEQUENCES_PREFIX()); + path_builder.append(sequence); } #[cfg(test)] diff --git a/cairo-contracts/packages/core/src/lib.cairo b/cairo-contracts/packages/core/src/lib.cairo index ff0a4f22..9b0aac8a 100644 --- a/cairo-contracts/packages/core/src/lib.cairo +++ b/cairo-contracts/packages/core/src/lib.cairo @@ -106,7 +106,8 @@ pub mod client { IClientStateValidationDispatcherTrait, IClientStateExecution, IClientStateExecutionDispatcher, IClientStateExecutionDispatcherTrait, IRegisterClient, IRegisterClientDispatcher, IRegisterClientDispatcherTrait, IClientQuery, - IClientQueryDispatcher, IClientQueryDispatcherTrait + IClientQueryDispatcher, IClientQueryDispatcherTrait, IRegisterRelayer, + IRegisterRelayerDispatcher, IRegisterRelayerDispatcherTrait }; pub use msgs::{MsgCreateClient, MsgRecoverClient, MsgUpdateClient, MsgUpgradeClient}; pub use types::{ diff --git a/cairo-contracts/packages/core/src/tests/channel.cairo b/cairo-contracts/packages/core/src/tests/channel.cairo index 4f97da9a..77cab92c 100644 --- a/cairo-contracts/packages/core/src/tests/channel.cairo +++ b/cairo-contracts/packages/core/src/tests/channel.cairo @@ -16,7 +16,7 @@ fn setup() -> ComponentState { } #[test] -fn test_intial_state() { +fn test_initial_state() { let state = setup(); let next_channel_sequence = state.read_next_channel_sequence(); assert!(next_channel_sequence.is_zero()); diff --git a/cairo-contracts/packages/core/src/tests/client.cairo b/cairo-contracts/packages/core/src/tests/client.cairo index 3c4df9b8..61f8df72 100644 --- a/cairo-contracts/packages/core/src/tests/client.cairo +++ b/cairo-contracts/packages/core/src/tests/client.cairo @@ -1,10 +1,10 @@ -use snforge_std::{spy_events, test_address}; +use snforge_std::{spy_events, test_address, start_cheat_caller_address_global}; use starknet_ibc_core::client::ClientHandlerComponent::{ ClientInitializerImpl, CoreRegisterClientImpl, CoreClientHandlerImpl, EventEmitterImpl, - ClientInternalImpl, ClientReaderTrait + ClientInternalImpl, ClientReaderTrait, ClientWriterTrait }; -use starknet_ibc_core::client::{ClientHandlerComponent, CreateResponse}; -use starknet_ibc_testkit::dummies::{CLIENT, CLIENT_TYPE, CLIENT_ID, HEIGHT}; +use starknet_ibc_core::client::{ClientHandlerComponent, CreateResponse, MsgUpdateClient}; +use starknet_ibc_testkit::dummies::{CLIENT, CLIENT_TYPE, CLIENT_ID, HEIGHT, RELAYER}; use starknet_ibc_testkit::event_spy::ClientEventSpyExt; use starknet_ibc_testkit::mocks::MockClientHandler; @@ -28,6 +28,23 @@ fn test_register_client() { assert_eq!(supported_client, CLIENT()); } +#[test] +fn test_allowed_relayers() { + let mut state = setup(); + assert!(!state.in_allowed_relayers(RELAYER())); + state.write_allowed_relayer(RELAYER()); + assert!(state.in_allowed_relayers(RELAYER())); +} + +#[should_panic(expected: 'ICS02: unauthorized relayer')] +#[test] +fn test_unauthorized_update_client() { + let mut state = setup(); + start_cheat_caller_address_global(RELAYER()); + let msg = MsgUpdateClient { client_id: CLIENT_ID(), client_message: array![] }; + state.update_client(msg); +} + #[test] fn test_get_client_ok() { let mut state = setup(); @@ -38,7 +55,7 @@ fn test_get_client_ok() { #[should_panic(expected: 'ICS02: client address is 0')] #[test] -fn test_get_cleint_fail() { +fn test_get_client_fail() { let state = setup(); state.get_client(CLIENT_TYPE()); } diff --git a/cairo-contracts/packages/core/src/tests/commitment.cairo b/cairo-contracts/packages/core/src/tests/commitment.cairo index 0ffae9ff..0a8c49e5 100644 --- a/cairo-contracts/packages/core/src/tests/commitment.cairo +++ b/cairo-contracts/packages/core/src/tests/commitment.cairo @@ -34,7 +34,7 @@ fn test_array_u8_into_array_u32() { assert_eq!(result, (array![4294967295], 0, 0)); // This corresponds to the following JSON: {"result": "AQ=="}, which - // represents the successful acknoledgement in ICS-20 application. + // represents the successful acknowledgement in ICS-20 application. let array = array![123, 34, 114, 101, 115, 117, 108, 116, 34, 58, 34, 65, 81, 61, 61, 34, 125]; let result = array_u8_into_array_u32(array); assert_eq!(result, (array![2065855077, 1937075316, 574235201, 1362967842], 125, 1)); diff --git a/cairo-contracts/packages/core/src/tests/connection.cairo b/cairo-contracts/packages/core/src/tests/connection.cairo index bc9d3561..c92fd6a4 100644 --- a/cairo-contracts/packages/core/src/tests/connection.cairo +++ b/cairo-contracts/packages/core/src/tests/connection.cairo @@ -19,7 +19,7 @@ fn setup() -> ComponentState { } #[test] -fn test_intial_state() { +fn test_initial_state() { let state = setup(); let next_connection_sequence = state.read_next_connection_sequence(); assert!(next_connection_sequence.is_zero()); diff --git a/cairo-contracts/packages/testkit/src/dummies/core.cairo b/cairo-contracts/packages/testkit/src/dummies/core.cairo index 19442ef6..597d7be9 100644 --- a/cairo-contracts/packages/testkit/src/dummies/core.cairo +++ b/cairo-contracts/packages/testkit/src/dummies/core.cairo @@ -25,6 +25,10 @@ pub fn TIMEOUT_TIMESTAMP(timestamp: u64) -> Timestamp { TIMESTAMP(timestamp) } +pub fn RELAYER() -> ContractAddress { + contract_address_const::<'RELAYER'>() +} + pub fn CLIENT() -> ContractAddress { contract_address_const::<'COMETBFT'>() } diff --git a/cairo-contracts/packages/testkit/src/handles/client.cairo b/cairo-contracts/packages/testkit/src/handles/client.cairo index 6a830ef6..380499eb 100644 --- a/cairo-contracts/packages/testkit/src/handles/client.cairo +++ b/cairo-contracts/packages/testkit/src/handles/client.cairo @@ -1,10 +1,13 @@ use openzeppelin_testing::declare_and_deploy; +use openzeppelin_utils::serde::SerializedAppend; +use starknet::ContractAddress; use starknet_ibc_core::client::ClientContract; #[generate_trait] pub impl ClientHandleImpl of ClientHandle { - fn deploy_client(contract_name: ByteArray) -> ClientContract { + fn deploy_client(contract_name: ByteArray, owner: ContractAddress) -> ClientContract { let mut call_data = array![]; + call_data.append_serde(owner); let address = declare_and_deploy(contract_name, call_data); diff --git a/cairo-contracts/packages/testkit/src/handles/core.cairo b/cairo-contracts/packages/testkit/src/handles/core.cairo index c0ba6aa4..337b21cc 100644 --- a/cairo-contracts/packages/testkit/src/handles/core.cairo +++ b/cairo-contracts/packages/testkit/src/handles/core.cairo @@ -7,8 +7,8 @@ use starknet_ibc_core::channel::{ }; use starknet_ibc_core::client::{ IClientHandlerDispatcher, IClientHandlerDispatcherTrait, IRegisterClientDispatcher, - IRegisterClientDispatcherTrait, MsgCreateClient, MsgUpdateClient, CreateResponse, - UpdateResponse, + IRegisterClientDispatcherTrait, IRegisterRelayerDispatcher, IRegisterRelayerDispatcherTrait, + MsgCreateClient, MsgUpdateClient, CreateResponse, UpdateResponse }; use starknet_ibc_core::commitment::Commitment; use starknet_ibc_core::connection::{ @@ -38,7 +38,7 @@ pub impl CoreHandleImpl of CoreHandle { IClientHandlerDispatcher { contract_address: *self.address } } - fn connecion_handler_dispatcher(self: @CoreContract) -> IConnectionHandlerDispatcher { + fn connection_handler_dispatcher(self: @CoreContract) -> IConnectionHandlerDispatcher { IConnectionHandlerDispatcher { contract_address: *self.address } } @@ -62,6 +62,10 @@ pub impl CoreHandleImpl of CoreHandle { IRegisterClientDispatcher { contract_address: *self.address } } + fn register_relayer_dispatcher(self: @CoreContract) -> IRegisterRelayerDispatcher { + IRegisterRelayerDispatcher { contract_address: *self.address } + } + fn create_client(self: @CoreContract, msg: MsgCreateClient) -> CreateResponse { self.client_handler_dispatcher().create_client(msg) } @@ -70,6 +74,10 @@ pub impl CoreHandleImpl of CoreHandle { self.client_handler_dispatcher().update_client(msg) } + fn register_relayer(self: @CoreContract, relayer_address: ContractAddress) { + self.register_relayer_dispatcher().register_relayer(relayer_address) + } + fn register_client(self: @CoreContract, client_type: felt252, client_address: ContractAddress) { self.register_client_dispatcher().register_client(client_type, client_address) } @@ -79,19 +87,19 @@ pub impl CoreHandleImpl of CoreHandle { } fn conn_open_init(self: @CoreContract, msg: MsgConnOpenInit) -> ConnectionId { - self.connecion_handler_dispatcher().conn_open_init(msg) + self.connection_handler_dispatcher().conn_open_init(msg) } fn conn_open_try(self: @CoreContract, msg: MsgConnOpenTry) -> ConnectionId { - self.connecion_handler_dispatcher().conn_open_try(msg) + self.connection_handler_dispatcher().conn_open_try(msg) } fn conn_open_ack(self: @CoreContract, msg: MsgConnOpenAck) { - self.connecion_handler_dispatcher().conn_open_ack(msg) + self.connection_handler_dispatcher().conn_open_ack(msg) } fn conn_open_confirm(self: @CoreContract, msg: MsgConnOpenConfirm) { - self.connecion_handler_dispatcher().conn_open_confirm(msg) + self.connection_handler_dispatcher().conn_open_confirm(msg) } fn connection_end(self: @CoreContract, connection_id: ConnectionId) -> ConnectionEnd { diff --git a/cairo-contracts/packages/testkit/src/lib.cairo b/cairo-contracts/packages/testkit/src/lib.cairo index ffdfb289..a9794367 100644 --- a/cairo-contracts/packages/testkit/src/lib.cairo +++ b/cairo-contracts/packages/testkit/src/lib.cairo @@ -30,7 +30,7 @@ pub mod dummies { pub use core::{ HEIGHT, TIMESTAMP, CLIENT, CLIENT_TYPE, CLIENT_ID, CONNECTION_ID, CONNECTION_END, PORT_ID, CHANNEL_ID, SEQUENCE, CHANNEL_END, VERSION_PROPOSAL, TIMEOUT_HEIGHT, TIMEOUT_TIMESTAMP, - STATE_PROOF, STATE_ROOT, IBC_PREFIX + STATE_PROOF, STATE_ROOT, IBC_PREFIX, RELAYER }; pub use transfer::{ NAME, SYMBOL, ERC20, AMOUNT, SUPPLY, OWNER, STARKNET, COSMOS, NATIVE_DENOM, HOSTED_DENOM, diff --git a/cairo-contracts/packages/testkit/src/mocks/cometbft.cairo b/cairo-contracts/packages/testkit/src/mocks/cometbft.cairo index ec7d7302..1946db02 100644 --- a/cairo-contracts/packages/testkit/src/mocks/cometbft.cairo +++ b/cairo-contracts/packages/testkit/src/mocks/cometbft.cairo @@ -1,9 +1,17 @@ #[starknet::contract] pub mod MockCometClient { - use starknet_ibc_clients::cometbft::CometClientComponent; + use core::num::traits::Zero; + use openzeppelin_access::ownable::OwnableComponent; + use starknet::ContractAddress; + use starknet_ibc_clients::cometbft::{CometClientComponent, CometErrors}; + component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); component!(path: CometClientComponent, storage: client, event: CometClientEvent); + #[abi(embed_v0)] + impl OwnableMixinImpl = OwnableComponent::OwnableMixinImpl; + impl OwnableInternalImpl = OwnableComponent::InternalImpl; + #[abi(embed_v0)] impl CometClientHandlerImpl = CometClientComponent::CometClientHandler; @@ -21,14 +29,24 @@ pub mod MockCometClient { #[storage] struct Storage { + #[substorage(v0)] + ownable: OwnableComponent::Storage, #[substorage(v0)] client: CometClientComponent::Storage, } #[event] - #[derive(Debug, Drop, starknet::Event)] + #[derive(Drop, starknet::Event)] pub enum Event { + #[flat] + OwnableEvent: OwnableComponent::Event, #[flat] CometClientEvent: CometClientComponent::Event, } + + #[constructor] + fn constructor(ref self: ContractState, owner: ContractAddress) { + assert(owner.is_non_zero(), CometErrors::ZERO_OWNER); + self.ownable.initializer(owner); + } } diff --git a/cairo-contracts/packages/testkit/src/mocks/transfer.cairo b/cairo-contracts/packages/testkit/src/mocks/transfer.cairo index e45bf4c2..eecbfc6d 100644 --- a/cairo-contracts/packages/testkit/src/mocks/transfer.cairo +++ b/cairo-contracts/packages/testkit/src/mocks/transfer.cairo @@ -1,11 +1,14 @@ #[starknet::contract] pub mod MockTransferApp { + use core::num::traits::Zero; use openzeppelin_access::ownable::OwnableComponent; use starknet::ClassHash; use starknet::ContractAddress; use starknet_ibc_apps::transfer::ERC20Contract; use starknet_ibc_apps::transfer::types::{PrefixedDenom, Memo, MsgTransfer}; - use starknet_ibc_apps::transfer::{TokenTransferComponent, TransferrableComponent}; + use starknet_ibc_apps::transfer::{ + TokenTransferComponent, TransferrableComponent, TransferErrors + }; use starknet_ibc_core::host::{PortId, ChannelId}; component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); @@ -19,9 +22,9 @@ pub mod MockTransferApp { // Transferrable #[abi(embed_v0)] - impl TokenTransferreableImpl = + impl TokenTransferrableImpl = TransferrableComponent::Transferrable; - impl TokenTransferreableInternalImpl = + impl TokenTransferrableInternalImpl = TransferrableComponent::TransferrableInternalImpl; // Token Transfer @@ -62,6 +65,7 @@ pub mod MockTransferApp { #[constructor] fn constructor(ref self: ContractState, owner: ContractAddress, erc20_class_hash: ClassHash) { + assert(owner.is_non_zero(), TransferErrors::ZERO_OWNER); self.ownable.initializer(owner); self.transferrable.initializer(); self.transfer.initializer(erc20_class_hash); diff --git a/cairo-contracts/packages/testkit/src/setup.cairo b/cairo-contracts/packages/testkit/src/setup.cairo index a22b1d6b..0e2ad5e6 100644 --- a/cairo-contracts/packages/testkit/src/setup.cairo +++ b/cairo-contracts/packages/testkit/src/setup.cairo @@ -12,7 +12,7 @@ use starknet_ibc_testkit::configs::{ TransferAppConfig, TransferAppConfigTrait, CoreConfig, CoreConfigTrait, CometClientConfig, CometClientConfigTrait }; -use starknet_ibc_testkit::dummies::{OWNER, CLIENT_TYPE}; +use starknet_ibc_testkit::dummies::{OWNER, CLIENT_TYPE, RELAYER}; use starknet_ibc_testkit::handles::{CoreContract, CoreHandle, AppHandle, ERC20Handle, ClientHandle}; #[derive(Drop, Serde)] @@ -38,7 +38,7 @@ pub impl SetupImpl of SetupTrait { /// Deploys an instance of IBC light client contract. fn deploy_client(self: @Setup, contract_name: ByteArray) -> ClientContract { - ClientHandle::deploy_client(contract_name) + ClientHandle::deploy_client(contract_name, *self.owner) } /// Deploys an instance of ERC20 contract. @@ -70,6 +70,8 @@ pub impl SetupImpl of SetupTrait { core.register_client(CLIENT_TYPE(), comet.address); + start_cheat_caller_address(core.address, RELAYER()); + (core, comet) } @@ -115,6 +117,8 @@ pub impl SetupImpl of SetupTrait { core.register_app(TRANSFER_PORT_ID(), ics20.address); + start_cheat_caller_address(core.address, RELAYER()); + (core, ics20, erc20) } } diff --git a/cairo-contracts/packages/utils/src/governance/component.cairo b/cairo-contracts/packages/utils/src/governance/component.cairo index 75727185..86d5404a 100644 --- a/cairo-contracts/packages/utils/src/governance/component.cairo +++ b/cairo-contracts/packages/utils/src/governance/component.cairo @@ -1,7 +1,7 @@ #[starknet::component] pub mod IBCGovernanceComponent { - use starknet::storage::StoragePointerWriteAccess; - use starknet::{ContractAddress, get_caller_address}; + use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; + use starknet::{ContractAddress, get_tx_info}; use starknet_ibc_utils::governance::IGovernance; #[storage] @@ -23,7 +23,11 @@ pub mod IBCGovernanceComponent { TContractState, +HasComponent, +Drop > of GovernanceInternalTrait { fn initializer(ref self: ComponentState) { - self.governor.write(get_caller_address()); + self.governor.write(get_tx_info().deref().account_contract_address); + } + + fn governor(self: @ComponentState) -> ContractAddress { + self.governor.read() } } } diff --git a/relayer/crates/starknet-integration-tests/src/tests/ics20.rs b/relayer/crates/starknet-integration-tests/src/tests/ics20.rs index c9da0a89..93eec6e0 100644 --- a/relayer/crates/starknet-integration-tests/src/tests/ics20.rs +++ b/relayer/crates/starknet-integration-tests/src/tests/ics20.rs @@ -191,20 +191,25 @@ fn test_starknet_ics20_contract() -> Result<(), Error> { class_hash }; - let comet_client_address = starknet_chain - .deploy_contract(&comet_client_class_hash, false, &Vec::new()) - .await?; + let cairo_encoding = StarknetCairoEncoding; - info!( - "deployed Comet client contract to address: {:?}", - comet_client_address - ); + let comet_client_address = { + let owner_call_data = cairo_encoding.encode(&ibc_core_address)?; + let contract_address = starknet_chain + .deploy_contract(&comet_client_class_hash, false, &owner_call_data) + .await?; + + info!( + "deployed Comet client contract to address: {:?}", + contract_address + ); + + contract_address + }; starknet_chain.ibc_core_contract_address = Some(ibc_core_address); starknet_chain.ibc_client_contract_address = Some(comet_client_address); - let cairo_encoding = StarknetCairoEncoding; - let event_encoding = StarknetEventEncoding { erc20_hashes: [erc20_class_hash].into(), ics20_hashes: [ics20_class_hash].into(), diff --git a/relayer/crates/starknet-integration-tests/src/tests/light_client.rs b/relayer/crates/starknet-integration-tests/src/tests/light_client.rs index 2b7220fd..e5c058a6 100644 --- a/relayer/crates/starknet-integration-tests/src/tests/light_client.rs +++ b/relayer/crates/starknet-integration-tests/src/tests/light_client.rs @@ -180,20 +180,25 @@ fn test_starknet_light_client() -> Result<(), Error> { class_hash }; - let comet_client_address = starknet_chain - .deploy_contract(&comet_client_class_hash, false, &Vec::new()) - .await?; + let cairo_encoding = StarknetCairoEncoding; - info!( - "deployed Comet client contract to address: {:?}", - comet_client_address - ); + let comet_client_address = { + let owner_call_data = cairo_encoding.encode(&ibc_core_address)?; + let contract_address = starknet_chain + .deploy_contract(&comet_client_class_hash, false, &owner_call_data) + .await?; + + info!( + "deployed Comet client contract to address: {:?}", + contract_address + ); + + contract_address + }; starknet_chain.ibc_core_contract_address = Some(ibc_core_address); starknet_chain.ibc_client_contract_address = Some(comet_client_address); - let cairo_encoding = StarknetCairoEncoding; - { // register comet client contract with ibc-core diff --git a/relayer/crates/starknet-integration-tests/src/tests/update_clients.rs b/relayer/crates/starknet-integration-tests/src/tests/update_clients.rs index b428cee8..26aaa323 100644 --- a/relayer/crates/starknet-integration-tests/src/tests/update_clients.rs +++ b/relayer/crates/starknet-integration-tests/src/tests/update_clients.rs @@ -130,16 +130,21 @@ fn test_relay_update_clients() -> Result<(), Error> { class_hash }; - let comet_client_address = starknet_chain - .deploy_contract(&comet_client_class_hash, false, &Vec::new()) - .await?; + let cairo_encoding = StarknetCairoEncoding; - info!( - "deployed Comet client contract to address: {:?}", - comet_client_address - ); + let comet_client_address = { + let owner_call_data = cairo_encoding.encode(&ibc_core_address)?; + let contract_address = starknet_chain + .deploy_contract(&comet_client_class_hash, false, &owner_call_data) + .await?; - let cairo_encoding = StarknetCairoEncoding; + info!( + "deployed Comet client contract to address: {:?}", + contract_address + ); + + contract_address + }; { // register comet client contract with ibc-core