diff --git a/relayer/Cargo.lock b/relayer/Cargo.lock index 7ab1b93a..ff7278e5 100644 --- a/relayer/Cargo.lock +++ b/relayer/Cargo.lock @@ -2258,6 +2258,8 @@ dependencies = [ "cairo-lang-starknet-classes", "cgp", "crypto-bigint", + "futures", + "hermes-async-runtime-components", "hermes-cairo-encoding-components", "hermes-chain-components", "hermes-chain-type-components", @@ -2288,6 +2290,7 @@ dependencies = [ "cairo-lang-starknet-classes", "cgp", "eyre", + "hermes-async-runtime-components", "hermes-cairo-encoding-components", "hermes-chain-type-components", "hermes-cosmos-chain-components", diff --git a/relayer/crates/cairo-encoding-components/src/types/as_starknet_event.rs b/relayer/crates/cairo-encoding-components/src/types/as_starknet_event.rs new file mode 100644 index 00000000..a30c92a1 --- /dev/null +++ b/relayer/crates/cairo-encoding-components/src/types/as_starknet_event.rs @@ -0,0 +1 @@ +pub struct AsStarknetEvent; diff --git a/relayer/crates/cairo-encoding-components/src/types/mod.rs b/relayer/crates/cairo-encoding-components/src/types/mod.rs index 306c5c12..6ba4df23 100644 --- a/relayer/crates/cairo-encoding-components/src/types/mod.rs +++ b/relayer/crates/cairo-encoding-components/src/types/mod.rs @@ -1,2 +1,3 @@ pub mod as_felt; +pub mod as_starknet_event; pub mod nat; diff --git a/relayer/crates/starknet-chain-components/Cargo.toml b/relayer/crates/starknet-chain-components/Cargo.toml index c6e201e6..93de6eea 100644 --- a/relayer/crates/starknet-chain-components/Cargo.toml +++ b/relayer/crates/starknet-chain-components/Cargo.toml @@ -14,6 +14,7 @@ cgp = { workspace = true } hermes-relayer-components = { workspace = true } hermes-test-components = { workspace = true } hermes-runtime-components = { workspace = true } +hermes-async-runtime-components = { workspace = true } hermes-chain-components = { workspace = true } hermes-chain-type-components = { workspace = true } hermes-encoding-components = { workspace = true } @@ -35,4 +36,5 @@ serde = { workspace = true } serde_json = { workspace = true } starknet = { workspace = true } tonic = { workspace = true } +futures = { workspace = true } crypto-bigint = "0.5.5" diff --git a/relayer/crates/starknet-chain-components/src/components/chain.rs b/relayer/crates/starknet-chain-components/src/components/chain.rs index 92539c18..cbe03596 100644 --- a/relayer/crates/starknet-chain-components/src/components/chain.rs +++ b/relayer/crates/starknet-chain-components/src/components/chain.rs @@ -73,6 +73,7 @@ use crate::impls::payload_builders::create_client::BuildStarknetCreateClientPayl use crate::impls::payload_builders::update_client::BuildStarknetUpdateClientPayload; use crate::impls::queries::ack_commitment::QueryStarknetAckCommitment; use crate::impls::queries::balance::QueryStarknetWalletBalance; +use crate::impls::queries::block_events::default::DefaultQueryBlockEvents; use crate::impls::queries::channel_end::QueryChannelEndFromStarknet; use crate::impls::queries::client_state::QueryCometClientState; use crate::impls::queries::connection_end::QueryConnectionEndFromStarknet; @@ -111,6 +112,7 @@ pub use crate::traits::contract::invoke::ContractInvokerComponent; pub use crate::traits::contract::message::InvokeContractMessageBuilderComponent; pub use crate::traits::messages::transfer::TransferTokenMessageBuilderComponent; pub use crate::traits::queries::address::ContractAddressQuerierComponent; +use crate::traits::queries::block_events::BlockEventsQuerierComponent; pub use crate::traits::queries::token_balance::TokenBalanceQuerierComponent; pub use crate::traits::transfer::TokenTransferComponent; pub use crate::traits::types::blob::BlobTypeComponent; @@ -229,6 +231,8 @@ cgp_preset! { ReadPacketSrcStarknetFields, ChainStatusQuerierComponent: QueryStarknetChainStatus, + BlockEventsQuerierComponent: + DefaultQueryBlockEvents, MessageSenderComponent: SendCallMessages, TxSubmitterComponent: diff --git a/relayer/crates/starknet-chain-components/src/components/starknet_to_cosmos.rs b/relayer/crates/starknet-chain-components/src/components/starknet_to_cosmos.rs index 5acb07cf..26f6ade9 100644 --- a/relayer/crates/starknet-chain-components/src/components/starknet_to_cosmos.rs +++ b/relayer/crates/starknet-chain-components/src/components/starknet_to_cosmos.rs @@ -94,6 +94,5 @@ cgp_preset! { PacketDstChannelIdGetterComponent, ]: ReadPacketDstStarknetFields, - } } diff --git a/relayer/crates/starknet-chain-components/src/impls/encoding/option.rs b/relayer/crates/starknet-chain-components/src/impls/encoding/option.rs index 5497a522..e2139a7f 100644 --- a/relayer/crates/starknet-chain-components/src/impls/encoding/option.rs +++ b/relayer/crates/starknet-chain-components/src/impls/encoding/option.rs @@ -21,11 +21,16 @@ where ) -> Result, Encoding::Error> { let class_hashes = encoding.get_field(PhantomData); - if class_hashes.contains(&event.class_hash) { - let value = encoding.decode(event)?; - Ok(Some(value)) - } else { - Ok(None) + match &event.class_hash { + Some(class_hash) => { + if class_hashes.contains(class_hash) { + let value = encoding.decode(event)?; + Ok(Some(value)) + } else { + Ok(None) + } + } + None => Ok(None), } } } diff --git a/relayer/crates/starknet-chain-components/src/impls/mod.rs b/relayer/crates/starknet-chain-components/src/impls/mod.rs index 84dc93b4..977616cc 100644 --- a/relayer/crates/starknet-chain-components/src/impls/mod.rs +++ b/relayer/crates/starknet-chain-components/src/impls/mod.rs @@ -14,6 +14,7 @@ pub mod queries; pub mod send_message; pub mod starknet_to_cosmos; pub mod submit_tx; +pub mod subscription; pub mod transfer; pub mod tx_response; pub mod types; diff --git a/relayer/crates/starknet-chain-components/src/impls/queries/block_events/default.rs b/relayer/crates/starknet-chain-components/src/impls/queries/block_events/default.rs new file mode 100644 index 00000000..577072d6 --- /dev/null +++ b/relayer/crates/starknet-chain-components/src/impls/queries/block_events/default.rs @@ -0,0 +1,38 @@ +use cgp::prelude::CanRaiseAsyncError; +use hermes_chain_components::traits::queries::chain_status::CanQueryChainHeight; +use hermes_chain_components::traits::types::event::HasEventType; +use hermes_chain_components::traits::types::height::HasHeightType; +use hermes_chain_type_components::traits::types::address::HasAddressType; +use hermes_runtime_components::traits::runtime::HasRuntime; +use hermes_runtime_components::traits::sleep::CanSleep; +use starknet::core::types::Felt; +use starknet::providers::ProviderError; + +use crate::impls::queries::block_events::get::GetStarknetBlockEvents; +use crate::impls::queries::block_events::retry::RetryQueryBlockEvents; +use crate::impls::queries::block_events::wait::WaitBlockHeightAndQueryEvents; +use crate::traits::provider::HasStarknetProvider; +use crate::traits::queries::block_events::BlockEventsQuerier; +use crate::types::event::StarknetEvent; + +pub struct DefaultQueryBlockEvents; + +impl BlockEventsQuerier for DefaultQueryBlockEvents +where + Chain: HasRuntime + + HasHeightType + + HasEventType + + HasAddressType
+ + HasStarknetProvider + + CanQueryChainHeight + + CanRaiseAsyncError, + Chain::Runtime: CanSleep, +{ + async fn query_block_events( + chain: &Chain, + height: &Chain::Height, + address: &Chain::Address, + ) -> Result, Chain::Error> { + >>::query_block_events(chain, height, address).await + } +} diff --git a/relayer/crates/starknet-chain-components/src/impls/queries/block_events/get.rs b/relayer/crates/starknet-chain-components/src/impls/queries/block_events/get.rs new file mode 100644 index 00000000..2afbf817 --- /dev/null +++ b/relayer/crates/starknet-chain-components/src/impls/queries/block_events/get.rs @@ -0,0 +1,51 @@ +use cgp::prelude::*; +use hermes_chain_components::traits::types::event::HasEventType; +use hermes_chain_components::traits::types::height::HasHeightType; +use hermes_chain_type_components::traits::types::address::HasAddressType; +use starknet::core::types::{BlockId, EventFilter, Felt}; +use starknet::providers::{Provider, ProviderError}; + +use crate::traits::provider::HasStarknetProvider; +use crate::traits::queries::block_events::BlockEventsQuerier; +use crate::types::event::StarknetEvent; + +pub struct GetStarknetBlockEvents; + +impl BlockEventsQuerier for GetStarknetBlockEvents +where + Chain: HasHeightType + + HasEventType + + HasAddressType
+ + HasStarknetProvider + + CanRaiseAsyncError, +{ + async fn query_block_events( + chain: &Chain, + height: &u64, + address: &Felt, + ) -> Result, Chain::Error> { + let provider = chain.provider(); + + let raw_events = provider + .get_events( + EventFilter { + from_block: Some(BlockId::Number(*height)), + to_block: Some(BlockId::Number(*height)), + address: Some(*address), + keys: None, + }, + None, + 1000, + ) + .await + .map_err(Chain::raise_error)?; + + let events = raw_events + .events + .into_iter() + .map(StarknetEvent::from) + .collect(); + + Ok(events) + } +} diff --git a/relayer/crates/starknet-chain-components/src/impls/queries/block_events/mod.rs b/relayer/crates/starknet-chain-components/src/impls/queries/block_events/mod.rs new file mode 100644 index 00000000..3b75f60d --- /dev/null +++ b/relayer/crates/starknet-chain-components/src/impls/queries/block_events/mod.rs @@ -0,0 +1,5 @@ +pub mod default; +pub mod get; +pub mod retry; +pub mod traces; +pub mod wait; diff --git a/relayer/crates/starknet-chain-components/src/impls/queries/block_events/retry.rs b/relayer/crates/starknet-chain-components/src/impls/queries/block_events/retry.rs new file mode 100644 index 00000000..368342a5 --- /dev/null +++ b/relayer/crates/starknet-chain-components/src/impls/queries/block_events/retry.rs @@ -0,0 +1,41 @@ +use core::marker::PhantomData; +use core::time::Duration; + +use cgp::prelude::HasAsyncErrorType; +use hermes_chain_components::traits::types::event::HasEventType; +use hermes_chain_components::traits::types::height::HasHeightType; +use hermes_chain_type_components::traits::types::address::HasAddressType; +use hermes_runtime_components::traits::runtime::HasRuntime; +use hermes_runtime_components::traits::sleep::CanSleep; + +use crate::traits::queries::block_events::BlockEventsQuerier; + +pub struct RetryQueryBlockEvents(pub PhantomData); + +impl BlockEventsQuerier for RetryQueryBlockEvents +where + Chain: HasRuntime + HasHeightType + HasAddressType + HasEventType + HasAsyncErrorType, + InQuerier: BlockEventsQuerier, + Chain::Runtime: CanSleep, +{ + async fn query_block_events( + chain: &Chain, + height: &Chain::Height, + address: &Chain::Address, + ) -> Result, Chain::Error> { + let runtime = chain.runtime(); + let mut sleep_time = Duration::from_millis(500); + + for _ in 0..5 { + let res = InQuerier::query_block_events(chain, height, address).await; + if let Ok(events) = res { + return Ok(events); + } + + runtime.sleep(sleep_time).await; + sleep_time *= 2; + } + + InQuerier::query_block_events(chain, height, address).await + } +} diff --git a/relayer/crates/starknet-chain-components/src/impls/queries/block_events/traces.rs b/relayer/crates/starknet-chain-components/src/impls/queries/block_events/traces.rs new file mode 100644 index 00000000..a55606ee --- /dev/null +++ b/relayer/crates/starknet-chain-components/src/impls/queries/block_events/traces.rs @@ -0,0 +1,55 @@ +use cgp::prelude::*; +use hermes_chain_components::traits::types::event::HasEventType; +use hermes_chain_components::traits::types::height::HasHeightType; +use hermes_chain_type_components::traits::types::address::HasAddressType; +use starknet::core::types::{BlockId, ExecuteInvocation, Felt, TransactionTrace}; +use starknet::providers::{Provider, ProviderError}; + +use crate::impls::send_message::extract_events_from_function_invocation; +use crate::traits::provider::HasStarknetProvider; +use crate::traits::queries::block_events::BlockEventsQuerier; +use crate::types::event::StarknetEvent; + +pub struct QueryStarknetBlockEventsFromTraces; + +impl BlockEventsQuerier for QueryStarknetBlockEventsFromTraces +where + Chain: HasHeightType + + HasEventType + + HasAddressType
+ + HasStarknetProvider + + CanRaiseAsyncError, +{ + async fn query_block_events( + chain: &Chain, + height: &u64, + address: &Felt, + ) -> Result, Chain::Error> { + let provider = chain.provider(); + + let traces = provider + .trace_block_transactions(BlockId::Number(*height)) + .await + .map_err(Chain::raise_error)?; + + let events: Vec = traces + .into_iter() + .filter_map(|trace| match trace.trace_root { + TransactionTrace::Invoke(invoke) => match invoke.execute_invocation { + ExecuteInvocation::Success(invoke) => { + if &invoke.contract_address == address { + Some(extract_events_from_function_invocation(invoke)) + } else { + None + } + } + _ => None, + }, + _ => None, + }) + .flatten() + .collect(); + + Ok(events) + } +} diff --git a/relayer/crates/starknet-chain-components/src/impls/queries/block_events/wait.rs b/relayer/crates/starknet-chain-components/src/impls/queries/block_events/wait.rs new file mode 100644 index 00000000..4a88684d --- /dev/null +++ b/relayer/crates/starknet-chain-components/src/impls/queries/block_events/wait.rs @@ -0,0 +1,38 @@ +use core::marker::PhantomData; +use core::time::Duration; + +use hermes_chain_components::traits::queries::chain_status::CanQueryChainHeight; +use hermes_chain_components::traits::types::event::HasEventType; +use hermes_chain_type_components::traits::types::address::HasAddressType; +use hermes_runtime_components::traits::runtime::HasRuntime; +use hermes_runtime_components::traits::sleep::CanSleep; + +use crate::traits::queries::block_events::BlockEventsQuerier; + +pub struct WaitBlockHeightAndQueryEvents(pub PhantomData); + +impl BlockEventsQuerier for WaitBlockHeightAndQueryEvents +where + Chain: HasRuntime + HasAddressType + HasEventType + CanQueryChainHeight, + InQuerier: BlockEventsQuerier, + Chain::Runtime: CanSleep, +{ + async fn query_block_events( + chain: &Chain, + height: &Chain::Height, + address: &Chain::Address, + ) -> Result, Chain::Error> { + let runtime = chain.runtime(); + + loop { + let current_height = chain.query_chain_height().await?; + if ¤t_height >= height { + break; + } else { + runtime.sleep(Duration::from_millis(200)).await; + } + } + + InQuerier::query_block_events(chain, height, address).await + } +} diff --git a/relayer/crates/starknet-chain-components/src/impls/queries/mod.rs b/relayer/crates/starknet-chain-components/src/impls/queries/mod.rs index 0039e263..7b4c3f10 100644 --- a/relayer/crates/starknet-chain-components/src/impls/queries/mod.rs +++ b/relayer/crates/starknet-chain-components/src/impls/queries/mod.rs @@ -1,5 +1,6 @@ pub mod ack_commitment; pub mod balance; +pub mod block_events; pub mod channel_end; pub mod client_state; pub mod connection_end; diff --git a/relayer/crates/starknet-chain-components/src/impls/send_message.rs b/relayer/crates/starknet-chain-components/src/impls/send_message.rs index 3b165f39..763400ec 100644 --- a/relayer/crates/starknet-chain-components/src/impls/send_message.rs +++ b/relayer/crates/starknet-chain-components/src/impls/send_message.rs @@ -51,7 +51,7 @@ where let message_responses = invocation .calls .into_iter() - .map(extract_events_from_function_invocation) + .map(extract_message_response_from_function_invocation) .collect(); Ok(message_responses) @@ -67,9 +67,18 @@ where } } -pub fn extract_events_from_function_invocation( +pub fn extract_message_response_from_function_invocation( invocation: FunctionInvocation, ) -> StarknetMessageResponse { + let result = invocation.result.clone(); + let events = extract_events_from_function_invocation(invocation); + + StarknetMessageResponse { result, events } +} + +pub fn extract_events_from_function_invocation( + invocation: FunctionInvocation, +) -> Vec { let mut events: Vec = invocation .events .into_iter() @@ -83,14 +92,11 @@ pub fn extract_events_from_function_invocation( .collect(); for inner in invocation.calls { - let mut message_response = extract_events_from_function_invocation(inner); - events.append(&mut message_response.events); + let mut in_events = extract_events_from_function_invocation(inner); + events.append(&mut in_events); } - StarknetMessageResponse { - result: invocation.result, - events, - } + events } impl Debug for UnexpectedTransactionTraceType { diff --git a/relayer/crates/starknet-chain-components/src/impls/subscription.rs b/relayer/crates/starknet-chain-components/src/impls/subscription.rs new file mode 100644 index 00000000..44f758cf --- /dev/null +++ b/relayer/crates/starknet-chain-components/src/impls/subscription.rs @@ -0,0 +1,143 @@ +use core::pin::Pin; +use std::sync::Arc; + +use cgp::prelude::*; +use futures::channel::mpsc::{unbounded, UnboundedSender}; +use futures::lock::Mutex; +use futures::Stream; +use hermes_async_runtime_components::subscription::impls::closure::CanCreateClosureSubscription; +use hermes_async_runtime_components::subscription::impls::multiplex::CanMultiplexSubscription; +use hermes_async_runtime_components::subscription::traits::subscription::Subscription; +use hermes_chain_components::traits::types::event::HasEventType; +use hermes_chain_components::traits::types::height::HasHeightType; +use hermes_chain_type_components::traits::types::address::HasAddressType; +use hermes_runtime_components::traits::runtime::HasRuntime; +use hermes_runtime_components::traits::spawn::CanSpawnTask; +use hermes_runtime_components::traits::task::Task; + +use crate::traits::queries::block_events::CanQueryBlockEvents; + +#[async_trait] +pub trait CanCreateStarknetSubscription: + HasHeightType + HasAddressType + HasEventType + HasAsyncErrorType +{ + async fn create_event_subscription( + self, + start_height: Self::Height, + address: Self::Address, + ) -> Result)>>, Self::Error>; +} + +#[async_trait] +pub trait CanSendStarknetEvents: + HasHeightType + HasAddressType + HasEventType + HasAsyncErrorType +{ + async fn send_starknet_events( + &self, + address: &Self::Address, + start_height: Arc>, + sender: UnboundedSender<(Self::Height, Arc)>, + ) -> Result<(), Self::Error>; +} + +impl CanSendStarknetEvents for Chain +where + Chain: HasHeightType + + HasAddressType + + CanQueryBlockEvents + + CanRaiseError<&'static str>, +{ + async fn send_starknet_events( + &self, + address: &Self::Address, + height_mutex: Arc>, + sender: UnboundedSender<(u64, Arc)>, + ) -> Result<(), Self::Error> { + loop { + let mut height_ref = height_mutex.lock().await; + let height = *height_ref; + + let events = self.query_block_events(&height, address).await?; + for event in events { + sender + .unbounded_send((height, Arc::new(event))) + .map_err(|_| Chain::raise_error("channel closed"))?; + } + + *height_ref = height + 1; + } + } +} + +pub struct PollStarknetEventsTask +where + Chain: HasHeightType + HasAddressType + HasEventType, +{ + pub chain: Chain, + pub address: Chain::Address, + pub height: Arc>, + pub sender: UnboundedSender<(Chain::Height, Arc)>, +} + +impl Task for PollStarknetEventsTask +where + Chain: CanSendStarknetEvents, +{ + async fn run(self) { + let _ = self + .chain + .send_starknet_events(&self.address, self.height, self.sender) + .await; + } +} + +impl CanCreateStarknetSubscription for Chain +where + Chain: Clone + HasRuntime + CanSendStarknetEvents, + Chain::Runtime: Clone + CanCreateClosureSubscription + CanMultiplexSubscription + CanSpawnTask, + Chain::Address: Clone, +{ + async fn create_event_subscription( + self, + height: Chain::Height, + address: Chain::Address, + ) -> Result)>>, Chain::Error> + { + let runtime = self.runtime().clone(); + let height_mutex = Arc::new(Mutex::new(height)); + + let subscription = Chain::Runtime::new_closure_subscription(move || { + let chain = self.clone(); + let address = address.clone(); + let height_mutex = height_mutex.clone(); + + Box::pin(async move { + let (sender, receiver) = unbounded(); + + let task = PollStarknetEventsTask { + chain: chain.clone(), + sender, + address, + height: height_mutex.clone(), + }; + + chain.runtime().spawn_task(task); + + let stream: Pin< + Box< + dyn Stream)> + + Send + + Sync + + 'static, + >, + > = Box::pin(receiver); + + Some(stream) + }) + }); + + let subscription = runtime.multiplex_subscription(subscription, |e| e); + + Ok(subscription) + } +} diff --git a/relayer/crates/starknet-chain-components/src/lib.rs b/relayer/crates/starknet-chain-components/src/lib.rs index e59116e3..1338cd09 100644 --- a/relayer/crates/starknet-chain-components/src/lib.rs +++ b/relayer/crates/starknet-chain-components/src/lib.rs @@ -1,4 +1,5 @@ #![allow(clippy::needless_lifetimes)] +#![allow(clippy::type_complexity)] pub mod components; pub mod impls; diff --git a/relayer/crates/starknet-chain-components/src/traits/queries/block_events.rs b/relayer/crates/starknet-chain-components/src/traits/queries/block_events.rs new file mode 100644 index 00000000..09bf0895 --- /dev/null +++ b/relayer/crates/starknet-chain-components/src/traits/queries/block_events.rs @@ -0,0 +1,19 @@ +use cgp::prelude::*; +use hermes_chain_components::traits::types::event::HasEventType; +use hermes_chain_components::traits::types::height::HasHeightType; +use hermes_chain_type_components::traits::types::address::HasAddressType; + +#[cgp_component { + provider: BlockEventsQuerier, + context: Chain, +}] +#[async_trait] +pub trait CanQueryBlockEvents: + HasHeightType + HasAddressType + HasEventType + HasAsyncErrorType +{ + async fn query_block_events( + &self, + height: &Self::Height, + address: &Self::Address, + ) -> Result, Self::Error>; +} diff --git a/relayer/crates/starknet-chain-components/src/traits/queries/mod.rs b/relayer/crates/starknet-chain-components/src/traits/queries/mod.rs index 86b5c989..8f66ddf6 100644 --- a/relayer/crates/starknet-chain-components/src/traits/queries/mod.rs +++ b/relayer/crates/starknet-chain-components/src/traits/queries/mod.rs @@ -1,2 +1,3 @@ pub mod address; +pub mod block_events; pub mod token_balance; diff --git a/relayer/crates/starknet-chain-components/src/types/event.rs b/relayer/crates/starknet-chain-components/src/types/event.rs index 840c4deb..7d6e3f77 100644 --- a/relayer/crates/starknet-chain-components/src/types/event.rs +++ b/relayer/crates/starknet-chain-components/src/types/event.rs @@ -1,9 +1,9 @@ -use starknet::core::types::{Felt, OrderedEvent}; +use starknet::core::types::{EmittedEvent, Felt, OrderedEvent}; #[derive(Debug)] pub struct StarknetEvent { pub contract_address: Felt, - pub class_hash: Felt, + pub class_hash: Option, pub selector: Option, pub keys: Vec, pub data: Vec, @@ -27,10 +27,25 @@ impl StarknetEvent { Self { contract_address, - class_hash, + class_hash: Some(class_hash), selector, keys: keys.collect(), data, } } } + +impl From for StarknetEvent { + fn from(event: EmittedEvent) -> Self { + let mut keys = event.keys.into_iter(); + let selector = keys.next(); + + Self { + contract_address: event.from_address, + class_hash: None, + selector, + keys: keys.collect(), + data: event.data, + } + } +} diff --git a/relayer/crates/starknet-chain-context/Cargo.toml b/relayer/crates/starknet-chain-context/Cargo.toml index 44f023b2..32560701 100644 --- a/relayer/crates/starknet-chain-context/Cargo.toml +++ b/relayer/crates/starknet-chain-context/Cargo.toml @@ -15,6 +15,7 @@ ibc = { workspace = true } hermes-error = { workspace = true } hermes-runtime = { workspace = true } hermes-runtime-components = { workspace = true } +hermes-async-runtime-components = { workspace = true } hermes-logging-components = { workspace = true } hermes-encoding-components = { workspace = true } hermes-logger = { workspace = true } diff --git a/relayer/crates/starknet-chain-context/src/contexts/chain.rs b/relayer/crates/starknet-chain-context/src/contexts/chain.rs index 0927e91a..d34f0094 100644 --- a/relayer/crates/starknet-chain-context/src/contexts/chain.rs +++ b/relayer/crates/starknet-chain-context/src/contexts/chain.rs @@ -6,6 +6,7 @@ use cgp::core::field::WithField; use cgp::core::types::WithType; use cgp::prelude::*; use hermes_cairo_encoding_components::types::as_felt::AsFelt; +use hermes_cairo_encoding_components::types::as_starknet_event::AsStarknetEvent; use hermes_chain_type_components::traits::fields::chain_id::HasChainId; use hermes_chain_type_components::traits::types::commitment_proof::HasCommitmentProofType; use hermes_chain_type_components::traits::types::height::HasHeightType; @@ -13,9 +14,8 @@ use hermes_chain_type_components::traits::types::message_response::HasMessageRes use hermes_cosmos_chain_components::components::delegate::DelegateCosmosChainComponents; use hermes_cosmos_chain_components::types::connection::CosmosInitConnectionOptions; use hermes_cosmos_relayer::contexts::chain::CosmosChain; -use hermes_encoding_components::impls::default_encoding::GetDefaultEncoding; use hermes_encoding_components::traits::has_encoding::{ - DefaultEncodingGetter, EncodingGetterComponent, HasDefaultEncoding, ProvideEncodingType, + DefaultEncodingGetter, EncodingGetter, HasDefaultEncoding, ProvideEncodingType, }; use hermes_encoding_components::types::AsBytes; use hermes_error::impls::ProvideHermesError; @@ -102,6 +102,7 @@ use hermes_relayer_components::chain::traits::types::ibc::{ }; use hermes_relayer_components::chain::traits::types::ibc_events::channel::HasChannelOpenTryEvent; use hermes_relayer_components::chain::traits::types::ibc_events::connection::HasConnectionOpenTryEvent; +use hermes_relayer_components::chain::traits::types::ibc_events::send_packet::HasSendPacketEvent; use hermes_relayer_components::chain::traits::types::ibc_events::write_ack::HasWriteAckEvent; use hermes_relayer_components::chain::traits::types::packet::HasOutgoingPacketType; use hermes_relayer_components::chain::traits::types::packets::ack::HasAcknowledgementType; @@ -122,6 +123,7 @@ use hermes_starknet_chain_components::components::chain::{ use hermes_starknet_chain_components::components::starknet_to_cosmos::StarknetToCosmosComponents; use hermes_starknet_chain_components::impls::account::GetStarknetAccountField; use hermes_starknet_chain_components::impls::provider::GetStarknetProviderField; +use hermes_starknet_chain_components::impls::subscription::CanCreateStarknetSubscription; use hermes_starknet_chain_components::traits::account::{ HasStarknetAccount, StarknetAccountGetterComponent, StarknetAccountTypeComponent, }; @@ -134,6 +136,7 @@ use hermes_starknet_chain_components::traits::provider::{ HasStarknetProvider, StarknetProviderGetterComponent, StarknetProviderTypeComponent, }; use hermes_starknet_chain_components::traits::queries::address::CanQueryContractAddress; +use hermes_starknet_chain_components::traits::queries::block_events::CanQueryBlockEvents; use hermes_starknet_chain_components::traits::queries::token_balance::CanQueryTokenBalance; use hermes_starknet_chain_components::traits::transfer::CanTransferToken; use hermes_starknet_chain_components::traits::types::blob::HasBlobType; @@ -163,6 +166,7 @@ use starknet::providers::JsonRpcClient; use starknet::signers::LocalWallet; use crate::contexts::encoding::cairo::StarknetCairoEncoding; +use crate::contexts::encoding::event::StarknetEventEncoding; use crate::contexts::encoding::protobuf::StarknetProtobufEncoding; use crate::impls::error::HandleStarknetChainError; @@ -174,6 +178,7 @@ pub struct StarknetChain { pub account: SingleOwnerAccount>, LocalWallet>, pub ibc_client_contract_address: Option, pub ibc_core_contract_address: Option, + pub event_encoding: StarknetEventEncoding, } pub struct StarknetChainContextComponents; @@ -194,7 +199,6 @@ delegate_components! { GlobalLoggerGetterComponent, ]: ProvideNoLogger, - EncodingGetterComponent: GetDefaultEncoding, [ StarknetProviderTypeComponent, StarknetProviderGetterComponent, @@ -227,12 +231,28 @@ impl ProvideEncodingType for StarknetChainContextComponen type Encoding = StarknetCairoEncoding; } +impl ProvideEncodingType for StarknetChainContextComponents { + type Encoding = StarknetEventEncoding; +} + impl DefaultEncodingGetter for StarknetChainContextComponents { fn default_encoding() -> &'static StarknetCairoEncoding { &StarknetCairoEncoding } } +impl EncodingGetter for StarknetChainContextComponents { + fn encoding(_chain: &StarknetChain) -> &StarknetCairoEncoding { + &StarknetCairoEncoding + } +} + +impl EncodingGetter for StarknetChainContextComponents { + fn encoding(chain: &StarknetChain) -> &StarknetEventEncoding { + &chain.event_encoding + } +} + impl ProvideEncodingType for StarknetChainContextComponents { type Encoding = StarknetProtobufEncoding; } @@ -277,8 +297,6 @@ pub trait CanUseStarknetChain: + HasConnectionEndType + HasChannelIdType + HasChannelEndType - // // FIXME: cannot use native PortId. PortIdTypeComponent needs to be wired for StarknetChainTypes - // + HasPortIdType + HasPortIdType + HasInitConnectionOptionsType + HasConnectionOpenInitPayloadType @@ -297,6 +315,8 @@ pub trait CanUseStarknetChain: + HasStarknetAccount + CanQueryChainStatus + CanQueryChainHeight + + CanQueryBlockEvents + + CanCreateStarknetSubscription + CanSendMessages + CanSendSingleMessage + CanSubmitTx @@ -366,8 +386,8 @@ pub trait CanUseStarknetChain: + HasSequenceType + CanQueryBalance + HasMemoType - // TODO(rano): need this to >::ibc_transfer_token - // + CanIbcTransferToken +// TODO(rano): need this to >::ibc_transfer_token +// + CanIbcTransferToken { } @@ -419,6 +439,8 @@ pub trait CanUseCosmosChainWithStarknet: HasClientStateType + HasAcknowledgementType> + HasSequenceType + + HasSendPacketEvent + + HasWriteAckEvent { } diff --git a/relayer/crates/starknet-chain-context/src/contexts/encoding/event.rs b/relayer/crates/starknet-chain-context/src/contexts/encoding/event.rs index 5547ec75..0a691573 100644 --- a/relayer/crates/starknet-chain-context/src/contexts/encoding/event.rs +++ b/relayer/crates/starknet-chain-context/src/contexts/encoding/event.rs @@ -23,7 +23,7 @@ use starknet::core::types::Felt; use crate::contexts::encoding::cairo::{ProvideCairoEncoding, StarknetCairoEncoding}; use crate::impls::error::HandleStarknetChainError; -#[derive(HasField, Default)] +#[derive(HasField, Default, Clone)] pub struct StarknetEventEncoding { pub erc20_hashes: HashSet, pub ics20_hashes: HashSet, diff --git a/relayer/crates/starknet-integration-tests/src/contexts/bootstrap.rs b/relayer/crates/starknet-integration-tests/src/contexts/bootstrap.rs index e6ecf5bc..cef25532 100644 --- a/relayer/crates/starknet-integration-tests/src/contexts/bootstrap.rs +++ b/relayer/crates/starknet-integration-tests/src/contexts/bootstrap.rs @@ -143,6 +143,7 @@ impl ChainDriverBuilder for StarknetBootstrapComponents { account, ibc_client_contract_address: None, ibc_core_contract_address: None, + event_encoding: Default::default(), }; let chain_driver = StarknetChainDriver { diff --git a/relayer/crates/starknet-integration-tests/src/tests/ics20.rs b/relayer/crates/starknet-integration-tests/src/tests/ics20.rs index 93eec6e0..a44aeae1 100644 --- a/relayer/crates/starknet-integration-tests/src/tests/ics20.rs +++ b/relayer/crates/starknet-integration-tests/src/tests/ics20.rs @@ -5,7 +5,9 @@ use std::path::PathBuf; use std::time::SystemTime; use cgp::prelude::*; -use hermes_chain_components::traits::queries::chain_status::CanQueryChainStatus; +use hermes_chain_components::traits::queries::chain_status::{ + CanQueryChainHeight, CanQueryChainStatus, +}; use hermes_chain_components::traits::queries::client_state::CanQueryClientStateWithLatestHeight; use hermes_cosmos_chain_components::types::channel::CosmosInitChannelOptions; use hermes_cosmos_chain_components::types::connection::CosmosInitConnectionOptions; @@ -64,8 +66,9 @@ use ibc::primitives::Timestamp as IbcTimestamp; use poseidon::Poseidon3Hasher; use sha2::{Digest, Sha256}; use starknet::accounts::Call; -use starknet::core::types::{Felt, U256}; +use starknet::core::types::{BlockId, EventFilter, Felt, U256}; use starknet::macros::{selector, short_string}; +use starknet::providers::Provider; use tracing::info; use crate::contexts::bootstrap::StarknetBootstrap; @@ -122,6 +125,11 @@ fn test_starknet_ics20_contract() -> Result<(), Error> { let starknet_chain = &mut starknet_chain_driver.chain; + info!( + "started starknet chain at port {}", + starknet_chain_driver.node_config.rpc_port + ); + let cosmos_chain_driver = cosmos_bootstrap.bootstrap_chain("cosmos").await?; let cosmos_chain = &cosmos_chain_driver.chain; @@ -210,7 +218,9 @@ fn test_starknet_ics20_contract() -> Result<(), Error> { starknet_chain.ibc_core_contract_address = Some(ibc_core_address); starknet_chain.ibc_client_contract_address = Some(comet_client_address); - let event_encoding = StarknetEventEncoding { + let cairo_encoding = StarknetCairoEncoding; + + starknet_chain.event_encoding = StarknetEventEncoding { erc20_hashes: [erc20_class_hash].into(), ics20_hashes: [ics20_class_hash].into(), ibc_client_hashes: [comet_client_class_hash].into(), @@ -514,8 +524,9 @@ fn test_starknet_ics20_contract() -> Result<(), Error> { } }; - // submit to ics20 contract + let event_start_height = starknet_chain.query_chain_height().await?; + // submit to ics20 contract let (send_packet_event, send_ics20_event) = { let call_data = cairo_encoding.encode(&starknet_ics20_send_message)?; @@ -531,13 +542,15 @@ fn test_starknet_ics20_contract() -> Result<(), Error> { info!("ICS20 send packet response: {:?}", response); - let mut ibc_packet_events: Vec = - event_encoding.filter_decode_events(&response.events)?; + let mut ibc_packet_events: Vec = starknet_chain + .event_encoding + .filter_decode_events(&response.events)?; info!("IBC packet events: {:?}", ibc_packet_events); - let mut ibc_transfer_events: Vec = - event_encoding.filter_decode_events(&response.events)?; + let mut ibc_transfer_events: Vec = starknet_chain + .event_encoding + .filter_decode_events(&response.events)?; info!("IBC transfer events: {:?}", ibc_transfer_events); @@ -552,6 +565,36 @@ fn test_starknet_ics20_contract() -> Result<(), Error> { panic!("expected send ics20 event"); }; + { + tokio::time::sleep(core::time::Duration::from_secs(1)).await; + + let event_end_height = starknet_chain.query_chain_height().await?; + + info!("polling events from {event_start_height} to {event_end_height}"); + + let events = starknet_chain + .rpc_client + .get_events( + EventFilter { + from_block: Some(BlockId::Number(event_start_height)), + to_block: None, + address: Some(ibc_core_address), + keys: None, + }, + None, + 1000, + ) + .await?; + + info!("polled events: {events:?}"); + + let parsed_events: Vec = starknet_chain + .event_encoding + .filter_decode_events(&response.events)?; + + info!("parsed polled events: {parsed_events:?}"); + } + (send_packet_event, send_ics20_event) }; @@ -727,13 +770,15 @@ fn test_starknet_ics20_contract() -> Result<(), Error> { info!("ICS20 send packet response: {:?}", response); - let mut ibc_packet_events: Vec = - event_encoding.filter_decode_events(&response.events)?; + let mut ibc_packet_events: Vec = starknet_chain + .event_encoding + .filter_decode_events(&response.events)?; info!("IBC packet events: {:?}", ibc_packet_events); - let mut ibc_transfer_events: Vec = - event_encoding.filter_decode_events(&response.events)?; + let mut ibc_transfer_events: Vec = starknet_chain + .event_encoding + .filter_decode_events(&response.events)?; info!("IBC transfer events: {:?}", ibc_transfer_events); diff --git a/relayer/crates/starknet-relayer/src/contexts/builder.rs b/relayer/crates/starknet-relayer/src/contexts/builder.rs index 61a81fbc..34b74988 100644 --- a/relayer/crates/starknet-relayer/src/contexts/builder.rs +++ b/relayer/crates/starknet-relayer/src/contexts/builder.rs @@ -146,6 +146,7 @@ impl StarknetBuilder { account, ibc_client_contract_address: None, ibc_core_contract_address: None, + event_encoding: Default::default(), }; Ok(context)