From 3620bca073af59f90e01ad77230a64b8e3360ccf 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` --- .github/actions/install_rust/action.yml | 2 +- Cargo.lock | 18 ++--- Cargo.toml | 1 + crates/papyrus_base_layer/Cargo.toml | 2 +- crates/papyrus_base_layer/src/eth_events.rs | 79 +++++++++++++++++++ .../src/ethereum_base_layer_contract.rs | 29 ++++--- crates/papyrus_base_layer/src/lib.rs | 2 + 7 files changed, 111 insertions(+), 22 deletions(-) create mode 100644 crates/papyrus_base_layer/src/eth_events.rs diff --git a/.github/actions/install_rust/action.yml b/.github/actions/install_rust/action.yml index f140e6a3b2..4d123f3992 100644 --- a/.github/actions/install_rust/action.yml +++ b/.github/actions/install_rust/action.yml @@ -13,7 +13,7 @@ runs: with: cache-base: main(-v[0-9].*)? inherit-toolchain: true - bins: taplo-cli@0.9.0, cargo-machete + bins: taplo-cli@0.9.3, cargo-machete # Install additional non-default toolchains (for rustfmt for example), NOP if input omitted. channel: ${{ inputs.extra_rust_toolchains }} env: diff --git a/Cargo.lock b/Cargo.lock index bf2d5d7d57..cb64fa2c53 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -254,9 +254,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "0.8.7" +version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb848c43f6b06ae3de2e4a67496cbbabd78ae87db0f1248934f15d76192c6a" +checksum = "6259a506ab13e1d658796c31e6e39d2e2ee89243bcc505ddc613b35732e0a430" dependencies = [ "alloy-rlp", "bytes", @@ -316,9 +316,9 @@ dependencies = [ [[package]] name = "alloy-rlp" -version = "0.3.8" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26154390b1d205a4a7ac7352aa2eb4f81f391399d4e2f546fb81a2f8bb383f62" +checksum = "f542548a609dca89fcd72b3b9f355928cf844d4363c5eed9c5273a3dd225e097" dependencies = [ "alloy-rlp-derive", "arrayvec", @@ -327,9 +327,9 @@ dependencies = [ [[package]] name = "alloy-rlp-derive" -version = "0.3.8" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d0f2d905ebd295e7effec65e5f6868d153936130ae718352771de3e7d03c75c" +checksum = "5a833d97bf8a5f0f878daf2c8451fff7de7f9de38baa5a45d936ec718d81255a" dependencies = [ "proc-macro2", "quote", @@ -2735,9 +2735,9 @@ checksum = "32b13ea120a812beba79e34316b3942a857c86ec1593cb34f27bb28272ce2cca" [[package]] name = "const-hex" -version = "1.13.1" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0121754e84117e65f9d90648ee6aa4882a6e63110307ab73967a4c5e7e69e586" +checksum = "4b0485bab839b018a8f1723fc5391819fea5f8f0f32288ef8a735fd096b6160c" dependencies = [ "cfg-if", "cpufeatures", @@ -7300,6 +7300,7 @@ dependencies = [ "alloy-json-rpc", "alloy-primitives", "alloy-provider", + "alloy-rpc-types-eth", "alloy-sol-types", "alloy-transport", "alloy-transport-http", @@ -7310,7 +7311,6 @@ dependencies = [ "papyrus_config", "pretty_assertions", "serde", - "serde_json", "starknet-types-core", "starknet_api", "tar", 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..3fa0fae8bd 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 @@ -24,7 +25,6 @@ async-trait.workspace = true ethers.workspace = true papyrus_config.workspace = true serde.workspace = true -serde_json.workspace = true starknet-types-core.workspace = true starknet_api.workspace = true tar = { workspace = true, optional = 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..cf507e1732 --- /dev/null +++ b/crates/papyrus_base_layer/src/eth_events.rs @@ -0,0 +1,79 @@ +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::ConsumedMessageToL2(_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_bytes_be_slice(from_address.0.as_slice()) + .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_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 f6a02b566f..4e185c9892 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,17 @@ impl BaseLayerContract for EthereumBaseLayerContract { async fn events( &self, - _from_block: u64, - _until_block: u64, - _event_identifiers: &[&str], + from_block: u64, + until_block: u64, + events: &[&str], ) -> EthereumBaseLayerResult> { - todo!("Implmeneted in a subsequent commit") + let filter = EthEventFilter::new() + .from_block(BlockNumberOrTag::Number(from_block)) + .events(events) + .to_block(until_block); + + let matching_logs = self.contract.provider().get_logs(&filter).await?; + matching_logs.into_iter().map(TryInto::try_into).collect() } async fn latest_l1_block_number(&self, finality: u64) -> EthereumBaseLayerResult> { @@ -98,13 +105,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 4f9e887291..4bc0d5bd47 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;