From 44c4260cbec133b68521491b120529ee4fafb580 Mon Sep 17 00:00:00 2001 From: Gilad Chase Date: Fri, 20 Dec 2024 14:23:39 +0200 Subject: [PATCH] feat(papyrus_base_layer): start implementing eth events api - Currently only implementing the simplest event to parse, the rest are coming up. - removed two unused error variants: `FeltParseError`, `Serde` --- Cargo.lock | 1 + Cargo.toml | 1 + crates/papyrus_base_layer/Cargo.toml | 1 + crates/papyrus_base_layer/src/eth_events.rs | 83 +++++++++++++++++++ .../src/ethereum_base_layer_contract.rs | 35 +++++--- crates/papyrus_base_layer/src/lib.rs | 2 + 6 files changed, 112 insertions(+), 11 deletions(-) create mode 100644 crates/papyrus_base_layer/src/eth_events.rs diff --git a/Cargo.lock b/Cargo.lock index e737394261..b49a145fc6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7298,6 +7298,7 @@ dependencies = [ "alloy-json-rpc", "alloy-primitives", "alloy-provider", + "alloy-rpc-types-eth", "alloy-sol-types", "alloy-transport", "alloy-transport-http", diff --git a/Cargo.toml b/Cargo.toml index a51d19f4d0..b178f1e258 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,6 +72,7 @@ alloy-dyn-abi = "0.8.3" alloy-json-rpc = "0.3.5" alloy-primitives = "0.8.3" alloy-provider = "0.3.5" +alloy-rpc-types-eth = "0.3.5" alloy-sol-types = "0.8.3" alloy-transport = "0.3.5" alloy-transport-http = "0.3.5" diff --git a/crates/papyrus_base_layer/Cargo.toml b/crates/papyrus_base_layer/Cargo.toml index dcc6387f5b..ab1718b42c 100644 --- a/crates/papyrus_base_layer/Cargo.toml +++ b/crates/papyrus_base_layer/Cargo.toml @@ -17,6 +17,7 @@ alloy-dyn-abi.workspace = true alloy-json-rpc.workspace = true alloy-primitives.workspace = true alloy-provider.workspace = true +alloy-rpc-types-eth.workspace = true alloy-sol-types = { workspace = true, features = ["json"] } alloy-transport.workspace = true alloy-transport-http.workspace = true diff --git a/crates/papyrus_base_layer/src/eth_events.rs b/crates/papyrus_base_layer/src/eth_events.rs new file mode 100644 index 0000000000..094a1f2508 --- /dev/null +++ b/crates/papyrus_base_layer/src/eth_events.rs @@ -0,0 +1,83 @@ +use std::sync::Arc; + +use alloy_primitives::{Address as EthereumContractAddress, U256}; +use alloy_rpc_types_eth::Log; +use alloy_sol_types::SolEventInterface; +use starknet_api::core::{EntryPointSelector, Nonce}; +use starknet_api::transaction::fields::Calldata; +use starknet_types_core::felt::Felt; + +use crate::ethereum_base_layer_contract::{ + EthereumBaseLayerError, + EthereumBaseLayerResult, + Starknet, +}; +use crate::{EventData, L1Event}; + +impl TryFrom for L1Event { + type Error = EthereumBaseLayerError; + + fn try_from(log: Log) -> EthereumBaseLayerResult { + let validate = true; + let log = log.inner; + + let event = Starknet::StarknetEvents::decode_log(&log, validate)?.data; + match event { + Starknet::StarknetEvents::LogMessageToL2(_event) => { + todo!() + } + Starknet::StarknetEvents::ConsumedMessageToL1(_event) => { + todo!() + } + Starknet::StarknetEvents::MessageToL2Canceled(event) => { + Ok(L1Event::MessageToL2Canceled(event.try_into()?)) + } + Starknet::StarknetEvents::MessageToL2CancellationStarted(_event) => { + todo!() + } + _ => Err(EthereumBaseLayerError::UnhandledL1Event(log)), + } + } +} + +impl TryFrom for EventData { + type Error = EthereumBaseLayerError; + + fn try_from(event: Starknet::MessageToL2Canceled) -> EthereumBaseLayerResult { + create_l1_event_data( + event.fromAddress, + event.toAddress, + event.selector, + &event.payload, + event.nonce, + ) + } +} + +pub fn create_l1_event_data( + from_address: EthereumContractAddress, + to_address: U256, + selector: U256, + payload: &[U256], + nonce: U256, +) -> EthereumBaseLayerResult { + Ok(EventData { + from_address: felt_from_eth_address(from_address) + .try_into() + .map_err(EthereumBaseLayerError::StarknetApiParsingError)?, + to_address: felt_from_u256(to_address) + .try_into() + .map_err(EthereumBaseLayerError::StarknetApiParsingError)?, + entry_point_selector: EntryPointSelector(felt_from_u256(selector)), + payload: Calldata(Arc::new(payload.iter().map(|&x| felt_from_u256(x)).collect())), + nonce: Nonce(felt_from_u256(nonce)), + }) +} + +pub fn felt_from_eth_address(address: EthereumContractAddress) -> Felt { + Felt::from_bytes_be_slice(address.0.as_slice()) +} + +pub fn felt_from_u256(num: U256) -> Felt { + Felt::from_bytes_be(&num.to_be_bytes()) +} diff --git a/crates/papyrus_base_layer/src/ethereum_base_layer_contract.rs b/crates/papyrus_base_layer/src/ethereum_base_layer_contract.rs index 1d7444af24..042c8bf174 100644 --- a/crates/papyrus_base_layer/src/ethereum_base_layer_contract.rs +++ b/crates/papyrus_base_layer/src/ethereum_base_layer_contract.rs @@ -3,9 +3,10 @@ use std::future::IntoFuture; use alloy_dyn_abi::SolType; use alloy_json_rpc::RpcError; -pub(crate) use alloy_primitives::Address as EthereumContractAddress; +use alloy_primitives::Address as EthereumContractAddress; use alloy_provider::network::Ethereum; use alloy_provider::{Provider, ProviderBuilder, RootProvider}; +use alloy_rpc_types_eth::{BlockNumberOrTag, Filter as EthEventFilter}; use alloy_sol_types::{sol, sol_data}; use alloy_transport::TransportErrorKind; use alloy_transport_http::{Client, Http}; @@ -15,12 +16,12 @@ use papyrus_config::{ParamPath, ParamPrivacyInput, SerializationType, Serialized use serde::{Deserialize, Serialize}; use starknet_api::block::{BlockHash, BlockHashAndNumber, BlockNumber}; use starknet_api::hash::StarkHash; -use starknet_types_core::felt; +use starknet_api::StarknetApiError; use url::Url; use crate::{BaseLayerContract, L1Event}; -type EthereumBaseLayerResult = Result; +pub type EthereumBaseLayerResult = Result; // Wraps the Starknet contract with a type that implements its interface, and is aware of its // events. @@ -81,11 +82,23 @@ impl BaseLayerContract for EthereumBaseLayerContract { async fn events( &self, - _from_block: u64, - _until_block: u64, - _event_identifiers: Vec<&str>, + from_block: u64, + until_block: u64, + events: Vec<&str>, ) -> EthereumBaseLayerResult> { - todo!("Implmeneted in a subsequent commit") + let filter = EthEventFilter::new() + .from_block(BlockNumberOrTag::Number(from_block)) + .events(events) + .to_block(until_block); + + // Get all logs from the latest block that match the filter. + self.contract + .provider() + .get_logs(&filter) + .await? + .into_iter() + .map(TryInto::try_into) + .collect() } async fn latest_l1_block_number(&self, finality: u64) -> EthereumBaseLayerResult> { @@ -98,13 +111,13 @@ pub enum EthereumBaseLayerError { #[error(transparent)] Contract(#[from] alloy_contract::Error), #[error(transparent)] - FeltParseError(#[from] felt::FromStrError), - #[error(transparent)] RpcError(#[from] RpcError), #[error(transparent)] - Serde(#[from] serde_json::Error), - #[error(transparent)] TypeError(#[from] alloy_sol_types::Error), + #[error("{0}")] + StarknetApiParsingError(StarknetApiError), + #[error("{0:?}")] + UnhandledL1Event(alloy_primitives::Log), } #[derive(Debug, Deserialize, Serialize, Clone, PartialEq)] diff --git a/crates/papyrus_base_layer/src/lib.rs b/crates/papyrus_base_layer/src/lib.rs index 05c29955dc..42797f0362 100644 --- a/crates/papyrus_base_layer/src/lib.rs +++ b/crates/papyrus_base_layer/src/lib.rs @@ -7,6 +7,8 @@ use starknet_api::transaction::L1HandlerTransaction; pub mod ethereum_base_layer_contract; +pub(crate) mod eth_events; + #[cfg(any(feature = "testing", test))] pub mod test_utils;