scroll-reth

diff: ignored:
+23356
-373
+193
-52

This is an overview of the changes in scroll-reth, a fork of reth.

diff --git reth/crates/scroll/alloy/consensus/Cargo.toml scroll-reth/crates/scroll/alloy/consensus/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..68b529906d078f3ea5e1ad11178cc53c3eb5cb31 --- /dev/null +++ scroll-reth/crates/scroll/alloy/consensus/Cargo.toml @@ -0,0 +1,103 @@ +[package] +name = "scroll-alloy-consensus" +version.workspace = true +edition.workspace = true +rust-version.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +exclude.workspace = true + +[lints] +workspace = true + +[dependencies] +# alloy +alloy-rlp.workspace = true +alloy-eips.workspace = true +alloy-consensus.workspace = true +alloy-primitives = { workspace = true, features = ["rlp"] } + +# misc +derive_more = { workspace = true, features = ["display"] } +serde_with = { workspace = true, optional = true } + +# arbitrary +arbitrary = { workspace = true, features = ["derive"], optional = true } + +reth-codecs = { workspace = true, optional = true } +reth-codecs-derive = { workspace = true, optional = true } + +# required by reth-codecs +modular-bitfield = { workspace = true, optional = true } + +# serde +alloy-serde = { workspace = true, optional = true } +serde = { workspace = true, features = ["derive"], optional = true } + +[dev-dependencies] +rand.workspace = true +bincode.workspace = true +serde_json.workspace = true +arbitrary = { workspace = true, features = ["derive"] } +alloy-primitives = { workspace = true, features = ["rand", "arbitrary"] } +reth-codecs = { workspace = true, features = ["test-utils"] } +proptest-arbitrary-interop.workspace = true +proptest.workspace = true +test-fuzz.workspace = true + +[features] +default = ["std"] +std = [ + "serde/std", + "alloy-primitives/std", + "reth-codecs?/std", + "alloy-consensus/std", + "alloy-eips/std", + "alloy-rlp/std", + "alloy-serde/std", + "proptest/std", + "rand/std", + "derive_more/std", + "serde_json/std", + "serde_with?/std", +] +k256 = [ + "alloy-primitives/k256", + "alloy-consensus/k256", +] +kzg = [ + "alloy-eips/kzg", + "alloy-consensus/kzg", + "std", +] +reth-codec = [ + "dep:reth-codecs", + "dep:reth-codecs-derive", + "modular-bitfield", + "std", +] +arbitrary = [ + "std", + "dep:arbitrary", + "alloy-primitives/arbitrary", + "alloy-consensus/arbitrary", + "alloy-eips/arbitrary", + "alloy-serde/arbitrary", + "reth-codecs?/arbitrary", + "alloy-primitives/rand", +] +serde = [ + "dep:serde", + "dep:alloy-serde", + "alloy-primitives/serde", + "alloy-consensus/serde", + "alloy-eips/serde", + "rand/serde", + "reth-codecs?/serde", +] +serde-bincode-compat = [ + "dep:serde_with", + "alloy-consensus/serde-bincode-compat", + "alloy-eips/serde-bincode-compat", +]
diff --git reth/crates/scroll/alloy/consensus/README.md scroll-reth/crates/scroll/alloy/consensus/README.md new file mode 100644 index 0000000000000000000000000000000000000000..b311fb2629b4e1adfe3066efc064fd386dc8e1f5 --- /dev/null +++ scroll-reth/crates/scroll/alloy/consensus/README.md @@ -0,0 +1,15 @@ +# scroll-alloy-consensus + +Scroll consensus interface. + +This crate contains constants, types, and functions for implementing Scroll EL consensus and communication. This +includes an extended `ScrollTxEnvelope` type with l1 messages. + +In general a type belongs in this crate if it exists in the `alloy-consensus` crate, but was modified from the base Ethereum protocol in Scroll. +For consensus types that are not modified by Scroll, the `alloy-consensus` types should be used instead. + +## Provenance + +Much of this code was ported from [reth-primitives] as part of ongoing alloy migrations. + +[reth-primitives]: https://github.com/paradigmxyz/reth/tree/main/crates/primitives
diff --git reth/crates/scroll/alloy/consensus/src/lib.rs scroll-reth/crates/scroll/alloy/consensus/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..c6eb0dad6ea269d1bf7ec9535f202c9b27064675 --- /dev/null +++ scroll-reth/crates/scroll/alloy/consensus/src/lib.rs @@ -0,0 +1,30 @@ +#![doc = include_str!("../README.md")] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/alloy-rs/core/main/assets/alloy.jpg", + html_favicon_url = "https://raw.githubusercontent.com/alloy-rs/core/main/assets/favicon.ico" +)] +#![cfg_attr(not(test), warn(unused_crate_dependencies))] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(not(feature = "std"))] +extern crate alloc as std; + +mod transaction; +pub use transaction::{ + ScrollAdditionalInfo, ScrollL1MessageTransactionFields, ScrollPooledTransaction, + ScrollTransactionInfo, ScrollTxEnvelope, ScrollTxType, ScrollTypedTransaction, TxL1Message, + L1_MESSAGE_TRANSACTION_TYPE, L1_MESSAGE_TX_TYPE_ID, +}; + +mod receipt; +pub use receipt::{ScrollReceiptEnvelope, ScrollReceiptWithBloom, ScrollTransactionReceipt}; + +#[cfg(feature = "serde")] +pub use transaction::serde_l1_message_tx_rpc; + +/// Bincode-compatible serde implementations. +#[cfg(all(feature = "serde", feature = "serde-bincode-compat"))] +pub mod serde_bincode_compat { + pub use super::transaction::serde_bincode_compat::*; +}
diff --git reth/crates/scroll/alloy/consensus/src/receipt/envelope.rs scroll-reth/crates/scroll/alloy/consensus/src/receipt/envelope.rs new file mode 100644 index 0000000000000000000000000000000000000000..2ba0afdd84d9962c5ae4325ac7c684010a869a6a --- /dev/null +++ scroll-reth/crates/scroll/alloy/consensus/src/receipt/envelope.rs @@ -0,0 +1,373 @@ +//! Receipt envelope types for Scroll. + +use crate::ScrollTxType; +use std::vec::Vec; + +use alloy_consensus::{Eip658Value, Receipt, ReceiptWithBloom, TxReceipt}; +use alloy_eips::{ + eip2718::{Decodable2718, Eip2718Error, Eip2718Result, Encodable2718}, + Typed2718, +}; +use alloy_primitives::{logs_bloom, Bloom, Log}; +use alloy_rlp::{length_of_length, BufMut, Decodable, Encodable}; + +/// Receipt envelope, as defined in [EIP-2718], modified for Scroll chains. +/// +/// This enum distinguishes between tagged and untagged legacy receipts, as the +/// in-protocol merkle tree may commit to EITHER 0-prefixed or raw. Therefore +/// we must ensure that encoding returns the precise byte-array that was +/// decoded, preserving the presence or absence of the `TransactionType` flag. +/// +/// Transaction receipt payloads are specified in their respective EIPs. +/// +/// [EIP-2718]: https://eips.ethereum.org/EIPS/eip-2718 +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(tag = "type"))] +#[non_exhaustive] +pub enum ScrollReceiptEnvelope<T = Log> { + /// Receipt envelope with no type flag. + #[cfg_attr(feature = "serde", serde(rename = "0x0", alias = "0x00"))] + Legacy(ReceiptWithBloom<Receipt<T>>), + /// Receipt envelope with type flag 1, containing a [EIP-2930] receipt. + /// + /// [EIP-2930]: https://eips.ethereum.org/EIPS/eip-2930 + #[cfg_attr(feature = "serde", serde(rename = "0x1", alias = "0x01"))] + Eip2930(ReceiptWithBloom<Receipt<T>>), + /// Receipt envelope with type flag 2, containing a [EIP-1559] receipt. + /// + /// [EIP-1559]: https://eips.ethereum.org/EIPS/eip-1559 + #[cfg_attr(feature = "serde", serde(rename = "0x2", alias = "0x02"))] + Eip1559(ReceiptWithBloom<Receipt<T>>), + /// Receipt envelope with type flag 4, containing a [EIP-7702] receipt. + /// + /// [EIP-7702]: https://eips.ethereum.org/EIPS/eip-7702 + #[cfg_attr(feature = "serde", serde(rename = "0x4", alias = "0x04"))] + Eip7702(ReceiptWithBloom<Receipt<T>>), + /// Receipt envelope with type flag 126, containing a [Scroll-L1-Message] receipt. + #[cfg_attr(feature = "serde", serde(rename = "0x7e", alias = "0x7E"))] + L1Message(ReceiptWithBloom<Receipt<T>>), +} + +impl ScrollReceiptEnvelope<Log> { + /// Creates a new [`ScrollReceiptEnvelope`] from the given parts. + pub fn from_parts<'a>( + status: bool, + cumulative_gas_used: u64, + logs: impl IntoIterator<Item = &'a Log>, + tx_type: ScrollTxType, + ) -> Self { + let logs = logs.into_iter().cloned().collect::<Vec<_>>(); + let logs_bloom = logs_bloom(&logs); + let inner_receipt = + Receipt { status: Eip658Value::Eip658(status), cumulative_gas_used, logs }; + match tx_type { + ScrollTxType::Legacy => { + Self::Legacy(ReceiptWithBloom { receipt: inner_receipt, logs_bloom }) + } + ScrollTxType::Eip2930 => { + Self::Eip2930(ReceiptWithBloom { receipt: inner_receipt, logs_bloom }) + } + ScrollTxType::Eip1559 => { + Self::Eip1559(ReceiptWithBloom { receipt: inner_receipt, logs_bloom }) + } + ScrollTxType::Eip7702 => { + Self::Eip7702(ReceiptWithBloom { receipt: inner_receipt, logs_bloom }) + } + ScrollTxType::L1Message => { + Self::L1Message(ReceiptWithBloom { receipt: inner_receipt, logs_bloom }) + } + } + } +} + +impl<T> ScrollReceiptEnvelope<T> { + /// Return the [`ScrollTxType`] of the inner receipt. + pub const fn tx_type(&self) -> ScrollTxType { + match self { + Self::Legacy(_) => ScrollTxType::Legacy, + Self::Eip2930(_) => ScrollTxType::Eip2930, + Self::Eip1559(_) => ScrollTxType::Eip1559, + Self::Eip7702(_) => ScrollTxType::Eip7702, + Self::L1Message(_) => ScrollTxType::L1Message, + } + } + + /// Return true if the transaction was successful. + pub const fn is_success(&self) -> bool { + self.status() + } + + /// Returns the success status of the receipt's transaction. + pub const fn status(&self) -> bool { + self.as_receipt().unwrap().status.coerce_status() + } + + /// Returns the cumulative gas used at this receipt. + pub const fn cumulative_gas_used(&self) -> u64 { + self.as_receipt().unwrap().cumulative_gas_used + } + + /// Return the receipt logs. + pub fn logs(&self) -> &[T] { + &self.as_receipt().unwrap().logs + } + + /// Return the receipt's bloom. + pub const fn logs_bloom(&self) -> &Bloom { + match self { + Self::Legacy(t) | + Self::Eip2930(t) | + Self::Eip1559(t) | + Self::Eip7702(t) | + Self::L1Message(t) => &t.logs_bloom, + } + } + + /// Returns the L1 message receipt if it is a deposit receipt. + pub const fn as_l1_message_receipt_with_bloom(&self) -> Option<&ReceiptWithBloom<Receipt<T>>> { + match self { + Self::L1Message(t) => Some(t), + _ => None, + } + } + + /// Returns the L1 message receipt if it is a deposit receipt. + pub const fn as_l1_message_receipt(&self) -> Option<&Receipt<T>> { + match self { + Self::L1Message(t) => Some(&t.receipt), + _ => None, + } + } + + /// Return the inner receipt. Currently this is infallible, however, future + /// receipt types may be added. + pub const fn as_receipt(&self) -> Option<&Receipt<T>> { + match self { + Self::Legacy(t) | + Self::Eip2930(t) | + Self::Eip1559(t) | + Self::Eip7702(t) | + Self::L1Message(t) => Some(&t.receipt), + } + } +} + +impl ScrollReceiptEnvelope { + /// Get the length of the inner receipt in the 2718 encoding. + pub fn inner_length(&self) -> usize { + match self { + Self::Legacy(t) | + Self::Eip2930(t) | + Self::Eip1559(t) | + Self::Eip7702(t) | + Self::L1Message(t) => t.length(), + } + } + + /// Calculate the length of the rlp payload of the network encoded receipt. + pub fn rlp_payload_length(&self) -> usize { + let length = self.inner_length(); + match self { + Self::Legacy(_) => length, + _ => length + 1, + } + } +} + +impl<T> TxReceipt for ScrollReceiptEnvelope<T> +where + T: Clone + core::fmt::Debug + PartialEq + Eq + Send + Sync, +{ + type Log = T; + + fn status_or_post_state(&self) -> Eip658Value { + self.as_receipt().unwrap().status + } + + fn status(&self) -> bool { + self.as_receipt().unwrap().status.coerce_status() + } + + /// Return the receipt's bloom. + fn bloom(&self) -> Bloom { + *self.logs_bloom() + } + + fn bloom_cheap(&self) -> Option<Bloom> { + Some(self.bloom()) + } + + /// Returns the cumulative gas used at this receipt. + fn cumulative_gas_used(&self) -> u64 { + self.as_receipt().unwrap().cumulative_gas_used + } + + /// Return the receipt logs. + fn logs(&self) -> &[T] { + &self.as_receipt().unwrap().logs + } +} + +impl Encodable for ScrollReceiptEnvelope { + fn encode(&self, out: &mut dyn alloy_rlp::BufMut) { + self.network_encode(out) + } + + fn length(&self) -> usize { + let mut payload_length = self.rlp_payload_length(); + if !self.is_legacy() { + payload_length += length_of_length(payload_length); + } + payload_length + } +} + +impl Decodable for ScrollReceiptEnvelope { + fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> { + Self::network_decode(buf) + .map_or_else(|_| Err(alloy_rlp::Error::Custom("Unexpected type")), Ok) + } +} + +impl Encodable2718 for ScrollReceiptEnvelope { + fn type_flag(&self) -> Option<u8> { + match self { + Self::Legacy(_) => None, + Self::Eip2930(_) => Some(ScrollTxType::Eip2930 as u8), + Self::Eip1559(_) => Some(ScrollTxType::Eip1559 as u8), + Self::Eip7702(_) => Some(ScrollTxType::Eip7702 as u8), + Self::L1Message(_) => Some(ScrollTxType::L1Message as u8), + } + } + + fn encode_2718_len(&self) -> usize { + self.inner_length() + !self.is_legacy() as usize + } + + fn encode_2718(&self, out: &mut dyn BufMut) { + match self.type_flag() { + None => {} + Some(ty) => out.put_u8(ty), + } + match self { + Self::Legacy(t) | + Self::Eip2930(t) | + Self::Eip1559(t) | + Self::Eip7702(t) | + Self::L1Message(t) => t.encode(out), + } + } +} + +impl Typed2718 for ScrollReceiptEnvelope { + fn ty(&self) -> u8 { + let ty = match self { + Self::Legacy(_) => ScrollTxType::Legacy, + Self::Eip2930(_) => ScrollTxType::Eip2930, + Self::Eip1559(_) => ScrollTxType::Eip1559, + Self::Eip7702(_) => ScrollTxType::Eip7702, + Self::L1Message(_) => ScrollTxType::L1Message, + }; + ty as u8 + } +} + +impl Decodable2718 for ScrollReceiptEnvelope { + fn typed_decode(ty: u8, buf: &mut &[u8]) -> Eip2718Result<Self> { + match ty.try_into().map_err(|_| Eip2718Error::UnexpectedType(ty))? { + ScrollTxType::Legacy => { + Err(alloy_rlp::Error::Custom("type-0 eip2718 transactions are not supported") + .into()) + } + ScrollTxType::Eip2930 => Ok(Self::Eip2930(Decodable::decode(buf)?)), + ScrollTxType::Eip1559 => Ok(Self::Eip1559(Decodable::decode(buf)?)), + ScrollTxType::Eip7702 => Ok(Self::Eip7702(Decodable::decode(buf)?)), + ScrollTxType::L1Message => Ok(Self::L1Message(Decodable::decode(buf)?)), + } + } + + fn fallback_decode(buf: &mut &[u8]) -> Eip2718Result<Self> { + Ok(Self::Legacy(Decodable::decode(buf)?)) + } +} + +#[cfg(all(test, feature = "arbitrary"))] +impl<'a, T> arbitrary::Arbitrary<'a> for ScrollReceiptEnvelope<T> +where + T: arbitrary::Arbitrary<'a>, +{ + fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> { + match u.int_in_range(0..=4)? { + 0 => Ok(Self::Legacy(ReceiptWithBloom::arbitrary(u)?)), + 1 => Ok(Self::Eip2930(ReceiptWithBloom::arbitrary(u)?)), + 2 => Ok(Self::Eip1559(ReceiptWithBloom::arbitrary(u)?)), + _ => Ok(Self::L1Message(ReceiptWithBloom::arbitrary(u)?)), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use alloy_consensus::{Receipt, ReceiptWithBloom}; + use alloy_eips::eip2718::Encodable2718; + use alloy_primitives::{address, b256, bytes, hex, Log, LogData}; + use alloy_rlp::Encodable; + + #[cfg(not(feature = "std"))] + use alloc::vec; + + // Test vector from: https://eips.ethereum.org/EIPS/eip-2481 + #[test] + fn encode_legacy_receipt() { + let expected = hex!("f901668001b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff"); + + let mut data = vec![]; + let receipt = ScrollReceiptEnvelope::Legacy(ReceiptWithBloom { + receipt: Receipt { + status: false.into(), + cumulative_gas_used: 0x1, + logs: vec![Log { + address: address!("0000000000000000000000000000000000000011"), + data: LogData::new_unchecked( + vec![ + b256!( + "000000000000000000000000000000000000000000000000000000000000dead" + ), + b256!( + "000000000000000000000000000000000000000000000000000000000000beef" + ), + ], + bytes!("0100ff"), + ), + }], + }, + logs_bloom: [0; 256].into(), + }); + + receipt.network_encode(&mut data); + + // check that the rlp length equals the length of the expected rlp + assert_eq!(receipt.length(), expected.len()); + assert_eq!(data, expected); + } + + #[test] + fn legacy_receipt_from_parts() { + let receipt = ScrollReceiptEnvelope::from_parts(true, 100, vec![], ScrollTxType::Legacy); + assert!(receipt.status()); + assert_eq!(receipt.cumulative_gas_used(), 100); + assert_eq!(receipt.logs().len(), 0); + assert_eq!(receipt.tx_type(), ScrollTxType::Legacy); + } + + #[test] + fn l1_message_receipt_from_parts() { + let receipt = ScrollReceiptEnvelope::from_parts(true, 100, vec![], ScrollTxType::L1Message); + assert!(receipt.status()); + assert_eq!(receipt.cumulative_gas_used(), 100); + assert_eq!(receipt.logs().len(), 0); + assert_eq!(receipt.tx_type(), ScrollTxType::L1Message); + } +}
diff --git reth/crates/scroll/alloy/consensus/src/receipt/mod.rs scroll-reth/crates/scroll/alloy/consensus/src/receipt/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..c06b4838314b7c82fa98a4e23231d6c0b00efc98 --- /dev/null +++ scroll-reth/crates/scroll/alloy/consensus/src/receipt/mod.rs @@ -0,0 +1,6 @@ +mod envelope; +pub use envelope::ScrollReceiptEnvelope; + +#[allow(clippy::module_inception)] +mod receipt; +pub use receipt::{ScrollReceiptWithBloom, ScrollTransactionReceipt};
diff --git reth/crates/scroll/alloy/consensus/src/receipt/receipt.rs scroll-reth/crates/scroll/alloy/consensus/src/receipt/receipt.rs new file mode 100644 index 0000000000000000000000000000000000000000..5cdd1b3e1134d5a0d3dc6f9e6888eb7d2a441814 --- /dev/null +++ scroll-reth/crates/scroll/alloy/consensus/src/receipt/receipt.rs @@ -0,0 +1,256 @@ +//! Transaction receipt types for Scroll. + +use alloy_consensus::{ + Eip658Value, Receipt, ReceiptWithBloom, RlpDecodableReceipt, RlpEncodableReceipt, TxReceipt, +}; +use alloy_primitives::{Bloom, Log, U256}; +use alloy_rlp::{Buf, BufMut, Decodable, Encodable, Header}; + +/// Receipt containing result of transaction execution. +#[derive(Clone, Debug, PartialEq, Eq, Default)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] +pub struct ScrollTransactionReceipt<T = Log> { + /// The inner receipt type. + #[cfg_attr(feature = "serde", serde(flatten))] + pub inner: Receipt<T>, + /// L1 fee for Scroll transactions. + pub l1_fee: U256, +} + +impl<T> ScrollTransactionReceipt<T> { + /// Returns a new [`ScrollTransactionReceipt`] from the inner receipt and the l1 fee. + pub const fn new(inner: Receipt<T>, l1_fee: U256) -> Self { + Self { inner, l1_fee } + } +} + +impl ScrollTransactionReceipt { + /// Calculates [`Log`]'s bloom filter. This is slow operation and [`ScrollReceiptWithBloom`] + /// can be used to cache this value. + pub fn bloom_slow(&self) -> Bloom { + self.inner.logs.iter().collect() + } + + /// Calculates the bloom filter for the receipt and returns the [`ScrollReceiptWithBloom`] + /// container type. + pub fn with_bloom(self) -> ScrollReceiptWithBloom { + self.into() + } +} + +impl<T: Encodable> ScrollTransactionReceipt<T> { + /// Returns length of RLP-encoded receipt fields with the given [`Bloom`] without an RLP header. + /// Does not include the L1 fee field which is not part of the consensus encoding of a receipt. + /// <https://github.com/scroll-tech/go-ethereum/blob/9fff27e4f34fb5097100ed76ee725ce056267f4b/core/types/receipt.go#L96-L102> + pub fn rlp_encoded_fields_length_with_bloom(&self, bloom: &Bloom) -> usize { + self.inner.rlp_encoded_fields_length_with_bloom(bloom) + } + + /// RLP-encodes receipt fields with the given [`Bloom`] without an RLP header. + /// Does not include the L1 fee field which is not part of the consensus encoding of a receipt. + /// <https://github.com/scroll-tech/go-ethereum/blob/9fff27e4f34fb5097100ed76ee725ce056267f4b/core/types/receipt.go#L96-L102> + pub fn rlp_encode_fields_with_bloom(&self, bloom: &Bloom, out: &mut dyn BufMut) { + self.inner.rlp_encode_fields_with_bloom(bloom, out); + } + + /// Returns RLP header for this receipt encoding with the given [`Bloom`]. + /// Does not include the L1 fee field which is not part of the consensus encoding of a receipt. + /// <https://github.com/scroll-tech/go-ethereum/blob/9fff27e4f34fb5097100ed76ee725ce056267f4b/core/types/receipt.go#L96-L102> + pub fn rlp_header_with_bloom(&self, bloom: &Bloom) -> Header { + Header { list: true, payload_length: self.rlp_encoded_fields_length_with_bloom(bloom) } + } +} + +impl<T: Decodable> ScrollTransactionReceipt<T> { + /// RLP-decodes receipt's field with a [`Bloom`]. + /// + /// Does not expect an RLP header. + pub fn rlp_decode_fields_with_bloom( + buf: &mut &[u8], + ) -> alloy_rlp::Result<ReceiptWithBloom<Self>> { + let ReceiptWithBloom { receipt: inner, logs_bloom } = + Receipt::rlp_decode_fields_with_bloom(buf)?; + + Ok(ReceiptWithBloom { logs_bloom, receipt: Self { inner, l1_fee: Default::default() } }) + } +} + +impl<T> AsRef<Receipt<T>> for ScrollTransactionReceipt<T> { + fn as_ref(&self) -> &Receipt<T> { + &self.inner + } +} + +impl<T> TxReceipt for ScrollTransactionReceipt<T> +where + T: AsRef<Log> + Clone + core::fmt::Debug + PartialEq + Eq + Send + Sync, +{ + type Log = T; + + fn status_or_post_state(&self) -> Eip658Value { + self.inner.status_or_post_state() + } + + fn status(&self) -> bool { + self.inner.status() + } + + fn bloom(&self) -> Bloom { + self.inner.bloom_slow() + } + + fn cumulative_gas_used(&self) -> u64 { + self.inner.cumulative_gas_used() + } + + fn logs(&self) -> &[Self::Log] { + self.inner.logs() + } +} + +impl<T: Encodable> RlpEncodableReceipt for ScrollTransactionReceipt<T> { + fn rlp_encoded_length_with_bloom(&self, bloom: &Bloom) -> usize { + self.rlp_header_with_bloom(bloom).length_with_payload() + } + + fn rlp_encode_with_bloom(&self, bloom: &Bloom, out: &mut dyn BufMut) { + self.rlp_header_with_bloom(bloom).encode(out); + self.rlp_encode_fields_with_bloom(bloom, out); + } +} + +impl<T: Decodable> RlpDecodableReceipt for ScrollTransactionReceipt<T> { + fn rlp_decode_with_bloom(buf: &mut &[u8]) -> alloy_rlp::Result<ReceiptWithBloom<Self>> { + let header = Header::decode(buf)?; + if !header.list { + return Err(alloy_rlp::Error::UnexpectedString); + } + + if buf.len() < header.payload_length { + return Err(alloy_rlp::Error::InputTooShort); + } + + // Note: we pass a separate buffer to `rlp_decode_fields_with_bloom` to allow it decode + // optional fields based on the remaining length. + let mut fields_buf = &buf[..header.payload_length]; + let this = Self::rlp_decode_fields_with_bloom(&mut fields_buf)?; + + if !fields_buf.is_empty() { + return Err(alloy_rlp::Error::UnexpectedLength); + } + + buf.advance(header.payload_length); + + Ok(this) + } +} + +/// [`ScrollTransactionReceipt`] with calculated bloom filter, modified for Scroll. +/// +/// This convenience type allows us to lazily calculate the bloom filter for a +/// receipt, similar to [`Sealed`]. +/// +/// [`Sealed`]: alloy_consensus::Sealed +pub type ScrollReceiptWithBloom<T = Log> = ReceiptWithBloom<ScrollTransactionReceipt<T>>; + +#[cfg(any(test, feature = "arbitrary"))] +impl<'a, T> arbitrary::Arbitrary<'a> for ScrollTransactionReceipt<T> +where + T: arbitrary::Arbitrary<'a>, +{ + fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> { + #[cfg(not(feature = "std"))] + use alloc::vec::Vec; + Ok(Self { + inner: Receipt { + status: Eip658Value::arbitrary(u)?, + cumulative_gas_used: u64::arbitrary(u)?, + logs: Vec::<T>::arbitrary(u)?, + }, + l1_fee: U256::arbitrary(u)?, + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use alloy_consensus::Receipt; + use alloy_primitives::{address, b256, bytes, hex, Bytes, Log, LogData}; + use alloy_rlp::{Decodable, Encodable}; + + #[cfg(not(feature = "std"))] + use alloc::{vec, vec::Vec}; + + // Test vector from: https://eips.ethereum.org/EIPS/eip-2481 + #[test] + fn decode_legacy_receipt() { + let data = hex!("f901668001b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff"); + + // EIP658Receipt + let expected = + ScrollReceiptWithBloom { + receipt: ScrollTransactionReceipt { + inner: Receipt { + status: false.into(), + cumulative_gas_used: 0x1, + logs: vec![Log { + address: address!("0000000000000000000000000000000000000011"), + data: LogData::new_unchecked( + vec![ + b256!("000000000000000000000000000000000000000000000000000000000000dead"), + b256!("000000000000000000000000000000000000000000000000000000000000beef"), + ], + bytes!("0100ff"), + ), + }], + }, + l1_fee: U256::ZERO + }, + logs_bloom: [0; 256].into(), + }; + + let receipt = ScrollReceiptWithBloom::decode(&mut &data[..]).unwrap(); + assert_eq!(receipt, expected); + } + + #[test] + fn gigantic_receipt() { + let receipt = ScrollTransactionReceipt { + inner: Receipt { + cumulative_gas_used: 16747627, + status: true.into(), + logs: vec![ + Log { + address: address!("4bf56695415f725e43c3e04354b604bcfb6dfb6e"), + data: LogData::new_unchecked( + vec![b256!( + "c69dc3d7ebff79e41f525be431d5cd3cc08f80eaf0f7819054a726eeb7086eb9" + )], + Bytes::from(vec![1; 0xffffff]), + ), + }, + Log { + address: address!("faca325c86bf9c2d5b413cd7b90b209be92229c2"), + data: LogData::new_unchecked( + vec![b256!( + "8cca58667b1e9ffa004720ac99a3d61a138181963b294d270d91c53d36402ae2" + )], + Bytes::from(vec![1; 0xffffff]), + ), + }, + ], + }, + l1_fee: U256::ZERO, + } + .with_bloom(); + + let mut data = vec![]; + + receipt.encode(&mut data); + let decoded = ScrollReceiptWithBloom::decode(&mut &data[..]).unwrap(); + + assert_eq!(decoded, receipt); + } +}
diff --git reth/crates/scroll/alloy/consensus/src/transaction/envelope.rs scroll-reth/crates/scroll/alloy/consensus/src/transaction/envelope.rs new file mode 100644 index 0000000000000000000000000000000000000000..4fd4b8d1e2b7903a335c1cd632b46600e6b1b082 --- /dev/null +++ scroll-reth/crates/scroll/alloy/consensus/src/transaction/envelope.rs @@ -0,0 +1,936 @@ +use crate::{ScrollPooledTransaction, ScrollTxType, ScrollTypedTransaction, TxL1Message}; +use core::hash::Hash; + +use alloy_consensus::{ + error::ValueError, transaction::RlpEcdsaDecodableTx, Sealable, Sealed, Signed, Transaction, + TxEip1559, TxEip2930, TxEip7702, TxLegacy, Typed2718, +}; +use alloy_eips::{ + eip2718::{Decodable2718, Eip2718Error, Eip2718Result, Encodable2718}, + eip2930::AccessList, + eip7702::SignedAuthorization, +}; +use alloy_primitives::{Address, Bytes, Signature, TxKind, B256, U256}; +use alloy_rlp::{Decodable, Encodable}; +#[cfg(feature = "reth-codec")] +use reth_codecs::{ + Compact, + __private::bytes::BufMut, + alloy::transaction::{CompactEnvelope, Envelope, FromTxCompact, ToTxCompact}, +}; + +/// The Ethereum [EIP-2718] Transaction Envelope, modified for Scroll chains. +/// +/// # Note: +/// +/// This enum distinguishes between tagged and untagged legacy transactions, as +/// the in-protocol merkle tree may commit to EITHER 0-prefixed or raw. +/// Therefore we must ensure that encoding returns the precise byte-array that +/// was decoded, preserving the presence or absence of the `TransactionType` +/// flag. +/// +/// [EIP-2718]: https://eips.ethereum.org/EIPS/eip-2718 +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr( + feature = "serde", + serde(into = "serde_from::TaggedTxEnvelope", from = "serde_from::MaybeTaggedTxEnvelope") +)] +#[cfg_attr(all(any(test, feature = "arbitrary"), feature = "k256"), derive(arbitrary::Arbitrary))] +pub enum ScrollTxEnvelope { + /// An untagged [`TxLegacy`]. + Legacy(Signed<TxLegacy>), + /// A [`TxEip2930`] tagged with type 1. + Eip2930(Signed<TxEip2930>), + /// A [`TxEip1559`] tagged with type 2. + Eip1559(Signed<TxEip1559>), + /// EIP-7702 transaction + Eip7702(Signed<TxEip7702>), + /// A [`TxL1Message`] tagged with type 0x7E. + L1Message(Sealed<TxL1Message>), +} + +impl From<Signed<TxLegacy>> for ScrollTxEnvelope { + fn from(v: Signed<TxLegacy>) -> Self { + Self::Legacy(v) + } +} + +impl From<Signed<TxEip2930>> for ScrollTxEnvelope { + fn from(v: Signed<TxEip2930>) -> Self { + Self::Eip2930(v) + } +} + +impl From<Signed<TxEip1559>> for ScrollTxEnvelope { + fn from(v: Signed<TxEip1559>) -> Self { + Self::Eip1559(v) + } +} + +impl From<Signed<TxEip7702>> for ScrollTxEnvelope { + fn from(v: Signed<TxEip7702>) -> Self { + Self::Eip7702(v) + } +} + +impl From<TxL1Message> for ScrollTxEnvelope { + fn from(v: TxL1Message) -> Self { + v.seal_slow().into() + } +} + +impl From<Sealed<TxL1Message>> for ScrollTxEnvelope { + fn from(v: Sealed<TxL1Message>) -> Self { + Self::L1Message(v) + } +} + +impl From<Signed<ScrollTypedTransaction>> for ScrollTxEnvelope { + fn from(value: Signed<ScrollTypedTransaction>) -> Self { + let (tx, sig, hash) = value.into_parts(); + match tx { + ScrollTypedTransaction::Legacy(tx_legacy) => { + let tx = Signed::new_unchecked(tx_legacy, sig, hash); + Self::Legacy(tx) + } + ScrollTypedTransaction::Eip2930(tx_eip2930) => { + let tx = Signed::new_unchecked(tx_eip2930, sig, hash); + Self::Eip2930(tx) + } + ScrollTypedTransaction::Eip1559(tx_eip1559) => { + let tx = Signed::new_unchecked(tx_eip1559, sig, hash); + Self::Eip1559(tx) + } + ScrollTypedTransaction::Eip7702(tx_eip7702) => { + let tx = Signed::new_unchecked(tx_eip7702, sig, hash); + Self::Eip7702(tx) + } + ScrollTypedTransaction::L1Message(tx) => { + Self::L1Message(Sealed::new_unchecked(tx, hash)) + } + } + } +} + +impl Typed2718 for ScrollTxEnvelope { + fn ty(&self) -> u8 { + match self { + Self::Legacy(tx) => tx.tx().ty(), + Self::Eip2930(tx) => tx.tx().ty(), + Self::Eip1559(tx) => tx.tx().ty(), + Self::Eip7702(tx) => tx.tx().ty(), + Self::L1Message(tx) => tx.ty(), + } + } +} + +impl Transaction for ScrollTxEnvelope { + fn chain_id(&self) -> Option<u64> { + match self { + Self::Legacy(tx) => tx.tx().chain_id(), + Self::Eip2930(tx) => tx.tx().chain_id(), + Self::Eip1559(tx) => tx.tx().chain_id(), + Self::Eip7702(tx) => tx.tx().chain_id(), + Self::L1Message(tx) => tx.chain_id(), + } + } + + fn nonce(&self) -> u64 { + match self { + Self::Legacy(tx) => tx.tx().nonce(), + Self::Eip2930(tx) => tx.tx().nonce(), + Self::Eip1559(tx) => tx.tx().nonce(), + Self::Eip7702(tx) => tx.tx().nonce(), + Self::L1Message(tx) => tx.nonce(), + } + } + + fn gas_limit(&self) -> u64 { + match self { + Self::Legacy(tx) => tx.tx().gas_limit(), + Self::Eip2930(tx) => tx.tx().gas_limit(), + Self::Eip1559(tx) => tx.tx().gas_limit(), + Self::Eip7702(tx) => tx.tx().gas_limit(), + Self::L1Message(tx) => tx.gas_limit(), + } + } + + fn gas_price(&self) -> Option<u128> { + match self { + Self::Legacy(tx) => tx.tx().gas_price(), + Self::Eip2930(tx) => tx.tx().gas_price(), + Self::Eip1559(tx) => tx.tx().gas_price(), + Self::Eip7702(tx) => tx.tx().gas_price(), + Self::L1Message(tx) => tx.gas_price(), + } + } + + fn max_fee_per_gas(&self) -> u128 { + match self { + Self::Legacy(tx) => tx.tx().max_fee_per_gas(), + Self::Eip2930(tx) => tx.tx().max_fee_per_gas(), + Self::Eip1559(tx) => tx.tx().max_fee_per_gas(), + Self::Eip7702(tx) => tx.tx().max_fee_per_gas(), + Self::L1Message(tx) => tx.max_fee_per_gas(), + } + } + + fn max_priority_fee_per_gas(&self) -> Option<u128> { + match self { + Self::Legacy(tx) => tx.tx().max_priority_fee_per_gas(), + Self::Eip2930(tx) => tx.tx().max_priority_fee_per_gas(), + Self::Eip1559(tx) => tx.tx().max_priority_fee_per_gas(), + Self::Eip7702(tx) => tx.tx().max_priority_fee_per_gas(), + Self::L1Message(tx) => tx.max_priority_fee_per_gas(), + } + } + + fn max_fee_per_blob_gas(&self) -> Option<u128> { + match self { + Self::Legacy(tx) => tx.tx().max_fee_per_blob_gas(), + Self::Eip2930(tx) => tx.tx().max_fee_per_blob_gas(), + Self::Eip1559(tx) => tx.tx().max_fee_per_blob_gas(), + Self::Eip7702(tx) => tx.tx().max_fee_per_blob_gas(), + Self::L1Message(tx) => tx.max_fee_per_blob_gas(), + } + } + + fn priority_fee_or_price(&self) -> u128 { + match self { + Self::Legacy(tx) => tx.tx().priority_fee_or_price(), + Self::Eip2930(tx) => tx.tx().priority_fee_or_price(), + Self::Eip1559(tx) => tx.tx().priority_fee_or_price(), + Self::Eip7702(tx) => tx.tx().priority_fee_or_price(), + Self::L1Message(tx) => tx.priority_fee_or_price(), + } + } + + fn to(&self) -> Option<Address> { + match self { + Self::Legacy(tx) => tx.tx().to(), + Self::Eip2930(tx) => tx.tx().to(), + Self::Eip1559(tx) => tx.tx().to(), + Self::Eip7702(tx) => tx.tx().to(), + Self::L1Message(tx) => tx.to(), + } + } + + fn kind(&self) -> TxKind { + match self { + Self::Legacy(tx) => tx.tx().kind(), + Self::Eip2930(tx) => tx.tx().kind(), + Self::Eip1559(tx) => tx.tx().kind(), + Self::Eip7702(tx) => tx.tx().kind(), + Self::L1Message(tx) => tx.kind(), + } + } + + fn value(&self) -> U256 { + match self { + Self::Legacy(tx) => tx.tx().value(), + Self::Eip2930(tx) => tx.tx().value(), + Self::Eip1559(tx) => tx.tx().value(), + Self::Eip7702(tx) => tx.tx().value(), + Self::L1Message(tx) => tx.value(), + } + } + + fn input(&self) -> &Bytes { + match self { + Self::Legacy(tx) => tx.tx().input(), + Self::Eip2930(tx) => tx.tx().input(), + Self::Eip1559(tx) => tx.tx().input(), + Self::Eip7702(tx) => tx.tx().input(), + Self::L1Message(tx) => tx.input(), + } + } + + fn access_list(&self) -> Option<&AccessList> { + match self { + Self::Legacy(tx) => tx.tx().access_list(), + Self::Eip2930(tx) => tx.tx().access_list(), + Self::Eip1559(tx) => tx.tx().access_list(), + Self::Eip7702(tx) => tx.tx().access_list(), + Self::L1Message(tx) => tx.access_list(), + } + } + + fn blob_versioned_hashes(&self) -> Option<&[B256]> { + match self { + Self::Legacy(tx) => tx.tx().blob_versioned_hashes(), + Self::Eip2930(tx) => tx.tx().blob_versioned_hashes(), + Self::Eip1559(tx) => tx.tx().blob_versioned_hashes(), + Self::Eip7702(tx) => tx.tx().blob_versioned_hashes(), + Self::L1Message(tx) => tx.blob_versioned_hashes(), + } + } + + fn authorization_list(&self) -> Option<&[SignedAuthorization]> { + match self { + Self::Legacy(tx) => tx.tx().authorization_list(), + Self::Eip2930(tx) => tx.tx().authorization_list(), + Self::Eip1559(tx) => tx.tx().authorization_list(), + Self::Eip7702(tx) => tx.tx().authorization_list(), + Self::L1Message(tx) => tx.authorization_list(), + } + } + + fn is_dynamic_fee(&self) -> bool { + match self { + Self::Legacy(tx) => tx.tx().is_dynamic_fee(), + Self::Eip2930(tx) => tx.tx().is_dynamic_fee(), + Self::Eip1559(tx) => tx.tx().is_dynamic_fee(), + Self::Eip7702(tx) => tx.tx().is_dynamic_fee(), + Self::L1Message(tx) => tx.is_dynamic_fee(), + } + } + + fn effective_gas_price(&self, base_fee: Option<u64>) -> u128 { + match self { + Self::Legacy(tx) => tx.tx().effective_gas_price(base_fee), + Self::Eip2930(tx) => tx.tx().effective_gas_price(base_fee), + Self::Eip1559(tx) => tx.tx().effective_gas_price(base_fee), + Self::Eip7702(tx) => tx.tx().effective_gas_price(base_fee), + Self::L1Message(tx) => tx.effective_gas_price(base_fee), + } + } + + fn is_create(&self) -> bool { + match self { + Self::Legacy(tx) => tx.tx().is_create(), + Self::Eip2930(tx) => tx.tx().is_create(), + Self::Eip1559(tx) => tx.tx().is_create(), + Self::Eip7702(tx) => tx.tx().is_create(), + Self::L1Message(tx) => tx.is_create(), + } + } +} + +impl ScrollTxEnvelope { + /// Returns true if the transaction is a legacy transaction. + #[inline] + pub const fn is_legacy(&self) -> bool { + matches!(self, Self::Legacy(_)) + } + + /// Returns true if the transaction is an EIP-2930 transaction. + #[inline] + pub const fn is_eip2930(&self) -> bool { + matches!(self, Self::Eip2930(_)) + } + + /// Returns true if the transaction is an EIP-1559 transaction. + #[inline] + pub const fn is_eip1559(&self) -> bool { + matches!(self, Self::Eip1559(_)) + } + + /// Returns true if the transaction is an EIP-7702 transaction. + #[inline] + pub const fn is_eip7702(&self) -> bool { + matches!(self, Self::Eip7702(_)) + } + + /// Returns true if the transaction is a deposit transaction. + #[inline] + pub const fn is_l1_message(&self) -> bool { + matches!(self, Self::L1Message(_)) + } + + /// Returns the [`TxLegacy`] variant if the transaction is a legacy transaction. + pub const fn as_legacy(&self) -> Option<&Signed<TxLegacy>> { + match self { + Self::Legacy(tx) => Some(tx), + _ => None, + } + } + + /// Returns the [`TxEip2930`] variant if the transaction is an EIP-2930 transaction. + pub const fn as_eip2930(&self) -> Option<&Signed<TxEip2930>> { + match self { + Self::Eip2930(tx) => Some(tx), + _ => None, + } + } + + /// Returns the [`TxEip1559`] variant if the transaction is an EIP-1559 transaction. + pub const fn as_eip1559(&self) -> Option<&Signed<TxEip1559>> { + match self { + Self::Eip1559(tx) => Some(tx), + _ => None, + } + } + + /// Returns the [`TxEip7702`] variant if the transaction is an EIP-1559 transaction. + pub const fn as_eip7702(&self) -> Option<&Signed<TxEip7702>> { + match self { + Self::Eip7702(tx) => Some(tx), + _ => None, + } + } + + /// Returns the [`TxL1Message`] variant if the transaction is a deposit transaction. + pub const fn as_l1_message(&self) -> Option<&Sealed<TxL1Message>> { + match self { + Self::L1Message(tx) => Some(tx), + _ => None, + } + } + + /// Return the [`ScrollTxType`] of the inner txn. + pub const fn tx_type(&self) -> ScrollTxType { + match self { + Self::Legacy(_) => ScrollTxType::Legacy, + Self::Eip2930(_) => ScrollTxType::Eip2930, + Self::Eip1559(_) => ScrollTxType::Eip1559, + Self::Eip7702(_) => ScrollTxType::Eip7702, + Self::L1Message(_) => ScrollTxType::L1Message, + } + } + + /// Return the length of the inner txn, including type byte length + pub fn eip2718_encoded_length(&self) -> usize { + match self { + Self::Legacy(t) => t.eip2718_encoded_length(), + Self::Eip2930(t) => t.eip2718_encoded_length(), + Self::Eip1559(t) => t.eip2718_encoded_length(), + Self::Eip7702(t) => t.eip2718_encoded_length(), + Self::L1Message(t) => t.eip2718_encoded_length(), + } + } + + /// Returns the signature for the transaction. + pub const fn signature(&self) -> Option<Signature> { + match self { + Self::Legacy(t) => Some(*t.signature()), + Self::Eip2930(t) => Some(*t.signature()), + Self::Eip1559(t) => Some(*t.signature()), + Self::Eip7702(t) => Some(*t.signature()), + Self::L1Message(_) => None, + } + } + + /// Converts the [`ScrollTxEnvelope`] into a [`ScrollPooledTransaction`], returns an error if + /// the transaction is a L1 message. + pub fn try_into_pooled(self) -> Result<ScrollPooledTransaction, ValueError<Self>> { + match self { + Self::Legacy(tx) => Ok(tx.into()), + Self::Eip2930(tx) => Ok(tx.into()), + Self::Eip1559(tx) => Ok(tx.into()), + Self::Eip7702(tx) => Ok(tx.into()), + Self::L1Message(tx) => Err(ValueError::new(tx.into(), "L1 messages cannot be pooled")), + } + } +} + +#[cfg(feature = "reth-codec")] +impl ToTxCompact for ScrollTxEnvelope { + fn to_tx_compact(&self, buf: &mut (impl BufMut + AsMut<[u8]>)) { + match self { + Self::Legacy(tx) => tx.tx().to_compact(buf), + Self::Eip2930(tx) => tx.tx().to_compact(buf), + Self::Eip1559(tx) => tx.tx().to_compact(buf), + Self::Eip7702(tx) => tx.tx().to_compact(buf), + Self::L1Message(tx) => tx.to_compact(buf), + }; + } +} + +#[cfg(feature = "reth-codec")] +impl FromTxCompact for ScrollTxEnvelope { + type TxType = ScrollTxType; + + fn from_tx_compact(buf: &[u8], tx_type: ScrollTxType, signature: Signature) -> (Self, &[u8]) { + match tx_type { + ScrollTxType::Legacy => { + let (tx, buf) = TxLegacy::from_compact(buf, buf.len()); + let tx = Signed::new_unhashed(tx, signature); + (Self::Legacy(tx), buf) + } + ScrollTxType::Eip2930 => { + let (tx, buf) = TxEip2930::from_compact(buf, buf.len()); + let tx = Signed::new_unhashed(tx, signature); + (Self::Eip2930(tx), buf) + } + ScrollTxType::Eip1559 => { + let (tx, buf) = TxEip1559::from_compact(buf, buf.len()); + let tx = Signed::new_unhashed(tx, signature); + (Self::Eip1559(tx), buf) + } + ScrollTxType::Eip7702 => { + let (tx, buf) = TxEip7702::from_compact(buf, buf.len()); + let tx = Signed::new_unhashed(tx, signature); + (Self::Eip7702(tx), buf) + } + ScrollTxType::L1Message => { + let (tx, buf) = TxL1Message::from_compact(buf, buf.len()); + let tx = Sealed::new(tx); + (Self::L1Message(tx), buf) + } + } + } +} + +#[cfg(feature = "reth-codec")] +const L1_MESSAGE_SIGNATURE: Signature = Signature::new(U256::ZERO, U256::ZERO, false); + +#[cfg(feature = "reth-codec")] +impl Envelope for ScrollTxEnvelope { + fn signature(&self) -> &Signature { + match self { + Self::Legacy(tx) => tx.signature(), + Self::Eip2930(tx) => tx.signature(), + Self::Eip1559(tx) => tx.signature(), + Self::Eip7702(tx) => tx.signature(), + Self::L1Message(_) => &L1_MESSAGE_SIGNATURE, + } + } + + fn tx_type(&self) -> Self::TxType { + Self::tx_type(self) + } +} + +#[cfg(feature = "reth-codec")] +impl Compact for ScrollTxEnvelope { + fn to_compact<B>(&self, buf: &mut B) -> usize + where + B: BufMut + AsMut<[u8]>, + { + CompactEnvelope::to_compact(self, buf) + } + + fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8]) { + CompactEnvelope::from_compact(buf, len) + } +} + +impl Encodable for ScrollTxEnvelope { + fn encode(&self, out: &mut dyn alloy_rlp::BufMut) { + self.network_encode(out) + } + + fn length(&self) -> usize { + self.network_len() + } +} + +impl Decodable for ScrollTxEnvelope { + fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> { + Ok(Self::network_decode(buf)?) + } +} + +impl Decodable2718 for ScrollTxEnvelope { + fn typed_decode(ty: u8, buf: &mut &[u8]) -> Eip2718Result<Self> { + match ty.try_into().map_err(|_| Eip2718Error::UnexpectedType(ty))? { + ScrollTxType::Eip2930 => Ok(Self::Eip2930(TxEip2930::rlp_decode_signed(buf)?)), + ScrollTxType::Eip1559 => Ok(Self::Eip1559(TxEip1559::rlp_decode_signed(buf)?)), + ScrollTxType::Eip7702 => Ok(Self::Eip7702(TxEip7702::rlp_decode_signed(buf)?)), + ScrollTxType::L1Message => Ok(Self::L1Message(TxL1Message::decode(buf)?.seal_slow())), + ScrollTxType::Legacy => { + Err(alloy_rlp::Error::Custom("type-0 eip2718 transactions are not supported") + .into()) + } + } + } + + fn fallback_decode(buf: &mut &[u8]) -> Eip2718Result<Self> { + Ok(Self::Legacy(TxLegacy::rlp_decode_signed(buf)?)) + } +} + +impl Encodable2718 for ScrollTxEnvelope { + fn type_flag(&self) -> Option<u8> { + match self { + Self::Legacy(_) => None, + Self::Eip2930(_) => Some(ScrollTxType::Eip2930 as u8), + Self::Eip1559(_) => Some(ScrollTxType::Eip1559 as u8), + Self::Eip7702(_) => Some(ScrollTxType::Eip7702 as u8), + Self::L1Message(_) => Some(ScrollTxType::L1Message as u8), + } + } + + fn encode_2718_len(&self) -> usize { + self.eip2718_encoded_length() + } + + fn encode_2718(&self, out: &mut dyn alloy_rlp::BufMut) { + match self { + // Legacy transactions have no difference between network and 2718 + Self::Legacy(tx) => tx.eip2718_encode(out), + Self::Eip2930(tx) => { + tx.eip2718_encode(out); + } + Self::Eip1559(tx) => { + tx.eip2718_encode(out); + } + Self::Eip7702(tx) => { + tx.eip2718_encode(out); + } + Self::L1Message(tx) => { + tx.eip2718_encode(out); + } + } + } + + fn trie_hash(&self) -> B256 { + match self { + Self::Legacy(tx) => *tx.hash(), + Self::Eip2930(tx) => *tx.hash(), + Self::Eip1559(tx) => *tx.hash(), + Self::Eip7702(tx) => *tx.hash(), + Self::L1Message(tx) => tx.seal(), + } + } +} + +#[cfg(feature = "k256")] +impl alloy_consensus::transaction::SignerRecoverable for ScrollTxEnvelope { + fn recover_signer(&self) -> Result<Address, alloy_consensus::crypto::RecoveryError> { + let signature_hash = match self { + Self::Legacy(tx) => tx.signature_hash(), + Self::Eip2930(tx) => tx.signature_hash(), + Self::Eip1559(tx) => tx.signature_hash(), + Self::Eip7702(tx) => tx.signature_hash(), + Self::L1Message(tx) => return Ok(tx.sender), + }; + let signature = self.signature().expect("handled L1 message in previous match"); + alloy_consensus::crypto::secp256k1::recover_signer(&signature, signature_hash) + } + + fn recover_signer_unchecked(&self) -> Result<Address, alloy_consensus::crypto::RecoveryError> { + let signature_hash = match self { + Self::Legacy(tx) => tx.signature_hash(), + Self::Eip2930(tx) => tx.signature_hash(), + Self::Eip1559(tx) => tx.signature_hash(), + Self::Eip7702(tx) => tx.signature_hash(), + Self::L1Message(tx) => return Ok(tx.sender), + }; + let signature = self.signature().expect("handled L1 message in previous match"); + alloy_consensus::crypto::secp256k1::recover_signer_unchecked(&signature, signature_hash) + } +} + +#[cfg(feature = "serde")] +mod serde_from { + //! NB: Why do we need this? + //! + //! Because the tag may be missing, we need an abstraction over tagged (with + //! type) and untagged (always legacy). This is [`MaybeTaggedTxEnvelope`]. + //! + //! The tagged variant is [`TaggedTxEnvelope`], which always has a type tag. + //! + //! We serialize via [`TaggedTxEnvelope`] and deserialize via + //! [`MaybeTaggedTxEnvelope`]. + use super::*; + + #[derive(Debug, serde::Deserialize)] + #[serde(untagged)] + pub(crate) enum MaybeTaggedTxEnvelope { + Tagged(TaggedTxEnvelope), + #[serde(with = "alloy_consensus::transaction::signed_legacy_serde")] + Untagged(Signed<TxLegacy>), + } + + #[derive(Debug, serde::Serialize, serde::Deserialize)] + #[serde(tag = "type")] + pub(crate) enum TaggedTxEnvelope { + #[serde( + rename = "0x0", + alias = "0x00", + with = "alloy_consensus::transaction::signed_legacy_serde" + )] + Legacy(Signed<TxLegacy>), + #[serde(rename = "0x1", alias = "0x01")] + Eip2930(Signed<TxEip2930>), + #[serde(rename = "0x2", alias = "0x02")] + Eip1559(Signed<TxEip1559>), + #[serde(rename = "0x4", alias = "0x04")] + Eip7702(Signed<TxEip7702>), + #[serde( + rename = "0x7e", + alias = "0x7E", + serialize_with = "crate::serde_l1_message_tx_rpc" + )] + L1Message(Sealed<TxL1Message>), + } + + impl From<MaybeTaggedTxEnvelope> for ScrollTxEnvelope { + fn from(value: MaybeTaggedTxEnvelope) -> Self { + match value { + MaybeTaggedTxEnvelope::Tagged(tagged) => tagged.into(), + MaybeTaggedTxEnvelope::Untagged(tx) => Self::Legacy(tx), + } + } + } + + impl From<TaggedTxEnvelope> for ScrollTxEnvelope { + fn from(value: TaggedTxEnvelope) -> Self { + match value { + TaggedTxEnvelope::Legacy(signed) => Self::Legacy(signed), + TaggedTxEnvelope::Eip2930(signed) => Self::Eip2930(signed), + TaggedTxEnvelope::Eip1559(signed) => Self::Eip1559(signed), + TaggedTxEnvelope::Eip7702(signed) => Self::Eip7702(signed), + TaggedTxEnvelope::L1Message(tx) => Self::L1Message(tx), + } + } + } + + impl From<ScrollTxEnvelope> for TaggedTxEnvelope { + fn from(value: ScrollTxEnvelope) -> Self { + match value { + ScrollTxEnvelope::Legacy(signed) => Self::Legacy(signed), + ScrollTxEnvelope::Eip2930(signed) => Self::Eip2930(signed), + ScrollTxEnvelope::Eip1559(signed) => Self::Eip1559(signed), + ScrollTxEnvelope::Eip7702(signed) => Self::Eip7702(signed), + ScrollTxEnvelope::L1Message(tx) => Self::L1Message(tx), + } + } + } +} + +/// Bincode-compatible serde implementation for `ScrollTxEnvelope`. +#[cfg(all(feature = "serde", feature = "serde-bincode-compat"))] +pub(super) mod serde_bincode_compat { + use crate::TxL1Message; + + use alloy_consensus::{ + transaction::serde_bincode_compat::{TxEip1559, TxEip2930, TxEip7702, TxLegacy}, + Sealed, Signed, + }; + use alloy_primitives::{Signature, B256}; + use serde::{Deserialize, Deserializer, Serialize, Serializer}; + use serde_with::{DeserializeAs, SerializeAs}; + + /// Bincode-compatible representation of an `ScrollTxEnvelope`. + #[derive(Debug, Serialize, Deserialize)] + pub enum ScrollTxEnvelope<'a> { + /// Legacy variant. + Legacy { + /// Transaction signature. + signature: Signature, + /// Borrowed legacy transaction data. + transaction: TxLegacy<'a>, + }, + /// EIP-2930 variant. + Eip2930 { + /// Transaction signature. + signature: Signature, + /// Borrowed EIP-2930 transaction data. + transaction: TxEip2930<'a>, + }, + /// EIP-1559 variant. + Eip1559 { + /// Transaction signature. + signature: Signature, + /// Borrowed EIP-1559 transaction data. + transaction: TxEip1559<'a>, + }, + /// EIP-7702 variant. + Eip7702 { + /// Transaction signature. + signature: Signature, + /// Borrowed EIP-7702 transaction data. + transaction: TxEip7702<'a>, + }, + /// L1 message variant. + TxL1Message { + /// Precomputed hash. + hash: B256, + /// Borrowed deposit transaction data. + transaction: TxL1Message, + }, + } + + impl<'a> From<&'a super::ScrollTxEnvelope> for ScrollTxEnvelope<'a> { + fn from(value: &'a super::ScrollTxEnvelope) -> Self { + match value { + super::ScrollTxEnvelope::Legacy(signed_legacy) => Self::Legacy { + signature: *signed_legacy.signature(), + transaction: signed_legacy.tx().into(), + }, + super::ScrollTxEnvelope::Eip2930(signed_2930) => Self::Eip2930 { + signature: *signed_2930.signature(), + transaction: signed_2930.tx().into(), + }, + super::ScrollTxEnvelope::Eip1559(signed_1559) => Self::Eip1559 { + signature: *signed_1559.signature(), + transaction: signed_1559.tx().into(), + }, + super::ScrollTxEnvelope::Eip7702(signed_7702) => Self::Eip7702 { + signature: *signed_7702.signature(), + transaction: signed_7702.tx().into(), + }, + super::ScrollTxEnvelope::L1Message(sealed_l1_message) => Self::TxL1Message { + hash: sealed_l1_message.seal(), + transaction: sealed_l1_message.inner().clone(), + }, + } + } + } + + impl<'a> From<ScrollTxEnvelope<'a>> for super::ScrollTxEnvelope { + fn from(value: ScrollTxEnvelope<'a>) -> Self { + match value { + ScrollTxEnvelope::Legacy { signature, transaction } => { + Self::Legacy(Signed::new_unhashed(transaction.into(), signature)) + } + ScrollTxEnvelope::Eip2930 { signature, transaction } => { + Self::Eip2930(Signed::new_unhashed(transaction.into(), signature)) + } + ScrollTxEnvelope::Eip1559 { signature, transaction } => { + Self::Eip1559(Signed::new_unhashed(transaction.into(), signature)) + } + ScrollTxEnvelope::Eip7702 { signature, transaction } => { + Self::Eip7702(Signed::new_unhashed(transaction.into(), signature)) + } + ScrollTxEnvelope::TxL1Message { hash, transaction } => { + Self::L1Message(Sealed::new_unchecked(transaction, hash)) + } + } + } + } + + impl SerializeAs<super::ScrollTxEnvelope> for ScrollTxEnvelope<'_> { + fn serialize_as<S>( + source: &super::ScrollTxEnvelope, + serializer: S, + ) -> Result<S::Ok, S::Error> + where + S: Serializer, + { + let borrowed = ScrollTxEnvelope::from(source); + borrowed.serialize(serializer) + } + } + + impl<'de> DeserializeAs<'de, super::ScrollTxEnvelope> for ScrollTxEnvelope<'de> { + fn deserialize_as<D>(deserializer: D) -> Result<super::ScrollTxEnvelope, D::Error> + where + D: Deserializer<'de>, + { + let borrowed = ScrollTxEnvelope::deserialize(deserializer)?; + Ok(borrowed.into()) + } + } + + #[cfg(test)] + mod tests { + use super::*; + use arbitrary::Arbitrary; + use rand::Rng; + use serde::{Deserialize, Serialize}; + use serde_with::serde_as; + + /// Tests a bincode round-trip for `ScrollTxEnvelope` using an arbitrary instance. + #[test] + fn test_scroll_tx_envelope_bincode_roundtrip_arbitrary() { + #[serde_as] + #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] + struct Data { + // Use the bincode-compatible representation defined in this module. + #[serde_as(as = "ScrollTxEnvelope<'_>")] + envelope: super::super::ScrollTxEnvelope, + } + + let mut bytes = [0u8; 1024]; + rand::rng().fill(bytes.as_mut_slice()); + let data = Data { + envelope: super::super::ScrollTxEnvelope::arbitrary( + &mut arbitrary::Unstructured::new(&bytes), + ) + .unwrap(), + }; + + let encoded = bincode::serialize(&data).unwrap(); + let decoded: Data = bincode::deserialize(&encoded).unwrap(); + assert_eq!(decoded, data); + } + } +} + +#[cfg(test)] +mod tests { + extern crate alloc; + use super::*; + use alloc::vec; + use alloy_primitives::{hex, Address, Bytes, U256}; + + #[test] + fn test_tx_gas_limit() { + let tx = TxL1Message { gas_limit: 1, ..Default::default() }; + let tx_envelope = ScrollTxEnvelope::L1Message(tx.seal_slow()); + assert_eq!(tx_envelope.gas_limit(), 1); + } + + #[test] + fn test_encode_decode_l1_message() { + let tx = TxL1Message { + queue_index: 1, + gas_limit: 2, + to: Address::left_padding_from(&[3]), + sender: Address::left_padding_from(&[4]), + value: U256::from(4_u64), + input: Bytes::from(vec![5]), + }; + let tx_envelope = ScrollTxEnvelope::L1Message(tx.seal_slow()); + let encoded = tx_envelope.encoded_2718(); + let decoded = ScrollTxEnvelope::decode_2718(&mut encoded.as_ref()).unwrap(); + assert_eq!(encoded.len(), tx_envelope.encode_2718_len()); + assert_eq!(decoded, tx_envelope); + } + + #[test] + #[cfg(feature = "serde")] + fn test_serde_roundtrip_l1_message() { + let tx = TxL1Message { + queue_index: 11, + gas_limit: u64::MAX, + sender: Address::random(), + to: Address::random(), + value: U256::MAX, + input: Bytes::new(), + }; + let tx_envelope = ScrollTxEnvelope::L1Message(tx.seal_slow()); + + let serialized = serde_json::to_string(&tx_envelope).unwrap(); + let deserialized: ScrollTxEnvelope = serde_json::from_str(&serialized).unwrap(); + + assert_eq!(tx_envelope, deserialized); + } + + #[test] + fn eip2718_l1_message_decode() { + // <https://scrollscan.com/tx/0xace7103cc22a372c81cda04e15442a721cd3d5d64eda2e1578ba310d91597d97> + let b = hex!("7ef9015a830e7991831e848094781e90f1c8fc4611c9b7497c3b47f99ef6969cbc80b901248ef1332e000000000000000000000000c186fa914353c44b2e33ebe05f21846f1048beda0000000000000000000000003bad7ad0728f9917d1bf08af5782dcbd516cdd96000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e799100000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000044493a4f8411b3f3d662006b9bf68884e71f1fc0f8ea04e4cb188354738202c3e34a473b93000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000947885bcbd5cecef1336b5300fb5186a12ddd8c478"); + + let tx = ScrollTxEnvelope::decode_2718(&mut b[..].as_ref()).unwrap(); + tx.as_l1_message().unwrap(); + } + + #[test] + fn eip1559_decode() { + use alloy_consensus::SignableTransaction; + use alloy_primitives::Signature; + let tx = TxEip1559 { + chain_id: 1u64, + nonce: 2, + max_fee_per_gas: 3, + max_priority_fee_per_gas: 4, + gas_limit: 5, + to: Address::left_padding_from(&[6]).into(), + value: U256::from(7_u64), + input: vec![8].into(), + access_list: Default::default(), + }; + let sig = Signature::test_signature(); + let tx_signed = tx.into_signed(sig); + let envelope: ScrollTxEnvelope = tx_signed.into(); + let encoded = envelope.encoded_2718(); + let mut slice = encoded.as_slice(); + let decoded = ScrollTxEnvelope::decode_2718(&mut slice).unwrap(); + assert!(matches!(decoded, ScrollTxEnvelope::Eip1559(_))); + } +}
diff --git reth/crates/scroll/alloy/consensus/src/transaction/l1_message.rs scroll-reth/crates/scroll/alloy/consensus/src/transaction/l1_message.rs new file mode 100644 index 0000000000000000000000000000000000000000..0c200904fbc2c86af0eec9940762d31869dd44f8 --- /dev/null +++ scroll-reth/crates/scroll/alloy/consensus/src/transaction/l1_message.rs @@ -0,0 +1,455 @@ +//! Scroll L1 message transaction + +use crate::ScrollTxType; +use std::vec::Vec; + +use alloy_consensus::{Sealable, Transaction, Typed2718}; +use alloy_eips::eip2718::{Decodable2718, Eip2718Error, Eip2718Result, Encodable2718}; +use alloy_primitives::{ + keccak256, + private::alloy_rlp::{Encodable, Header}, + Address, Bytes, ChainId, Signature, TxHash, TxKind, B256, U256, +}; +use alloy_rlp::Decodable; +#[cfg(any(test, feature = "reth-codec"))] +use {reth_codecs::Compact, reth_codecs_derive::add_arbitrary_tests}; + +/// L1 message transaction type id, 0x7e in hex. +pub const L1_MESSAGE_TRANSACTION_TYPE: u8 = 126; + +/// A message transaction sent from the settlement layer to the L2 for execution. +/// +/// The signature of the L1 message is already verified on the L1 and as such doesn't contain +/// a signature field. Gas for the transaction execution on Scroll is already paid for on the L1. +/// +/// # Bincode compatibility +/// +/// `bincode` crate doesn't work with optionally serializable serde fields and some of the execution +/// types require optional serialization for RPC compatibility. Since `TxL1Message` doesn't +/// contain optionally serializable fields, no `bincode` compatible bridge implementation is +/// required. +#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] +#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(any(test, feature = "serde"), serde(rename_all = "camelCase"))] +#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))] +#[cfg_attr(any(test, feature = "reth-codec"), derive(Compact))] +#[cfg_attr(any(test, feature = "reth-codec"), add_arbitrary_tests(compact, rlp))] +pub struct TxL1Message { + /// The queue index of the message in the L1 contract queue. + #[cfg_attr(feature = "serde", serde(with = "alloy_serde::quantity"))] + pub queue_index: u64, + /// The gas limit for the transaction. Gas is paid for when message is sent from the L1. + #[cfg_attr(feature = "serde", serde(with = "alloy_serde::quantity", rename = "gas"))] + pub gas_limit: u64, + /// The destination for the transaction. `Address` is used in place of `TxKind` since contract + /// creations aren't allowed via L1 message transactions. + pub to: Address, + /// The value sent. + pub value: U256, + /// The L1 sender of the transaction. + pub sender: Address, + /// The input of the transaction. + pub input: Bytes, +} + +impl TxL1Message { + /// Returns an empty signature for the [`TxL1Message`], which don't include a signature. + pub const fn signature() -> Signature { + Signature::new(U256::ZERO, U256::ZERO, false) + } + + /// Decodes the inner [`TxL1Message`] fields from RLP bytes. + /// + /// NOTE: This assumes a RLP header has already been decoded, and _just_ decodes the following + /// RLP fields in the following order: + /// + /// - `queue_index` + /// - `gas_limit` + /// - `to` + /// - `value` + /// - `input` + /// - `sender` + pub fn rlp_decode_fields(buf: &mut &[u8]) -> alloy_rlp::Result<Self> { + Ok(Self { + queue_index: Decodable::decode(buf)?, + gas_limit: Decodable::decode(buf)?, + to: Decodable::decode(buf)?, + value: Decodable::decode(buf)?, + input: Decodable::decode(buf)?, + sender: Decodable::decode(buf)?, + }) + } + + /// Decodes the transaction from RLP bytes. + pub fn rlp_decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> { + let header = Header::decode(buf)?; + if !header.list { + return Err(alloy_rlp::Error::UnexpectedString); + } + let remaining = buf.len(); + + let this = Self::rlp_decode_fields(buf)?; + + if buf.len() + header.payload_length != remaining { + return Err(alloy_rlp::Error::ListLengthMismatch { + expected: header.payload_length, + got: remaining - buf.len(), + }); + } + + Ok(this) + } + + /// Outputs the length of the transaction's fields, without a RLP header. + pub fn rlp_encoded_fields_length(&self) -> usize { + self.queue_index.length() + + self.gas_limit.length() + + self.to.length() + + self.value.length() + + self.input.0.length() + + self.sender.length() + } + + /// Encode the fields of the transaction without a RLP header. + /// <https://github.com/scroll-tech/go-ethereum/blob/9fff27e4f34fb5097100ed76ee725ce056267f4b/core/types/l1_message_tx.go#L12-L19> + pub fn rlp_encode_fields(&self, out: &mut dyn alloy_rlp::BufMut) { + self.queue_index.encode(out); + self.gas_limit.encode(out); + self.to.encode(out); + self.value.encode(out); + self.input.encode(out); + self.sender.encode(out); + } + + pub(crate) const fn tx_type(&self) -> u8 { + L1_MESSAGE_TRANSACTION_TYPE + } + + /// Create a RLP header for the transaction. + fn rlp_header(&self) -> Header { + Header { list: true, payload_length: self.rlp_encoded_fields_length() } + } + + /// RLP encodes the transaction. + pub fn rlp_encode(&self, out: &mut dyn alloy_rlp::BufMut) { + self.rlp_header().encode(out); + self.rlp_encode_fields(out); + } + + /// Get the length of the transaction when RLP encoded. + pub fn rlp_encoded_length(&self) -> usize { + self.rlp_header().length_with_payload() + } + + /// Get the length of the transaction when EIP-2718 encoded. This is the + /// 1 byte type flag + the length of the RLP encoded transaction. + pub fn eip2718_encoded_length(&self) -> usize { + self.rlp_encoded_length() + 1 + } + + /// EIP-2718 encode the transaction. + pub fn eip2718_encode(&self, out: &mut dyn alloy_rlp::BufMut) { + out.put_u8(L1_MESSAGE_TRANSACTION_TYPE); + self.rlp_encode(out) + } + + /// Calculates the in-memory size of the [`TxL1Message`] transaction. + #[inline] + pub fn size(&self) -> usize { + size_of::<u64>() + // queue_index + size_of::<u64>() + // gas_limit + size_of::<Address>() + // to + size_of::<U256>() + // value + self.input.len() + // input + size_of::<Address>() // sender + } + + /// Calculates the hash of the [`TxL1Message`] transaction. + pub fn tx_hash(&self) -> TxHash { + let mut buf = Vec::with_capacity(self.eip2718_encoded_length()); + self.eip2718_encode(&mut buf); + keccak256(&buf) + } +} + +impl Typed2718 for TxL1Message { + fn ty(&self) -> u8 { + ScrollTxType::L1Message as u8 + } +} + +impl Encodable2718 for TxL1Message { + fn type_flag(&self) -> Option<u8> { + Some(self.tx_type()) + } + + fn encode_2718_len(&self) -> usize { + self.eip2718_encoded_length() + } + + fn encode_2718(&self, out: &mut dyn alloy_rlp::BufMut) { + out.put_u8(self.tx_type()); + self.rlp_encode(out); + } +} + +impl Decodable2718 for TxL1Message { + fn typed_decode(ty: u8, buf: &mut &[u8]) -> Eip2718Result<Self> { + if ty != L1_MESSAGE_TRANSACTION_TYPE { + return Err(Eip2718Error::UnexpectedType(ty)); + } + let tx = Self::rlp_decode(buf)?; + Ok(tx) + } + + fn fallback_decode(buf: &mut &[u8]) -> Eip2718Result<Self> { + let tx = Self::decode(buf)?; + Ok(tx) + } +} + +impl Encodable for TxL1Message { + fn encode(&self, out: &mut dyn alloy_rlp::BufMut) { + self.rlp_encode(out) + } + + fn length(&self) -> usize { + self.rlp_encoded_length() + } +} + +impl Decodable for TxL1Message { + fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> { + Self::rlp_decode(buf) + } +} + +impl Transaction for TxL1Message { + fn chain_id(&self) -> Option<ChainId> { + None + } + + fn nonce(&self) -> u64 { + 0u64 + } + + fn gas_limit(&self) -> u64 { + self.gas_limit + } + + fn gas_price(&self) -> Option<u128> { + None + } + + fn max_fee_per_gas(&self) -> u128 { + 0 + } + + fn max_priority_fee_per_gas(&self) -> Option<u128> { + None + } + + fn max_fee_per_blob_gas(&self) -> Option<u128> { + None + } + + fn priority_fee_or_price(&self) -> u128 { + 0 + } + + fn effective_gas_price(&self, _base_fee: Option<u64>) -> u128 { + 0 + } + + fn is_dynamic_fee(&self) -> bool { + false + } + + fn kind(&self) -> TxKind { + TxKind::Call(self.to) + } + + fn is_create(&self) -> bool { + false + } + + fn value(&self) -> U256 { + self.value + } + + fn input(&self) -> &Bytes { + &self.input + } + + fn access_list(&self) -> Option<&alloy_eips::eip2930::AccessList> { + None + } + + fn blob_versioned_hashes(&self) -> Option<&[B256]> { + None + } + + fn authorization_list(&self) -> Option<&[alloy_eips::eip7702::SignedAuthorization]> { + None + } +} + +impl Sealable for TxL1Message { + fn hash_slow(&self) -> B256 { + self.tx_hash() + } +} + +/// Scroll specific transaction fields +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(any(test, feature = "serde"), serde(rename_all = "camelCase"))] +pub struct ScrollL1MessageTransactionFields { + /// The index of the transaction in the message queue. + #[cfg_attr(any(test, feature = "serde"), serde(with = "alloy_serde::quantity"))] + pub queue_index: u64, + /// The sender of the transaction on the L1. + pub sender: Address, +} + +/// L1 message transactions don't have a signature, however, we include an empty signature in the +/// response for better compatibility. +/// +/// This function can be used as `serialize_with` serde attribute for the [`TxL1Message`] and will +/// flatten [`TxL1Message::signature`] into response. +/// +/// <https://github.com/scroll-tech/go-ethereum/blob/develop/core/types/l1_message_tx.go#L51>. +#[cfg(feature = "serde")] +pub fn serde_l1_message_tx_rpc<T: serde::Serialize, S: serde::Serializer>( + value: &T, + serializer: S, +) -> Result<S::Ok, S::Error> { + use serde::Serialize; + + #[derive(Serialize)] + struct SerdeHelper<'a, T> { + #[serde(flatten)] + value: &'a T, + #[serde(flatten)] + signature: Signature, + } + + SerdeHelper { value, signature: TxL1Message::signature() }.serialize(serializer) +} + +#[cfg(test)] +mod tests { + use super::*; + use alloy_eips::eip2718::Encodable2718; + use alloy_primitives::{address, bytes, hex, Bytes, U256}; + use alloy_rlp::BytesMut; + use arbitrary::Arbitrary; + use rand::Rng; + use reth_codecs::{test_utils::UnusedBits, validate_bitflag_backwards_compat}; + + #[test] + fn test_rlp_roundtrip() { + // <https://scrollscan.com/tx/0xace7103cc22a372c81cda04e15442a721cd3d5d64eda2e1578ba310d91597d97> + let bytes = Bytes::from_static(&hex!("7ef9015a830e7991831e848094781e90f1c8fc4611c9b7497c3b47f99ef6969cbc80b901248ef1332e000000000000000000000000c186fa914353c44b2e33ebe05f21846f1048beda0000000000000000000000003bad7ad0728f9917d1bf08af5782dcbd516cdd96000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e799100000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000044493a4f8411b3f3d662006b9bf68884e71f1fc0f8ea04e4cb188354738202c3e34a473b93000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000947885bcbd5cecef1336b5300fb5186a12ddd8c478")); + let tx_a = TxL1Message::decode(&mut bytes[1..].as_ref()).unwrap(); + let mut buf_a = BytesMut::default(); + tx_a.encode(&mut buf_a); + assert_eq!(&buf_a[..], &bytes[1..]); + } + + #[test] + fn test_encode_decode_fields() { + let original = TxL1Message { + queue_index: 100, + gas_limit: 0, + to: Address::default(), + value: U256::default(), + sender: Address::default(), + input: Bytes::default(), + }; + let mut buffer = BytesMut::new(); + original.rlp_encode_fields(&mut buffer); + let decoded = TxL1Message::rlp_decode_fields(&mut &buffer[..]).expect("Failed to decode"); + + assert_eq!(original, decoded); + } + + #[test] + fn test_encode_with_and_without_header() { + let tx_deposit = TxL1Message { + queue_index: 0, + gas_limit: 50000, + to: Address::default(), + value: U256::default(), + sender: Address::default(), + input: Bytes::default(), + }; + + let mut buffer_with_header = BytesMut::new(); + tx_deposit.encode(&mut buffer_with_header); + + let mut buffer_without_header = BytesMut::new(); + tx_deposit.rlp_encode_fields(&mut buffer_without_header); + + assert!(buffer_with_header.len() > buffer_without_header.len()); + } + + #[test] + fn test_payload_length() { + let tx_deposit = TxL1Message { + queue_index: 0, + gas_limit: 50000, + to: Address::default(), + value: U256::default(), + sender: Address::default(), + input: Bytes::default(), + }; + + assert!(tx_deposit.size() > tx_deposit.rlp_encoded_fields_length()); + } + + #[test] + fn test_deserialize_hex_to_u64() { + let rpc_tx = r#"{"gas":"0x1e8480","input":"0x8ef1332e000000000000000000000000c186fa914353c44b2e33ebe05f21846f1048beda0000000000000000000000003bad7ad0728f9917d1bf08af5782dcbd516cdd96000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e7ba000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000044493a4f846ffc1507cbfe98a2b0ba1f06ea7e4eb749c001f78f6cb5540daa556a0566322a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","to":"0x781e90f1c8fc4611c9b7497c3b47f99ef6969cbc","value":"0x0","sender":"0x7885bcbd5cecef1336b5300fb5186a12ddd8c478","queueIndex":"0xe7ba0"}"#; + // let obj: TxL1Message = serde_json::from_str(rpc_tx).unwrap(); + let obj = serde_json::from_str::<TxL1Message>(rpc_tx).unwrap(); + assert_eq!(obj.queue_index, 0xe7ba0); + } + + #[test] + fn test_bincode_roundtrip() { + let mut bytes = [0u8; 1024]; + rand::rng().fill(bytes.as_mut_slice()); + let tx = TxL1Message::arbitrary(&mut arbitrary::Unstructured::new(&bytes)).unwrap(); + + let encoded = bincode::serialize(&tx).unwrap(); + let decoded: TxL1Message = bincode::deserialize(&encoded).unwrap(); + assert_eq!(decoded, tx); + } + + #[test] + fn test_eip2718_encode() { + let tx = + TxL1Message { + queue_index: 947883, + gas_limit: 2000000, + to: address!("781e90f1c8fc4611c9b7497c3b47f99ef6969cbc"), + value: U256::ZERO, + sender: address!("7885bcbd5cecef1336b5300fb5186a12ddd8c478"), + input: bytes!("8ef1332e000000000000000000000000c186fa914353c44b2e33ebe05f21846f1048beda0000000000000000000000003bad7ad0728f9917d1bf08af5782dcbd516cdd96000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e76ab00000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000044493a4f84f464e58d4bfa93bcc57abfb14dbe1b8ff46cd132b5709aab227f269727943d2f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + } + ; + let bytes = Bytes::from_static(&hex!("7ef9015a830e76ab831e848094781e90f1c8fc4611c9b7497c3b47f99ef6969cbc80b901248ef1332e000000000000000000000000c186fa914353c44b2e33ebe05f21846f1048beda0000000000000000000000003bad7ad0728f9917d1bf08af5782dcbd516cdd96000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e76ab00000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000044493a4f84f464e58d4bfa93bcc57abfb14dbe1b8ff46cd132b5709aab227f269727943d2f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000947885bcbd5cecef1336b5300fb5186a12ddd8c478")); + + let mut encoded = BytesMut::default(); + tx.encode_2718(&mut encoded); + + assert_eq!(encoded, bytes.as_ref()) + } + + #[test] + fn test_compaction_backwards_compatibility() { + assert_eq!(TxL1Message::bitflag_encoded_bytes(), 2); + validate_bitflag_backwards_compat!(TxL1Message, UnusedBits::NotZero); + } +}
diff --git reth/crates/scroll/alloy/consensus/src/transaction/meta.rs scroll-reth/crates/scroll/alloy/consensus/src/transaction/meta.rs new file mode 100644 index 0000000000000000000000000000000000000000..d1ffc5f5acd663a9c042645dbb4d6e576edebfb2 --- /dev/null +++ scroll-reth/crates/scroll/alloy/consensus/src/transaction/meta.rs @@ -0,0 +1,28 @@ +use alloy_consensus::transaction::TransactionInfo; +use alloy_primitives::U256; + +/// Additional receipt metadata required for Scroll transactions. +/// +/// These fields are used to provide additional context for in RPC responses. +#[derive(Debug, Clone, Copy, Default, Eq, PartialEq)] +pub struct ScrollAdditionalInfo { + /// Only present in RPC responses. + pub l1_fee: U256, +} + +/// Additional fields in the context of a block that contains this transaction. +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +pub struct ScrollTransactionInfo { + /// Additional transaction information. + pub inner: TransactionInfo, + /// Additional metadata for Scroll. + pub additional_info: ScrollAdditionalInfo, +} + +impl ScrollTransactionInfo { + /// Creates a new [`ScrollTransactionInfo`] with the given [`TransactionInfo`] and + /// [`ScrollAdditionalInfo`]. + pub const fn new(inner: TransactionInfo, additional_info: ScrollAdditionalInfo) -> Self { + Self { inner, additional_info } + } +}
diff --git reth/crates/scroll/alloy/consensus/src/transaction/mod.rs scroll-reth/crates/scroll/alloy/consensus/src/transaction/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..c43aab42897510c7ebc3e78f704cbd3f0870dda7 --- /dev/null +++ scroll-reth/crates/scroll/alloy/consensus/src/transaction/mod.rs @@ -0,0 +1,28 @@ +//! Transaction types for Scroll. + +mod tx_type; +pub use tx_type::{ScrollTxType, L1_MESSAGE_TX_TYPE_ID}; + +mod envelope; +pub use envelope::ScrollTxEnvelope; + +mod l1_message; +pub use l1_message::{ScrollL1MessageTransactionFields, TxL1Message, L1_MESSAGE_TRANSACTION_TYPE}; + +mod meta; +pub use meta::{ScrollAdditionalInfo, ScrollTransactionInfo}; + +mod typed; +pub use typed::ScrollTypedTransaction; + +mod pooled; +pub use pooled::ScrollPooledTransaction; + +#[cfg(feature = "serde")] +pub use l1_message::serde_l1_message_tx_rpc; + +/// Bincode-compatible serde implementations for transaction types. +#[cfg(all(feature = "serde", feature = "serde-bincode-compat"))] +pub(super) mod serde_bincode_compat { + pub use super::envelope::serde_bincode_compat::*; +}
diff --git reth/crates/scroll/alloy/consensus/src/transaction/pooled.rs scroll-reth/crates/scroll/alloy/consensus/src/transaction/pooled.rs new file mode 100644 index 0000000000000000000000000000000000000000..f59dca54ee2e909e6b1d436ccbc33ce740a3b3f2 --- /dev/null +++ scroll-reth/crates/scroll/alloy/consensus/src/transaction/pooled.rs @@ -0,0 +1,552 @@ +//! Defines the exact transaction variants that are allowed to be propagated over the eth p2p +//! protocol in op. + +use crate::{ScrollTxEnvelope, ScrollTxType}; +use alloy_consensus::{ + error::ValueError, + transaction::{RlpEcdsaDecodableTx, TxEip1559, TxEip2930, TxLegacy}, + SignableTransaction, Signed, Transaction, TxEip7702, TxEnvelope, Typed2718, +}; +use alloy_eips::{ + eip2718::{Decodable2718, Eip2718Error, Eip2718Result, Encodable2718}, + eip2930::AccessList, + eip7702::SignedAuthorization, +}; +use alloy_primitives::{bytes, Bytes, ChainId, Signature, TxHash, TxKind, B256, U256}; +use alloy_rlp::{Decodable, Encodable, Header}; +use core::hash::{Hash, Hasher}; + +/// All possible transactions that can be included in a response to `GetPooledTransactions`. +/// A response to `GetPooledTransactions`. This can include a typed signed transaction, but cannot +/// include a deposit transaction or EIP-4844 transaction. +/// +/// The difference between this and the [`ScrollTxEnvelope`] is that this type does not have the +/// L1 message variant, which is not expected to be pooled. +#[derive(Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(all(any(test, feature = "arbitrary"), feature = "k256"), derive(arbitrary::Arbitrary))] +pub enum ScrollPooledTransaction { + /// An untagged [`TxLegacy`]. + Legacy(Signed<TxLegacy>), + /// A [`TxEip2930`] transaction tagged with type 1. + Eip2930(Signed<TxEip2930>), + /// A [`TxEip1559`] transaction tagged with type 2. + Eip1559(Signed<TxEip1559>), + /// A [`TxEip7702`] transaction tagged with type 4. + Eip7702(Signed<TxEip7702>), +} + +impl ScrollPooledTransaction { + /// Heavy operation that returns the signature hash over rlp encoded transaction. It is only + /// for signature signing or signer recovery. + pub fn signature_hash(&self) -> B256 { + match self { + Self::Legacy(tx) => tx.signature_hash(), + Self::Eip2930(tx) => tx.signature_hash(), + Self::Eip1559(tx) => tx.signature_hash(), + Self::Eip7702(tx) => tx.signature_hash(), + } + } + + /// Reference to transaction hash. Used to identify transaction. + pub fn hash(&self) -> &TxHash { + match self { + Self::Legacy(tx) => tx.hash(), + Self::Eip2930(tx) => tx.hash(), + Self::Eip1559(tx) => tx.hash(), + Self::Eip7702(tx) => tx.hash(), + } + } + + /// Returns the signature of the transaction. + pub const fn signature(&self) -> &Signature { + match self { + Self::Legacy(tx) => tx.signature(), + Self::Eip2930(tx) => tx.signature(), + Self::Eip1559(tx) => tx.signature(), + Self::Eip7702(tx) => tx.signature(), + } + } + + /// The length of the 2718 encoded envelope in network format. This is the + /// length of the header + the length of the type flag and inner encoding. + fn network_len(&self) -> usize { + let mut payload_length = self.encode_2718_len(); + if !self.is_legacy() { + payload_length += Header { list: false, payload_length }.length(); + } + + payload_length + } + + /// Recover the signer of the transaction. + #[cfg(feature = "k256")] + pub fn recover_signer( + &self, + ) -> Result<alloy_primitives::Address, alloy_primitives::SignatureError> { + match self { + Self::Legacy(tx) => tx.recover_signer(), + Self::Eip2930(tx) => tx.recover_signer(), + Self::Eip1559(tx) => tx.recover_signer(), + Self::Eip7702(tx) => tx.recover_signer(), + } + } + + /// This encodes the transaction _without_ the signature, and is only suitable for creating a + /// hash intended for signing. + pub fn encode_for_signing(&self, out: &mut dyn bytes::BufMut) { + match self { + Self::Legacy(tx) => tx.tx().encode_for_signing(out), + Self::Eip2930(tx) => tx.tx().encode_for_signing(out), + Self::Eip1559(tx) => tx.tx().encode_for_signing(out), + Self::Eip7702(tx) => tx.tx().encode_for_signing(out), + } + } + + /// Converts the transaction into the ethereum [`TxEnvelope`]. + pub fn into_envelope(self) -> TxEnvelope { + match self { + Self::Legacy(tx) => tx.into(), + Self::Eip2930(tx) => tx.into(), + Self::Eip1559(tx) => tx.into(), + Self::Eip7702(tx) => tx.into(), + } + } + + /// Converts the transaction into the scroll [`ScrollTxEnvelope`]. + pub fn into_scroll_envelope(self) -> ScrollTxEnvelope { + match self { + Self::Legacy(tx) => tx.into(), + Self::Eip2930(tx) => tx.into(), + Self::Eip1559(tx) => tx.into(), + Self::Eip7702(tx) => tx.into(), + } + } + + /// Returns the [`TxLegacy`] variant if the transaction is a legacy transaction. + pub const fn as_legacy(&self) -> Option<&TxLegacy> { + match self { + Self::Legacy(tx) => Some(tx.tx()), + _ => None, + } + } + + /// Returns the [`TxEip2930`] variant if the transaction is an EIP-2930 transaction. + pub const fn as_eip2930(&self) -> Option<&TxEip2930> { + match self { + Self::Eip2930(tx) => Some(tx.tx()), + _ => None, + } + } + + /// Returns the [`TxEip1559`] variant if the transaction is an EIP-1559 transaction. + pub const fn as_eip1559(&self) -> Option<&TxEip1559> { + match self { + Self::Eip1559(tx) => Some(tx.tx()), + _ => None, + } + } + + /// Returns the [`TxEip7702`] variant if the transaction is an EIP-1559 transaction. + pub const fn as_eip7702(&self) -> Option<&TxEip7702> { + match self { + Self::Eip7702(tx) => Some(tx.tx()), + _ => None, + } + } +} + +impl From<Signed<TxLegacy>> for ScrollPooledTransaction { + fn from(v: Signed<TxLegacy>) -> Self { + Self::Legacy(v) + } +} + +impl From<Signed<TxEip2930>> for ScrollPooledTransaction { + fn from(v: Signed<TxEip2930>) -> Self { + Self::Eip2930(v) + } +} + +impl From<Signed<TxEip1559>> for ScrollPooledTransaction { + fn from(v: Signed<TxEip1559>) -> Self { + Self::Eip1559(v) + } +} + +impl From<Signed<TxEip7702>> for ScrollPooledTransaction { + fn from(v: Signed<TxEip7702>) -> Self { + Self::Eip7702(v) + } +} + +impl Hash for ScrollPooledTransaction { + fn hash<H: Hasher>(&self, state: &mut H) { + self.trie_hash().hash(state); + } +} + +impl Encodable for ScrollPooledTransaction { + /// This encodes the transaction _with_ the signature, and an rlp header. + /// + /// For legacy transactions, it encodes the transaction data: + /// `rlp(tx-data)` + /// + /// For EIP-2718 typed transactions, it encodes the transaction type followed by the rlp of the + /// transaction: + /// `rlp(tx-type || rlp(tx-data))` + fn encode(&self, out: &mut dyn bytes::BufMut) { + self.network_encode(out); + } + + fn length(&self) -> usize { + self.network_len() + } +} + +impl Decodable for ScrollPooledTransaction { + /// Decodes an enveloped [`ScrollPooledTransaction`]. + /// + /// CAUTION: this expects that `buf` is `rlp(tx_type || rlp(tx-data))` + fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> { + Ok(Self::network_decode(buf)?) + } +} + +impl Encodable2718 for ScrollPooledTransaction { + fn type_flag(&self) -> Option<u8> { + match self { + Self::Legacy(_) => None, + Self::Eip2930(_) => Some(0x01), + Self::Eip1559(_) => Some(0x02), + Self::Eip7702(_) => Some(0x04), + } + } + + fn encode_2718_len(&self) -> usize { + match self { + Self::Legacy(tx) => tx.eip2718_encoded_length(), + Self::Eip2930(tx) => tx.eip2718_encoded_length(), + Self::Eip1559(tx) => tx.eip2718_encoded_length(), + Self::Eip7702(tx) => tx.eip2718_encoded_length(), + } + } + + fn encode_2718(&self, out: &mut dyn alloy_rlp::BufMut) { + match self { + Self::Legacy(tx) => tx.eip2718_encode(out), + Self::Eip2930(tx) => tx.eip2718_encode(out), + Self::Eip1559(tx) => tx.eip2718_encode(out), + Self::Eip7702(tx) => tx.eip2718_encode(out), + } + } + + fn trie_hash(&self) -> B256 { + *self.hash() + } +} + +impl Decodable2718 for ScrollPooledTransaction { + fn typed_decode(ty: u8, buf: &mut &[u8]) -> Eip2718Result<Self> { + match ty.try_into().map_err(|_| alloy_rlp::Error::Custom("unexpected tx type"))? { + ScrollTxType::Eip2930 => Ok(TxEip2930::rlp_decode_signed(buf)?.into()), + ScrollTxType::Eip1559 => Ok(TxEip1559::rlp_decode_signed(buf)?.into()), + ScrollTxType::Eip7702 => Ok(TxEip7702::rlp_decode_signed(buf)?.into()), + ScrollTxType::Legacy => Err(Eip2718Error::UnexpectedType(ScrollTxType::Legacy.into())), + ScrollTxType::L1Message => { + Err(Eip2718Error::UnexpectedType(ScrollTxType::L1Message.into())) + } + } + } + + fn fallback_decode(buf: &mut &[u8]) -> Eip2718Result<Self> { + TxLegacy::rlp_decode_signed(buf).map(Into::into).map_err(Into::into) + } +} + +impl Transaction for ScrollPooledTransaction { + fn chain_id(&self) -> Option<ChainId> { + match self { + Self::Legacy(tx) => tx.tx().chain_id(), + Self::Eip2930(tx) => tx.tx().chain_id(), + Self::Eip1559(tx) => tx.tx().chain_id(), + Self::Eip7702(tx) => tx.tx().chain_id(), + } + } + + fn nonce(&self) -> u64 { + match self { + Self::Legacy(tx) => tx.tx().nonce(), + Self::Eip2930(tx) => tx.tx().nonce(), + Self::Eip1559(tx) => tx.tx().nonce(), + Self::Eip7702(tx) => tx.tx().nonce(), + } + } + + fn gas_limit(&self) -> u64 { + match self { + Self::Legacy(tx) => tx.tx().gas_limit(), + Self::Eip2930(tx) => tx.tx().gas_limit(), + Self::Eip1559(tx) => tx.tx().gas_limit(), + Self::Eip7702(tx) => tx.tx().gas_limit(), + } + } + + fn gas_price(&self) -> Option<u128> { + match self { + Self::Legacy(tx) => tx.tx().gas_price(), + Self::Eip2930(tx) => tx.tx().gas_price(), + Self::Eip1559(tx) => tx.tx().gas_price(), + Self::Eip7702(tx) => tx.tx().gas_price(), + } + } + + fn max_fee_per_gas(&self) -> u128 { + match self { + Self::Legacy(tx) => tx.tx().max_fee_per_gas(), + Self::Eip2930(tx) => tx.tx().max_fee_per_gas(), + Self::Eip1559(tx) => tx.tx().max_fee_per_gas(), + Self::Eip7702(tx) => tx.tx().max_fee_per_gas(), + } + } + + fn max_priority_fee_per_gas(&self) -> Option<u128> { + match self { + Self::Legacy(tx) => tx.tx().max_priority_fee_per_gas(), + Self::Eip2930(tx) => tx.tx().max_priority_fee_per_gas(), + Self::Eip1559(tx) => tx.tx().max_priority_fee_per_gas(), + Self::Eip7702(tx) => tx.tx().max_priority_fee_per_gas(), + } + } + + fn max_fee_per_blob_gas(&self) -> Option<u128> { + match self { + Self::Legacy(tx) => tx.tx().max_fee_per_blob_gas(), + Self::Eip2930(tx) => tx.tx().max_fee_per_blob_gas(), + Self::Eip1559(tx) => tx.tx().max_fee_per_blob_gas(), + Self::Eip7702(tx) => tx.tx().max_fee_per_blob_gas(), + } + } + + fn priority_fee_or_price(&self) -> u128 { + match self { + Self::Legacy(tx) => tx.tx().priority_fee_or_price(), + Self::Eip2930(tx) => tx.tx().priority_fee_or_price(), + Self::Eip1559(tx) => tx.tx().priority_fee_or_price(), + Self::Eip7702(tx) => tx.tx().priority_fee_or_price(), + } + } + + fn effective_gas_price(&self, base_fee: Option<u64>) -> u128 { + match self { + Self::Legacy(tx) => tx.tx().effective_gas_price(base_fee), + Self::Eip2930(tx) => tx.tx().effective_gas_price(base_fee), + Self::Eip1559(tx) => tx.tx().effective_gas_price(base_fee), + Self::Eip7702(tx) => tx.tx().effective_gas_price(base_fee), + } + } + + fn is_dynamic_fee(&self) -> bool { + match self { + Self::Legacy(tx) => tx.tx().is_dynamic_fee(), + Self::Eip2930(tx) => tx.tx().is_dynamic_fee(), + Self::Eip1559(tx) => tx.tx().is_dynamic_fee(), + Self::Eip7702(tx) => tx.tx().is_dynamic_fee(), + } + } + + fn kind(&self) -> TxKind { + match self { + Self::Legacy(tx) => tx.tx().kind(), + Self::Eip2930(tx) => tx.tx().kind(), + Self::Eip1559(tx) => tx.tx().kind(), + Self::Eip7702(tx) => tx.tx().kind(), + } + } + + fn is_create(&self) -> bool { + match self { + Self::Legacy(tx) => tx.tx().is_create(), + Self::Eip2930(tx) => tx.tx().is_create(), + Self::Eip1559(tx) => tx.tx().is_create(), + Self::Eip7702(tx) => tx.tx().is_create(), + } + } + + fn value(&self) -> U256 { + match self { + Self::Legacy(tx) => tx.tx().value(), + Self::Eip2930(tx) => tx.tx().value(), + Self::Eip1559(tx) => tx.tx().value(), + Self::Eip7702(tx) => tx.tx().value(), + } + } + + fn input(&self) -> &Bytes { + match self { + Self::Legacy(tx) => tx.tx().input(), + Self::Eip2930(tx) => tx.tx().input(), + Self::Eip1559(tx) => tx.tx().input(), + Self::Eip7702(tx) => tx.tx().input(), + } + } + + fn access_list(&self) -> Option<&AccessList> { + match self { + Self::Legacy(tx) => tx.tx().access_list(), + Self::Eip2930(tx) => tx.tx().access_list(), + Self::Eip1559(tx) => tx.tx().access_list(), + Self::Eip7702(tx) => tx.tx().access_list(), + } + } + + fn blob_versioned_hashes(&self) -> Option<&[B256]> { + match self { + Self::Legacy(tx) => tx.tx().blob_versioned_hashes(), + Self::Eip2930(tx) => tx.tx().blob_versioned_hashes(), + Self::Eip1559(tx) => tx.tx().blob_versioned_hashes(), + Self::Eip7702(tx) => tx.tx().blob_versioned_hashes(), + } + } + + fn authorization_list(&self) -> Option<&[SignedAuthorization]> { + match self { + Self::Legacy(tx) => tx.tx().authorization_list(), + Self::Eip2930(tx) => tx.tx().authorization_list(), + Self::Eip1559(tx) => tx.tx().authorization_list(), + Self::Eip7702(tx) => tx.tx().authorization_list(), + } + } +} + +impl Typed2718 for ScrollPooledTransaction { + fn ty(&self) -> u8 { + match self { + Self::Legacy(tx) => tx.tx().ty(), + Self::Eip2930(tx) => tx.tx().ty(), + Self::Eip1559(tx) => tx.tx().ty(), + Self::Eip7702(tx) => tx.tx().ty(), + } + } +} + +impl From<ScrollPooledTransaction> for TxEnvelope { + fn from(tx: ScrollPooledTransaction) -> Self { + tx.into_envelope() + } +} + +impl From<ScrollPooledTransaction> for ScrollTxEnvelope { + fn from(tx: ScrollPooledTransaction) -> Self { + tx.into_scroll_envelope() + } +} + +impl TryFrom<ScrollTxEnvelope> for ScrollPooledTransaction { + type Error = ValueError<ScrollTxEnvelope>; + + fn try_from(value: ScrollTxEnvelope) -> Result<Self, Self::Error> { + value.try_into_pooled() + } +} + +#[cfg(feature = "k256")] +impl alloy_consensus::transaction::SignerRecoverable for ScrollPooledTransaction { + fn recover_signer( + &self, + ) -> Result<alloy_primitives::Address, alloy_consensus::crypto::RecoveryError> { + let signature_hash = self.signature_hash(); + alloy_consensus::crypto::secp256k1::recover_signer(self.signature(), signature_hash) + } + + fn recover_signer_unchecked( + &self, + ) -> Result<alloy_primitives::Address, alloy_consensus::crypto::RecoveryError> { + let signature_hash = self.signature_hash(); + alloy_consensus::crypto::secp256k1::recover_signer_unchecked( + self.signature(), + signature_hash, + ) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use alloy_primitives::{address, hex}; + use bytes::Bytes; + + #[test] + fn invalid_legacy_pooled_decoding_input_too_short() { + let input_too_short = [ + // this should fail because the payload length is longer than expected + &hex!("d90b0280808bc5cd028083c5cdfd9e407c56565656")[..], + // these should fail decoding + // + // The `c1` at the beginning is a list header, and the rest is a valid legacy + // transaction, BUT the payload length of the list header is 1, and the payload is + // obviously longer than one byte. + &hex!("c10b02808083c5cd028883c5cdfd9e407c56565656"), + &hex!("c10b0280808bc5cd028083c5cdfd9e407c56565656"), + // this one is 19 bytes, and the buf is long enough, but the transaction will not + // consume that many bytes. + &hex!("d40b02808083c5cdeb8783c5acfd9e407c5656565656"), + &hex!("d30102808083c5cd02887dc5cdfd9e64fd9e407c56"), + ]; + + for hex_data in &input_too_short { + let input_rlp = &mut &hex_data[..]; + let res = ScrollPooledTransaction::decode(input_rlp); + + assert!( + res.is_err(), + "expected err after decoding rlp input: {:x?}", + Bytes::copy_from_slice(hex_data) + ); + + // this is a legacy tx so we can attempt the same test with decode_enveloped + let input_rlp = &mut &hex_data[..]; + let res = ScrollPooledTransaction::decode_2718(input_rlp); + + assert!( + res.is_err(), + "expected err after decoding enveloped rlp input: {:x?}", + Bytes::copy_from_slice(hex_data) + ); + } + } + + // <https://holesky.etherscan.io/tx/0x7f60faf8a410a80d95f7ffda301d5ab983545913d3d789615df3346579f6c849> + #[test] + fn decode_eip1559_enveloped() { + let data = hex!("02f903d382426882ba09832dc6c0848674742682ed9694714b6a4ea9b94a8a7d9fd362ed72630688c8898c80b90364492d24749189822d8512430d3f3ff7a2ede675ac08265c08e2c56ff6fdaa66dae1cdbe4a5d1d7809f3e99272d067364e597542ac0c369d69e22a6399c3e9bee5da4b07e3f3fdc34c32c3d88aa2268785f3e3f8086df0934b10ef92cfffc2e7f3d90f5e83302e31382e302d64657600000000000000000000000000000000000000000000569e75fc77c1a856f6daaf9e69d8a9566ca34aa47f9133711ce065a571af0cfd000000000000000000000000e1e210594771824dad216568b91c9cb4ceed361c00000000000000000000000000000000000000000000000000000000000546e00000000000000000000000000000000000000000000000000000000000e4e1c00000000000000000000000000000000000000000000000000000000065d6750c00000000000000000000000000000000000000000000000000000000000f288000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002cf600000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000000f1628e56fa6d8c50e5b984a58c0df14de31c7b857ce7ba499945b99252976a93d06dcda6776fc42167fbe71cb59f978f5ef5b12577a90b132d14d9c6efa528076f0161d7bf03643cfc5490ec5084f4a041db7f06c50bd97efa08907ba79ddcac8b890f24d12d8db31abbaaf18985d54f400449ee0559a4452afe53de5853ce090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000064ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000c080a01428023fc54a27544abc421d5d017b9a7c5936ad501cbdecd0d9d12d04c1a033a0753104bbf1c87634d6ff3f0ffa0982710612306003eb022363b57994bdef445a" +); + + let res = ScrollPooledTransaction::decode_2718(&mut &data[..]).unwrap(); + assert_eq!(res.to(), Some(address!("714b6a4ea9b94a8a7d9fd362ed72630688c8898c"))); + } + + #[test] + fn legacy_valid_pooled_decoding() { + // d3 <- payload length, d3 - c0 = 0x13 = 19 + // 0b <- nonce + // 02 <- gas_price + // 80 <- gas_limit + // 80 <- to (Create) + // 83 c5cdeb <- value + // 87 83c5acfd9e407c <- input + // 56 <- v (eip155, so modified with a chain id) + // 56 <- r + // 56 <- s + let data = &hex!("d30b02808083c5cdeb8783c5acfd9e407c565656")[..]; + + let input_rlp = &mut &data[..]; + let res = ScrollPooledTransaction::decode(input_rlp); + assert!(res.is_ok()); + assert!(input_rlp.is_empty()); + + // we can also decode_enveloped + let res = ScrollPooledTransaction::decode_2718(&mut &data[..]); + assert!(res.is_ok()); + } +}
diff --git reth/crates/scroll/alloy/consensus/src/transaction/tx_type.rs scroll-reth/crates/scroll/alloy/consensus/src/transaction/tx_type.rs new file mode 100644 index 0000000000000000000000000000000000000000..f980f3d6872db74ed351a991c6305812a5caea57 --- /dev/null +++ scroll-reth/crates/scroll/alloy/consensus/src/transaction/tx_type.rs @@ -0,0 +1,211 @@ +//! Contains the transaction type identifier for Scroll. + +use alloy_consensus::Typed2718; +use alloy_eips::eip2718::Eip2718Error; +use alloy_primitives::{U64, U8}; +use alloy_rlp::{BufMut, Decodable, Encodable}; +use derive_more::Display; +#[cfg(feature = "reth-codec")] +use reth_codecs::{ + __private::bytes, + txtype::{ + COMPACT_EXTENDED_IDENTIFIER_FLAG, COMPACT_IDENTIFIER_EIP1559, COMPACT_IDENTIFIER_EIP2930, + COMPACT_IDENTIFIER_LEGACY, + }, + Compact, +}; + +/// Identifier for an Scroll L1 message transaction +pub const L1_MESSAGE_TX_TYPE_ID: u8 = 126; // 0x7E + +/// Scroll `TransactionType` flags as specified in <https://docs.scroll.io/en/technology/chain/transactions/>. +#[repr(u8)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash, Display)] +pub enum ScrollTxType { + /// Legacy transaction type. + #[display("legacy")] + Legacy = 0, + /// EIP-2930 transaction type. + #[display("eip2930")] + Eip2930 = 1, + /// EIP-1559 transaction type. + #[display("eip1559")] + Eip1559 = 2, + /// EIP-7702 transaction type. + #[display("eip7702")] + Eip7702 = 4, + /// L1 message transaction type. + #[display("l1_message")] + L1Message = L1_MESSAGE_TX_TYPE_ID, +} + +impl ScrollTxType { + /// List of all variants. + pub const ALL: [Self; 5] = + [Self::Legacy, Self::Eip1559, Self::Eip2930, Self::Eip7702, Self::L1Message]; +} + +#[cfg(any(test, feature = "arbitrary"))] +impl arbitrary::Arbitrary<'_> for ScrollTxType { + fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> { + let i = u.choose_index(Self::ALL.len())?; + Ok(Self::ALL[i]) + } +} + +impl From<ScrollTxType> for U8 { + fn from(tx_type: ScrollTxType) -> Self { + Self::from(u8::from(tx_type)) + } +} + +impl From<ScrollTxType> for u8 { + fn from(v: ScrollTxType) -> Self { + v as Self + } +} + +impl TryFrom<u8> for ScrollTxType { + type Error = Eip2718Error; + + fn try_from(value: u8) -> Result<Self, Self::Error> { + Ok(match value { + x if x == Self::Legacy as u8 => Self::Legacy, + x if x == Self::Eip2930 as u8 => Self::Eip2930, + x if x == Self::Eip1559 as u8 => Self::Eip1559, + x if x == Self::Eip7702 as u8 => Self::Eip7702, + x if x == Self::L1Message as u8 => Self::L1Message, + _ => return Err(Eip2718Error::UnexpectedType(value)), + }) + } +} + +impl TryFrom<u64> for ScrollTxType { + type Error = &'static str; + + fn try_from(value: u64) -> Result<Self, Self::Error> { + let err = || "invalid tx type"; + let value: u8 = value.try_into().map_err(|_| err())?; + Self::try_from(value).map_err(|_| err()) + } +} + +impl TryFrom<U64> for ScrollTxType { + type Error = &'static str; + + fn try_from(value: U64) -> Result<Self, Self::Error> { + value.to::<u64>().try_into() + } +} + +impl PartialEq<u8> for ScrollTxType { + fn eq(&self, other: &u8) -> bool { + (*self as u8) == *other + } +} + +impl PartialEq<ScrollTxType> for u8 { + fn eq(&self, other: &ScrollTxType) -> bool { + *self == *other as Self + } +} + +impl Encodable for ScrollTxType { + fn encode(&self, out: &mut dyn BufMut) { + (*self as u8).encode(out); + } + + fn length(&self) -> usize { + 1 + } +} + +impl Decodable for ScrollTxType { + fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> { + let ty = u8::decode(buf)?; + + Self::try_from(ty).map_err(|_| alloy_rlp::Error::Custom("invalid transaction type")) + } +} + +#[cfg(feature = "reth-codec")] +impl Compact for ScrollTxType { + fn to_compact<B>(&self, buf: &mut B) -> usize + where + B: BufMut + AsMut<[u8]>, + { + match self { + Self::Legacy => COMPACT_IDENTIFIER_LEGACY, + Self::Eip2930 => COMPACT_IDENTIFIER_EIP2930, + Self::Eip1559 => COMPACT_IDENTIFIER_EIP1559, + Self::Eip7702 => { + buf.put_u8(alloy_eips::eip7702::constants::EIP7702_TX_TYPE_ID); + COMPACT_EXTENDED_IDENTIFIER_FLAG + } + Self::L1Message => { + buf.put_u8(L1_MESSAGE_TX_TYPE_ID); + COMPACT_EXTENDED_IDENTIFIER_FLAG + } + } + } + + // For backwards compatibility purposes only 2 bits of the type are encoded in the identifier + // parameter. In the case of a [`COMPACT_EXTENDED_IDENTIFIER_FLAG`], the full transaction type + // is read from the buffer as a single byte. + fn from_compact(mut buf: &[u8], identifier: usize) -> (Self, &[u8]) { + use bytes::Buf; + ( + match identifier { + COMPACT_IDENTIFIER_LEGACY => Self::Legacy, + COMPACT_IDENTIFIER_EIP2930 => Self::Eip2930, + COMPACT_IDENTIFIER_EIP1559 => Self::Eip1559, + COMPACT_EXTENDED_IDENTIFIER_FLAG => { + let extended_identifier = buf.get_u8(); + match extended_identifier { + alloy_eips::eip7702::constants::EIP7702_TX_TYPE_ID => Self::Eip7702, + L1_MESSAGE_TX_TYPE_ID => Self::L1Message, + _ => panic!("Unsupported TxType identifier: {extended_identifier}"), + } + } + _ => panic!("Unknown identifier for TxType: {identifier}"), + }, + buf, + ) + } +} + +impl Typed2718 for ScrollTxType { + fn ty(&self) -> u8 { + (*self).into() + } +} + +#[cfg(test)] +mod tests { + use super::*; + extern crate alloc; + use alloc::{vec, vec::Vec}; + + #[test] + fn test_all_tx_types() { + assert_eq!(ScrollTxType::ALL.len(), 5); + let all = vec![ + ScrollTxType::Legacy, + ScrollTxType::Eip1559, + ScrollTxType::Eip2930, + ScrollTxType::Eip7702, + ScrollTxType::L1Message, + ]; + assert_eq!(ScrollTxType::ALL.to_vec(), all); + } + + #[test] + fn tx_type_roundtrip() { + for &tx_type in &ScrollTxType::ALL { + let mut buf = Vec::new(); + tx_type.encode(&mut buf); + let decoded = ScrollTxType::decode(&mut &buf[..]).unwrap(); + assert_eq!(tx_type, decoded); + } + } +}
diff --git reth/crates/scroll/alloy/consensus/src/transaction/typed.rs scroll-reth/crates/scroll/alloy/consensus/src/transaction/typed.rs new file mode 100644 index 0000000000000000000000000000000000000000..9bd6a4cb76c8b8d0bd4c05655ef22cb21cb7aa61 --- /dev/null +++ scroll-reth/crates/scroll/alloy/consensus/src/transaction/typed.rs @@ -0,0 +1,597 @@ +use crate::{ScrollTxEnvelope, ScrollTxType, TxL1Message}; +use alloy_consensus::{ + transaction::RlpEcdsaEncodableTx, SignableTransaction, Signed, Transaction, TxEip1559, + TxEip2930, TxEip7702, TxLegacy, Typed2718, +}; +use alloy_eips::{eip2930::AccessList, Encodable2718}; +use alloy_primitives::{bytes::BufMut, Address, Bytes, ChainId, Signature, TxHash, TxKind, B256}; +#[cfg(feature = "reth-codec")] +use { + reth_codecs::{Compact, __private::bytes}, + reth_codecs_derive::generate_tests, +}; + +/// The `TypedTransaction` enum represents all Ethereum transaction request types, modified for +/// Scroll +/// +/// Its variants correspond to specific allowed transactions: +/// 1. `Legacy` (pre-EIP2718) [`TxLegacy`] +/// 2. `EIP2930` (state access lists) [`TxEip2930`] +/// 3. `EIP1559` [`TxEip1559`] +/// 4. `Eip7702` [`TxEip7702`] +/// 5. `L1Message` [`TxL1Message`] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr( + feature = "serde", + serde( + from = "serde_from::MaybeTaggedTypedTransaction", + into = "serde_from::TaggedTypedTransaction" + ) +)] +pub enum ScrollTypedTransaction { + /// Legacy transaction + Legacy(TxLegacy), + /// EIP-2930 transaction + Eip2930(TxEip2930), + /// EIP-1559 transaction + Eip1559(TxEip1559), + /// EIP-7702 transaction + Eip7702(TxEip7702), + /// Scroll L1 message transaction + L1Message(TxL1Message), +} + +impl From<TxLegacy> for ScrollTypedTransaction { + fn from(tx: TxLegacy) -> Self { + Self::Legacy(tx) + } +} + +impl From<TxEip2930> for ScrollTypedTransaction { + fn from(tx: TxEip2930) -> Self { + Self::Eip2930(tx) + } +} + +impl From<TxEip1559> for ScrollTypedTransaction { + fn from(tx: TxEip1559) -> Self { + Self::Eip1559(tx) + } +} + +impl From<TxEip7702> for ScrollTypedTransaction { + fn from(tx: TxEip7702) -> Self { + Self::Eip7702(tx) + } +} + +impl From<TxL1Message> for ScrollTypedTransaction { + fn from(tx: TxL1Message) -> Self { + Self::L1Message(tx) + } +} + +impl From<ScrollTxEnvelope> for ScrollTypedTransaction { + fn from(envelope: ScrollTxEnvelope) -> Self { + match envelope { + ScrollTxEnvelope::Legacy(tx) => Self::Legacy(tx.strip_signature()), + ScrollTxEnvelope::Eip2930(tx) => Self::Eip2930(tx.strip_signature()), + ScrollTxEnvelope::Eip1559(tx) => Self::Eip1559(tx.strip_signature()), + ScrollTxEnvelope::Eip7702(tx) => Self::Eip7702(tx.strip_signature()), + ScrollTxEnvelope::L1Message(tx) => Self::L1Message(tx.into_inner()), + } + } +} + +impl ScrollTypedTransaction { + /// Return the [`ScrollTxType`] of the inner txn. + pub const fn tx_type(&self) -> ScrollTxType { + match self { + Self::Legacy(_) => ScrollTxType::Legacy, + Self::Eip2930(_) => ScrollTxType::Eip2930, + Self::Eip1559(_) => ScrollTxType::Eip1559, + Self::Eip7702(_) => ScrollTxType::Eip7702, + Self::L1Message(_) => ScrollTxType::L1Message, + } + } + + /// Return the inner legacy transaction if it exists. + pub const fn legacy(&self) -> Option<&TxLegacy> { + match self { + Self::Legacy(tx) => Some(tx), + _ => None, + } + } + + /// Return the inner EIP-2930 transaction if it exists. + pub const fn eip2930(&self) -> Option<&TxEip2930> { + match self { + Self::Eip2930(tx) => Some(tx), + _ => None, + } + } + + /// Return the inner EIP-1559 transaction if it exists. + pub const fn eip1559(&self) -> Option<&TxEip1559> { + match self { + Self::Eip1559(tx) => Some(tx), + _ => None, + } + } + + /// Return the inner EIP-1559 transaction if it exists. + pub const fn eip7702(&self) -> Option<&TxEip7702> { + match self { + Self::Eip7702(tx) => Some(tx), + _ => None, + } + } + + /// Return the inner l1 message if it exists. + pub const fn l1_message(&self) -> Option<&TxL1Message> { + match self { + Self::L1Message(tx) => Some(tx), + _ => None, + } + } + + /// Calculates the signing hash for the transaction. + pub fn signature_hash(&self) -> B256 { + match self { + Self::Legacy(tx) => tx.signature_hash(), + Self::Eip2930(tx) => tx.signature_hash(), + Self::Eip1559(tx) => tx.signature_hash(), + Self::Eip7702(tx) => tx.signature_hash(), + Self::L1Message(_) => B256::ZERO, + } + } +} + +impl Typed2718 for ScrollTypedTransaction { + fn ty(&self) -> u8 { + match self { + Self::Legacy(_) => ScrollTxType::Legacy as u8, + Self::Eip2930(_) => ScrollTxType::Eip2930 as u8, + Self::Eip1559(_) => ScrollTxType::Eip1559 as u8, + Self::Eip7702(_) => ScrollTxType::Eip7702 as u8, + Self::L1Message(_) => ScrollTxType::L1Message as u8, + } + } +} + +impl Transaction for ScrollTypedTransaction { + fn chain_id(&self) -> Option<alloy_primitives::ChainId> { + match self { + Self::Legacy(tx) => tx.chain_id(), + Self::Eip2930(tx) => tx.chain_id(), + Self::Eip1559(tx) => tx.chain_id(), + Self::Eip7702(tx) => tx.chain_id(), + Self::L1Message(tx) => tx.chain_id(), + } + } + + fn nonce(&self) -> u64 { + match self { + Self::Legacy(tx) => tx.nonce(), + Self::Eip2930(tx) => tx.nonce(), + Self::Eip1559(tx) => tx.nonce(), + Self::Eip7702(tx) => tx.nonce(), + Self::L1Message(tx) => tx.nonce(), + } + } + + fn gas_limit(&self) -> u64 { + match self { + Self::Legacy(tx) => tx.gas_limit(), + Self::Eip2930(tx) => tx.gas_limit(), + Self::Eip1559(tx) => tx.gas_limit(), + Self::Eip7702(tx) => tx.gas_limit(), + Self::L1Message(tx) => tx.gas_limit(), + } + } + + fn gas_price(&self) -> Option<u128> { + match self { + Self::Legacy(tx) => tx.gas_price(), + Self::Eip2930(tx) => tx.gas_price(), + Self::Eip1559(tx) => tx.gas_price(), + Self::Eip7702(tx) => tx.gas_price(), + Self::L1Message(tx) => tx.gas_price(), + } + } + + fn max_fee_per_gas(&self) -> u128 { + match self { + Self::Legacy(tx) => tx.max_fee_per_gas(), + Self::Eip2930(tx) => tx.max_fee_per_gas(), + Self::Eip1559(tx) => tx.max_fee_per_gas(), + Self::Eip7702(tx) => tx.max_fee_per_gas(), + Self::L1Message(tx) => tx.max_fee_per_gas(), + } + } + + fn max_priority_fee_per_gas(&self) -> Option<u128> { + match self { + Self::Legacy(tx) => tx.max_priority_fee_per_gas(), + Self::Eip2930(tx) => tx.max_priority_fee_per_gas(), + Self::Eip1559(tx) => tx.max_priority_fee_per_gas(), + Self::Eip7702(tx) => tx.max_priority_fee_per_gas(), + Self::L1Message(tx) => tx.max_priority_fee_per_gas(), + } + } + + fn max_fee_per_blob_gas(&self) -> Option<u128> { + match self { + Self::Legacy(tx) => tx.max_fee_per_blob_gas(), + Self::Eip2930(tx) => tx.max_fee_per_blob_gas(), + Self::Eip1559(tx) => tx.max_fee_per_blob_gas(), + Self::Eip7702(tx) => tx.max_fee_per_blob_gas(), + Self::L1Message(tx) => tx.max_fee_per_blob_gas(), + } + } + + fn priority_fee_or_price(&self) -> u128 { + match self { + Self::Legacy(tx) => tx.priority_fee_or_price(), + Self::Eip2930(tx) => tx.priority_fee_or_price(), + Self::Eip1559(tx) => tx.priority_fee_or_price(), + Self::Eip7702(tx) => tx.priority_fee_or_price(), + Self::L1Message(tx) => tx.priority_fee_or_price(), + } + } + + fn to(&self) -> Option<Address> { + match self { + Self::Legacy(tx) => tx.to(), + Self::Eip2930(tx) => tx.to(), + Self::Eip1559(tx) => tx.to(), + Self::Eip7702(tx) => tx.to(), + Self::L1Message(tx) => tx.to(), + } + } + + fn kind(&self) -> TxKind { + match self { + Self::Legacy(tx) => tx.kind(), + Self::Eip2930(tx) => tx.kind(), + Self::Eip1559(tx) => tx.kind(), + Self::Eip7702(tx) => tx.kind(), + Self::L1Message(tx) => tx.kind(), + } + } + + fn value(&self) -> alloy_primitives::U256 { + match self { + Self::Legacy(tx) => tx.value(), + Self::Eip2930(tx) => tx.value(), + Self::Eip1559(tx) => tx.value(), + Self::Eip7702(tx) => tx.value(), + Self::L1Message(tx) => tx.value(), + } + } + + fn input(&self) -> &Bytes { + match self { + Self::Legacy(tx) => tx.input(), + Self::Eip2930(tx) => tx.input(), + Self::Eip1559(tx) => tx.input(), + Self::Eip7702(tx) => tx.input(), + Self::L1Message(tx) => tx.input(), + } + } + + fn access_list(&self) -> Option<&AccessList> { + match self { + Self::Legacy(tx) => tx.access_list(), + Self::Eip2930(tx) => tx.access_list(), + Self::Eip1559(tx) => tx.access_list(), + Self::Eip7702(tx) => tx.access_list(), + Self::L1Message(tx) => tx.access_list(), + } + } + + fn blob_versioned_hashes(&self) -> Option<&[alloy_primitives::B256]> { + match self { + Self::Legacy(tx) => tx.blob_versioned_hashes(), + Self::Eip2930(tx) => tx.blob_versioned_hashes(), + Self::Eip1559(tx) => tx.blob_versioned_hashes(), + Self::Eip7702(tx) => tx.blob_versioned_hashes(), + Self::L1Message(tx) => tx.blob_versioned_hashes(), + } + } + + fn authorization_list(&self) -> Option<&[alloy_eips::eip7702::SignedAuthorization]> { + match self { + Self::Legacy(tx) => tx.authorization_list(), + Self::Eip2930(tx) => tx.authorization_list(), + Self::Eip1559(tx) => tx.authorization_list(), + Self::Eip7702(tx) => tx.authorization_list(), + Self::L1Message(tx) => tx.authorization_list(), + } + } + + fn is_dynamic_fee(&self) -> bool { + match self { + Self::Legacy(tx) => tx.is_dynamic_fee(), + Self::Eip2930(tx) => tx.is_dynamic_fee(), + Self::Eip1559(tx) => tx.is_dynamic_fee(), + Self::Eip7702(tx) => tx.is_dynamic_fee(), + Self::L1Message(tx) => tx.is_dynamic_fee(), + } + } + + fn effective_gas_price(&self, base_fee: Option<u64>) -> u128 { + match self { + Self::Legacy(tx) => tx.effective_gas_price(base_fee), + Self::Eip2930(tx) => tx.effective_gas_price(base_fee), + Self::Eip1559(tx) => tx.effective_gas_price(base_fee), + Self::Eip7702(tx) => tx.effective_gas_price(base_fee), + Self::L1Message(tx) => tx.effective_gas_price(base_fee), + } + } + + fn is_create(&self) -> bool { + match self { + Self::Legacy(tx) => tx.is_create(), + Self::Eip2930(tx) => tx.is_create(), + Self::Eip1559(tx) => tx.is_create(), + Self::Eip7702(tx) => tx.is_create(), + Self::L1Message(tx) => tx.is_create(), + } + } +} + +impl RlpEcdsaEncodableTx for ScrollTypedTransaction { + fn rlp_encoded_fields_length(&self) -> usize { + match self { + Self::Legacy(tx) => tx.rlp_encoded_fields_length(), + Self::Eip2930(tx) => tx.rlp_encoded_fields_length(), + Self::Eip1559(tx) => tx.rlp_encoded_fields_length(), + Self::Eip7702(tx) => tx.rlp_encoded_fields_length(), + Self::L1Message(tx) => tx.rlp_encoded_fields_length(), + } + } + + fn rlp_encode_fields(&self, out: &mut dyn alloy_rlp::BufMut) { + match self { + Self::Legacy(tx) => tx.rlp_encode_fields(out), + Self::Eip2930(tx) => tx.rlp_encode_fields(out), + Self::Eip1559(tx) => tx.rlp_encode_fields(out), + Self::Eip7702(tx) => tx.rlp_encode_fields(out), + Self::L1Message(tx) => tx.rlp_encode_fields(out), + } + } + + fn eip2718_encode_with_type(&self, signature: &Signature, _ty: u8, out: &mut dyn BufMut) { + match self { + Self::Legacy(tx) => tx.eip2718_encode_with_type(signature, tx.ty(), out), + Self::Eip2930(tx) => tx.eip2718_encode_with_type(signature, tx.ty(), out), + Self::Eip1559(tx) => tx.eip2718_encode_with_type(signature, tx.ty(), out), + Self::Eip7702(tx) => tx.eip2718_encode_with_type(signature, tx.ty(), out), + Self::L1Message(tx) => tx.encode_2718(out), + } + } + + fn eip2718_encode(&self, signature: &Signature, out: &mut dyn BufMut) { + match self { + Self::Legacy(tx) => tx.eip2718_encode(signature, out), + Self::Eip2930(tx) => tx.eip2718_encode(signature, out), + Self::Eip1559(tx) => tx.eip2718_encode(signature, out), + Self::Eip7702(tx) => tx.eip2718_encode(signature, out), + Self::L1Message(tx) => tx.encode_2718(out), + } + } + + fn network_encode_with_type(&self, signature: &Signature, _ty: u8, out: &mut dyn BufMut) { + match self { + Self::Legacy(tx) => tx.network_encode_with_type(signature, tx.ty(), out), + Self::Eip2930(tx) => tx.network_encode_with_type(signature, tx.ty(), out), + Self::Eip1559(tx) => tx.network_encode_with_type(signature, tx.ty(), out), + Self::Eip7702(tx) => tx.network_encode_with_type(signature, tx.ty(), out), + Self::L1Message(tx) => tx.network_encode(out), + } + } + + fn network_encode(&self, signature: &Signature, out: &mut dyn BufMut) { + match self { + Self::Legacy(tx) => tx.network_encode(signature, out), + Self::Eip2930(tx) => tx.network_encode(signature, out), + Self::Eip1559(tx) => tx.network_encode(signature, out), + Self::Eip7702(tx) => tx.network_encode(signature, out), + Self::L1Message(tx) => tx.network_encode(out), + } + } + + fn tx_hash_with_type(&self, signature: &Signature, _ty: u8) -> TxHash { + match self { + Self::Legacy(tx) => tx.tx_hash_with_type(signature, tx.ty()), + Self::Eip2930(tx) => tx.tx_hash_with_type(signature, tx.ty()), + Self::Eip1559(tx) => tx.tx_hash_with_type(signature, tx.ty()), + Self::Eip7702(tx) => tx.tx_hash_with_type(signature, tx.ty()), + Self::L1Message(tx) => tx.tx_hash(), + } + } + + fn tx_hash(&self, signature: &Signature) -> TxHash { + match self { + Self::Legacy(tx) => tx.tx_hash(signature), + Self::Eip2930(tx) => tx.tx_hash(signature), + Self::Eip1559(tx) => tx.tx_hash(signature), + Self::Eip7702(tx) => tx.tx_hash(signature), + Self::L1Message(tx) => tx.tx_hash(), + } + } +} + +impl SignableTransaction<Signature> for ScrollTypedTransaction { + fn set_chain_id(&mut self, chain_id: ChainId) { + match self { + Self::Legacy(tx) => tx.set_chain_id(chain_id), + Self::Eip2930(tx) => tx.set_chain_id(chain_id), + Self::Eip1559(tx) => tx.set_chain_id(chain_id), + Self::Eip7702(tx) => tx.set_chain_id(chain_id), + Self::L1Message(_) => {} + } + } + + fn encode_for_signing(&self, out: &mut dyn BufMut) { + match self { + Self::Legacy(tx) => tx.encode_for_signing(out), + Self::Eip2930(tx) => tx.encode_for_signing(out), + Self::Eip1559(tx) => tx.encode_for_signing(out), + Self::Eip7702(tx) => tx.encode_for_signing(out), + Self::L1Message(_) => {} + } + } + + fn payload_len_for_signature(&self) -> usize { + match self { + Self::Legacy(tx) => tx.payload_len_for_signature(), + Self::Eip2930(tx) => tx.payload_len_for_signature(), + Self::Eip1559(tx) => tx.payload_len_for_signature(), + Self::Eip7702(tx) => tx.payload_len_for_signature(), + Self::L1Message(_) => 0, + } + } + + fn into_signed(self, signature: Signature) -> Signed<Self, Signature> + where + Self: Sized, + { + let hash = self.tx_hash(&signature); + Signed::new_unchecked(self, signature, hash) + } +} + +#[cfg(feature = "reth-codec")] +impl Compact for ScrollTypedTransaction { + fn to_compact<B>(&self, out: &mut B) -> usize + where + B: bytes::BufMut + AsMut<[u8]>, + { + let identifier = self.tx_type().to_compact(out); + match self { + Self::Legacy(tx) => tx.to_compact(out), + Self::Eip2930(tx) => tx.to_compact(out), + Self::Eip1559(tx) => tx.to_compact(out), + Self::Eip7702(tx) => tx.to_compact(out), + Self::L1Message(tx) => tx.to_compact(out), + }; + identifier + } + + fn from_compact(buf: &[u8], identifier: usize) -> (Self, &[u8]) { + let (tx_type, buf) = ScrollTxType::from_compact(buf, identifier); + match tx_type { + ScrollTxType::Legacy => { + let (tx, buf) = Compact::from_compact(buf, buf.len()); + (Self::Legacy(tx), buf) + } + ScrollTxType::Eip2930 => { + let (tx, buf) = Compact::from_compact(buf, buf.len()); + (Self::Eip2930(tx), buf) + } + ScrollTxType::Eip1559 => { + let (tx, buf) = Compact::from_compact(buf, buf.len()); + (Self::Eip1559(tx), buf) + } + ScrollTxType::Eip7702 => { + let (tx, buf) = Compact::from_compact(buf, buf.len()); + (Self::Eip7702(tx), buf) + } + ScrollTxType::L1Message => { + let (tx, buf) = Compact::from_compact(buf, buf.len()); + (Self::L1Message(tx), buf) + } + } + } +} + +#[cfg(feature = "reth-codec")] +generate_tests!( + #[compact] + ScrollTypedTransaction, + ScrollTypedTransactionTests +); + +#[cfg(feature = "serde")] +mod serde_from { + //! NB: Why do we need this? + //! + //! Because the tag may be missing, we need an abstraction over tagged (with + //! type) and untagged (always legacy). This is + //! [`MaybeTaggedTypedTransaction`]. + //! + //! The tagged variant is [`TaggedTypedTransaction`], which always has a + //! type tag. + //! + //! We serialize via [`TaggedTypedTransaction`] and deserialize via + //! [`MaybeTaggedTypedTransaction`]. + use super::*; + + #[derive(Debug, serde::Deserialize)] + #[serde(untagged)] + pub(crate) enum MaybeTaggedTypedTransaction { + Tagged(TaggedTypedTransaction), + Untagged(TxLegacy), + } + + #[derive(Debug, serde::Serialize, serde::Deserialize)] + #[serde(tag = "type")] + pub(crate) enum TaggedTypedTransaction { + /// `Legacy` transaction + #[serde(rename = "0x00", alias = "0x0")] + Legacy(TxLegacy), + /// `EIP-2930` transaction + #[serde(rename = "0x01", alias = "0x1")] + Eip2930(TxEip2930), + /// `EIP-1559` transaction + #[serde(rename = "0x02", alias = "0x2")] + Eip1559(TxEip1559), + /// `EIP-7702` transaction + #[serde(rename = "0x04", alias = "0x4")] + Eip7702(TxEip7702), + /// `L1Message` transaction + #[serde( + rename = "0x7e", + alias = "0x7E", + serialize_with = "crate::serde_l1_message_tx_rpc" + )] + L1Message(TxL1Message), + } + + impl From<MaybeTaggedTypedTransaction> for ScrollTypedTransaction { + fn from(value: MaybeTaggedTypedTransaction) -> Self { + match value { + MaybeTaggedTypedTransaction::Tagged(tagged) => tagged.into(), + MaybeTaggedTypedTransaction::Untagged(tx) => Self::Legacy(tx), + } + } + } + + impl From<TaggedTypedTransaction> for ScrollTypedTransaction { + fn from(value: TaggedTypedTransaction) -> Self { + match value { + TaggedTypedTransaction::Legacy(signed) => Self::Legacy(signed), + TaggedTypedTransaction::Eip2930(signed) => Self::Eip2930(signed), + TaggedTypedTransaction::Eip1559(signed) => Self::Eip1559(signed), + TaggedTypedTransaction::Eip7702(signed) => Self::Eip7702(signed), + TaggedTypedTransaction::L1Message(tx) => Self::L1Message(tx), + } + } + } + + impl From<ScrollTypedTransaction> for TaggedTypedTransaction { + fn from(value: ScrollTypedTransaction) -> Self { + match value { + ScrollTypedTransaction::Legacy(signed) => Self::Legacy(signed), + ScrollTypedTransaction::Eip2930(signed) => Self::Eip2930(signed), + ScrollTypedTransaction::Eip1559(signed) => Self::Eip1559(signed), + ScrollTypedTransaction::Eip7702(signed) => Self::Eip7702(signed), + ScrollTypedTransaction::L1Message(tx) => Self::L1Message(tx), + } + } + } +}
diff --git reth/crates/scroll/alloy/evm/Cargo.toml scroll-reth/crates/scroll/alloy/evm/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..22b2ebd3a068aae67c2cf9bffd348b340e1c8d8a --- /dev/null +++ scroll-reth/crates/scroll/alloy/evm/Cargo.toml @@ -0,0 +1,71 @@ +[package] +name = "scroll-alloy-evm" +version.workspace = true +edition.workspace = true +rust-version.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +exclude.workspace = true + +[lints] +workspace = true + +[dependencies] +# alloy +alloy-consensus = { workspace = true, default-features = false } +alloy-eips = { workspace = true, default-features = false } +alloy-evm = { workspace = true, default-features = false } +alloy-primitives = { workspace = true, default-features = false } + +# revm +revm = { workspace = true, default-features = false, features = ["optional_no_base_fee"] } + +# scroll +revm-scroll = { workspace = true, default-features = false } + +# scroll +scroll-alloy-consensus = { workspace = true, default-features = false } +scroll-alloy-hardforks = { workspace = true, default-features = false } + +# misc +auto_impl = { workspace = true, default-features = false } +serde = { workspace = true, default-features = false, features = ["derive"], optional = true } +zstd = { version = "=0.13.3", features = ["experimental"], default-features = false, optional = true } + +[dev-dependencies] +alloy-hardforks.workspace = true +alloy-primitives = { workspace = true, features = ["getrandom"] } +eyre.workspace = true +reth-evm.workspace = true +reth-scroll-chainspec.workspace = true +reth-scroll-evm.workspace = true + +[features] +std = [ + "alloy-evm/std", + "alloy-primitives/std", + "revm-scroll/std", + "revm/std", + "serde/std", + "alloy-consensus/std", + "alloy-eips/std", + "scroll-alloy-consensus/std", + "scroll-alloy-hardforks/std", + "reth-evm/std", + "reth-scroll-chainspec/std", + "reth-scroll-evm/std", + "zstd_compression", +] +serde = [ + "dep:serde", + "alloy-primitives/serde", + "revm-scroll/serde", + "revm/serde", + "alloy-eips/serde", + "alloy-consensus/serde", + "scroll-alloy-consensus/serde", + "scroll-alloy-hardforks/serde", + "alloy-hardforks/serde", +] +zstd_compression = ["zstd"]
diff --git reth/crates/scroll/alloy/evm/src/block/curie.rs scroll-reth/crates/scroll/alloy/evm/src/block/curie.rs new file mode 100644 index 0000000000000000000000000000000000000000..61d873bac532d05e869a97d92510ce1a7c9233fe --- /dev/null +++ scroll-reth/crates/scroll/alloy/evm/src/block/curie.rs @@ -0,0 +1,179 @@ +//! Curie fork transition for Scroll. +//! +//! On block 7096836, Scroll performed a transition to the Curie fork state, which brought various +//! changes to the protocol: +//! 1. Fee reduction cost thanks to the use of compressed blobs on the L1. +//! 2. Modified [EIP-1559](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md) pricing +//! model along with support for EIP-1559 and EIP-2930 transactions. +//! 3. Support for `MLOAD`, `TLOAD` and `MCOPY` ([EIP-1153](https://eips.ethereum.org/EIPS/eip-1153) +//! and [EIP-5656](https://eips.ethereum.org/EIPS/eip-5656)). +//! 4. Dynamic block time. +//! +//! Compressed blobs allowed for more transactions to be stored in each blob, reducing the DA cost +//! per transactions. Accordingly, the L1 gas oracle contract's bytecode was updated in order to +//! reflect the update in the DA costs: +//! - original formula: `(l1GasUsed(txRlp) + overhead) * l1BaseFee * scalar`. +//! - updated formula: `l1BaseFee * commitScalar + len(txRlp) * l1BlobBaseFee * blobScalar`. +//! +//! More details on the Curie update: <https://scroll.io/blog/compressing-the-gas-scrolls-curie-upgrade> + +use alloc::vec; +use revm::{ + bytecode::Bytecode, + database::{states::StorageSlot, State}, + primitives::{address, bytes, Address, Bytes, U256}, + state::AccountInfo, + Database, +}; + +/// L1 gas price oracle address. +/// <https://scrollscan.com/address/0x5300000000000000000000000000000000000002> +pub const L1_GAS_PRICE_ORACLE_ADDRESS: Address = + address!("5300000000000000000000000000000000000002"); +/// Bytecode of L1 gas price oracle at Curie transition. +pub const CURIE_L1_GAS_PRICE_ORACLE_BYTECODE: Bytes = bytes!("608060405234801561000f575f80fd5b5060043610610132575f3560e01c8063715018a6116100b4578063a911d77f11610079578063a911d77f1461024c578063bede39b514610254578063de26c4a114610267578063e88a60ad1461027a578063f2fde38b1461028d578063f45e65d8146102a0575f80fd5b8063715018a6146101eb57806384189161146101f35780638da5cb5b146101fc57806393e59dc114610226578063944b247f14610239575f80fd5b80633d0f963e116100fa5780633d0f963e146101a057806349948e0e146101b3578063519b4bd3146101c65780636a5e67e5146101cf57806370465597146101d8575f80fd5b80630c18c1621461013657806313dad5be1461015257806323e524ac1461016f5780633577afc51461017857806339455d3a1461018d575b5f80fd5b61013f60025481565b6040519081526020015b60405180910390f35b60085461015f9060ff1681565b6040519015158152602001610149565b61013f60065481565b61018b6101863660046109b3565b6102a9565b005b61018b61019b3660046109ca565b61033b565b61018b6101ae3660046109ea565b610438565b61013f6101c1366004610a2b565b6104bb565b61013f60015481565b61013f60075481565b61018b6101e63660046109b3565b6104e0565b61018b61056e565b61013f60055481565b5f5461020e906001600160a01b031681565b6040516001600160a01b039091168152602001610149565b60045461020e906001600160a01b031681565b61018b6102473660046109b3565b6105a2565b61018b61062e565b61018b6102623660046109b3565b61068a565b61013f610275366004610a2b565b610747565b61018b6102883660046109b3565b610764565b61018b61029b3660046109ea565b6107f0565b61013f60035481565b5f546001600160a01b031633146102db5760405162461bcd60e51b81526004016102d290610ad6565b60405180910390fd5b621c9c388111156102ff57604051635742c80560e11b815260040160405180910390fd5b60028190556040518181527f32740b35c0ea213650f60d44366b4fb211c9033b50714e4a1d34e65d5beb9bb4906020015b60405180910390a150565b6004805460405163efc7840160e01b815233928101929092526001600160a01b03169063efc7840190602401602060405180830381865afa158015610382573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103a69190610b0d565b6103c3576040516326b3506d60e11b815260040160405180910390fd5b600182905560058190556040518281527f351fb23757bb5ea0546c85b7996ddd7155f96b939ebaa5ff7bc49c75f27f2c449060200160405180910390a16040518181527f9a14bfb5d18c4c3cf14cae19c23d7cf1bcede357ea40ca1f75cd49542c71c214906020015b60405180910390a15050565b5f546001600160a01b031633146104615760405162461bcd60e51b81526004016102d290610ad6565b600480546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527f22d1c35fe072d2e42c3c8f9bd4a0d34aa84a0101d020a62517b33fdb3174e5f7910161042c565b6008545f9060ff16156104d7576104d18261087b565b92915050565b6104d1826108c1565b5f546001600160a01b031633146105095760405162461bcd60e51b81526004016102d290610ad6565b610519633b9aca006103e8610b40565b81111561053957604051631e44fdeb60e11b815260040160405180910390fd5b60038190556040518181527f3336cd9708eaf2769a0f0dc0679f30e80f15dcd88d1921b5a16858e8b85c591a90602001610330565b5f546001600160a01b031633146105975760405162461bcd60e51b81526004016102d290610ad6565b6105a05f610904565b565b5f546001600160a01b031633146105cb5760405162461bcd60e51b81526004016102d290610ad6565b6105d9633b9aca0080610b40565b8111156105f95760405163874f603160e01b815260040160405180910390fd5b60068190556040518181527f2ab3f5a4ebbcbf3c24f62f5454f52f10e1a8c9dcc5acac8f19199ce881a6a10890602001610330565b5f546001600160a01b031633146106575760405162461bcd60e51b81526004016102d290610ad6565b60085460ff161561067b576040516379f9c57560e01b815260040160405180910390fd5b6008805460ff19166001179055565b6004805460405163efc7840160e01b815233928101929092526001600160a01b03169063efc7840190602401602060405180830381865afa1580156106d1573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106f59190610b0d565b610712576040516326b3506d60e11b815260040160405180910390fd5b60018190556040518181527f351fb23757bb5ea0546c85b7996ddd7155f96b939ebaa5ff7bc49c75f27f2c4490602001610330565b6008545f9060ff161561075b57505f919050565b6104d182610953565b5f546001600160a01b0316331461078d5760405162461bcd60e51b81526004016102d290610ad6565b61079b633b9aca0080610b40565b8111156107bb5760405163f37ec21560e01b815260040160405180910390fd5b60078190556040518181527f6b332a036d8c3ead57dcb06c87243bd7a2aed015ddf2d0528c2501dae56331aa90602001610330565b5f546001600160a01b031633146108195760405162461bcd60e51b81526004016102d290610ad6565b6001600160a01b03811661086f5760405162461bcd60e51b815260206004820152601d60248201527f6e6577206f776e657220697320746865207a65726f206164647265737300000060448201526064016102d2565b61087881610904565b50565b5f633b9aca0060055483516007546108939190610b40565b61089d9190610b40565b6001546006546108ad9190610b40565b6108b79190610b57565b6104d19190610b6a565b5f806108cc83610953565b90505f600154826108dd9190610b40565b9050633b9aca00600354826108f29190610b40565b6108fc9190610b6a565b949350505050565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b80515f908190815b818110156109a45784818151811061097557610975610b89565b01602001516001600160f81b0319165f036109955760048301925061099c565b6010830192505b60010161095b565b50506002540160400192915050565b5f602082840312156109c3575f80fd5b5035919050565b5f80604083850312156109db575f80fd5b50508035926020909101359150565b5f602082840312156109fa575f80fd5b81356001600160a01b0381168114610a10575f80fd5b9392505050565b634e487b7160e01b5f52604160045260245ffd5b5f60208284031215610a3b575f80fd5b813567ffffffffffffffff80821115610a52575f80fd5b818401915084601f830112610a65575f80fd5b813581811115610a7757610a77610a17565b604051601f8201601f19908116603f01168101908382118183101715610a9f57610a9f610a17565b81604052828152876020848701011115610ab7575f80fd5b826020860160208301375f928101602001929092525095945050505050565b60208082526017908201527f63616c6c6572206973206e6f7420746865206f776e6572000000000000000000604082015260600190565b5f60208284031215610b1d575f80fd5b81518015158114610a10575f80fd5b634e487b7160e01b5f52601160045260245ffd5b80820281158282048414176104d1576104d1610b2c565b808201808211156104d1576104d1610b2c565b5f82610b8457634e487b7160e01b5f52601260045260245ffd5b500490565b634e487b7160e01b5f52603260045260245ffdfea26469706673582212200c2ac583f18be4f94ab169ae6f2ea3a708a7c0d4424746b120b177adb39e626064736f6c63430008180033"); +/// Storage update of L1 gas price oracle at Curie transition. +pub const CURIE_L1_GAS_PRICE_ORACLE_STORAGE: [(U256, U256); 4] = [ + (L1_BLOB_BASE_FEE_SLOT, INITIAL_L1_BLOB_BASE_FEE), + (COMMIT_SCALAR_SLOT, INITIAL_COMMIT_SCALAR), + (BLOB_SCALAR_SLOT, INITIAL_BLOB_SCALAR), + (IS_CURIE_SLOT, IS_CURIE), +]; + +/// L1 gas price oracle blob base fee slot. Added in the Curie fork. +pub const L1_BLOB_BASE_FEE_SLOT: U256 = U256::from_limbs([5, 0, 0, 0]); +/// L1 gas price oracle commit scalar slot. Added in the Curie fork. +pub const COMMIT_SCALAR_SLOT: U256 = U256::from_limbs([6, 0, 0, 0]); +/// L1 gas price oracle blob scalar slot. Added in the Curie fork. +pub const BLOB_SCALAR_SLOT: U256 = U256::from_limbs([7, 0, 0, 0]); +/// L1 gas price oracle "is Curie" slot. Added in the Curie fork. +pub const IS_CURIE_SLOT: U256 = U256::from_limbs([8, 0, 0, 0]); + +/// The initial blob base fee used by the oracle contract. +pub const INITIAL_L1_BLOB_BASE_FEE: U256 = U256::from_limbs([1, 0, 0, 0]); +/// The initial commit scalar used by the oracle contract. +pub const INITIAL_COMMIT_SCALAR: U256 = U256::from_limbs([230759955285, 0, 0, 0]); +/// The initial blob scalar used by the oracle contract. +pub const INITIAL_BLOB_SCALAR: U256 = U256::from_limbs([417565260, 0, 0, 0]); +/// Curie slot is set to 1 (true) after the Curie block fork. +pub const IS_CURIE: U256 = U256::from_limbs([1, 0, 0, 0]); + +/// Applies the Scroll Curie hard fork to the state: +/// - Updates the L1 oracle contract bytecode to reflect the DA cost reduction. +/// - Sets the initial blob base fee, commit and blob scalar and sets the `isCurie` slot to 1 +/// (true). +pub(super) fn apply_curie_hard_fork<DB: Database>(state: &mut State<DB>) -> Result<(), DB::Error> { + let oracle = state.load_cache_account(L1_GAS_PRICE_ORACLE_ADDRESS)?; + + // compute the code hash + let bytecode = Bytecode::new_raw(CURIE_L1_GAS_PRICE_ORACLE_BYTECODE); + let code_hash = bytecode.hash_slow(); + + // get the old oracle account info + let old_oracle_info = oracle.account_info().unwrap_or_default(); + + // init new oracle account information + let new_oracle_info = AccountInfo { code_hash, code: Some(bytecode), ..old_oracle_info }; + + // init new storage + let new_storage = CURIE_L1_GAS_PRICE_ORACLE_STORAGE + .into_iter() + .map(|(slot, present_value)| { + ( + slot, + StorageSlot { + present_value, + previous_or_original_value: oracle.storage_slot(slot).unwrap_or_default(), + }, + ) + }) + .collect(); + + // create transition for oracle new account info and storage + let transition = oracle.change(new_oracle_info, new_storage); + + // add transition + if let Some(s) = state.transition_state.as_mut() { + s.add_transitions(vec![(L1_GAS_PRICE_ORACLE_ADDRESS, transition)]) + } + + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + use revm::{ + database::{ + states::{bundle_state::BundleRetention, plain_account::PlainStorage, StorageSlot}, + EmptyDB, State, + }, + primitives::{bytes, keccak256, U256}, + state::{AccountInfo, Bytecode}, + Database, + }; + use std::str::FromStr; + + #[test] + fn test_apply_curie_fork() -> eyre::Result<()> { + // init state + let db = EmptyDB::new(); + let mut state = + State::builder().with_database(db).with_bundle_update().without_state_clear().build(); + + // oracle pre fork state + let bytecode_pre_fork = Bytecode::new_raw( bytes!("608060405234801561001057600080fd5b50600436106100cf5760003560e01c8063715018a61161008c578063bede39b511610066578063bede39b51461018d578063de26c4a1146101a0578063f2fde38b146101b3578063f45e65d8146101c657600080fd5b8063715018a6146101475780638da5cb5b1461014f57806393e59dc11461017a57600080fd5b80630c18c162146100d45780633577afc5146100f05780633d0f963e1461010557806349948e0e14610118578063519b4bd31461012b5780637046559714610134575b600080fd5b6100dd60025481565b6040519081526020015b60405180910390f35b6101036100fe366004610671565b6101cf565b005b61010361011336600461068a565b610291565b6100dd6101263660046106d0565b61031c565b6100dd60015481565b610103610142366004610671565b610361565b610103610416565b600054610162906001600160a01b031681565b6040516001600160a01b0390911681526020016100e7565b600454610162906001600160a01b031681565b61010361019b366004610671565b61044c565b6100dd6101ae3660046106d0565b610533565b6101036101c136600461068a565b610595565b6100dd60035481565b6000546001600160a01b031633146102025760405162461bcd60e51b81526004016101f990610781565b60405180910390fd5b621c9c388111156102555760405162461bcd60e51b815260206004820152601760248201527f657863656564206d6178696d756d206f7665726865616400000000000000000060448201526064016101f9565b60028190556040518181527f32740b35c0ea213650f60d44366b4fb211c9033b50714e4a1d34e65d5beb9bb4906020015b60405180910390a150565b6000546001600160a01b031633146102bb5760405162461bcd60e51b81526004016101f990610781565b600480546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527f22d1c35fe072d2e42c3c8f9bd4a0d34aa84a0101d020a62517b33fdb3174e5f7910160405180910390a15050565b60008061032883610533565b905060006001548261033a91906107b8565b9050633b9aca006003548261034f91906107b8565b61035991906107e5565b949350505050565b6000546001600160a01b0316331461038b5760405162461bcd60e51b81526004016101f990610781565b61039b633b9aca006103e86107b8565b8111156103e15760405162461bcd60e51b8152602060048201526014602482015273657863656564206d6178696d756d207363616c6560601b60448201526064016101f9565b60038190556040518181527f3336cd9708eaf2769a0f0dc0679f30e80f15dcd88d1921b5a16858e8b85c591a90602001610286565b6000546001600160a01b031633146104405760405162461bcd60e51b81526004016101f990610781565b61044a6000610621565b565b6004805460405163efc7840160e01b815233928101929092526001600160a01b03169063efc7840190602401602060405180830381865afa158015610495573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104b99190610807565b6104fe5760405162461bcd60e51b81526020600482015260166024820152752737ba103bb434ba32b634b9ba32b21039b2b73232b960511b60448201526064016101f9565b60018190556040518181527f351fb23757bb5ea0546c85b7996ddd7155f96b939ebaa5ff7bc49c75f27f2c4490602001610286565b80516000908190815b818110156105865784818151811061055657610556610829565b01602001516001600160f81b0319166000036105775760048301925061057e565b6010830192505b60010161053c565b50506002540160400192915050565b6000546001600160a01b031633146105bf5760405162461bcd60e51b81526004016101f990610781565b6001600160a01b0381166106155760405162461bcd60e51b815260206004820152601d60248201527f6e6577206f776e657220697320746865207a65726f206164647265737300000060448201526064016101f9565b61061e81610621565b50565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60006020828403121561068357600080fd5b5035919050565b60006020828403121561069c57600080fd5b81356001600160a01b03811681146106b357600080fd5b9392505050565b634e487b7160e01b600052604160045260246000fd5b6000602082840312156106e257600080fd5b813567ffffffffffffffff808211156106fa57600080fd5b818401915084601f83011261070e57600080fd5b813581811115610720576107206106ba565b604051601f8201601f19908116603f01168101908382118183101715610748576107486106ba565b8160405282815287602084870101111561076157600080fd5b826020860160208301376000928101602001929092525095945050505050565b60208082526017908201527f63616c6c6572206973206e6f7420746865206f776e6572000000000000000000604082015260600190565b60008160001904831182151516156107e057634e487b7160e01b600052601160045260246000fd5b500290565b60008261080257634e487b7160e01b600052601260045260246000fd5b500490565b60006020828403121561081957600080fd5b815180151581146106b357600080fd5b634e487b7160e01b600052603260045260246000fdfea26469706673582212205ea335809638809cf032c794fd966e2439020737b1dcc2218435cb438286efcf64736f6c63430008100033")); + let oracle_pre_fork = AccountInfo { + code_hash: bytecode_pre_fork.hash_slow(), + code: Some(bytecode_pre_fork), + ..Default::default() + }; + let oracle_storage_pre_fork = PlainStorage::from_iter([ + (U256::ZERO, U256::from_str("0x13d24a7ff6f5ec5ff0e9c40fc3b8c9c01c65437b")?), + (U256::from(1), U256::from(0x15f50e5e)), + (U256::from(2), U256::from(0x38)), + (U256::from(3), U256::from(0x3e95ba80)), + (U256::from(4), U256::from_str("0x5300000000000000000000000000000000000003")?), + ]); + state.insert_account_with_storage( + L1_GAS_PRICE_ORACLE_ADDRESS, + oracle_pre_fork.clone(), + oracle_storage_pre_fork.clone(), + ); + + // apply curie fork + apply_curie_hard_fork(&mut state)?; + + // merge transitions + state.merge_transitions(BundleRetention::Reverts); + let bundle = state.take_bundle(); + + // check oracle account info + let oracle = bundle.state.get(&L1_GAS_PRICE_ORACLE_ADDRESS).unwrap().clone(); + let code_hash = keccak256(&CURIE_L1_GAS_PRICE_ORACLE_BYTECODE); + let bytecode = Bytecode::new_raw(CURIE_L1_GAS_PRICE_ORACLE_BYTECODE); + let expected_oracle_info = + AccountInfo { code_hash, code: Some(bytecode.clone()), ..Default::default() }; + + assert_eq!(oracle.original_info.unwrap(), oracle_pre_fork); + assert_eq!(oracle.info.unwrap(), expected_oracle_info); + + // check oracle storage changeset + let mut storage = oracle.storage.into_iter().collect::<Vec<(U256, StorageSlot)>>(); + storage.sort_by(|(a, _), (b, _)| a.cmp(b)); + for (got, expected) in storage.into_iter().zip(CURIE_L1_GAS_PRICE_ORACLE_STORAGE) { + assert_eq!(got.0, expected.0); + assert_eq!(got.1, StorageSlot { present_value: expected.1, ..Default::default() }); + } + + // check oracle original storage + for (slot, value) in oracle_storage_pre_fork { + assert_eq!(state.storage(L1_GAS_PRICE_ORACLE_ADDRESS, slot)?, value) + } + + // check deployed contract + assert_eq!(bundle.contracts.get(&code_hash).unwrap().clone(), bytecode); + + Ok(()) + } +}
diff --git reth/crates/scroll/alloy/evm/src/block/feynman.rs scroll-reth/crates/scroll/alloy/evm/src/block/feynman.rs new file mode 100644 index 0000000000000000000000000000000000000000..4f4467507bbe240ed0137426d1f05f98065bff74 --- /dev/null +++ scroll-reth/crates/scroll/alloy/evm/src/block/feynman.rs @@ -0,0 +1,182 @@ +//! Feynman fork transition for Scroll. + +use alloc::vec; +use revm::{ + bytecode::Bytecode, + database::{states::StorageSlot, State}, + primitives::{bytes, Bytes, U256}, + state::AccountInfo, + Database, +}; + +use super::curie::L1_GAS_PRICE_ORACLE_ADDRESS; + +/// Bytecode of L1 gas price oracle at Feynman transition. +const FEYNMAN_L1_GAS_PRICE_ORACLE_BYTECODE: Bytes = bytes!("608060405234801561000f575f80fd5b50600436106101a1575f3560e01c806384189161116100f3578063c63b9e2d11610093578063e88a60ad1161006e578063e88a60ad1461032e578063f2fde38b14610341578063f45e65d814610354578063fe5b04151461035d575f80fd5b8063c63b9e2d146102ff578063c91e514914610312578063de26c4a11461031b575f80fd5b8063944b247f116100ce578063944b247f146102be578063a911d77f146102d1578063aa5e9334146102d9578063bede39b5146102ec575f80fd5b806384189161146102785780638da5cb5b1461028157806393e59dc1146102ab575f80fd5b80633d0f963e1161015e5780636112d6db116101395780636112d6db1461024b5780636a5e67e514610254578063704655971461025d578063715018a614610270575f80fd5b80633d0f963e1461021c57806349948e0e1461022f578063519b4bd314610242575f80fd5b80630c18c162146101a557806313dad5be146101c157806323e524ac146101de5780633577afc5146101e757806339455d3a146101fc5780633b7656bb1461020f575b5f80fd5b6101ae60025481565b6040519081526020015b60405180910390f35b6008546101ce9060ff1681565b60405190151581526020016101b8565b6101ae60065481565b6101fa6101f5366004610c73565b610365565b005b6101fa61020a366004610c8a565b6103f7565b600b546101ce9060ff1681565b6101fa61022a366004610caa565b6104f4565b6101ae61023d366004610ceb565b610577565b6101ae60015481565b6101ae600a5481565b6101ae60075481565b6101fa61026b366004610c73565b6105b0565b6101fa61063e565b6101ae60055481565b5f54610293906001600160a01b031681565b6040516001600160a01b0390911681526020016101b8565b600454610293906001600160a01b031681565b6101fa6102cc366004610c73565b610672565b6101fa6106fe565b6101fa6102e7366004610c73565b61075a565b6101fa6102fa366004610c73565b6107f4565b6101fa61030d366004610c73565b6108b1565b6101ae60095481565b6101ae610329366004610ceb565b61094a565b6101fa61033c366004610c73565b610974565b6101fa61034f366004610caa565b610a00565b6101ae60035481565b6101fa610a8b565b5f546001600160a01b031633146103975760405162461bcd60e51b815260040161038e90610d96565b60405180910390fd5b621c9c388111156103bb57604051635742c80560e11b815260040160405180910390fd5b60028190556040518181527f32740b35c0ea213650f60d44366b4fb211c9033b50714e4a1d34e65d5beb9bb4906020015b60405180910390a150565b6004805460405163efc7840160e01b815233928101929092526001600160a01b03169063efc7840190602401602060405180830381865afa15801561043e573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104629190610dcd565b61047f576040516326b3506d60e11b815260040160405180910390fd5b600182905560058190556040518281527f351fb23757bb5ea0546c85b7996ddd7155f96b939ebaa5ff7bc49c75f27f2c449060200160405180910390a16040518181527f9a14bfb5d18c4c3cf14cae19c23d7cf1bcede357ea40ca1f75cd49542c71c214906020015b60405180910390a15050565b5f546001600160a01b0316331461051d5760405162461bcd60e51b815260040161038e90610d96565b600480546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527f22d1c35fe072d2e42c3c8f9bd4a0d34aa84a0101d020a62517b33fdb3174e5f791016104e8565b600b545f9060ff16156105935761058d82610ae7565b92915050565b60085460ff16156105a75761058d82610b45565b61058d82610b81565b5f546001600160a01b031633146105d95760405162461bcd60e51b815260040161038e90610d96565b6105e9633b9aca006103e8610e00565b81111561060957604051631e44fdeb60e11b815260040160405180910390fd5b60038190556040518181527f3336cd9708eaf2769a0f0dc0679f30e80f15dcd88d1921b5a16858e8b85c591a906020016103ec565b5f546001600160a01b031633146106675760405162461bcd60e51b815260040161038e90610d96565b6106705f610bc4565b565b5f546001600160a01b0316331461069b5760405162461bcd60e51b815260040161038e90610d96565b6106a9633b9aca0080610e00565b8111156106c95760405163874f603160e01b815260040160405180910390fd5b60068190556040518181527f2ab3f5a4ebbcbf3c24f62f5454f52f10e1a8c9dcc5acac8f19199ce881a6a108906020016103ec565b5f546001600160a01b031633146107275760405162461bcd60e51b815260040161038e90610d96565b60085460ff161561074b576040516379f9c57560e01b815260040160405180910390fd5b6008805460ff19166001179055565b5f546001600160a01b031633146107835760405162461bcd60e51b815260040161038e90610d96565b633b9aca008110806107a1575061079e633b9aca0080610e00565b81115b156107bf5760405163d9b5dcdf60e01b815260040160405180910390fd5b60098190556040518181527fd50d3079c77df569cd58d55d4e5614bfe7066449009425d22bde8e75242f50bb906020016103ec565b6004805460405163efc7840160e01b815233928101929092526001600160a01b03169063efc7840190602401602060405180830381865afa15801561083b573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061085f9190610dcd565b61087c576040516326b3506d60e11b815260040160405180910390fd5b60018190556040518181527f351fb23757bb5ea0546c85b7996ddd7155f96b939ebaa5ff7bc49c75f27f2c44906020016103ec565b5f546001600160a01b031633146108da5760405162461bcd60e51b815260040161038e90610d96565b633b9aca008110806108f857506108f5633b9aca0080610e00565b81115b156109155760405162ae184360e01b815260040160405180910390fd5b600a8190556040518181527f8647cebb7e57360673a28415c0bed2f68c42a86c5035f1c9b2eda2b09509288a906020016103ec565b600b545f9060ff168061095f575060085460ff165b1561096b57505f919050565b61058d82610c13565b5f546001600160a01b0316331461099d5760405162461bcd60e51b815260040161038e90610d96565b6109ab633b9aca0080610e00565b8111156109cb5760405163f37ec21560e01b815260040160405180910390fd5b60078190556040518181527f6b332a036d8c3ead57dcb06c87243bd7a2aed015ddf2d0528c2501dae56331aa906020016103ec565b5f546001600160a01b03163314610a295760405162461bcd60e51b815260040161038e90610d96565b6001600160a01b038116610a7f5760405162461bcd60e51b815260206004820152601d60248201527f6e6577206f776e657220697320746865207a65726f2061646472657373000000604482015260640161038e565b610a8881610bc4565b50565b5f546001600160a01b03163314610ab45760405162461bcd60e51b815260040161038e90610d96565b600b5460ff1615610ad857604051631a7c228b60e21b815260040160405180910390fd5b600b805460ff19166001179055565b5f633b9aca0080600a548451600554600754610b039190610e00565b600154600654610b139190610e00565b610b1d9190610e17565b610b279190610e00565b610b319190610e00565b610b3b9190610e2a565b61058d9190610e2a565b5f633b9aca006005548351600754610b5d9190610e00565b610b679190610e00565b600154600654610b779190610e00565b610b3b9190610e17565b5f80610b8c83610c13565b90505f60015482610b9d9190610e00565b9050633b9aca0060035482610bb29190610e00565b610bbc9190610e2a565b949350505050565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b80515f908190815b81811015610c6457848181518110610c3557610c35610e49565b01602001516001600160f81b0319165f03610c5557600483019250610c5c565b6010830192505b600101610c1b565b50506002540160400192915050565b5f60208284031215610c83575f80fd5b5035919050565b5f8060408385031215610c9b575f80fd5b50508035926020909101359150565b5f60208284031215610cba575f80fd5b81356001600160a01b0381168114610cd0575f80fd5b9392505050565b634e487b7160e01b5f52604160045260245ffd5b5f60208284031215610cfb575f80fd5b813567ffffffffffffffff80821115610d12575f80fd5b818401915084601f830112610d25575f80fd5b813581811115610d3757610d37610cd7565b604051601f8201601f19908116603f01168101908382118183101715610d5f57610d5f610cd7565b81604052828152876020848701011115610d77575f80fd5b826020860160208301375f928101602001929092525095945050505050565b60208082526017908201527f63616c6c6572206973206e6f7420746865206f776e6572000000000000000000604082015260600190565b5f60208284031215610ddd575f80fd5b81518015158114610cd0575f80fd5b634e487b7160e01b5f52601160045260245ffd5b808202811582820484141761058d5761058d610dec565b8082018082111561058d5761058d610dec565b5f82610e4457634e487b7160e01b5f52601260045260245ffd5b500490565b634e487b7160e01b5f52603260045260245ffdfea164736f6c6343000818000a"); + +/// L1 gas price oracle compression penalty threshold slot. Added in the Feynman fork. +pub const PENALTY_THRESHOLD_SLOT: U256 = U256::from_limbs([9, 0, 0, 0]); +/// L1 gas price oracle compression penalty factor slot. Added in the Feynman fork. +pub const PENALTY_FACTOR_SLOT: U256 = U256::from_limbs([10, 0, 0, 0]); +/// L1 gas price oracle "is Feynman" slot. Added in the Feynman fork. +pub const IS_FEYNMAN_SLOT: U256 = U256::from_limbs([11, 0, 0, 0]); + +/// The initial compression penalty threshold used by the oracle contract. +const INITIAL_PENALTY_THRESHOLD: U256 = U256::from_limbs([1_000_000_000, 0, 0, 0]); +/// The initial compression penalty factor used by the oracle contract. +const INITIAL_PENALTY_FACTOR: U256 = U256::from_limbs([1_000_000_000, 0, 0, 0]); +/// Feynman slot is set to 1 (true) after the Feynman block fork. +const IS_FEYNMAN: U256 = U256::from_limbs([1, 0, 0, 0]); + +/// Storage update of L1 gas price oracle at Feynman transition. +const FEYNMAN_L1_GAS_PRICE_ORACLE_STORAGE: [(U256, U256); 3] = [ + (PENALTY_THRESHOLD_SLOT, INITIAL_PENALTY_THRESHOLD), + (PENALTY_FACTOR_SLOT, INITIAL_PENALTY_FACTOR), + (IS_FEYNMAN_SLOT, IS_FEYNMAN), +]; + +/// Applies the Scroll Feynman hard fork to the state: +/// - Updates the L1 oracle contract bytecode to reflect the DA cost reduction. +/// - Sets the initial compression penalty threshold and penalty factor values. +/// - Sets the `isFeynman` slot to 1 (true). +pub(super) fn apply_feynman_hard_fork<DB: Database>( + state: &mut State<DB>, +) -> Result<(), DB::Error> { + let oracle = state.load_cache_account(L1_GAS_PRICE_ORACLE_ADDRESS)?; + + // No-op if already applied. + // Note: This requires a storage read for every Feynman block, and it means this + // read needs to be included in the execution witness. Unfortunately, there is no + // other reliable way to apply the change only at the transition block, since + // `ScrollBlockExecutor` does not have access to the parent timestamp. + if matches!(oracle.storage_slot(IS_FEYNMAN_SLOT), Some(val) if val == IS_FEYNMAN) { + return Ok(()) + } + + // compute the code hash + let bytecode = Bytecode::new_raw(FEYNMAN_L1_GAS_PRICE_ORACLE_BYTECODE); + let code_hash = bytecode.hash_slow(); + + // get the old oracle account info + let old_oracle_info = oracle.account_info().unwrap_or_default(); + + // init new oracle account information + let new_oracle_info = AccountInfo { code_hash, code: Some(bytecode), ..old_oracle_info }; + + // init new storage + let new_storage = FEYNMAN_L1_GAS_PRICE_ORACLE_STORAGE + .into_iter() + .map(|(slot, present_value)| { + ( + slot, + StorageSlot { + present_value, + previous_or_original_value: oracle.storage_slot(slot).unwrap_or_default(), + }, + ) + }) + .collect(); + + // create transition for oracle new account info and storage + let transition = oracle.change(new_oracle_info, new_storage); + + // add transition + if let Some(s) = state.transition_state.as_mut() { + s.add_transitions(vec![(L1_GAS_PRICE_ORACLE_ADDRESS, transition)]) + } + + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + use revm::{ + database::{ + states::{bundle_state::BundleRetention, plain_account::PlainStorage, StorageSlot}, + EmptyDB, State, + }, + primitives::{keccak256, U256}, + state::{AccountInfo, Bytecode}, + Database, + }; + use std::str::FromStr; + + use super::super::curie::CURIE_L1_GAS_PRICE_ORACLE_BYTECODE; + + #[test] + fn test_apply_feynman_fork() -> eyre::Result<()> { + // init state + let db = EmptyDB::new(); + let mut state = + State::builder().with_database(db).with_bundle_update().without_state_clear().build(); + + // oracle pre fork state + let bytecode_pre_fork = Bytecode::new_raw(CURIE_L1_GAS_PRICE_ORACLE_BYTECODE); + let oracle_pre_fork = AccountInfo { + code_hash: bytecode_pre_fork.hash_slow(), + code: Some(bytecode_pre_fork), + ..Default::default() + }; + let oracle_storage_pre_fork = PlainStorage::from_iter([ + // owner + (U256::ZERO, U256::from_str("0x13d24a7ff6f5ec5ff0e9c40fc3b8c9c01c65437b")?), + // l1BaseFee + (U256::from(1), U256::from(0x15f50e5e)), + // overhead + (U256::from(2), U256::from(0x38)), + // scalar + (U256::from(3), U256::from(0x3e95ba80)), + // whitelist + (U256::from(4), U256::from_str("0x5300000000000000000000000000000000000003")?), + // l1BlobBaseFee + (U256::from(5), U256::from(0x15f50e5e)), + // commitScalar + (U256::from(6), U256::from(0x3e95ba80)), + // blobScalar + (U256::from(7), U256::from(0x3e95ba80)), + // isCurie + (U256::from(8), U256::from(1)), + ]); + state.insert_account_with_storage( + L1_GAS_PRICE_ORACLE_ADDRESS, + oracle_pre_fork.clone(), + oracle_storage_pre_fork.clone(), + ); + + // apply feynman fork + apply_feynman_hard_fork(&mut state)?; + + // merge transitions + state.merge_transitions(BundleRetention::Reverts); + let bundle = state.take_bundle(); + + // check oracle account info + let oracle = bundle.state.get(&L1_GAS_PRICE_ORACLE_ADDRESS).unwrap().clone(); + let code_hash = keccak256(&FEYNMAN_L1_GAS_PRICE_ORACLE_BYTECODE); + let bytecode = Bytecode::new_raw(FEYNMAN_L1_GAS_PRICE_ORACLE_BYTECODE); + let expected_oracle_info = + AccountInfo { code_hash, code: Some(bytecode.clone()), ..Default::default() }; + + assert_eq!(oracle.original_info.unwrap(), oracle_pre_fork); + assert_eq!(oracle.info.unwrap(), expected_oracle_info); + + // check oracle storage changeset + let mut storage = oracle.storage.into_iter().collect::<Vec<(U256, StorageSlot)>>(); + storage.sort_by(|(a, _), (b, _)| a.cmp(b)); + for (got, expected) in storage.into_iter().zip(FEYNMAN_L1_GAS_PRICE_ORACLE_STORAGE) { + assert_eq!(got.0, expected.0); + assert_eq!(got.1, StorageSlot { present_value: expected.1, ..Default::default() }); + } + + // check oracle original storage + for (slot, value) in oracle_storage_pre_fork { + assert_eq!(state.storage(L1_GAS_PRICE_ORACLE_ADDRESS, slot)?, value) + } + + // check deployed contract + assert_eq!(bundle.contracts.get(&code_hash).unwrap().clone(), bytecode); + + Ok(()) + } +}
diff --git reth/crates/scroll/alloy/evm/src/block/mod.rs scroll-reth/crates/scroll/alloy/evm/src/block/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..fdac4d5e0246537d7b4e640c08691ed98575bd07 --- /dev/null +++ scroll-reth/crates/scroll/alloy/evm/src/block/mod.rs @@ -0,0 +1,401 @@ +pub mod curie; +pub mod feynman; + +pub use receipt_builder::{ReceiptBuilderCtx, ScrollReceiptBuilder}; +mod receipt_builder; + +use crate::{ + block::{ + curie::{apply_curie_hard_fork, L1_GAS_PRICE_ORACLE_ADDRESS}, + feynman::apply_feynman_hard_fork, + }, + system_caller::ScrollSystemCaller, + FromTxWithCompressionRatio, ScrollEvm, ScrollEvmFactory, ScrollTransactionIntoTxEnv, + ToTxWithCompressionRatio, +}; +use alloc::{boxed::Box, format, vec::Vec}; + +use alloy_consensus::{Transaction, TxReceipt, Typed2718}; +use alloy_eips::Encodable2718; +use alloy_evm::{ + block::{ + BlockExecutionError, BlockExecutionResult, BlockExecutor, BlockExecutorFactory, + BlockExecutorFor, BlockValidationError, CommitChanges, ExecutableTx, OnStateHook, + }, + Database, Evm, EvmFactory, FromRecoveredTx, FromTxWithEncoded, +}; +use alloy_primitives::{B256, U256}; +use revm::{ + context::{ + result::{ExecutionResult, InvalidTransaction, ResultAndState}, + TxEnv, + }, + database::State, + handler::PrecompileProvider, + interpreter::InterpreterResult, + DatabaseCommit, Inspector, +}; +use revm_scroll::builder::ScrollContext; +use scroll_alloy_consensus::L1_MESSAGE_TRANSACTION_TYPE; +use scroll_alloy_hardforks::{ScrollHardfork, ScrollHardforks}; + +/// A cache for transaction compression ratios. +pub type ScrollTxCompressionRatios = Vec<U256>; + +/// Context for Scroll Block Execution. +#[derive(Debug, Default, Clone)] +pub struct ScrollBlockExecutionCtx { + /// Parent block hash. + pub parent_hash: B256, +} + +/// Block executor for Scroll. +#[derive(Debug)] +pub struct ScrollBlockExecutor<Evm, R: ScrollReceiptBuilder, Spec> { + /// Spec. + spec: Spec, + /// Receipt builder. + receipt_builder: R, + /// The EVM used by executor. + evm: Evm, + /// Context for block execution. + ctx: ScrollBlockExecutionCtx, + /// Receipts of executed transactions. + receipts: Vec<R::Receipt>, + /// Total gas used by executed transactions. + gas_used: u64, + /// Utility to call system smart contracts. + system_caller: ScrollSystemCaller<Spec>, +} + +impl<E, R: ScrollReceiptBuilder, Spec> ScrollBlockExecutor<E, R, Spec> { + /// Returns the spec for [`ScrollBlockExecutor`]. + pub const fn spec(&self) -> &Spec { + &self.spec + } +} + +impl<E, R, Spec> ScrollBlockExecutor<E, R, Spec> +where + E: EvmExt, + R: ScrollReceiptBuilder, + Spec: ScrollHardforks + Clone, +{ + /// Creates a new [`ScrollBlockExecutor`]. + pub fn new(evm: E, ctx: ScrollBlockExecutionCtx, spec: Spec, receipt_builder: R) -> Self { + Self { + evm, + ctx, + system_caller: ScrollSystemCaller::new(spec.clone()), + spec, + receipt_builder, + receipts: Vec::new(), + gas_used: 0, + } + } +} + +impl<'db, DB, E, R, Spec> ScrollBlockExecutor<E, R, Spec> +where + DB: Database + 'db, + E: EvmExt< + DB = &'db mut State<DB>, + Tx: FromRecoveredTx<R::Transaction> + + FromTxWithEncoded<R::Transaction> + + FromTxWithCompressionRatio<R::Transaction>, + >, + R: ScrollReceiptBuilder<Transaction: Transaction + Encodable2718, Receipt: TxReceipt>, + Spec: ScrollHardforks, +{ + /// Executes all transactions in a block, applying pre and post execution changes. The provided + /// transaction compression ratios are expected to be in the same order as the + /// transactions. + pub fn execute_block_with_compression_cache( + mut self, + transactions: impl IntoIterator< + Item = impl ExecutableTx<Self> + + ToTxWithCompressionRatio<<Self as BlockExecutor>::Transaction>, + >, + compression_ratios: ScrollTxCompressionRatios, + ) -> Result<BlockExecutionResult<R::Receipt>, BlockExecutionError> + where + Self: Sized, + { + self.apply_pre_execution_changes()?; + + for (tx, compression_ratio) in transactions.into_iter().zip(compression_ratios.into_iter()) + { + let tx = tx.with_compression_ratio(compression_ratio); + self.execute_transaction(&tx)?; + } + + self.apply_post_execution_changes() + } +} + +impl<'db, DB, E, R, Spec> BlockExecutor for ScrollBlockExecutor<E, R, Spec> +where + DB: Database + 'db, + E: EvmExt< + DB = &'db mut State<DB>, + Tx: FromRecoveredTx<R::Transaction> + FromTxWithEncoded<R::Transaction>, + >, + R: ScrollReceiptBuilder<Transaction: Transaction + Encodable2718, Receipt: TxReceipt>, + Spec: ScrollHardforks, +{ + type Transaction = R::Transaction; + type Receipt = R::Receipt; + type Evm = E; + + fn apply_pre_execution_changes(&mut self) -> Result<(), BlockExecutionError> { + // set state clear flag if the block is after the Spurious Dragon hardfork. + let state_clear_flag = + self.spec.is_spurious_dragon_active_at_block(self.evm.block().number); + self.evm.db_mut().set_state_clear_flag(state_clear_flag); + + // load the l1 gas oracle contract in cache. + let _ = self + .evm + .db_mut() + .load_cache_account(L1_GAS_PRICE_ORACLE_ADDRESS) + .map_err(BlockExecutionError::other)?; + + // apply gas oracle predeploy upgrade at Curie transition block. + if self + .spec + .scroll_fork_activation(ScrollHardfork::Curie) + .transitions_at_block(self.evm.block().number) + { + if let Err(err) = apply_curie_hard_fork(self.evm.db_mut()) { + return Err(BlockExecutionError::msg(format!( + "error occurred at Curie fork: {err:?}" + ))); + }; + } + + // apply gas oracle predeploy upgrade at Feynman transition block. + if self + .spec + .scroll_fork_activation(ScrollHardfork::Feynman) + .active_at_timestamp(self.evm.block().timestamp) + { + if let Err(err) = apply_feynman_hard_fork(self.evm.db_mut()) { + return Err(BlockExecutionError::msg(format!( + "error occurred at Feynman fork: {err:?}" + ))); + }; + } + + // apply eip-2935. + self.system_caller.apply_blockhashes_contract_call(self.ctx.parent_hash, &mut self.evm)?; + + Ok(()) + } + + fn execute_transaction_with_commit_condition( + &mut self, + tx: impl ExecutableTx<Self>, + f: impl FnOnce(&ExecutionResult<<Self::Evm as Evm>::HaltReason>) -> CommitChanges, + ) -> Result<Option<u64>, BlockExecutionError> { + let chain_spec = &self.spec; + let is_l1_message = tx.tx().ty() == L1_MESSAGE_TRANSACTION_TYPE; + // The sum of the transaction’s gas limit and the gas utilized in this block prior, + // must be no greater than the block’s gasLimit. + let block_available_gas = self.evm.block().gas_limit - self.gas_used; + if tx.tx().gas_limit() > block_available_gas { + return Err(BlockValidationError::TransactionGasLimitMoreThanAvailableBlockGas { + transaction_gas_limit: tx.tx().gas_limit(), + block_available_gas, + } + .into()) + } + + let hash = tx.tx().trie_hash(); + + let block = self.evm.block(); + // verify the transaction type is accepted by the current fork. + if tx.tx().is_eip2930() && !chain_spec.is_curie_active_at_block(block.number) { + return Err(BlockValidationError::InvalidTx { + hash, + error: Box::new(InvalidTransaction::Eip2930NotSupported), + } + .into()) + } + if tx.tx().is_eip1559() && !chain_spec.is_curie_active_at_block(block.number) { + return Err(BlockValidationError::InvalidTx { + hash, + error: Box::new(InvalidTransaction::Eip1559NotSupported), + } + .into()) + } + if tx.tx().is_eip4844() { + return Err(BlockValidationError::InvalidTx { + hash, + error: Box::new(InvalidTransaction::Eip4844NotSupported), + } + .into()) + } + if tx.tx().is_eip7702() && !chain_spec.is_euclid_v2_active_at_timestamp(block.timestamp) { + return Err(BlockValidationError::InvalidTx { + hash, + error: Box::new(InvalidTransaction::Eip7702NotSupported), + } + .into()) + } + + // disable the base fee and nonce checks for l1 messages. + self.evm.with_base_fee_check(!is_l1_message); + self.evm.with_nonce_check(!is_l1_message); + + // execute the transaction and commit the result to the database + let ResultAndState { result, state } = + self.evm.transact(tx).map_err(move |err| BlockExecutionError::evm(err, hash))?; + + if !f(&result).should_commit() { + return Ok(None) + }; + + let l1_fee = if is_l1_message { + U256::ZERO + } else { + // compute l1 fee for all non-l1 transaction + self.evm.l1_fee().expect("l1 fee loaded") + }; + + let gas_used = result.gas_used(); + self.gas_used += gas_used; + + let ctx = ReceiptBuilderCtx::<'_, Self::Transaction, E> { + tx: tx.tx(), + result, + cumulative_gas_used: self.gas_used, + l1_fee, + }; + self.receipts.push(self.receipt_builder.build_receipt(ctx)); + + self.evm.db_mut().commit(state); + + Ok(Some(gas_used)) + } + + fn finish(self) -> Result<(Self::Evm, BlockExecutionResult<R::Receipt>), BlockExecutionError> { + Ok(( + self.evm, + BlockExecutionResult { + receipts: self.receipts, + requests: Default::default(), + gas_used: self.gas_used, + }, + )) + } + + fn set_state_hook(&mut self, _hook: Option<Box<dyn OnStateHook>>) {} + + fn evm_mut(&mut self) -> &mut Self::Evm { + &mut self.evm + } + + fn evm(&self) -> &Self::Evm { + &self.evm + } +} + +/// An extension of the [`Evm`] trait for Scroll. +pub trait EvmExt: Evm { + /// Sets whether the evm should enable or disable the base fee checks. + fn with_base_fee_check(&mut self, enabled: bool); + /// Sets whether the evm should enable or disable the nonce checks. + fn with_nonce_check(&mut self, enabled: bool); + /// Returns the l1 fee for the transaction. + fn l1_fee(&self) -> Option<U256>; +} + +impl<DB, I, P> EvmExt for ScrollEvm<DB, I, P> +where + DB: Database, + I: Inspector<ScrollContext<DB>>, + P: PrecompileProvider<ScrollContext<DB>, Output = InterpreterResult>, +{ + fn with_base_fee_check(&mut self, enabled: bool) { + self.ctx_mut().cfg.disable_base_fee = !enabled; + } + + fn with_nonce_check(&mut self, enabled: bool) { + self.ctx_mut().cfg.disable_nonce_check = !enabled; + } + + fn l1_fee(&self) -> Option<U256> { + let l1_block_info = &self.ctx().chain; + let transaction_rlp_bytes = self.ctx().tx.rlp_bytes.as_ref()?; + let compression_ratio = self.ctx().tx.compression_ratio; + Some(l1_block_info.calculate_tx_l1_cost( + transaction_rlp_bytes, + self.ctx().cfg.spec, + compression_ratio, + )) + } +} + +/// Scroll block executor factory. +#[derive(Debug, Clone, Default, Copy)] +pub struct ScrollBlockExecutorFactory<R, Spec = ScrollHardfork, EvmFactory = ScrollEvmFactory> { + /// Receipt builder. + receipt_builder: R, + /// Chain specification. + spec: Spec, + /// EVM factory. + evm_factory: EvmFactory, +} + +impl<R, Spec, EvmFactory> ScrollBlockExecutorFactory<R, Spec, EvmFactory> { + /// Creates a new [`ScrollBlockExecutorFactory`] with the given receipt builder, spec and + /// factory. + pub const fn new(receipt_builder: R, spec: Spec, evm_factory: EvmFactory) -> Self { + Self { receipt_builder, spec, evm_factory } + } + + /// Exposes the receipt builder. + pub const fn receipt_builder(&self) -> &R { + &self.receipt_builder + } + + /// Exposes the chain specification. + pub const fn spec(&self) -> &Spec { + &self.spec + } + + /// Exposes the EVM factory. + pub const fn evm_factory(&self) -> &EvmFactory { + &self.evm_factory + } +} + +impl<R, Spec> BlockExecutorFactory for ScrollBlockExecutorFactory<R, Spec> +where + R: ScrollReceiptBuilder<Transaction: Transaction + Encodable2718, Receipt: TxReceipt>, + Spec: ScrollHardforks, + ScrollTransactionIntoTxEnv<TxEnv>: + FromRecoveredTx<R::Transaction> + FromTxWithEncoded<R::Transaction>, + Self: 'static, +{ + type EvmFactory = ScrollEvmFactory; + type ExecutionCtx<'a> = ScrollBlockExecutionCtx; + type Transaction = R::Transaction; + type Receipt = R::Receipt; + + fn evm_factory(&self) -> &Self::EvmFactory { + &self.evm_factory + } + + fn create_executor<'a, DB, I>( + &'a self, + evm: <ScrollEvmFactory as EvmFactory>::Evm<&'a mut State<DB>, I>, + ctx: Self::ExecutionCtx<'a>, + ) -> impl BlockExecutorFor<'a, Self, DB, I> + where + DB: Database + 'a, + I: Inspector<ScrollContext<&'a mut State<DB>>> + 'a, + { + ScrollBlockExecutor::new(evm, ctx, &self.spec, &self.receipt_builder) + } +}
diff --git reth/crates/scroll/alloy/evm/src/block/receipt_builder.rs scroll-reth/crates/scroll/alloy/evm/src/block/receipt_builder.rs new file mode 100644 index 0000000000000000000000000000000000000000..eab12b25ba75b95c9f7cbcaeff5f1119cebeacee --- /dev/null +++ scroll-reth/crates/scroll/alloy/evm/src/block/receipt_builder.rs @@ -0,0 +1,32 @@ +use alloy_evm::Evm; +use alloy_primitives::U256; +use core::fmt::Debug; +use revm::context::result::ExecutionResult; + +/// Context for building a receipt. +#[derive(Debug)] +pub struct ReceiptBuilderCtx<'a, T, E: Evm> { + /// Transaction + pub tx: &'a T, + /// Result of transaction execution. + pub result: ExecutionResult<E::HaltReason>, + /// Cumulative gas used. + pub cumulative_gas_used: u64, + /// L1 fee. + pub l1_fee: U256, +} + +/// Type that knows how to build a receipt based on execution result. +#[auto_impl::auto_impl(&, Arc)] +pub trait ScrollReceiptBuilder: Debug { + /// Transaction type. + type Transaction; + /// Receipt type. + type Receipt; + + /// Builds a receipt given a transaction and the result of the execution. + fn build_receipt<'a, E: Evm>( + &self, + ctx: ReceiptBuilderCtx<'a, Self::Transaction, E>, + ) -> Self::Receipt; +}
diff --git reth/crates/scroll/alloy/evm/src/lib.rs scroll-reth/crates/scroll/alloy/evm/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..0c3aa1889f20d612107ce67ff2b7b9e35b1c18ff --- /dev/null +++ scroll-reth/crates/scroll/alloy/evm/src/lib.rs @@ -0,0 +1,275 @@ +//! Alloy Evm API for Scroll. + +#![cfg_attr(not(test), warn(unused_crate_dependencies))] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] +#![cfg_attr(not(feature = "std"), no_std)] + +mod block; +pub use block::{ + curie, feynman, EvmExt, ReceiptBuilderCtx, ScrollBlockExecutionCtx, ScrollBlockExecutor, + ScrollBlockExecutorFactory, ScrollReceiptBuilder, ScrollTxCompressionRatios, +}; + +mod tx; +pub use tx::{ + compute_compression_ratio, FromTxWithCompressionRatio, ScrollTransactionIntoTxEnv, + ToTxWithCompressionRatio, WithCompressionRatio, +}; + +mod system_caller; + +extern crate alloc; + +use alloc::vec::Vec; +use alloy_evm::{precompiles::PrecompilesMap, Database, Evm, EvmEnv, EvmFactory}; +use alloy_primitives::{Address, Bytes, TxKind, U256}; +use core::{ + fmt::Debug, + ops::{Deref, DerefMut}, +}; +use revm::{ + context::{result::HaltReason, BlockEnv, TxEnv}, + context_interface::result::{EVMError, ResultAndState}, + handler::PrecompileProvider, + inspector::NoOpInspector, + interpreter::{interpreter::EthInterpreter, InterpreterResult}, + Context, ExecuteEvm, InspectEvm, Inspector, +}; +use revm_scroll::{ + builder::{ + DefaultScrollContext, EuclidEipActivations, FeynmanEipActivations, ScrollBuilder, + ScrollContext, + }, + instructions::ScrollInstructions, + precompile::ScrollPrecompileProvider, + ScrollSpecId, ScrollTransaction, +}; + +/// Re-export `TX_L1_FEE_PRECISION_U256` from `revm-scroll` for convenience. +pub use revm_scroll::l1block::TX_L1_FEE_PRECISION_U256; + +/// Scroll EVM implementation. +#[allow(missing_debug_implementations)] +pub struct ScrollEvm<DB: Database, I, P = ScrollPrecompileProvider> { + inner: revm_scroll::ScrollEvm< + ScrollContext<DB>, + I, + ScrollInstructions<EthInterpreter, ScrollContext<DB>>, + P, + >, + inspect: bool, +} + +impl<DB: Database, I, P> ScrollEvm<DB, I, P> { + /// Provides a reference to the EVM context. + pub const fn ctx(&self) -> &ScrollContext<DB> { + &self.inner.0.ctx + } + + /// Provides a mutable reference to the EVM context. + pub const fn ctx_mut(&mut self) -> &mut ScrollContext<DB> { + &mut self.inner.0.ctx + } +} + +impl<DB: Database, I, P> Deref for ScrollEvm<DB, I, P> { + type Target = ScrollContext<DB>; + + #[inline] + fn deref(&self) -> &Self::Target { + self.ctx() + } +} + +impl<DB: Database, I, P> DerefMut for ScrollEvm<DB, I, P> { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + self.ctx_mut() + } +} + +impl<DB, I, P> Evm for ScrollEvm<DB, I, P> +where + DB: Database, + I: Inspector<ScrollContext<DB>>, + P: PrecompileProvider<ScrollContext<DB>, Output = InterpreterResult>, +{ + type DB = DB; + type Tx = ScrollTransactionIntoTxEnv<TxEnv>; + type Error = EVMError<DB::Error>; + type HaltReason = HaltReason; + type Spec = ScrollSpecId; + type Precompiles = P; + type Inspector = I; + + fn block(&self) -> &BlockEnv { + &self.block + } + + fn chain_id(&self) -> u64 { + self.cfg.chain_id + } + + fn transact_raw( + &mut self, + tx: Self::Tx, + ) -> Result<ResultAndState<Self::HaltReason>, Self::Error> { + if self.inspect { + self.inner.set_tx(tx.into()); + self.inner.inspect_replay() + } else { + self.inner.transact(tx.into()) + } + } + + fn transact_system_call( + &mut self, + caller: Address, + contract: Address, + data: Bytes, + ) -> Result<ResultAndState<Self::HaltReason>, Self::Error> { + let tx = ScrollTransaction { + base: TxEnv { + caller, + kind: TxKind::Call(contract), + // Explicitly set nonce to 0 so revm does not do any nonce checks + nonce: 0, + gas_limit: 30_000_000, + value: U256::ZERO, + data, + // Setting the gas price to zero enforces that no value is transferred as part of + // the call, and that the call will not count against the block's + // gas limit + gas_price: 0, + // The chain ID check is not relevant here and is disabled if set to None + chain_id: None, + // Setting the gas priority fee to None ensures the effective gas price is derived + // from the `gas_price` field, which we need to be zero + gas_priority_fee: None, + access_list: Default::default(), + // blob fields can be None for this tx + blob_hashes: Vec::new(), + max_fee_per_blob_gas: 0, + tx_type: 0, + authorization_list: Default::default(), + }, + rlp_bytes: Some(Default::default()), + // System transactions (similar to L1MessageTx) do not pay a rollup fee, + // so this field is not used; we just set it to the default value. + compression_ratio: Some(TX_L1_FEE_PRECISION_U256), + }; + + let mut gas_limit = tx.base.gas_limit; + let mut basefee = 0; + let mut disable_nonce_check = true; + + // ensure the block gas limit is >= the tx + core::mem::swap(&mut self.block.gas_limit, &mut gas_limit); + // disable the base fee check for this call by setting the base fee to zero + core::mem::swap(&mut self.block.basefee, &mut basefee); + // disable the nonce check + core::mem::swap(&mut self.cfg.disable_nonce_check, &mut disable_nonce_check); + + let res = self.transact(ScrollTransactionIntoTxEnv::from(tx)); + + // swap back to the previous gas limit + core::mem::swap(&mut self.block.gas_limit, &mut gas_limit); + // swap back to the previous base fee + core::mem::swap(&mut self.block.basefee, &mut basefee); + // swap back to the previous nonce check flag + core::mem::swap(&mut self.cfg.disable_nonce_check, &mut disable_nonce_check); + + res + } + + fn db_mut(&mut self) -> &mut Self::DB { + &mut self.journaled_state.database + } + + fn finish(self) -> (Self::DB, EvmEnv<Self::Spec>) + where + Self: Sized, + { + let Context { block: block_env, cfg: cfg_env, journaled_state, .. } = self.inner.0.ctx; + + (journaled_state.database, EvmEnv { block_env, cfg_env }) + } + + fn set_inspector_enabled(&mut self, enabled: bool) { + self.inspect = enabled; + } + + fn precompiles(&self) -> &Self::Precompiles { + &self.inner.0.precompiles + } + + fn precompiles_mut(&mut self) -> &mut Self::Precompiles { + &mut self.inner.0.precompiles + } + + fn inspector(&self) -> &Self::Inspector { + &self.inner.0.inspector + } + + fn inspector_mut(&mut self) -> &mut Self::Inspector { + &mut self.inner.0.inspector + } +} + +/// Factory producing [`ScrollEvm`]s. +#[derive(Debug, Default, Clone, Copy)] +#[non_exhaustive] +pub struct ScrollEvmFactory; + +impl EvmFactory for ScrollEvmFactory { + type Evm<DB: Database, I: Inspector<ScrollContext<DB>>> = ScrollEvm<DB, I, Self::Precompiles>; + type Context<DB: Database> = ScrollContext<DB>; + type Tx = ScrollTransactionIntoTxEnv<TxEnv>; + type Error<DBError: core::error::Error + Send + Sync + 'static> = EVMError<DBError>; + type HaltReason = HaltReason; + type Spec = ScrollSpecId; + type Precompiles = PrecompilesMap; + + fn create_evm<DB: Database>( + &self, + db: DB, + input: EvmEnv<ScrollSpecId>, + ) -> Self::Evm<DB, NoOpInspector> { + let spec_id = input.cfg_env.spec; + ScrollEvm { + inner: Context::scroll() + .with_db(db) + .with_block(input.block_env) + .with_cfg(input.cfg_env) + .maybe_with_eip_7702() + .maybe_with_eip_7623() + .build_scroll_with_inspector(NoOpInspector {}) + .with_precompiles(PrecompilesMap::from_static( + ScrollPrecompileProvider::new_with_spec(spec_id).precompiles(), + )), + inspect: false, + } + } + + fn create_evm_with_inspector<DB: Database, I: Inspector<Self::Context<DB>>>( + &self, + db: DB, + input: EvmEnv<ScrollSpecId>, + inspector: I, + ) -> Self::Evm<DB, I> { + let spec_id = input.cfg_env.spec; + ScrollEvm { + inner: Context::scroll() + .with_db(db) + .with_block(input.block_env) + .with_cfg(input.cfg_env) + .maybe_with_eip_7702() + .maybe_with_eip_7623() + .build_scroll_with_inspector(inspector) + .with_precompiles(PrecompilesMap::from_static( + ScrollPrecompileProvider::new_with_spec(spec_id).precompiles(), + )), + inspect: true, + } + } +}
diff --git reth/crates/scroll/alloy/evm/src/system_caller.rs scroll-reth/crates/scroll/alloy/evm/src/system_caller.rs new file mode 100644 index 0000000000000000000000000000000000000000..9da990d4c4df27d10a504e8ca664d5d6d7448ab8 --- /dev/null +++ scroll-reth/crates/scroll/alloy/evm/src/system_caller.rs @@ -0,0 +1,203 @@ +use alloc::string::ToString; + +use alloy_eips::eip2935::HISTORY_STORAGE_ADDRESS; +use alloy_evm::{ + block::{BlockExecutionError, BlockValidationError}, + Evm, +}; +use alloy_primitives::B256; +use revm::{context::result::ResultAndState, DatabaseCommit}; +use scroll_alloy_hardforks::ScrollHardforks; + +/// An ephemeral helper type for executing system calls. +/// +/// This can be used to chain system transaction calls. +#[derive(Debug)] +pub(crate) struct ScrollSystemCaller<Spec> { + spec: Spec, +} + +impl<Spec> ScrollSystemCaller<Spec> { + /// Create a new system caller with the given spec. + pub(crate) const fn new(spec: Spec) -> Self { + Self { spec } + } +} + +impl<Spec> ScrollSystemCaller<Spec> +where + Spec: ScrollHardforks, +{ + /// Applies the pre-block call to the EIP-2935 blockhashes contract. + pub(crate) fn apply_blockhashes_contract_call( + &self, + parent_block_hash: B256, + evm: &mut impl Evm<DB: DatabaseCommit>, + ) -> Result<(), BlockExecutionError> { + let result_and_state = + transact_blockhashes_contract_call(&self.spec, parent_block_hash, evm)?; + + if let Some(res) = result_and_state { + evm.db_mut().commit(res.state); + } + + Ok(()) + } +} + +/// Applies the pre-block call to the [EIP-2935] blockhashes contract, using the given block, +/// chain specification, and EVM. +/// +/// If Feynman is not activated, or the block is the genesis block, then this is a no-op, and no +/// state changes are made. +/// +/// Returns `None` if Feynman is not active or the block is the genesis block, otherwise returns the +/// result of the call. +/// +/// [EIP-2935]: https://eips.ethereum.org/EIPS/eip-2935 +#[inline] +fn transact_blockhashes_contract_call<Halt>( + spec: impl ScrollHardforks, + parent_block_hash: B256, + evm: &mut impl Evm<HaltReason = Halt>, +) -> Result<Option<ResultAndState<Halt>>, BlockExecutionError> { + // if Feynman is not active at timestamp then no system transaction occurs. + if !spec.is_feynman_active_at_timestamp(evm.block().timestamp) { + return Ok(None); + } + + // if the block number is zero (genesis block) then no system transaction may occur as per + // EIP-2935 + if evm.block().number == 0 { + return Ok(None); + } + + let res = match evm.transact_system_call( + alloy_eips::eip4788::SYSTEM_ADDRESS, + HISTORY_STORAGE_ADDRESS, + parent_block_hash.0.into(), + ) { + Ok(res) => res, + Err(e) => { + return Err(BlockValidationError::BlockHashContractCall { message: e.to_string() }.into()) + } + }; + + Ok(Some(res)) +} + +#[cfg(test)] +mod tests { + use super::*; + use std::{convert::Infallible, sync::Arc}; + + use crate::curie::L1_GAS_PRICE_ORACLE_ADDRESS; + use alloy_consensus::{Block, BlockBody, Header}; + use alloy_eips::eip2935::HISTORY_STORAGE_CODE; + use alloy_hardforks::ForkCondition; + use alloy_primitives::{keccak256, U256}; + use reth_evm::ConfigureEvm; + use reth_scroll_chainspec::{ScrollChainConfig, ScrollChainSpecBuilder}; + use reth_scroll_evm::ScrollEvmConfig; + use revm::{ + bytecode::Bytecode, + context::ContextTr, + database::{EmptyDBTyped, State}, + state::AccountInfo, + Database, + }; + use scroll_alloy_consensus::ScrollTxEnvelope; + use scroll_alloy_hardforks::{ScrollChainHardforks, ScrollHardfork}; + + #[test] + fn test_should_not_apply_blockhashes_contract_call_before_feynman() { + // initiate system caller. + let system_caller = ScrollSystemCaller::new(ScrollChainHardforks::new([ + (ScrollHardfork::EuclidV2, ForkCondition::Timestamp(0)), + (ScrollHardfork::Feynman, ForkCondition::Timestamp(100)), + ])); + + // initiate db with system contract. + let db = EmptyDBTyped::<Infallible>::new(); + let mut state = + State::builder().with_database(db).with_bundle_update().without_state_clear().build(); + state.insert_account( + HISTORY_STORAGE_ADDRESS, + AccountInfo { + code_hash: keccak256(HISTORY_STORAGE_CODE.clone()), + code: Some(Bytecode::new_raw(HISTORY_STORAGE_CODE.clone())), + ..Default::default() + }, + ); + + // load l1 oracle in state. + state.insert_account(L1_GAS_PRICE_ORACLE_ADDRESS, Default::default()); + + // prepare chain spec. + let chain_spec = + Arc::new(ScrollChainSpecBuilder::scroll_mainnet().build(ScrollChainConfig::mainnet())); + let evm_config = ScrollEvmConfig::scroll(chain_spec); + + let header = Header { + parent_hash: B256::random(), + number: 1, + gas_limit: 20_000_000, + ..Default::default() + }; + let block: Block<ScrollTxEnvelope, _> = Block { header, body: BlockBody::default() }; + + // initiate the evm and apply the block hashes contract call. + let mut evm = evm_config.evm_for_block(state, &block.header); + system_caller.apply_blockhashes_contract_call(block.parent_hash, &mut evm).unwrap(); + + // assert the storage slot remains unchanged. + let parent_hash = evm.db().storage(HISTORY_STORAGE_ADDRESS, U256::ZERO).unwrap(); + assert_eq!(parent_hash, U256::ZERO); + } + + #[test] + fn test_should_apply_blockhashes_contract_call_after_feynman() { + // initiate system caller. + let system_caller = ScrollSystemCaller::new(ScrollChainHardforks::new([( + ScrollHardfork::Feynman, + ForkCondition::Timestamp(0), + )])); + + // initiate db with system contract. + let db = EmptyDBTyped::<Infallible>::new(); + let mut state = + State::builder().with_database(db).with_bundle_update().without_state_clear().build(); + state.insert_account( + HISTORY_STORAGE_ADDRESS, + AccountInfo { + code_hash: keccak256(HISTORY_STORAGE_CODE.clone()), + code: Some(Bytecode::new_raw(HISTORY_STORAGE_CODE.clone())), + ..Default::default() + }, + ); + + // load l1 oracle in state. + state.insert_account(L1_GAS_PRICE_ORACLE_ADDRESS, Default::default()); + + // prepare chain spec. + let chain_spec = + Arc::new(ScrollChainSpecBuilder::scroll_mainnet().build(ScrollChainConfig::mainnet())); + let evm_config = ScrollEvmConfig::scroll(chain_spec); + + let header = Header { + parent_hash: B256::random(), + number: 1, + gas_limit: 20_000_000, + ..Default::default() + }; + let block: Block<ScrollTxEnvelope, _> = Block { header, body: BlockBody::default() }; + + // initiate the evm and apply the block hashes contract call. + let mut evm = evm_config.evm_for_block(state, &block.header); + system_caller.apply_blockhashes_contract_call(block.parent_hash, &mut evm).unwrap(); + + // assert the hash is written to storage. + let parent_hash = evm.db().storage(HISTORY_STORAGE_ADDRESS, U256::ZERO).unwrap(); + assert_eq!(Into::<B256>::into(parent_hash), block.parent_hash); + } +}
diff --git reth/crates/scroll/alloy/evm/src/tx/compression.rs scroll-reth/crates/scroll/alloy/evm/src/tx/compression.rs new file mode 100644 index 0000000000000000000000000000000000000000..e0bbc749eab1aa5f7baee1233cd1987e90f33cf1 --- /dev/null +++ scroll-reth/crates/scroll/alloy/evm/src/tx/compression.rs @@ -0,0 +1,293 @@ +use super::FromRecoveredTx; +use crate::ScrollTransactionIntoTxEnv; +use alloy_consensus::transaction::Recovered; +use alloy_eips::{Encodable2718, Typed2718}; +use alloy_evm::{IntoTxEnv, RecoveredTx}; +use alloy_primitives::{Address, Bytes, TxKind, U256}; +use revm::context::TxEnv; +use revm_scroll::l1block::TX_L1_FEE_PRECISION_U256; +use scroll_alloy_consensus::{ScrollTxEnvelope, TxL1Message}; +pub use zstd_compression::compute_compression_ratio; + +#[cfg(feature = "zstd_compression")] +mod zstd_compression { + use super::*; + use std::io::Write; + use zstd::{ + stream::Encoder, + zstd_safe::{CParameter, ParamSwitch}, + }; + + /// The maximum size of the compression window in bytes (`2^CL_WINDOW_LIMIT`). + const CL_WINDOW_LIMIT: u32 = 22; + + /// The zstd block size target. + const N_BLOCK_SIZE_TARGET: u32 = 124 * 1024; + + fn compressor(target_block_size: u32) -> Encoder<'static, Vec<u8>> { + let mut encoder = Encoder::new(Vec::new(), 0).expect("Failed to create zstd encoder"); + encoder + .set_parameter(CParameter::LiteralCompressionMode(ParamSwitch::Disable)) + .expect("Failed to set literal compression mode"); + encoder + .set_parameter(CParameter::WindowLog(CL_WINDOW_LIMIT)) + .expect("Failed to set window log"); + encoder + .set_parameter(CParameter::TargetCBlockSize(target_block_size)) + .expect("Failed to set target block size"); + encoder.include_checksum(false).expect("Failed to disable checksum"); + encoder.include_magicbytes(false).expect("Failed to disable magic bytes"); + encoder.include_dictid(false).expect("Failed to disable dictid"); + encoder.include_contentsize(true).expect("Failed to include content size"); + encoder + } + + /// Computes the compression ratio for the provided bytes. + /// + /// This is computed as: + /// `max(1, original_size * TX_L1_FEE_PRECISION_U256 / encoded_size)` + pub fn compute_compression_ratio<T: AsRef<[u8]>>(bytes: &T) -> U256 { + // Instantiate the compressor + let mut compressor = compressor(N_BLOCK_SIZE_TARGET); + + // Set the pledged source size to the length of the bytes + // and write the bytes to the compressor. + let original_bytes_len = bytes.as_ref().len(); + compressor + .set_pledged_src_size(Some(original_bytes_len as u64)) + .expect("failed to set pledged source size"); + compressor.write_all(bytes.as_ref()).expect("failed to write bytes to compressor"); + + // Finish the compression and get the result. + let result = compressor.finish().expect("failed to finish compression"); + let encoded_bytes_len = result.len(); + + // Make sure that the compression ratio >= 1.0 + if encoded_bytes_len > original_bytes_len { + return TX_L1_FEE_PRECISION_U256; + } + + // compression_ratio(tx) = size(tx) * PRECISION / size(zstd(tx)) + U256::from(original_bytes_len) + .saturating_mul(TX_L1_FEE_PRECISION_U256) + .wrapping_div(U256::from(encoded_bytes_len)) + } +} + +#[cfg(not(feature = "zstd_compression"))] +mod zstd_compression { + use super::*; + + /// Computes the compression ratio for the provided bytes. This panics if the compression + /// feature is not enabled. This is to support `no_std` environments where zstd is not + /// available. + pub fn compute_compression_ratio<T: AsRef<[u8]>>(_bytes: &T) -> U256 { + panic!("Compression feature is not enabled. Please enable the 'compression' feature to use this function."); + } +} + +/// A generic wrapper for a type that includes a compression ratio and encoded bytes. +#[derive(Debug, Clone)] +pub struct WithCompressionRatio<T> { + // The original value. + value: T, + // The compression ratio: + // compression_ratio = max(1, size(v) * 1e9 / size(compress(v))) + compression_ratio: U256, + // The raw encoded bytes of `value`, without compression. + encoded_bytes: Bytes, +} + +/// A trait for types that can be constructed from a transaction, +/// its sender, encoded bytes and compression ratio. +pub trait FromTxWithCompressionRatio<Tx> { + /// Builds a `TxEnv` from a transaction, its sender, encoded transaction bytes, + /// and a compression ratio. + fn from_tx_with_compression_ratio( + tx: &Tx, + sender: Address, + encoded: Bytes, + compression_ratio: Option<U256>, + ) -> Self; +} + +impl<TxEnv, T> FromTxWithCompressionRatio<&T> for TxEnv +where + TxEnv: FromTxWithCompressionRatio<T>, +{ + fn from_tx_with_compression_ratio( + tx: &&T, + sender: Address, + encoded: Bytes, + compression_ratio: Option<U256>, + ) -> Self { + TxEnv::from_tx_with_compression_ratio(tx, sender, encoded, compression_ratio) + } +} + +impl<T, TxEnv: FromTxWithCompressionRatio<T>> IntoTxEnv<TxEnv> + for WithCompressionRatio<Recovered<T>> +{ + fn into_tx_env(self) -> TxEnv { + let recovered = &self.value; + TxEnv::from_tx_with_compression_ratio( + recovered.inner(), + recovered.signer(), + self.encoded_bytes.clone(), + Some(self.compression_ratio), + ) + } +} + +impl<T, TxEnv: FromTxWithCompressionRatio<T>> IntoTxEnv<TxEnv> + for &WithCompressionRatio<Recovered<T>> +{ + fn into_tx_env(self) -> TxEnv { + let recovered = &self.value; + TxEnv::from_tx_with_compression_ratio( + recovered.inner(), + recovered.signer(), + self.encoded_bytes.clone(), + Some(self.compression_ratio), + ) + } +} + +impl<T, TxEnv: FromTxWithCompressionRatio<T>> IntoTxEnv<TxEnv> + for WithCompressionRatio<&Recovered<T>> +{ + fn into_tx_env(self) -> TxEnv { + let recovered = &self.value; + TxEnv::from_tx_with_compression_ratio( + recovered.inner(), + *recovered.signer(), + self.encoded_bytes.clone(), + Some(self.compression_ratio), + ) + } +} + +impl<T, TxEnv: FromTxWithCompressionRatio<T>> IntoTxEnv<TxEnv> + for &WithCompressionRatio<&Recovered<T>> +{ + fn into_tx_env(self) -> TxEnv { + let recovered = &self.value; + TxEnv::from_tx_with_compression_ratio( + recovered.inner(), + *recovered.signer(), + self.encoded_bytes.clone(), + Some(self.compression_ratio), + ) + } +} + +impl FromTxWithCompressionRatio<ScrollTxEnvelope> for ScrollTransactionIntoTxEnv<TxEnv> { + fn from_tx_with_compression_ratio( + tx: &ScrollTxEnvelope, + caller: Address, + encoded: Bytes, + compression_ratio: Option<U256>, + ) -> Self { + let base = match &tx { + ScrollTxEnvelope::Legacy(tx) => TxEnv::from_recovered_tx(tx.tx(), caller), + ScrollTxEnvelope::Eip2930(tx) => TxEnv::from_recovered_tx(tx.tx(), caller), + ScrollTxEnvelope::Eip1559(tx) => TxEnv::from_recovered_tx(tx.tx(), caller), + ScrollTxEnvelope::Eip7702(tx) => TxEnv::from_recovered_tx(tx.tx(), caller), + ScrollTxEnvelope::L1Message(tx) => { + let TxL1Message { to, value, gas_limit, input, queue_index: _, sender: _ } = &**tx; + TxEnv { + tx_type: tx.ty(), + caller, + gas_limit: *gas_limit, + kind: TxKind::Call(*to), + value: *value, + data: input.clone(), + ..Default::default() + } + } + }; + + Self::new(base, Some(encoded), compression_ratio) + } +} + +/// A trait that allows a type to be converted into [`WithCompressionRatio`]. +pub trait ToTxWithCompressionRatio<Tx> { + /// Converts the type into a [`WithCompressionRatio`] instance using the provided compression + /// ratio. + fn with_compression_ratio( + &self, + compression_ratio: U256, + ) -> WithCompressionRatio<Recovered<&Tx>>; +} + +impl<Tx: Encodable2718> ToTxWithCompressionRatio<Tx> for Recovered<&Tx> { + fn with_compression_ratio( + &self, + compression_ratio: U256, + ) -> WithCompressionRatio<Recovered<&Tx>> { + let encoded_bytes = self.inner().encoded_2718(); + WithCompressionRatio { + value: *self, + compression_ratio, + encoded_bytes: encoded_bytes.into(), + } + } +} + +impl<Tx, T: RecoveredTx<Tx>> RecoveredTx<Tx> for WithCompressionRatio<T> { + fn tx(&self) -> &Tx { + self.value.tx() + } + + fn signer(&self) -> &Address { + self.value.signer() + } +} + +#[cfg(test)] +mod tests { + use super::compute_compression_ratio; + use alloy_primitives::{bytes, U256}; + + #[test] + fn test_compute_compression_ratio() -> eyre::Result<()> { + // eth-transfer + // https://scrollscan.com/tx/0x8c7eba9a56e25c4402a1d9fdbe6fbe70e6f6f89484b2e4f5c329258a924193b4 + let bytes = bytes!("0x02f86b83082750830461a40183830fb782523f94802b65b5d9016621e66003aed0b16615093f328b8080c001a0a1fa6bbede5ae355eaec83fdcda65eab240476895e649576552850de726596cca0424eb1f5221865817b270d85caf8611d35ea6d7c2e86c9c31af5c06df04a2587"); + let ratio = compute_compression_ratio(&bytes); + assert_eq!(ratio, U256::from(1_000_000_000u64)); // 1x (not compressible) + + // scr-transfer + // https://scrollscan.com/tx/0x7b681ce914c9774aff364d2b099b2ba41dea44bcd59dbebb9d4c4b6853893179 + let bytes = bytes!("0x02f8b28308275001830f4240840279876683015c2894d29687c813d741e2f938f4ac377128810e217b1b80b844a9059cbb000000000000000000000000687b50a70d33d71f9a82dd330b8c091e4d77250800000000000000000000000000000000000000000000000ac96dda943e512bb9c080a0fdacacd07ed7c708e2193b803d731d3d288dcd39c317f321f243cd790406868ba0285444ab799632c88fd47c874c218bceb1589843949b5bc0f3ead1df069f3233"); + let ratio = compute_compression_ratio(&bytes); + assert_eq!(ratio, U256::from(1_117_283_950u64)); // 1.1x + + // syncswap-swap + // https://scrollscan.com/tx/0x59a7b72503400b6719f3cb670c7b1e7e45ce5076f30b98bdaad3b07a5d0fbc02 + let bytes = bytes!("0xf902cf830887d783a57282830493e09480e38291e06339d10aab483c65695d004dbd5c6980b902642cc4081e00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000005ec79b80000000000000000000000000000000000000000000000000003328b944c400000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000006000000000000000000000000053000000000000000000000000000000000000040000000000000000000000000000000000000000000000000091a94863ca800000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000814a23b053fd0f102aeeda0459215c2444799c7000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000600000000000000000000000005300000000000000000000000000000000000004000000000000000000000000485ca81b70255da2fe3fd0814b57d1b08fce784e0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000083104ec3a050db0fbfa3fd83aa9077abdd4edb3dc504661d6fb3b39f973fe994de8fb0ac41a044983fa3d16aa0e156a1b3382fa763f9831be5a5c158f849be524d41d100ab52"); + let ratio = compute_compression_ratio(&bytes); + assert_eq!(ratio, U256::from(3_059_322_033u64)); // 3.1x + + // uniswap-swap + // https://scrollscan.com/tx/0x65b268bd8ef416f44983ee277d748de044243272b0f106b71ff03cc8501a05da + let bytes = bytes!("0xf9014e830887e0836b92a7830493e094fc30937f5cde93df8d48acaf7e6f5d8d8a31f63680b8e45023b4df00000000000000000000000006efdbff2a14a7c8e15944d1f4a48f9f95f663a4000000000000000000000000530000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000001f4000000000000000000000000485ca81b70255da2fe3fd0814b57d1b08fce784e000000000000000000000000000000000000000000000000006a94d74f43000000000000000000000000000000000000000000000000000000000000045af675000000000000000000000000000000000000000000000000000000000000000083104ec4a0a527358d5bfb89dcc7939265c6add9faf4697415174723e509f795ad44021d98a0776f4a8a8a51da98b70d960a5bd1faf3c79b8dddc0bc2c642a4c2634c6990f02"); + let ratio = compute_compression_ratio(&bytes); + assert_eq!(ratio, U256::from(1_710_659_898u64)); // 1.7x + + // etherfi-deposit + // https://scrollscan.com/tx/0x41a77736afd54134b6c673e967c9801e326495074012b4033bd557920cbe5a71 + let bytes = bytes!("0x02f901d58308275082f88a834c4b4084044c7166831066099462f623161fdb6564925c3f9b783cbdfef4ce8aec80b9016463baa26000000000000000000000000077a7e3215a621a9935d32a046212ebfcffa3bff900000000000000000000000006efdbff2a14a7c8e15944d1f4a48f9f95f663a400000000000000000000000008c6f91e2b681faf5e17227f2a44c307b3c1364c0000000000000000000000000000000000000000000000000000000002d4cae000000000000000000000000000000000000000000000000000000000028f7f83000000000000000000000000249e3fa81d73244f956ecd529715323b6d02f24b00000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000041a95314c3a11f86cc673f2afd60d27f559cb2edcc0da5af030adffc97f9a5edc3314efbadd32878e289017f644a4afa365da5367fefe583f7c4ff0c6047e2c1ff1b00000000000000000000000000000000000000000000000000000000000000c080a05b2d22b8aaf6d334471e74899cfc4c81186f8a94f278c97ee211727d8027ceafa0450352d9a1782180c27a03889d317d31e725dda38a5bd3c0531950b879ed50a1"); + let ratio = compute_compression_ratio(&bytes); + assert_eq!(ratio, U256::from(1_496_835_443u64)); // 1.4x + + // edgepushoracle-postupdate + // https://scrollscan.com/tx/0x8271c68146a3b07b1ebf52ce0b550751f49cbd72fa0596ef14ff56d1f23a0bec + let bytes = bytes!("0xf9046f83015e4c836e0b6b8303cbf8946a5b3ab3274b738eab25205af6e2d4dd7781292480b9040449a1a4fb000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000005f725f60000000000000000000000000000000000000000000000000000000003d0cac600000000000000000000000000000000000000000000000000000000685d50cd000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000002a0000000000000000000000000000000000000000000000000000000000000004155903b95865fc5a5dd7d4d876456140dd0b815695647fc41eb1924f4cfe267265130b5a5d77125c44cf6a5a81edba6d5850ba00f90ab83281c9b44e17528fd74010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000416f000e0498733998e6a1a6454e116c1b1f95f7e000400b6a54029406cf288bdc615b62de8e2db533d6010ca57001e0b8a4b3f05ed516a31830516c52b9df206e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000410dabc77a807d729ff62c3be740d492d884f026ad2770fa7c4bdec569e201643656b07f2009d2129173738571417734a3df051cebc7b8233bec6d9471c21c098700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041eb009614c939170e9ff3d3e06c3a2c45810fe46a364ce28ecec5e220f5fd86cd6e0f70ab9093dd6b22b69980246496b600c8fcb054047962d4128efa48b692f301000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041a31b4dd4f0a482372d75c7a8c5f11aa8084a5f358579866f1d25a26a15beb2b5153400bfa7fa3d6fba138c02dd1eb8a5a97d62178d98c5632a153396a566e5ed000000000000000000000000000000000000000000000000000000000000000083104ec4a05cb4eee77676d432c672008594825b957d34ae5dd786ed294501849bb1ce285aa01325f37cdc945863ec0474932102bc944cb98a663db6d30ae23c2ebb5f9ce070"); + let ratio = compute_compression_ratio(&bytes); + assert_eq!(ratio, U256::from(2_139_097_744u64)); // 2.1x + + Ok(()) + } +}
diff --git reth/crates/scroll/alloy/evm/src/tx/mod.rs scroll-reth/crates/scroll/alloy/evm/src/tx/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..2a6619dd1e5d4a338d6bafaf39164f72da5a73d3 --- /dev/null +++ scroll-reth/crates/scroll/alloy/evm/src/tx/mod.rs @@ -0,0 +1,274 @@ +use alloy_consensus::crypto::secp256k1::recover_signer; +use alloy_eips::{Encodable2718, Typed2718}; +use alloy_evm::{FromRecoveredTx, FromTxWithEncoded, IntoTxEnv}; +use alloy_primitives::{Address, Bytes, TxKind, B256, U256}; +use core::ops::{Deref, DerefMut}; +use revm::context::{ + either::Either, + transaction::{RecoveredAuthority, RecoveredAuthorization}, + Transaction, TxEnv, +}; +use revm_scroll::ScrollTransaction; +use scroll_alloy_consensus::{ScrollTxEnvelope, TxL1Message, L1_MESSAGE_TRANSACTION_TYPE}; + +mod compression; +pub use compression::{ + compute_compression_ratio, FromTxWithCompressionRatio, ToTxWithCompressionRatio, + WithCompressionRatio, +}; + +/// This structure wraps around a [`ScrollTransaction`] and allows us to implement the [`IntoTxEnv`] +/// trait. This can be removed when the interface is improved. Without this wrapper, we would need +/// to implement the trait in `revm-scroll`, which adds a dependency on `alloy-evm` in the crate. +/// Any changes to `alloy-evm` would require changes to `revm-scroll` which isn't desired. +#[derive(Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct ScrollTransactionIntoTxEnv<T: Transaction>(ScrollTransaction<T>); + +impl<T: Transaction> ScrollTransactionIntoTxEnv<T> { + /// Returns a new [`ScrollTransactionIntoTxEnv`]. + pub fn new(base: T, rlp_bytes: Option<Bytes>, compression_ratio: Option<U256>) -> Self { + Self(ScrollTransaction::new(base, rlp_bytes, compression_ratio)) + } +} + +impl<T: Transaction> From<ScrollTransaction<T>> for ScrollTransactionIntoTxEnv<T> { + fn from(value: ScrollTransaction<T>) -> Self { + Self(value) + } +} + +impl<T: Transaction> From<ScrollTransactionIntoTxEnv<T>> for ScrollTransaction<T> { + fn from(value: ScrollTransactionIntoTxEnv<T>) -> Self { + value.0 + } +} + +impl<T: Transaction> Deref for ScrollTransactionIntoTxEnv<T> { + type Target = ScrollTransaction<T>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<T: Transaction> DerefMut for ScrollTransactionIntoTxEnv<T> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl<T: Transaction> IntoTxEnv<Self> for ScrollTransactionIntoTxEnv<T> { + fn into_tx_env(self) -> Self { + self + } +} + +impl<T: Transaction> Transaction for ScrollTransactionIntoTxEnv<T> { + type AccessListItem<'a> + = T::AccessListItem<'a> + where + T: 'a; + type Authorization<'a> + = T::Authorization<'a> + where + T: 'a; + + fn tx_type(&self) -> u8 { + self.0.tx_type() + } + + fn caller(&self) -> Address { + self.0.caller() + } + + fn gas_limit(&self) -> u64 { + self.0.gas_limit() + } + + fn value(&self) -> U256 { + self.0.value() + } + + fn input(&self) -> &Bytes { + self.0.input() + } + + fn nonce(&self) -> u64 { + self.0.nonce() + } + + fn kind(&self) -> TxKind { + self.0.kind() + } + + fn chain_id(&self) -> Option<u64> { + self.0.chain_id() + } + + fn gas_price(&self) -> u128 { + self.0.gas_price() + } + + fn access_list( + &self, + ) -> Option<impl Iterator<Item = <ScrollTransaction<T> as Transaction>::AccessListItem<'_>>> + { + self.0.access_list() + } + + fn blob_versioned_hashes(&self) -> &[B256] { + self.0.blob_versioned_hashes() + } + + fn max_fee_per_blob_gas(&self) -> u128 { + self.0.max_fee_per_blob_gas() + } + + fn authorization_list_len(&self) -> usize { + self.0.authorization_list_len() + } + + fn authorization_list(&self) -> impl Iterator<Item = Self::Authorization<'_>> { + self.0.authorization_list() + } + + fn max_priority_fee_per_gas(&self) -> Option<u128> { + self.0.max_priority_fee_per_gas() + } +} + +impl FromTxWithEncoded<ScrollTxEnvelope> for ScrollTransactionIntoTxEnv<TxEnv> { + fn from_encoded_tx(tx: &ScrollTxEnvelope, caller: Address, encoded: Bytes) -> Self { + let base = match &tx { + ScrollTxEnvelope::Legacy(tx) => TxEnv::from_recovered_tx(tx.tx(), caller), + ScrollTxEnvelope::Eip2930(tx) => TxEnv::from_recovered_tx(tx.tx(), caller), + ScrollTxEnvelope::Eip1559(tx) => TxEnv::from_recovered_tx(tx.tx(), caller), + ScrollTxEnvelope::Eip7702(tx) => TxEnv::from_recovered_tx(tx.tx(), caller), + ScrollTxEnvelope::L1Message(tx) => { + let TxL1Message { to, value, gas_limit, input, queue_index: _, sender: _ } = &**tx; + TxEnv { + tx_type: tx.ty(), + caller, + gas_limit: *gas_limit, + kind: TxKind::Call(*to), + value: *value, + data: input.clone(), + ..Default::default() + } + } + }; + + let encoded = (!tx.is_l1_message()).then_some(encoded); + let compression_ratio = encoded.as_ref().map(compute_compression_ratio); + Self::new(base, encoded, compression_ratio) + } +} + +impl FromRecoveredTx<ScrollTxEnvelope> for ScrollTransactionIntoTxEnv<TxEnv> { + fn from_recovered_tx(tx: &ScrollTxEnvelope, sender: Address) -> Self { + let envelope = tx.encoded_2718(); + + let base = match &tx { + ScrollTxEnvelope::Legacy(tx) => TxEnv { + gas_limit: tx.tx().gas_limit, + gas_price: tx.tx().gas_price, + gas_priority_fee: None, + kind: tx.tx().to, + value: tx.tx().value, + data: tx.tx().input.clone(), + chain_id: tx.tx().chain_id, + nonce: tx.tx().nonce, + access_list: Default::default(), + blob_hashes: Default::default(), + max_fee_per_blob_gas: Default::default(), + authorization_list: Default::default(), + tx_type: 0, + caller: sender, + }, + ScrollTxEnvelope::Eip2930(tx) => TxEnv { + gas_limit: tx.tx().gas_limit, + gas_price: tx.tx().gas_price, + gas_priority_fee: None, + kind: tx.tx().to, + value: tx.tx().value, + data: tx.tx().input.clone(), + chain_id: Some(tx.tx().chain_id), + nonce: tx.tx().nonce, + access_list: tx.tx().access_list.clone(), + blob_hashes: Default::default(), + max_fee_per_blob_gas: Default::default(), + authorization_list: Default::default(), + tx_type: 1, + caller: sender, + }, + ScrollTxEnvelope::Eip1559(tx) => TxEnv { + gas_limit: tx.tx().gas_limit, + gas_price: tx.tx().max_fee_per_gas, + gas_priority_fee: Some(tx.tx().max_priority_fee_per_gas), + kind: tx.tx().to, + value: tx.tx().value, + data: tx.tx().input.clone(), + chain_id: Some(tx.tx().chain_id), + nonce: tx.tx().nonce, + access_list: tx.tx().access_list.clone(), + blob_hashes: Default::default(), + max_fee_per_blob_gas: Default::default(), + authorization_list: Default::default(), + tx_type: 2, + caller: sender, + }, + ScrollTxEnvelope::Eip7702(tx) => TxEnv { + gas_limit: tx.tx().gas_limit, + gas_price: tx.tx().max_fee_per_gas, + gas_priority_fee: Some(tx.tx().max_priority_fee_per_gas), + kind: tx.tx().to.into(), + value: tx.tx().value, + data: tx.tx().input.clone(), + chain_id: Some(tx.tx().chain_id), + nonce: tx.tx().nonce, + access_list: tx.tx().access_list.clone(), + blob_hashes: Default::default(), + max_fee_per_blob_gas: Default::default(), + authorization_list: tx + .tx() + .authorization_list + .iter() + .map(|auth| { + Either::Right(RecoveredAuthorization::new_unchecked( + auth.inner().clone(), + auth.signature() + .ok() + .and_then(|signature| { + recover_signer(&signature, auth.signature_hash()).ok() + }) + .map_or(RecoveredAuthority::Invalid, RecoveredAuthority::Valid), + )) + }) + .collect(), + tx_type: 4, + caller: sender, + }, + ScrollTxEnvelope::L1Message(tx) => TxEnv { + gas_limit: tx.gas_limit, + gas_price: 0, + gas_priority_fee: None, + kind: TxKind::Call(tx.to), + value: tx.value, + data: tx.input.clone(), + chain_id: None, + nonce: 0, + access_list: Default::default(), + blob_hashes: Default::default(), + max_fee_per_blob_gas: Default::default(), + authorization_list: Default::default(), + tx_type: L1_MESSAGE_TRANSACTION_TYPE, + caller: sender, + }, + }; + + let rlp_bytes = (!tx.is_l1_message()).then_some(envelope.into()); + let compression_ratio = rlp_bytes.as_ref().map(compute_compression_ratio); + Self::new(base, rlp_bytes, compression_ratio) + } +}
diff --git reth/crates/scroll/alloy/hardforks/Cargo.toml scroll-reth/crates/scroll/alloy/hardforks/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..7dea06ea9bbaa8c5bc6fbc2477fb2c49420db09a --- /dev/null +++ scroll-reth/crates/scroll/alloy/hardforks/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "scroll-alloy-hardforks" +version.workspace = true +edition.workspace = true +rust-version.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +exclude.workspace = true + +[lints] +workspace = true + +[dependencies] +# alloy +alloy-hardforks.workspace = true + +# misc +auto_impl = { workspace = true, default-features = false } +serde = { workspace = true, optional = true } + +[features] +default = ["std"] +std = ["serde?/std"] +serde = ["dep:serde", "alloy-hardforks/serde"]
diff --git reth/crates/scroll/alloy/hardforks/src/hardfork.rs scroll-reth/crates/scroll/alloy/hardforks/src/hardfork.rs new file mode 100644 index 0000000000000000000000000000000000000000..c942c840d4d3326b553959ab1e95898ee92d8a46 --- /dev/null +++ scroll-reth/crates/scroll/alloy/hardforks/src/hardfork.rs @@ -0,0 +1,89 @@ +//! Hard forks of scroll protocol. + +use alloy_hardforks::{hardfork, ForkCondition}; + +hardfork!( + /// The name of the Scroll hardfork + #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] + ScrollHardfork { + /// Archimedes: scroll test hardfork. + Archimedes, + /// Bernoulli: <https://scroll.io/blog/blobs-are-here-scrolls-bernoulli-upgrade>. + Bernoulli, + /// Curie: <https://scroll.io/blog/compressing-the-gas-scrolls-curie-upgrade>. + Curie, + /// Darwin: <https://scroll.io/blog/proof-recursion-scrolls-darwin-upgrade>. + Darwin, + /// DarwinV2 <https://x.com/Scroll_ZKP/status/1830565514755584269>. + DarwinV2, + /// Euclid <https://docs.scroll.io/en/technology/overview/scroll-upgrades/euclid-upgrade/> + Euclid, + /// EuclidV2 <https://docs.scroll.io/en/technology/overview/scroll-upgrades/euclid-upgrade/> + EuclidV2, + /// Feynman <https://docs.scroll.io/en/technology/overview/scroll-upgrades/feynman-upgrade/> + Feynman + } +); + +impl ScrollHardfork { + /// Scroll mainnet list of hardforks. + pub const fn scroll_mainnet() -> [(Self, ForkCondition); 8] { + [ + (Self::Archimedes, ForkCondition::Block(0)), + (Self::Bernoulli, ForkCondition::Block(5220340)), + (Self::Curie, ForkCondition::Block(7096836)), + (Self::Darwin, ForkCondition::Timestamp(1724227200)), + (Self::DarwinV2, ForkCondition::Timestamp(1725264000)), + (Self::Euclid, ForkCondition::Timestamp(1744815600)), + (Self::EuclidV2, ForkCondition::Timestamp(1745305200)), + // TODO: update + (Self::Feynman, ForkCondition::Timestamp(u64::MAX)), + ] + } + + /// Scroll sepolia list of hardforks. + pub const fn scroll_sepolia() -> [(Self, ForkCondition); 8] { + [ + (Self::Archimedes, ForkCondition::Block(0)), + (Self::Bernoulli, ForkCondition::Block(3747132)), + (Self::Curie, ForkCondition::Block(4740239)), + (Self::Darwin, ForkCondition::Timestamp(1723622400)), + (Self::DarwinV2, ForkCondition::Timestamp(1724832000)), + (Self::Euclid, ForkCondition::Timestamp(1741680000)), + (Self::EuclidV2, ForkCondition::Timestamp(1741852800)), + // TODO: update + (Self::Feynman, ForkCondition::Timestamp(u64::MAX)), + ] + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::str::FromStr; + + #[test] + fn check_scroll_hardfork_from_str() { + let hardfork_str = + ["BernOulLi", "CUrie", "DaRwIn", "DaRwInV2", "EUcliD", "eUClidv2", "FEYnmaN"]; + let expected_hardforks = [ + ScrollHardfork::Bernoulli, + ScrollHardfork::Curie, + ScrollHardfork::Darwin, + ScrollHardfork::DarwinV2, + ScrollHardfork::Euclid, + ScrollHardfork::EuclidV2, + ScrollHardfork::Feynman, + ]; + + let hardforks: Vec<ScrollHardfork> = + hardfork_str.iter().map(|h| ScrollHardfork::from_str(h).unwrap()).collect(); + + assert_eq!(hardforks, expected_hardforks); + } + + #[test] + fn check_nonexistent_hardfork_from_str() { + assert!(ScrollHardfork::from_str("not a hardfork").is_err()); + } +}
diff --git reth/crates/scroll/alloy/hardforks/src/lib.rs scroll-reth/crates/scroll/alloy/hardforks/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..a4b755ac03b36069fdf976e335a45e4994873fd3 --- /dev/null +++ scroll-reth/crates/scroll/alloy/hardforks/src/lib.rs @@ -0,0 +1,104 @@ +//! Scroll-Reth hard forks. + +#![cfg_attr(not(feature = "std"), no_std)] +#[cfg(not(feature = "std"))] +extern crate alloc as std; + +use alloy_hardforks::{EthereumHardfork, EthereumHardforks, ForkCondition}; +use std::vec::Vec; + +pub use hardfork::ScrollHardfork; +pub mod hardfork; + +/// Extends [`EthereumHardforks`] with scroll helper methods. +#[auto_impl::auto_impl(&, Arc)] +pub trait ScrollHardforks: EthereumHardforks { + /// Retrieves [`ForkCondition`] by an [`ScrollHardfork`]. If `fork` is not present, returns + /// [`ForkCondition::Never`]. + fn scroll_fork_activation(&self, fork: ScrollHardfork) -> ForkCondition; + + /// Convenience method to check if [`Bernoulli`](ScrollHardfork::Bernoulli) is active at a given + /// block number. + fn is_bernoulli_active_at_block(&self, block_number: u64) -> bool { + self.scroll_fork_activation(ScrollHardfork::Bernoulli).active_at_block(block_number) + } + + /// Returns `true` if [`Curie`](ScrollHardfork::Curie) is active at given block block number. + fn is_curie_active_at_block(&self, block_number: u64) -> bool { + self.scroll_fork_activation(ScrollHardfork::Curie).active_at_block(block_number) + } + + /// Returns `true` if [`Darwin`](ScrollHardfork::Darwin) is active at given block timestamp. + fn is_darwin_active_at_timestamp(&self, timestamp: u64) -> bool { + self.scroll_fork_activation(ScrollHardfork::Darwin).active_at_timestamp(timestamp) + } + + /// Returns `true` if [`DarwinV2`](ScrollHardfork::DarwinV2) is active at given block timestamp. + fn is_darwin_v2_active_at_timestamp(&self, timestamp: u64) -> bool { + self.scroll_fork_activation(ScrollHardfork::DarwinV2).active_at_timestamp(timestamp) + } + + /// Returns `true` if [`Euclid`](ScrollHardfork::Euclid) is active at given block timestamp. + fn is_euclid_active_at_timestamp(&self, timestamp: u64) -> bool { + self.scroll_fork_activation(ScrollHardfork::Euclid).active_at_timestamp(timestamp) + } + + /// Returns `true` if [`EuclidV2`](ScrollHardfork::EuclidV2) is active at given block timestamp. + fn is_euclid_v2_active_at_timestamp(&self, timestamp: u64) -> bool { + self.scroll_fork_activation(ScrollHardfork::EuclidV2).active_at_timestamp(timestamp) + } + + /// Returns `true` if [`Feynman`](ScrollHardfork::Feynman) is active at given block timestamp. + fn is_feynman_active_at_timestamp(&self, timestamp: u64) -> bool { + self.scroll_fork_activation(ScrollHardfork::Feynman).active_at_timestamp(timestamp) + } +} + +/// A type allowing to configure activation [`ForkCondition`]s for a given list of +/// [`ScrollHardfork`]s. +#[derive(Debug, Clone)] +pub struct ScrollChainHardforks { + /// Scroll hardfork activations. + forks: Vec<(ScrollHardfork, ForkCondition)>, +} + +impl ScrollChainHardforks { + /// Creates a new [`ScrollChainHardforks`] with the given list of forks. + pub fn new(forks: impl IntoIterator<Item = (ScrollHardfork, ForkCondition)>) -> Self { + let mut forks = forks.into_iter().collect::<Vec<_>>(); + forks.sort(); + Self { forks } + } + + /// Creates a new [`ScrollChainHardforks`] with Scroll mainnet configuration. + pub fn scroll_mainnet() -> Self { + Self::new(ScrollHardfork::scroll_mainnet()) + } + + /// Creates a new [`ScrollChainHardforks`] with Scroll Sepolia configuration. + pub fn scroll_sepolia() -> Self { + Self::new(ScrollHardfork::scroll_sepolia()) + } +} + +impl EthereumHardforks for ScrollChainHardforks { + fn ethereum_fork_activation(&self, fork: EthereumHardfork) -> ForkCondition { + if fork < EthereumHardfork::ArrowGlacier { + ForkCondition::Block(0) + } else if fork <= EthereumHardfork::Shanghai { + self.scroll_fork_activation(ScrollHardfork::Bernoulli) + } else { + ForkCondition::Never + } + } +} + +impl ScrollHardforks for ScrollChainHardforks { + fn scroll_fork_activation(&self, fork: ScrollHardfork) -> ForkCondition { + let Ok(idx) = self.forks.binary_search_by(|(f, _)| f.cmp(&fork)) else { + return ForkCondition::Never; + }; + + self.forks[idx].1 + } +}
diff --git reth/crates/scroll/alloy/network/Cargo.toml scroll-reth/crates/scroll/alloy/network/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..24449a64ec846f1f7984597a68bbe66f0ecb9a4a --- /dev/null +++ scroll-reth/crates/scroll/alloy/network/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "scroll-alloy-network" +version.workspace = true +edition.workspace = true +rust-version.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +exclude.workspace = true + +[lints] +workspace = true + +[dependencies] +alloy-provider = { workspace = true, default-features = false } + +scroll-alloy-consensus = { workspace = true, default-features = false } +scroll-alloy-rpc-types = { workspace = true, default-features = false } + +# alloy +alloy-consensus = { workspace = true, default-features = false } +alloy-network = { workspace = true, default-features = false } +alloy-primitives = { workspace = true, default-features = false } +alloy-rpc-types-eth = { workspace = true, default-features = false } +alloy-signer = { workspace = true, default-features = false } + +[features] +std = [ + "alloy-consensus/std", + "alloy-primitives/std", + "alloy-rpc-types-eth/std", + "scroll-alloy-consensus/std", + "scroll-alloy-rpc-types/std", +]
diff --git reth/crates/scroll/alloy/network/README.md scroll-reth/crates/scroll/alloy/network/README.md new file mode 100644 index 0000000000000000000000000000000000000000..da1f2fd66158a70e84a5a764eb413aea6be31f8f --- /dev/null +++ scroll-reth/crates/scroll/alloy/network/README.md @@ -0,0 +1,8 @@ +# scroll-alloy-network + +Scroll blockchain RPC behavior abstraction. + +This crate contains a simple abstraction of the RPC behavior of an +Scroll blockchain. It is intended to be used by the Alloy client to +provide a consistent interface to the rest of the library, regardless of +changes the underlying blockchain makes to the RPC interface.
diff --git reth/crates/scroll/alloy/network/src/lib.rs scroll-reth/crates/scroll/alloy/network/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..102dd4247d18cb4e46406ff316a194e654ab5770 --- /dev/null +++ scroll-reth/crates/scroll/alloy/network/src/lib.rs @@ -0,0 +1,241 @@ +#![doc = include_str!("../README.md")] +#![cfg_attr(not(test), warn(unused_crate_dependencies))] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] + +use alloy_consensus::{TxEnvelope, TxType, TypedTransaction}; +pub use alloy_network::*; +use alloy_primitives::{Address, Bytes, ChainId, TxKind, U256}; +use alloy_provider::fillers::{ + ChainIdFiller, GasFiller, JoinFill, NonceFiller, RecommendedFillers, +}; +use alloy_rpc_types_eth::AccessList; +use scroll_alloy_consensus::{self, ScrollTxEnvelope, ScrollTxType, ScrollTypedTransaction}; +use scroll_alloy_rpc_types::ScrollTransactionRequest; + +/// Types for a Scroll-stack network. +#[derive(Clone, Copy, Debug)] +pub struct Scroll { + _private: (), +} + +impl Network for Scroll { + type TxType = ScrollTxType; + + type TxEnvelope = scroll_alloy_consensus::ScrollTxEnvelope; + + type UnsignedTx = scroll_alloy_consensus::ScrollTypedTransaction; + + type ReceiptEnvelope = scroll_alloy_consensus::ScrollReceiptEnvelope; + + type Header = alloy_consensus::Header; + + type TransactionRequest = scroll_alloy_rpc_types::ScrollTransactionRequest; + + type TransactionResponse = scroll_alloy_rpc_types::Transaction; + + type ReceiptResponse = scroll_alloy_rpc_types::ScrollTransactionReceipt; + + type HeaderResponse = alloy_rpc_types_eth::Header; + + type BlockResponse = + alloy_rpc_types_eth::Block<Self::TransactionResponse, Self::HeaderResponse>; +} + +impl TransactionBuilder<Scroll> for ScrollTransactionRequest { + fn chain_id(&self) -> Option<ChainId> { + self.as_ref().chain_id() + } + + fn set_chain_id(&mut self, chain_id: ChainId) { + self.as_mut().set_chain_id(chain_id); + } + + fn nonce(&self) -> Option<u64> { + self.as_ref().nonce() + } + + fn set_nonce(&mut self, nonce: u64) { + self.as_mut().set_nonce(nonce); + } + + fn input(&self) -> Option<&Bytes> { + self.as_ref().input() + } + + fn set_input<T: Into<Bytes>>(&mut self, input: T) { + self.as_mut().set_input(input); + } + + fn from(&self) -> Option<Address> { + self.as_ref().from() + } + + fn set_from(&mut self, from: Address) { + self.as_mut().set_from(from); + } + + fn kind(&self) -> Option<TxKind> { + self.as_ref().kind() + } + + fn clear_kind(&mut self) { + self.as_mut().clear_kind(); + } + + fn set_kind(&mut self, kind: TxKind) { + self.as_mut().set_kind(kind); + } + + fn value(&self) -> Option<U256> { + self.as_ref().value() + } + + fn set_value(&mut self, value: U256) { + self.as_mut().set_value(value); + } + + fn gas_price(&self) -> Option<u128> { + self.as_ref().gas_price() + } + + fn set_gas_price(&mut self, gas_price: u128) { + self.as_mut().set_gas_price(gas_price); + } + + fn max_fee_per_gas(&self) -> Option<u128> { + self.as_ref().max_fee_per_gas() + } + + fn set_max_fee_per_gas(&mut self, max_fee_per_gas: u128) { + self.as_mut().set_max_fee_per_gas(max_fee_per_gas); + } + + fn max_priority_fee_per_gas(&self) -> Option<u128> { + self.as_ref().max_priority_fee_per_gas() + } + + fn set_max_priority_fee_per_gas(&mut self, max_priority_fee_per_gas: u128) { + self.as_mut().set_max_priority_fee_per_gas(max_priority_fee_per_gas); + } + + fn gas_limit(&self) -> Option<u64> { + self.as_ref().gas_limit() + } + + fn set_gas_limit(&mut self, gas_limit: u64) { + self.as_mut().set_gas_limit(gas_limit); + } + + fn access_list(&self) -> Option<&AccessList> { + self.as_ref().access_list() + } + + fn set_access_list(&mut self, access_list: AccessList) { + self.as_mut().set_access_list(access_list); + } + + fn complete_type(&self, ty: ScrollTxType) -> Result<(), Vec<&'static str>> { + match ty { + ScrollTxType::L1Message => Err(vec!["not implemented for L1 message tx"]), + _ => { + let ty = TxType::try_from(ty as u8).unwrap(); + self.as_ref().complete_type(ty) + } + } + } + + fn can_submit(&self) -> bool { + self.as_ref().can_submit() + } + + fn can_build(&self) -> bool { + self.as_ref().can_build() + } + + #[doc(alias = "output_transaction_type")] + fn output_tx_type(&self) -> ScrollTxType { + match self.as_ref().preferred_type() { + TxType::Eip1559 | TxType::Eip4844 => ScrollTxType::Eip1559, + TxType::Eip2930 => ScrollTxType::Eip2930, + TxType::Legacy => ScrollTxType::Legacy, + TxType::Eip7702 => ScrollTxType::Eip7702, + } + } + + #[doc(alias = "output_transaction_type_checked")] + fn output_tx_type_checked(&self) -> Option<ScrollTxType> { + self.as_ref().buildable_type().map(|tx_ty| match tx_ty { + TxType::Eip1559 | TxType::Eip4844 => ScrollTxType::Eip1559, + TxType::Eip2930 => ScrollTxType::Eip2930, + TxType::Legacy => ScrollTxType::Legacy, + TxType::Eip7702 => ScrollTxType::Eip7702, + }) + } + + fn prep_for_submission(&mut self) { + self.as_mut().prep_for_submission(); + } + + fn build_unsigned(self) -> BuildResult<ScrollTypedTransaction, Scroll> { + if let Err((tx_type, missing)) = self.as_ref().missing_keys() { + let tx_type = ScrollTxType::try_from(tx_type as u8).unwrap(); + return Err(TransactionBuilderError::InvalidTransactionRequest(tx_type, missing) + .into_unbuilt(self)); + } + Ok(self.build_typed_tx().expect("checked by missing_keys")) + } + + async fn build<W: NetworkWallet<Scroll>>( + self, + wallet: &W, + ) -> Result<<Scroll as Network>::TxEnvelope, TransactionBuilderError<Scroll>> { + Ok(wallet.sign_request(self).await?) + } +} + +impl NetworkWallet<Scroll> for EthereumWallet { + fn default_signer_address(&self) -> Address { + NetworkWallet::<Ethereum>::default_signer_address(self) + } + + fn has_signer_for(&self, address: &Address) -> bool { + NetworkWallet::<Ethereum>::has_signer_for(self, address) + } + + fn signer_addresses(&self) -> impl Iterator<Item = Address> { + NetworkWallet::<Ethereum>::signer_addresses(self) + } + + async fn sign_transaction_from( + &self, + sender: Address, + tx: ScrollTypedTransaction, + ) -> alloy_signer::Result<ScrollTxEnvelope> { + let tx = match tx { + ScrollTypedTransaction::Legacy(tx) => TypedTransaction::Legacy(tx), + ScrollTypedTransaction::Eip2930(tx) => TypedTransaction::Eip2930(tx), + ScrollTypedTransaction::Eip1559(tx) => TypedTransaction::Eip1559(tx), + ScrollTypedTransaction::Eip7702(tx) => TypedTransaction::Eip7702(tx), + ScrollTypedTransaction::L1Message(_) => { + return Err(alloy_signer::Error::other("not implemented for deposit tx")) + } + }; + let tx = NetworkWallet::<Ethereum>::sign_transaction_from(self, sender, tx).await?; + + Ok(match tx { + TxEnvelope::Eip1559(tx) => ScrollTxEnvelope::Eip1559(tx), + TxEnvelope::Eip2930(tx) => ScrollTxEnvelope::Eip2930(tx), + TxEnvelope::Eip7702(tx) => ScrollTxEnvelope::Eip7702(tx), + TxEnvelope::Legacy(tx) => ScrollTxEnvelope::Legacy(tx), + _ => unreachable!(), + }) + } +} + +impl RecommendedFillers for Scroll { + type RecommendedFillers = JoinFill<GasFiller, JoinFill<NonceFiller, ChainIdFiller>>; + + fn recommended_fillers() -> Self::RecommendedFillers { + Default::default() + } +}
diff --git reth/crates/scroll/alloy/provider/Cargo.toml scroll-reth/crates/scroll/alloy/provider/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..907b5856f33355850b3161d5f4e248f94580fd63 --- /dev/null +++ scroll-reth/crates/scroll/alloy/provider/Cargo.toml @@ -0,0 +1,77 @@ +[package] +name = "scroll-alloy-provider" +version.workspace = true +edition.workspace = true +rust-version.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +exclude.workspace = true + +[lints] +workspace = true + +[dependencies] +# alloy +alloy-provider.workspace = true +alloy-primitives.workspace = true +alloy-rpc-types-engine = { workspace = true, features = ["serde"] } +alloy-rpc-client.workspace = true +alloy-transport.workspace = true +alloy-transport-http = { workspace = true, features = ["jwt-auth"] } + +# scroll +scroll-alloy-network.workspace = true +scroll-alloy-rpc-types-engine = { workspace = true, features = ["serde"] } + +# reth +reth-rpc-api = { workspace = true, features = ["client"] } + +# reth-scroll +reth-scroll-engine-primitives.workspace = true + +# misc +async-trait.workspace = true +derive_more.workspace = true +eyre.workspace = true +http-body-util.workspace = true +reqwest.workspace = true +tower.workspace = true +thiserror.workspace = true +jsonrpsee.workspace = true + +[dev-dependencies] +reth-payload-builder = { workspace = true, features = ["test-utils"] } +reth-engine-primitives.workspace = true +reth-payload-primitives.workspace = true +reth-primitives.workspace = true +reth-primitives-traits.workspace = true +reth-provider = { workspace = true, features = ["test-utils"] } +reth-rpc-builder.workspace = true +reth-rpc-engine-api.workspace = true +reth-scroll-engine-primitives.workspace = true +reth-scroll-node.workspace = true +reth-scroll-payload = { workspace = true, features = ["test-utils"] } +reth-scroll-chainspec.workspace = true +reth-tasks.workspace = true +reth-tracing.workspace = true +reth-transaction-pool.workspace = true + +tokio = { workspace = true, features = ["rt", "rt-multi-thread"] } +futures-util.workspace = true + +[features] +default = ["std"] +std = [ + "alloy-primitives/std", + "alloy-rpc-types-engine/std", + "scroll-alloy-rpc-types-engine/std", + "derive_more/std", + "reth-engine-primitives/std", + "reth-primitives/std", + "reth-primitives-traits/std", + "futures-util/std", + "reth-scroll-chainspec/std", + "thiserror/std", + "scroll-alloy-network/std", +]
diff --git reth/crates/scroll/alloy/provider/src/engine/client.rs scroll-reth/crates/scroll/alloy/provider/src/engine/client.rs new file mode 100644 index 0000000000000000000000000000000000000000..145b833815c7bdd4ab9874f7dcdfaf461218375c --- /dev/null +++ scroll-reth/crates/scroll/alloy/provider/src/engine/client.rs @@ -0,0 +1,79 @@ +use super::{ScrollEngineApi, ScrollEngineApiResult}; +use alloy_primitives::{BlockHash, U64}; +use alloy_rpc_types_engine::{ + ClientVersionV1, ExecutionPayloadBodiesV1, ExecutionPayloadV1, ForkchoiceState, + ForkchoiceUpdated, PayloadId, PayloadStatus, +}; + +use reth_rpc_api::EngineApiClient; +use reth_scroll_engine_primitives::ScrollEngineTypes; +use scroll_alloy_rpc_types_engine::ScrollPayloadAttributes; + +/// A Client for a type that implements the [`EngineApiClient`] trait. +#[derive(Debug)] +pub struct ScrollAuthApiEngineClient<T> { + client: T, +} + +impl<T> ScrollAuthApiEngineClient<T> { + /// Creates a new [`ScrollAuthApiEngineClient`] with the given client. + pub const fn new(client: T) -> Self { + Self { client } + } +} + +#[async_trait::async_trait] +impl<EC: EngineApiClient<ScrollEngineTypes> + Sync> ScrollEngineApi + for ScrollAuthApiEngineClient<EC> +{ + async fn new_payload_v1( + &self, + payload: ExecutionPayloadV1, + ) -> ScrollEngineApiResult<PayloadStatus> { + Ok(self.client.new_payload_v1(payload).await?) + } + + async fn fork_choice_updated_v1( + &self, + fork_choice_state: ForkchoiceState, + payload_attributes: Option<ScrollPayloadAttributes>, + ) -> ScrollEngineApiResult<ForkchoiceUpdated> { + Ok(self.client.fork_choice_updated_v1(fork_choice_state, payload_attributes).await?) + } + + async fn get_payload_v1( + &self, + payload_id: PayloadId, + ) -> ScrollEngineApiResult<ExecutionPayloadV1> { + Ok(self.client.get_payload_v1(payload_id).await?) + } + + async fn get_payload_bodies_by_hash_v1( + &self, + block_hashes: Vec<BlockHash>, + ) -> ScrollEngineApiResult<ExecutionPayloadBodiesV1> { + Ok(self.client.get_payload_bodies_by_hash_v1(block_hashes).await?) + } + + async fn get_payload_bodies_by_range_v1( + &self, + start: U64, + count: U64, + ) -> ScrollEngineApiResult<ExecutionPayloadBodiesV1> { + Ok(self.client.get_payload_bodies_by_range_v1(start, count).await?) + } + + async fn get_client_version_v1( + &self, + client_version: ClientVersionV1, + ) -> ScrollEngineApiResult<Vec<ClientVersionV1>> { + Ok(self.client.get_client_version_v1(client_version).await?) + } + + async fn exchange_capabilities( + &self, + capabilities: Vec<String>, + ) -> ScrollEngineApiResult<Vec<String>> { + Ok(self.exchange_capabilities(capabilities).await?) + } +}
diff --git reth/crates/scroll/alloy/provider/src/engine/mod.rs scroll-reth/crates/scroll/alloy/provider/src/engine/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..1edfbefbfc219eee7c7fce09959f6701f522d192 --- /dev/null +++ scroll-reth/crates/scroll/alloy/provider/src/engine/mod.rs @@ -0,0 +1,104 @@ +mod provider; + +pub use provider::ScrollAuthEngineApiProvider; + +mod client; +pub use client::ScrollAuthApiEngineClient; + +use super::error::ScrollEngineApiError; +use alloy_primitives::{BlockHash, U64}; +use alloy_rpc_types_engine::{ + ClientVersionV1, ExecutionPayloadBodiesV1, ExecutionPayloadV1, ForkchoiceState, + ForkchoiceUpdated, PayloadId, PayloadStatus, +}; + +use scroll_alloy_rpc_types_engine::ScrollPayloadAttributes; + +/// A type alias for the result of the Scroll Engine API methods. +pub type ScrollEngineApiResult<T> = Result<T, ScrollEngineApiError>; + +/// Engine API trait for Scroll. Only exposes versions of the API that are supported. +/// Note: +/// > The provider should use a JWT authentication layer. +#[async_trait::async_trait] +pub trait ScrollEngineApi { + /// See also <https://github.com/ethereum/execution-apis/blob/6709c2a795b707202e93c4f2867fa0bf2640a84f/src/engine/paris.md#engine_newpayloadv1> + /// Caution: This should not accept the `withdrawals` field + async fn new_payload_v1( + &self, + payload: ExecutionPayloadV1, + ) -> ScrollEngineApiResult<PayloadStatus>; + + /// See also <https://github.com/ethereum/execution-apis/blob/6709c2a795b707202e93c4f2867fa0bf2640a84f/src/engine/paris.md#engine_forkchoiceupdatedv1> + /// Caution: This should not accept the `withdrawals` field in the payload attributes. + /// + /// Modifications: + /// - Adds the below fields to the `payload_attributes`: + /// - transactions: an optional list of transactions to include at the start of the block. + /// - `no_tx_pool`: a boolean which signals whether pool transactions need to be included in + /// the payload building task. + async fn fork_choice_updated_v1( + &self, + fork_choice_state: ForkchoiceState, + payload_attributes: Option<ScrollPayloadAttributes>, + ) -> ScrollEngineApiResult<ForkchoiceUpdated>; + + /// See also <https://github.com/ethereum/execution-apis/blob/6709c2a795b707202e93c4f2867fa0bf2640a84f/src/engine/paris.md#engine_getpayloadv1> + /// + /// Returns the most recent version of the payload that is available in the corresponding + /// payload build process at the time of receiving this call. + /// + /// Caution: This should not return the `withdrawals` field + /// + /// Note: + /// > Provider software MAY stop the corresponding build process after serving this call. + async fn get_payload_v1( + &self, + payload_id: PayloadId, + ) -> ScrollEngineApiResult<ExecutionPayloadV1>; + + /// See also <https://github.com/ethereum/execution-apis/blob/6452a6b194d7db269bf1dbd087a267251d3cc7f8/src/engine/shanghai.md#engine_getpayloadbodiesbyhashv1> + async fn get_payload_bodies_by_hash_v1( + &self, + block_hashes: Vec<BlockHash>, + ) -> ScrollEngineApiResult<ExecutionPayloadBodiesV1>; + + /// See also <https://github.com/ethereum/execution-apis/blob/6452a6b194d7db269bf1dbd087a267251d3cc7f8/src/engine/shanghai.md#engine_getpayloadbodiesbyrangev1> + /// + /// Returns the execution payload bodies by the range starting at `start`, containing `count` + /// blocks. + /// + /// WARNING: This method is associated with the `BeaconBlocksByRange` message in the consensus + /// layer p2p specification, meaning the input should be treated as untrusted or potentially + /// adversarial. + /// + /// Implementers should take care when acting on the input to this method, specifically + /// ensuring that the range is limited properly, and that the range boundaries are computed + /// correctly and without panics. + async fn get_payload_bodies_by_range_v1( + &self, + start: U64, + count: U64, + ) -> ScrollEngineApiResult<ExecutionPayloadBodiesV1>; + + /// This function will return the `ClientVersionV1` object. + /// See also: + /// <https://github.com/ethereum/execution-apis/blob/03911ffc053b8b806123f1fc237184b0092a485a/src/engine/identification.md#engine_getclientversionv1>make fmt + /// + /// + /// - When connected to a single execution client, the consensus client **MUST** receive an + /// array with a single `ClientVersionV1` object. + /// - When connected to multiple execution clients via a multiplexer, the multiplexer **MUST** + /// concatenate the responses from each execution client into a single, + /// flat array before returning the response to the consensus client. + async fn get_client_version_v1( + &self, + client_version: ClientVersionV1, + ) -> ScrollEngineApiResult<Vec<ClientVersionV1>>; + + /// See also <https://github.com/ethereum/execution-apis/blob/6452a6b194d7db269bf1dbd087a267251d3cc7f8/src/engine/common.md#capabilities> + async fn exchange_capabilities( + &self, + capabilities: Vec<String>, + ) -> ScrollEngineApiResult<Vec<String>>; +}
diff --git reth/crates/scroll/alloy/provider/src/engine/provider.rs scroll-reth/crates/scroll/alloy/provider/src/engine/provider.rs new file mode 100644 index 0000000000000000000000000000000000000000..ade3de86edb6dee8464880e46da72ef149b0201a --- /dev/null +++ scroll-reth/crates/scroll/alloy/provider/src/engine/provider.rs @@ -0,0 +1,218 @@ +use super::{ScrollEngineApi, ScrollEngineApiResult}; +use alloy_primitives::{bytes::Bytes, BlockHash, U64}; +use alloy_provider::{Provider, RootProvider}; +use alloy_rpc_client::RpcClient; +use alloy_rpc_types_engine::{ + ClientVersionV1, ExecutionPayloadBodiesV1, ExecutionPayloadV1, ForkchoiceState, + ForkchoiceUpdated, JwtSecret, PayloadId, PayloadStatus, +}; +use alloy_transport::utils::guess_local_url; +use alloy_transport_http::{ + hyper_util, hyper_util::rt::TokioExecutor, AuthLayer, Http, HyperClient, +}; +use http_body_util::Full; +use reqwest::Url; +use scroll_alloy_network::Scroll; +use scroll_alloy_rpc_types_engine::ScrollPayloadAttributes; +use std::fmt::Debug; + +/// An authenticated [`alloy_provider::Provider`] to the [`ScrollEngineApi`]. +#[derive(Clone)] +pub struct ScrollAuthEngineApiProvider<P> { + provider: P, +} + +impl<P> Debug for ScrollAuthEngineApiProvider<P> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ScrollAuthEngineApiProvider").field("provider", &"provider").finish() + } +} + +impl ScrollAuthEngineApiProvider<RootProvider<Scroll>> { + /// Returns a new [`ScrollAuthEngineApiProvider`], authenticated for interfacing with the Engine + /// API server at the provided URL using the passed JWT secret. + pub fn new(jwt_secret: JwtSecret, url: Url) -> Self { + let auth_layer = AuthLayer::new(jwt_secret); + let hyper_client = hyper_util::client::legacy::Client::builder(TokioExecutor::new()) + .build_http::<Full<Bytes>>(); + + let service = tower::ServiceBuilder::new().layer(auth_layer).service(hyper_client); + let transport = HyperClient::<Full<Bytes>, _>::with_service(service); + + let is_url_local = guess_local_url(&url); + let http = Http::with_client(transport, url); + let client = RpcClient::new(http, is_url_local); + + let provider = RootProvider::<Scroll>::new(client); + Self { provider } + } +} + +impl<P> ScrollAuthEngineApiProvider<P> { + /// Returns a new [`ScrollAuthEngineApiProvider`] from the given provider. + pub const fn from_provider(provider: P) -> Self { + Self { provider } + } +} + +#[async_trait::async_trait] +impl<P: Provider<Scroll>> ScrollEngineApi for ScrollAuthEngineApiProvider<P> { + async fn new_payload_v1( + &self, + payload: ExecutionPayloadV1, + ) -> ScrollEngineApiResult<PayloadStatus> { + Ok(self.provider.client().request("engine_newPayloadV1", (payload,)).await?) + } + + async fn fork_choice_updated_v1( + &self, + fork_choice_state: ForkchoiceState, + payload_attributes: Option<ScrollPayloadAttributes>, + ) -> ScrollEngineApiResult<ForkchoiceUpdated> { + Ok(self + .provider + .client() + .request("engine_forkchoiceUpdatedV1", (fork_choice_state, payload_attributes)) + .await?) + } + + async fn get_payload_v1( + &self, + payload_id: PayloadId, + ) -> ScrollEngineApiResult<ExecutionPayloadV1> { + Ok(self.provider.client().request("engine_getPayloadV1", (payload_id,)).await?) + } + + async fn get_payload_bodies_by_hash_v1( + &self, + block_hashes: Vec<BlockHash>, + ) -> ScrollEngineApiResult<ExecutionPayloadBodiesV1> { + Ok(self + .provider + .client() + .request("engine_getPayloadBodiesByHashV1", (block_hashes,)) + .await?) + } + + async fn get_payload_bodies_by_range_v1( + &self, + start: U64, + count: U64, + ) -> ScrollEngineApiResult<ExecutionPayloadBodiesV1> { + Ok(self + .provider + .client() + .request("engine_getPayloadBodiesByRangeV1", (start, count)) + .await?) + } + + async fn get_client_version_v1( + &self, + client_version: ClientVersionV1, + ) -> ScrollEngineApiResult<Vec<ClientVersionV1>> { + Ok(self.provider.client().request("engine_getClientVersionV1", (client_version,)).await?) + } + + async fn exchange_capabilities( + &self, + capabilities: Vec<String>, + ) -> ScrollEngineApiResult<Vec<String>> { + Ok(self.provider.client().request("engine_exchangeCapabilities", (capabilities,)).await?) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::engine::ScrollEngineApi; + use alloy_primitives::U64; + use alloy_rpc_types_engine::{ + ClientCode, ClientVersionV1, ExecutionPayloadV1, ForkchoiceState, PayloadId, + }; + use reth_engine_primitives::BeaconConsensusEngineHandle; + use reth_payload_builder::{PayloadBuilderHandle, PayloadBuilderService}; + use reth_payload_primitives::PayloadTypes; + use reth_primitives::{Block, TransactionSigned}; + use reth_primitives_traits::block::Block as _; + use reth_provider::{test_utils::NoopProvider, CanonStateNotification}; + use reth_rpc_builder::auth::{AuthRpcModule, AuthServerConfig, AuthServerHandle}; + use reth_rpc_engine_api::{capabilities::EngineCapabilities, EngineApi}; + use reth_scroll_chainspec::SCROLL_MAINNET; + use reth_scroll_engine_primitives::{ + ScrollBuiltPayload, ScrollEngineTypes, ScrollPayloadBuilderAttributes, + }; + use reth_scroll_node::ScrollEngineValidator; + use reth_scroll_payload::NoopPayloadJobGenerator; + use reth_tasks::TokioTaskExecutor; + use reth_transaction_pool::noop::NoopTransactionPool; + use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; + use tokio::sync::mpsc::unbounded_channel; + + fn spawn_test_payload_service<T>() -> PayloadBuilderHandle<T> + where + T: PayloadTypes< + PayloadBuilderAttributes = ScrollPayloadBuilderAttributes, + BuiltPayload = ScrollBuiltPayload, + > + 'static, + { + let (service, handle) = PayloadBuilderService::< + NoopPayloadJobGenerator<ScrollPayloadBuilderAttributes, ScrollBuiltPayload>, + futures_util::stream::Empty<CanonStateNotification>, + T, + >::new(Default::default(), futures_util::stream::empty()); + tokio::spawn(service); + handle + } + + async fn launch_auth(jwt_secret: JwtSecret) -> AuthServerHandle { + let config = AuthServerConfig::builder(jwt_secret) + .socket_addr(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0))) + .build(); + let (tx, _rx) = unbounded_channel(); + let beacon_engine_handle = BeaconConsensusEngineHandle::<ScrollEngineTypes>::new(tx); + let client = ClientVersionV1 { + code: ClientCode::RH, + name: "Reth".to_string(), + version: "v0.2.0-beta.5".to_string(), + commit: "defa64b2".to_string(), + }; + + let engine_api = EngineApi::new( + NoopProvider::default(), + SCROLL_MAINNET.clone(), + beacon_engine_handle, + spawn_test_payload_service().into(), + NoopTransactionPool::default(), + Box::<TokioTaskExecutor>::default(), + client, + EngineCapabilities::default(), + ScrollEngineValidator::new(SCROLL_MAINNET.clone()), + false, + ); + let module = AuthRpcModule::new(engine_api); + module.start_server(config).await.unwrap() + } + + #[allow(unused_must_use)] + #[tokio::test(flavor = "multi_thread")] + async fn test_engine_api_provider() -> eyre::Result<()> { + reth_tracing::init_test_tracing(); + + let secret = JwtSecret::random(); + let handle = launch_auth(secret).await; + let url = handle.http_url().parse()?; + let provider = ScrollAuthEngineApiProvider::new(secret, url); + + let block = Block::<TransactionSigned>::default().seal_slow(); + let execution_payload = + ExecutionPayloadV1::from_block_unchecked(block.hash(), &block.clone().into_block()); + provider.new_payload_v1(execution_payload).await; + provider.fork_choice_updated_v1(ForkchoiceState::default(), None).await; + provider.get_payload_v1(PayloadId::new([0, 0, 0, 0, 0, 0, 0, 0])).await; + provider.get_payload_bodies_by_hash_v1(vec![]).await; + provider.get_payload_bodies_by_range_v1(U64::ZERO, U64::from(1u64)).await; + provider.exchange_capabilities(vec![]).await; + + Ok(()) + } +}
diff --git reth/crates/scroll/alloy/provider/src/error.rs scroll-reth/crates/scroll/alloy/provider/src/error.rs new file mode 100644 index 0000000000000000000000000000000000000000..88cfc1eb7af1b9a04c63935492e3d0bed790c3d5 --- /dev/null +++ scroll-reth/crates/scroll/alloy/provider/src/error.rs @@ -0,0 +1,10 @@ +/// The error type for the Scroll engine API. +#[derive(thiserror::Error, Debug)] +pub enum ScrollEngineApiError { + /// Error when decoding a response from an rpsee client. + #[error("Jsonrpsee error: {0}")] + JsonRpseeError(#[from] jsonrpsee::core::ClientError), + /// Error when decoding a response from an alloy client. + #[error("Alloy error: {0}")] + AlloyError(#[from] alloy_transport::TransportError), +}
diff --git reth/crates/scroll/alloy/provider/src/lib.rs scroll-reth/crates/scroll/alloy/provider/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..812b635562098ab12bbead82f1a5a6bd1a1646f9 --- /dev/null +++ scroll-reth/crates/scroll/alloy/provider/src/lib.rs @@ -0,0 +1,9 @@ +//! Providers implementations fitted to Scroll needs. + +mod engine; +pub use engine::{ + ScrollAuthApiEngineClient, ScrollAuthEngineApiProvider, ScrollEngineApi, ScrollEngineApiResult, +}; + +mod error; +pub use error::ScrollEngineApiError;
diff --git reth/crates/scroll/alloy/rpc-types-engine/Cargo.toml scroll-reth/crates/scroll/alloy/rpc-types-engine/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..3bb27c31ddf2f4bcd438128262206b225848800b --- /dev/null +++ scroll-reth/crates/scroll/alloy/rpc-types-engine/Cargo.toml @@ -0,0 +1,45 @@ +[package] +name = "scroll-alloy-rpc-types-engine" +version.workspace = true +edition.workspace = true +rust-version.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +exclude.workspace = true + +[lints] +workspace = true + +[dependencies] +# alloy +alloy-primitives.workspace = true +alloy-rpc-types-engine.workspace = true + +# misc +serde = { workspace = true, optional = true } + +# test-utils +arbitrary = { workspace = true, optional = true } + +[dev-dependencies] +serde_json.workspace = true + +[features] +default = ["std"] +arbitrary = [ + "dep:arbitrary", + "alloy-primitives/arbitrary", + "alloy-rpc-types-engine/arbitrary", +] +std = [ + "alloy-primitives/std", + "alloy-rpc-types-engine/std", + "serde?/std", + "serde_json/std", +] +serde = [ + "dep:serde", + "alloy-primitives/serde", + "alloy-rpc-types-engine/serde", +]
diff --git reth/crates/scroll/alloy/rpc-types-engine/src/attributes.rs scroll-reth/crates/scroll/alloy/rpc-types-engine/src/attributes.rs new file mode 100644 index 0000000000000000000000000000000000000000..80ff2f203b09ee5f6ffedb36bf06448d1f545dfb --- /dev/null +++ scroll-reth/crates/scroll/alloy/rpc-types-engine/src/attributes.rs @@ -0,0 +1,88 @@ +//! Scroll-specific payload attributes. + +use alloc::vec::Vec; +use alloy_primitives::{Bytes, U256}; +use alloy_rpc_types_engine::PayloadAttributes; + +/// The payload attributes for block building tailored for Scroll. +#[derive(Debug, Clone, Default, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] +pub struct ScrollPayloadAttributes { + /// The payload attributes. + pub payload_attributes: PayloadAttributes, + /// An optional array of transaction to be forced included in the block (includes l1 messages). + pub transactions: Option<Vec<Bytes>>, + /// Indicates whether the payload building job should happen with or without pool transactions. + pub no_tx_pool: bool, + /// The pre-Euclid block data hint, necessary for the block builder to derive the correct block + /// hash. + pub block_data_hint: Option<BlockDataHint>, +} + +/// Block data provided as a hint to the payload attributes. +#[derive(Debug, Clone, Default, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] +pub struct BlockDataHint { + /// The extra data for the block. + pub extra_data: Bytes, + /// The difficulty for the block. + pub difficulty: U256, +} + +#[cfg(feature = "arbitrary")] +impl<'a> arbitrary::Arbitrary<'a> for BlockDataHint { + fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> { + Ok(Self { extra_data: Bytes::arbitrary(u)?, difficulty: U256::arbitrary(u)? }) + } +} + +#[cfg(feature = "arbitrary")] +impl<'a> arbitrary::Arbitrary<'a> for ScrollPayloadAttributes { + fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> { + Ok(Self { + payload_attributes: PayloadAttributes { + timestamp: u64::arbitrary(u)?, + prev_randao: alloy_primitives::B256::arbitrary(u)?, + suggested_fee_recipient: alloy_primitives::Address::arbitrary(u)?, + withdrawals: None, + parent_beacon_block_root: Some(alloy_primitives::B256::arbitrary(u)?), + }, + transactions: Some(Vec::arbitrary(u)?), + no_tx_pool: bool::arbitrary(u)?, + block_data_hint: Some(BlockDataHint::arbitrary(u)?), + }) + } +} + +#[cfg(all(test, feature = "serde"))] +mod test { + use super::*; + use alloy_primitives::{Address, B256}; + use alloy_rpc_types_engine::PayloadAttributes; + + #[test] + fn test_serde_roundtrip_attributes() { + let attributes = ScrollPayloadAttributes { + payload_attributes: PayloadAttributes { + timestamp: 0x1337, + prev_randao: B256::ZERO, + suggested_fee_recipient: Address::ZERO, + withdrawals: Default::default(), + parent_beacon_block_root: Some(B256::ZERO), + }, + transactions: Some(vec![b"hello".to_vec().into()]), + no_tx_pool: true, + block_data_hint: Some(BlockDataHint { + extra_data: b"world".into(), + difficulty: U256::from(10), + }), + }; + + let ser = serde_json::to_string(&attributes).unwrap(); + let de: ScrollPayloadAttributes = serde_json::from_str(&ser).unwrap(); + + assert_eq!(attributes, de); + } +}
diff --git reth/crates/scroll/alloy/rpc-types-engine/src/lib.rs scroll-reth/crates/scroll/alloy/rpc-types-engine/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..fcf62c269351c5e3dfe1ffa10ac66cc5fff95f6d --- /dev/null +++ scroll-reth/crates/scroll/alloy/rpc-types-engine/src/lib.rs @@ -0,0 +1,8 @@ +//! Scroll types for interaction with the Engine API via RPC. + +#![cfg_attr(not(feature = "std"), no_std)] + +mod attributes; +pub use attributes::{BlockDataHint, ScrollPayloadAttributes}; + +extern crate alloc;
diff --git reth/crates/scroll/alloy/rpc-types/Cargo.toml scroll-reth/crates/scroll/alloy/rpc-types/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..ceb402689dfbbedaf1f23ad06d36d309ff1916af --- /dev/null +++ scroll-reth/crates/scroll/alloy/rpc-types/Cargo.toml @@ -0,0 +1,61 @@ +[package] +name = "scroll-alloy-rpc-types" +version.workspace = true +edition.workspace = true +rust-version.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +exclude.workspace = true + +[lints] +workspace = true + +[dependencies] +scroll-alloy-consensus = { workspace = true, features = ["serde"] } +alloy-consensus = { workspace = true, default-features = false } +alloy-eips = { workspace = true, features = ["serde"], default-features = false } +alloy-network-primitives = { workspace = true, default-features = false } +alloy-primitives = { workspace = true, features = ["map", "rlp", "serde"], default-features = false } +alloy-rpc-types-eth = { workspace = true, features = ["serde"], default-features = false } +alloy-serde = { workspace = true, default-features = false } +arbitrary = { workspace = true, features = ["derive", "derive"], optional = true } +derive_more = { workspace = true, default-features = false } +serde = { workspace = true, features = ["derive", "alloc", "derive"], default-features = false } +serde_json = { workspace = true, features = ["alloc"], default-features = false } + +[dev-dependencies] +similar-asserts = "1.6" +alloy-consensus = { workspace = true, features = ["arbitrary"], default-features = false } +alloy-primitives = { workspace = true, features = ["arbitrary"], default-features = false } +alloy-rpc-types-eth = { workspace = true, features = ["arbitrary"], default-features = false } +arbitrary = { workspace = true, features = ["derive", "derive"] } + +[features] +arbitrary = [ + "std", + "dep:arbitrary", + "alloy-primitives/arbitrary", + "alloy-rpc-types-eth/arbitrary", + "scroll-alloy-consensus/arbitrary", + "alloy-consensus/arbitrary", + "alloy-eips/arbitrary", + "alloy-serde/arbitrary", +] +default = ["std"] +k256 = [ + "alloy-rpc-types-eth/k256", + "scroll-alloy-consensus/k256", +] +std = [ + "alloy-network-primitives/std", + "alloy-eips/std", + "alloy-primitives/std", + "alloy-rpc-types-eth/std", + "alloy-consensus/std", + "alloy-serde/std", + "derive_more/std", + "serde/std", + "serde_json/std", + "scroll-alloy-consensus/std", +]
diff --git reth/crates/scroll/alloy/rpc-types/README.md scroll-reth/crates/scroll/alloy/rpc-types/README.md new file mode 100644 index 0000000000000000000000000000000000000000..963a99c4b23daddceeb49feb55a579509ed91ac1 --- /dev/null +++ scroll-reth/crates/scroll/alloy/rpc-types/README.md @@ -0,0 +1,3 @@ +# scroll-alloy-rpc-types + +Scroll RPC-related types.
diff --git reth/crates/scroll/alloy/rpc-types/src/lib.rs scroll-reth/crates/scroll/alloy/rpc-types/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..3236795f79ae6fa5ee6b9ffba84a4806cd38af6d --- /dev/null +++ scroll-reth/crates/scroll/alloy/rpc-types/src/lib.rs @@ -0,0 +1,14 @@ +#![doc = include_str!("../README.md")] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/alloy-rs/core/main/assets/alloy.jpg", + html_favicon_url = "https://raw.githubusercontent.com/alloy-rs/core/main/assets/favicon.ico" +)] +#![cfg_attr(not(test), warn(unused_crate_dependencies))] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] +#![cfg_attr(not(any(test, feature = "std")), no_std)] + +mod receipt; +pub use receipt::{ScrollTransactionReceipt, ScrollTransactionReceiptFields}; + +mod transaction; +pub use transaction::{ScrollL1MessageTransactionFields, ScrollTransactionRequest, Transaction};
diff --git reth/crates/scroll/alloy/rpc-types/src/receipt.rs scroll-reth/crates/scroll/alloy/rpc-types/src/receipt.rs new file mode 100644 index 0000000000000000000000000000000000000000..6a0a04c660252c0180e7940015f49d6e7d2b0c3c --- /dev/null +++ scroll-reth/crates/scroll/alloy/rpc-types/src/receipt.rs @@ -0,0 +1,176 @@ +//! Receipt types for RPC + +use alloy_consensus::{Receipt, ReceiptWithBloom}; +use alloy_serde::OtherFields; +use serde::{Deserialize, Serialize}; + +use scroll_alloy_consensus::ScrollReceiptEnvelope; + +/// Scroll Transaction Receipt type +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +#[doc(alias = "ScrollTxReceipt")] +pub struct ScrollTransactionReceipt { + /// Regular eth transaction receipt including deposit receipts + #[serde(flatten)] + pub inner: + alloy_rpc_types_eth::TransactionReceipt<ScrollReceiptEnvelope<alloy_rpc_types_eth::Log>>, + /// L1 fee for the transaction. + #[serde(default, skip_serializing_if = "Option::is_none", with = "alloy_serde::quantity::opt")] + pub l1_fee: Option<u128>, +} + +impl alloy_network_primitives::ReceiptResponse for ScrollTransactionReceipt { + fn contract_address(&self) -> Option<alloy_primitives::Address> { + self.inner.contract_address + } + + fn status(&self) -> bool { + self.inner.inner.status() + } + + fn block_hash(&self) -> Option<alloy_primitives::BlockHash> { + self.inner.block_hash + } + + fn block_number(&self) -> Option<u64> { + self.inner.block_number + } + + fn transaction_hash(&self) -> alloy_primitives::TxHash { + self.inner.transaction_hash + } + + fn transaction_index(&self) -> Option<u64> { + self.inner.transaction_index() + } + + fn gas_used(&self) -> u64 { + self.inner.gas_used() + } + + fn effective_gas_price(&self) -> u128 { + self.inner.effective_gas_price() + } + + fn blob_gas_used(&self) -> Option<u64> { + self.inner.blob_gas_used() + } + + fn blob_gas_price(&self) -> Option<u128> { + self.inner.blob_gas_price() + } + + fn from(&self) -> alloy_primitives::Address { + self.inner.from() + } + + fn to(&self) -> Option<alloy_primitives::Address> { + self.inner.to() + } + + fn cumulative_gas_used(&self) -> u64 { + self.inner.cumulative_gas_used() + } + + fn state_root(&self) -> Option<alloy_primitives::B256> { + self.inner.state_root() + } +} + +/// Additional fields for Scroll transaction receipts: <https://github.com/scroll-tech/go-ethereum/blob/develop/core/types/receipt.go#L78> +#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +#[doc(alias = "ScrollTxReceiptFields")] +pub struct ScrollTransactionReceiptFields { + /// L1 fee for the transaction. + #[serde(default, skip_serializing_if = "Option::is_none", with = "alloy_serde::quantity::opt")] + pub l1_fee: Option<u128>, +} + +impl From<ScrollTransactionReceiptFields> for OtherFields { + fn from(value: ScrollTransactionReceiptFields) -> Self { + serde_json::to_value(value).unwrap().try_into().unwrap() + } +} + +impl From<ScrollTransactionReceipt> for ScrollReceiptEnvelope<alloy_primitives::Log> { + fn from(value: ScrollTransactionReceipt) -> Self { + let inner_envelope = value.inner.inner; + + /// Helper function to convert the inner logs within a [`ReceiptWithBloom`] from RPC to + /// consensus types. + #[inline(always)] + fn convert_standard_receipt( + receipt: ReceiptWithBloom<Receipt<alloy_rpc_types_eth::Log>>, + ) -> ReceiptWithBloom<Receipt<alloy_primitives::Log>> { + let ReceiptWithBloom { logs_bloom, receipt } = receipt; + + let consensus_logs = receipt.logs.into_iter().map(|log| log.inner).collect(); + ReceiptWithBloom { + receipt: Receipt { + status: receipt.status, + cumulative_gas_used: receipt.cumulative_gas_used, + logs: consensus_logs, + }, + logs_bloom, + } + } + + match inner_envelope { + ScrollReceiptEnvelope::Legacy(receipt) => { + Self::Legacy(convert_standard_receipt(receipt)) + } + ScrollReceiptEnvelope::Eip2930(receipt) => { + Self::Eip2930(convert_standard_receipt(receipt)) + } + ScrollReceiptEnvelope::Eip1559(receipt) => { + Self::Eip1559(convert_standard_receipt(receipt)) + } + ScrollReceiptEnvelope::L1Message(receipt) => { + Self::L1Message(convert_standard_receipt(receipt)) + } + _ => unreachable!("Unsupported ScrollReceiptEnvelope variant"), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::json; + + // <https://github.com/alloy-rs/op-alloy/issues/18> + #[test] + fn parse_rpc_receipt() { + let s = r#"{ + "blockHash": "0x9e6a0fb7e22159d943d760608cc36a0fb596d1ab3c997146f5b7c55c8c718c67", + "blockNumber": "0x6cfef89", + "contractAddress": null, + "cumulativeGasUsed": "0xfa0d", + "effectiveGasPrice": "0x0", + "from": "0xdeaddeaddeaddeaddeaddeaddeaddeaddead0001", + "gasUsed": "0xfa0d", + "logs": [], + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "status": "0x1", + "to": "0x4200000000000000000000000000000000000015", + "transactionHash": "0xb7c74afdeb7c89fb9de2c312f49b38cb7a850ba36e064734c5223a477e83fdc9", + "transactionIndex": "0x0", + "type": "0x7e" + }"#; + + let receipt: ScrollTransactionReceipt = serde_json::from_str(s).unwrap(); + let value = serde_json::to_value(&receipt).unwrap(); + let expected_value = serde_json::from_str::<serde_json::Value>(s).unwrap(); + assert_eq!(value, expected_value); + } + + #[test] + fn serialize_empty_scroll_transaction_receipt_fields_struct() { + let scroll_fields = ScrollTransactionReceiptFields::default(); + + let json = serde_json::to_value(scroll_fields).unwrap(); + assert_eq!(json, json!({})); + } +}
diff --git reth/crates/scroll/alloy/rpc-types/src/transaction.rs scroll-reth/crates/scroll/alloy/rpc-types/src/transaction.rs new file mode 100644 index 0000000000000000000000000000000000000000..9aa28541527a1912b63749f1c66f3cc0b860b4e0 --- /dev/null +++ scroll-reth/crates/scroll/alloy/rpc-types/src/transaction.rs @@ -0,0 +1,315 @@ +//! Scroll specific types related to transactions. + +use alloy_consensus::{transaction::Recovered, Transaction as _, Typed2718}; +use alloy_eips::{eip2930::AccessList, eip7702::SignedAuthorization}; +use alloy_primitives::{Address, BlockHash, Bytes, ChainId, TxKind, B256, U256}; +use alloy_serde::OtherFields; +use scroll_alloy_consensus::{ScrollTransactionInfo, ScrollTxEnvelope}; +use serde::{Deserialize, Serialize}; + +mod request; +pub use request::ScrollTransactionRequest; + +/// Scroll Transaction type +#[derive( + Clone, Debug, PartialEq, Eq, Serialize, Deserialize, derive_more::Deref, derive_more::DerefMut, +)] +#[serde(try_from = "tx_serde::TransactionSerdeHelper", into = "tx_serde::TransactionSerdeHelper")] +#[cfg_attr(all(any(test, feature = "arbitrary"), feature = "k256"), derive(arbitrary::Arbitrary))] +pub struct Transaction { + /// Ethereum Transaction Types + #[deref] + #[deref_mut] + pub inner: alloy_rpc_types_eth::Transaction<ScrollTxEnvelope>, +} + +impl Transaction { + /// Returns a rpc [`Transaction`] with a [`ScrollTransactionInfo`] and + /// [`Recovered<ScrollTxEnvelope>`] as input. + pub fn from_transaction( + tx: Recovered<ScrollTxEnvelope>, + tx_info: ScrollTransactionInfo, + ) -> Self { + let base_fee = tx_info.inner.base_fee; + let effective_gas_price = if tx.is_l1_message() { + // For l1 messages, we set the `gasPrice` field to 0 in rpc + 0 + } else { + base_fee + .map(|base_fee| { + tx.effective_tip_per_gas(base_fee).unwrap_or_default() + base_fee as u128 + }) + .unwrap_or_else(|| tx.max_fee_per_gas()) + }; + + Self { + inner: alloy_rpc_types_eth::Transaction { + inner: tx, + block_hash: tx_info.inner.block_hash, + block_number: tx_info.inner.block_number, + transaction_index: tx_info.inner.index, + effective_gas_price: Some(effective_gas_price), + }, + } + } +} + +impl Typed2718 for Transaction { + fn ty(&self) -> u8 { + self.inner.ty() + } +} + +impl alloy_consensus::Transaction for Transaction { + fn chain_id(&self) -> Option<ChainId> { + self.inner.chain_id() + } + + fn nonce(&self) -> u64 { + self.inner.nonce() + } + + fn gas_limit(&self) -> u64 { + self.inner.gas_limit() + } + + fn gas_price(&self) -> Option<u128> { + self.inner.gas_price() + } + + fn max_fee_per_gas(&self) -> u128 { + self.inner.max_fee_per_gas() + } + + fn max_priority_fee_per_gas(&self) -> Option<u128> { + self.inner.max_priority_fee_per_gas() + } + + fn max_fee_per_blob_gas(&self) -> Option<u128> { + self.inner.max_fee_per_blob_gas() + } + + fn priority_fee_or_price(&self) -> u128 { + self.inner.priority_fee_or_price() + } + + fn effective_gas_price(&self, base_fee: Option<u64>) -> u128 { + self.inner.effective_gas_price(base_fee) + } + + fn is_dynamic_fee(&self) -> bool { + self.inner.is_dynamic_fee() + } + + fn kind(&self) -> TxKind { + self.inner.kind() + } + + fn is_create(&self) -> bool { + self.inner.is_create() + } + + fn to(&self) -> Option<Address> { + self.inner.to() + } + + fn value(&self) -> U256 { + self.inner.value() + } + + fn input(&self) -> &Bytes { + self.inner.input() + } + + fn access_list(&self) -> Option<&AccessList> { + self.inner.access_list() + } + + fn blob_versioned_hashes(&self) -> Option<&[B256]> { + self.inner.blob_versioned_hashes() + } + + fn authorization_list(&self) -> Option<&[SignedAuthorization]> { + self.inner.authorization_list() + } +} + +impl alloy_network_primitives::TransactionResponse for Transaction { + fn tx_hash(&self) -> alloy_primitives::TxHash { + self.inner.tx_hash() + } + + fn block_hash(&self) -> Option<BlockHash> { + self.inner.block_hash() + } + + fn block_number(&self) -> Option<u64> { + self.inner.block_number() + } + + fn transaction_index(&self) -> Option<u64> { + self.inner.transaction_index() + } + + fn from(&self) -> Address { + self.inner.from() + } +} + +/// Scroll specific transaction fields +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ScrollL1MessageTransactionFields { + /// The index of the transaction in the message queue. + pub queue_index: u64, + /// The sender of the transaction on the L1. + pub sender: Address, +} + +impl From<ScrollL1MessageTransactionFields> for OtherFields { + fn from(value: ScrollL1MessageTransactionFields) -> Self { + serde_json::to_value(value).unwrap().try_into().unwrap() + } +} + +impl AsRef<ScrollTxEnvelope> for Transaction { + fn as_ref(&self) -> &ScrollTxEnvelope { + self.inner.as_ref() + } +} + +mod tx_serde { + //! Helper module for serializing and deserializing Scroll [`Transaction`]. + //! + //! This is needed because we might need to deserialize the `from` field into both + //! [`alloy_rpc_types_eth::Transaction::from`] and + //! [`scroll_alloy_consensus::TxL1Message`]. + //! + //! Additionally, we need similar logic for the `gasPrice` field + + use super::*; + use alloy_consensus::transaction::Recovered; + use serde::de::Error; + + /// Helper struct which will be flattened into the transaction and will only contain `from` + /// field if inner [`ScrollTxEnvelope`] did not consume it. + #[derive(Serialize, Deserialize)] + struct OptionalFields { + #[serde(default, skip_serializing_if = "Option::is_none")] + from: Option<Address>, + #[serde( + default, + rename = "gasPrice", + skip_serializing_if = "Option::is_none", + with = "alloy_serde::quantity::opt" + )] + effective_gas_price: Option<u128>, + } + + #[derive(Serialize, Deserialize)] + #[serde(rename_all = "camelCase")] + pub(crate) struct TransactionSerdeHelper { + #[serde(flatten)] + inner: ScrollTxEnvelope, + #[serde(default)] + block_hash: Option<BlockHash>, + #[serde(default, with = "alloy_serde::quantity::opt")] + block_number: Option<u64>, + #[serde(default, with = "alloy_serde::quantity::opt")] + transaction_index: Option<u64>, + #[serde(flatten)] + other: OptionalFields, + } + + impl From<Transaction> for TransactionSerdeHelper { + fn from(value: Transaction) -> Self { + let Transaction { + inner: + alloy_rpc_types_eth::Transaction { + inner: recovered, + block_hash, + block_number, + transaction_index, + effective_gas_price, + }, + .. + } = value; + + // if inner transaction has its own `gasPrice` don't serialize it in this struct. + let effective_gas_price = + effective_gas_price.filter(|_| recovered.gas_price().is_none()); + let (inner, from) = recovered.into_parts(); + + Self { + inner, + block_hash, + block_number, + transaction_index, + other: OptionalFields { from: Some(from), effective_gas_price }, + } + } + } + + impl TryFrom<TransactionSerdeHelper> for Transaction { + type Error = serde_json::Error; + + fn try_from(value: TransactionSerdeHelper) -> Result<Self, Self::Error> { + let TransactionSerdeHelper { + inner, + block_hash, + block_number, + transaction_index, + other, + } = value; + + // Try to get `from` field from inner envelope or from `MaybeFrom`, otherwise return + // error + let from = if let Some(from) = other.from { + from + } else if let ScrollTxEnvelope::L1Message(tx) = &inner { + tx.sender + } else { + return Err(serde_json::Error::custom("missing `from` field")); + }; + + let effective_gas_price = other.effective_gas_price.or_else(|| inner.gas_price()); + let recovered = Recovered::new_unchecked(inner, from); + + Ok(Self { + inner: alloy_rpc_types_eth::Transaction { + inner: recovered, + block_hash, + block_number, + transaction_index, + effective_gas_price, + }, + }) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use alloy_primitives::address; + + #[test] + fn can_deserialize_deposit() { + // cast rpc eth_getTransactionByHash + // 0x5c1c3785c8bf5d7f1cb714abd1d22e32642887215602c3a14a5e9ee105bad6aa --rpc-url https://rpc.scroll.io + let rpc_tx = r#"{"blockHash":"0x018ed80ea8340984a1f4841490284d6e51d71f9e9411feeca41e007a89fbfdff","blockNumber":"0xb81121","from":"0x7885bcbd5cecef1336b5300fb5186a12ddd8c478","gas":"0x1e8480","gasPrice":"0x0","hash":"0x5c1c3785c8bf5d7f1cb714abd1d22e32642887215602c3a14a5e9ee105bad6aa","input":"0x8ef1332e000000000000000000000000c186fa914353c44b2e33ebe05f21846f1048beda0000000000000000000000003bad7ad0728f9917d1bf08af5782dcbd516cdd96000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e7ba000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000044493a4f846ffc1507cbfe98a2b0ba1f06ea7e4eb749c001f78f6cb5540daa556a0566322a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","to":"0x781e90f1c8fc4611c9b7497c3b47f99ef6969cbc","transactionIndex":"0x0","value":"0x0","type":"0x7e","v":"0x0","r":"0x0","s":"0x0","sender":"0x7885bcbd5cecef1336b5300fb5186a12ddd8c478","queueIndex":"0xe7ba0", "yParity":"0x0"}"#; + + let tx = serde_json::from_str::<Transaction>(rpc_tx).unwrap(); + + let ScrollTxEnvelope::L1Message(inner) = tx.as_ref() else { + panic!("Expected deposit transaction"); + }; + assert_eq!(inner.sender, address!("7885bcbd5cecef1336b5300fb5186a12ddd8c478")); + assert_eq!(inner.queue_index, 0xe7ba0); + assert_eq!(tx.inner.effective_gas_price, Some(0)); + + let deserialized = serde_json::to_value(&tx).unwrap(); + let expected = serde_json::from_str::<serde_json::Value>(rpc_tx).unwrap(); + similar_asserts::assert_eq!(deserialized, expected); + } +}
diff --git reth/crates/scroll/alloy/rpc-types/src/transaction/request.rs scroll-reth/crates/scroll/alloy/rpc-types/src/transaction/request.rs new file mode 100644 index 0000000000000000000000000000000000000000..b8e1b9cc060e316485f247c996c5383b304e1561 --- /dev/null +++ scroll-reth/crates/scroll/alloy/rpc-types/src/transaction/request.rs @@ -0,0 +1,184 @@ +use alloy_consensus::{ + Sealed, SignableTransaction, Signed, TxEip1559, TxEip4844, TypedTransaction, +}; +use alloy_primitives::{Address, Signature, TxKind, U256}; +use alloy_rpc_types_eth::{AccessList, TransactionInput, TransactionRequest}; +use serde::{Deserialize, Serialize}; + +use scroll_alloy_consensus::{ScrollTxEnvelope, ScrollTypedTransaction, TxL1Message}; + +/// `ScrollTransactionRequest` is a wrapper around the `TransactionRequest` struct. +/// This struct derives several traits to facilitate easier use and manipulation +/// in the codebase. +#[derive( + Clone, + Debug, + Default, + PartialEq, + Eq, + Hash, + derive_more::From, + derive_more::AsRef, + derive_more::AsMut, + Serialize, + Deserialize, +)] +#[serde(transparent)] +pub struct ScrollTransactionRequest(TransactionRequest); + +impl ScrollTransactionRequest { + /// Sets the from field in the call to the provided address + #[inline] + pub const fn from(mut self, from: Address) -> Self { + self.0.from = Some(from); + self + } + + /// Sets the transactions type for the transactions. + #[doc(alias = "tx_type")] + pub const fn transaction_type(mut self, transaction_type: u8) -> Self { + self.0.transaction_type = Some(transaction_type); + self + } + + /// Sets the gas limit for the transaction. + pub const fn gas_limit(mut self, gas_limit: u64) -> Self { + self.0.gas = Some(gas_limit); + self + } + + /// Sets the nonce for the transaction. + pub const fn nonce(mut self, nonce: u64) -> Self { + self.0.nonce = Some(nonce); + self + } + + /// Sets the maximum fee per gas for the transaction. + pub const fn max_fee_per_gas(mut self, max_fee_per_gas: u128) -> Self { + self.0.max_fee_per_gas = Some(max_fee_per_gas); + self + } + + /// Sets the maximum priority fee per gas for the transaction. + pub const fn max_priority_fee_per_gas(mut self, max_priority_fee_per_gas: u128) -> Self { + self.0.max_priority_fee_per_gas = Some(max_priority_fee_per_gas); + self + } + + /// Sets the recipient address for the transaction. + #[inline] + pub const fn to(mut self, to: Address) -> Self { + self.0.to = Some(TxKind::Call(to)); + self + } + + /// Sets the value (amount) for the transaction. + pub const fn value(mut self, value: U256) -> Self { + self.0.value = Some(value); + self + } + + /// Sets the access list for the transaction. + pub fn access_list(mut self, access_list: AccessList) -> Self { + self.0.access_list = Some(access_list); + self + } + + /// Sets the input data for the transaction. + pub fn input(mut self, input: TransactionInput) -> Self { + self.0.input = input; + self + } + + /// Builds [`ScrollTypedTransaction`] from this builder. See + /// [`TransactionRequest::build_typed_tx`] for more info. + /// + /// Note that EIP-4844 transactions are not supported by Scroll and will be converted into + /// EIP-1559 transactions. + pub fn build_typed_tx(self) -> Result<ScrollTypedTransaction, Self> { + let tx = self.0.build_typed_tx().map_err(Self)?; + match tx { + TypedTransaction::Legacy(tx) => Ok(ScrollTypedTransaction::Legacy(tx)), + TypedTransaction::Eip1559(tx) => Ok(ScrollTypedTransaction::Eip1559(tx)), + TypedTransaction::Eip2930(tx) => Ok(ScrollTypedTransaction::Eip2930(tx)), + TypedTransaction::Eip4844(tx) => { + let tx: TxEip4844 = tx.into(); + Ok(ScrollTypedTransaction::Eip1559(TxEip1559 { + chain_id: tx.chain_id, + nonce: tx.nonce, + gas_limit: tx.gas_limit, + max_priority_fee_per_gas: tx.max_priority_fee_per_gas, + max_fee_per_gas: tx.max_fee_per_gas, + to: TxKind::Call(tx.to), + value: tx.value, + access_list: tx.access_list, + input: tx.input, + })) + } + TypedTransaction::Eip7702(_) => { + unimplemented!("EIP-7702 support is not implemented yet") + } + } + } +} + +impl From<TxL1Message> for ScrollTransactionRequest { + fn from(tx_l1_message: TxL1Message) -> Self { + let to = TxKind::from(tx_l1_message.to); + Self(TransactionRequest { + from: Some(tx_l1_message.sender), + to: Some(to), + value: Some(tx_l1_message.value), + gas: Some(tx_l1_message.gas_limit), + input: tx_l1_message.input.into(), + ..Default::default() + }) + } +} + +impl From<Sealed<TxL1Message>> for ScrollTransactionRequest { + fn from(value: Sealed<TxL1Message>) -> Self { + value.into_inner().into() + } +} + +impl<T> From<Signed<T, Signature>> for ScrollTransactionRequest +where + T: SignableTransaction<Signature> + Into<TransactionRequest>, +{ + fn from(value: Signed<T, Signature>) -> Self { + #[cfg(feature = "k256")] + let from = value.recover_signer().ok(); + #[cfg(not(feature = "k256"))] + let from = None; + + let mut inner: TransactionRequest = value.strip_signature().into(); + inner.from = from; + + Self(inner) + } +} + +impl From<ScrollTypedTransaction> for ScrollTransactionRequest { + fn from(tx: ScrollTypedTransaction) -> Self { + match tx { + ScrollTypedTransaction::Legacy(tx) => Self(tx.into()), + ScrollTypedTransaction::Eip2930(tx) => Self(tx.into()), + ScrollTypedTransaction::Eip1559(tx) => Self(tx.into()), + ScrollTypedTransaction::Eip7702(tx) => Self(tx.into()), + ScrollTypedTransaction::L1Message(tx) => tx.into(), + } + } +} + +impl From<ScrollTxEnvelope> for ScrollTransactionRequest { + fn from(value: ScrollTxEnvelope) -> Self { + match value { + ScrollTxEnvelope::Eip2930(tx) => tx.into(), + ScrollTxEnvelope::Eip1559(tx) => tx.into(), + ScrollTxEnvelope::Eip7702(tx) => tx.into(), + ScrollTxEnvelope::L1Message(tx) => tx.into(), + _ => Default::default(), + } + } +}
diff --git reth/crates/scroll/bin/scroll-reth/Cargo.toml scroll-reth/crates/scroll/bin/scroll-reth/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..8ca0487033fd720d87f35e98d8876abeb464133d --- /dev/null +++ scroll-reth/crates/scroll/bin/scroll-reth/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "scroll-reth" +version.workspace = true +edition.workspace = true +rust-version.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +exclude.workspace = true + +[lints] +workspace = true + +[dependencies] +# reth +reth-cli-util.workspace = true +reth-engine-local = { workspace = true, features = ["scroll-alloy-traits"] } +reth-provider.workspace = true + +# scroll +reth-scroll-cli.workspace = true +reth-scroll-node.workspace = true + +# misc +clap = { workspace = true, features = ["derive", "env"] } +tracing.workspace = true + +[features] +skip-state-root-validation = [ + "reth-scroll-node/skip-state-root-validation", + "reth-provider/skip-state-root-validation", +] +dev = ["reth-scroll-cli/dev"] + +[[bin]] +name = "scroll-reth" +path = "src/main.rs" +required-features = ["skip-state-root-validation"]
diff --git reth/crates/scroll/bin/scroll-reth/src/main.rs scroll-reth/crates/scroll/bin/scroll-reth/src/main.rs new file mode 100644 index 0000000000000000000000000000000000000000..cddbce9da50aaa7b06f618389bd2548e41cb5367 --- /dev/null +++ scroll-reth/crates/scroll/bin/scroll-reth/src/main.rs @@ -0,0 +1,29 @@ +//! Scroll binary + +#[global_allocator] +static ALLOC: reth_cli_util::allocator::Allocator = reth_cli_util::allocator::new_allocator(); + +fn main() { + use clap::Parser; + use reth_scroll_cli::{Cli, ScrollChainSpecParser, ScrollRollupArgs}; + use reth_scroll_node::ScrollNode; + use tracing::info; + + reth_cli_util::sigsegv_handler::install(); + + // Enable backtraces unless a RUST_BACKTRACE value has already been explicitly provided. + if std::env::var_os("RUST_BACKTRACE").is_none() { + std::env::set_var("RUST_BACKTRACE", "1"); + } + + if let Err(err) = Cli::<ScrollChainSpecParser, ScrollRollupArgs>::parse() + .run::<_, _, ScrollNode>(|builder, _| async move { + info!(target: "reth::cli", "Launching node"); + let handle = builder.launch_node(ScrollNode).await?; + handle.node_exit_future.await + }) + { + eprintln!("Error: {err:?}"); + std::process::exit(1); + } +}
diff --git reth/crates/scroll/chainspec/Cargo.toml scroll-reth/crates/scroll/chainspec/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..5df5dc4f98805ef3508fd82b78d1195b72e0e873 --- /dev/null +++ scroll-reth/crates/scroll/chainspec/Cargo.toml @@ -0,0 +1,66 @@ +[package] +name = "reth-scroll-chainspec" +version.workspace = true +edition.workspace = true +rust-version.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +description = "EVM chain spec implementation for scroll." + +[lints] +workspace = true + +[dependencies] +# reth +reth-chainspec = { workspace = true, default-features = false } +reth-ethereum-forks = { workspace = true, default-features = false } +reth-network-peers = { workspace = true, default-features = false } +reth-primitives-traits = { workspace = true, default-features = false } +reth-trie-common = { workspace = true, default-features = false } + +# scroll +reth-scroll-forks = { workspace = true, default-features = false } +scroll-alloy-hardforks = { workspace = true, default-features = false } + +# ethereum +alloy-chains = { workspace = true, default-features = false } +alloy-genesis = { workspace = true, default-features = false } +alloy-primitives = { workspace = true, default-features = false } +alloy-consensus = { workspace = true, default-features = false } +alloy-eips = { workspace = true, default-features = false } +alloy-serde = { workspace = true, default-features = false } + +# io +serde_json = { workspace = true, default-features = false } +serde = { workspace = true, default-features = false, features = ["derive"] } + +# misc +derive_more = { workspace = true, default-features = false } +once_cell = { workspace = true, default-features = false } + +[dev-dependencies] +alloy-genesis.workspace = true +reth-chainspec = { workspace = true, features = ["test-utils"] } + +[features] +default = ["std"] +std = [ + "alloy-chains/std", + "alloy-genesis/std", + "alloy-primitives/std", + "alloy-eips/std", + "alloy-serde/std", + "reth-chainspec/std", + "reth-ethereum-forks/std", + "reth-primitives-traits/std", + "reth-scroll-forks/std", + "reth-trie-common/std", + "alloy-consensus/std", + "once_cell/std", + "serde/std", + "derive_more/std", + "reth-network-peers/std", + "serde_json/std", + "scroll-alloy-hardforks/std", +]
diff --git reth/crates/scroll/chainspec/res/genesis/dev.json scroll-reth/crates/scroll/chainspec/res/genesis/dev.json new file mode 100644 index 0000000000000000000000000000000000000000..7f4fc2d48ca32b44ef59b534d92162fd6d9299de --- /dev/null +++ scroll-reth/crates/scroll/chainspec/res/genesis/dev.json @@ -0,0 +1,86 @@ +{ + "nonce": "0x0", + "timestamp": "0x6490fdd2", + "extraData": "0x", + "gasLimit": "0x1c9c380", + "difficulty": "0x0", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "coinbase": "0x0000000000000000000000000000000000000000", + "alloc": { + "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266": { + "balance": "0xD3C21BCECCEDA1000000" + }, + "0x70997970C51812dc3A010C7d01b50e0d17dc79C8": { + "balance": "0xD3C21BCECCEDA1000000" + }, + "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC": { + "balance": "0xD3C21BCECCEDA1000000" + }, + "0x90F79bf6EB2c4f870365E785982E1f101E93b906": { + "balance": "0xD3C21BCECCEDA1000000" + }, + "0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65": { + "balance": "0xD3C21BCECCEDA1000000" + }, + "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc": { + "balance": "0xD3C21BCECCEDA1000000" + }, + "0x976EA74026E726554dB657fA54763abd0C3a0aa9": { + "balance": "0xD3C21BCECCEDA1000000" + }, + "0x14dC79964da2C08b23698B3D3cc7Ca32193d9955": { + "balance": "0xD3C21BCECCEDA1000000" + }, + "0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f": { + "balance": "0xD3C21BCECCEDA1000000" + }, + "0xa0Ee7A142d267C1f36714E4a8F75612F20a79720": { + "balance": "0xD3C21BCECCEDA1000000" + }, + "0xBcd4042DE499D14e55001CcbB24a551F3b954096": { + "balance": "0xD3C21BCECCEDA1000000" + }, + "0x71bE63f3384f5fb98995898A86B02Fb2426c5788": { + "balance": "0xD3C21BCECCEDA1000000" + }, + "0xFABB0ac9d68B0B445fB7357272Ff202C5651694a": { + "balance": "0xD3C21BCECCEDA1000000" + }, + "0x1CBd3b2770909D4e10f157cABC84C7264073C9Ec": { + "balance": "0xD3C21BCECCEDA1000000" + }, + "0xdF3e18d64BC6A983f673Ab319CCaE4f1a57C7097": { + "balance": "0xD3C21BCECCEDA1000000" + }, + "0xcd3B766CCDd6AE721141F452C550Ca635964ce71": { + "balance": "0xD3C21BCECCEDA1000000" + }, + "0x2546BcD3c84621e976D8185a91A922aE77ECEc30": { + "balance": "0xD3C21BCECCEDA1000000" + }, + "0xbDA5747bFD65F08deb54cb465eB87D40e51B197E": { + "balance": "0xD3C21BCECCEDA1000000" + }, + "0xdD2FD4581271e230360230F9337D5c0430Bf44C0": { + "balance": "0xD3C21BCECCEDA1000000" + }, + "0x8626f6940E2eb28930eFb4CeF49B2d1F2C9C1199": { + "balance": "0xD3C21BCECCEDA1000000" + }, + "0x5300000000000000000000000000000000000002": { + "balance": "0xd3c21bcecceda1000000", + "storage": { + "0x01": "0x000000000000000000000000000000000000000000000000000000003758e6b0", + "0x02": "0x0000000000000000000000000000000000000000000000000000000000000038", + "0x03": "0x000000000000000000000000000000000000000000000000000000003e95ba80", + "0x04": "0x0000000000000000000000005300000000000000000000000000000000000003", + "0x05": "0x000000000000000000000000000000000000000000000000000000008390c2c1", + "0x06": "0x00000000000000000000000000000000000000000000000000000069cf265bfe", + "0x07": "0x00000000000000000000000000000000000000000000000000000000168b9aa3" + } + } + }, + "number": "0x0", + "gasUsed": "0x0", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000" +} \ No newline at end of file
diff --git reth/crates/scroll/chainspec/res/genesis/scroll.json scroll-reth/crates/scroll/chainspec/res/genesis/scroll.json new file mode 100644 index 0000000000000000000000000000000000000000..ef6360ac9d90367c5029a8f6858c062a88866cef --- /dev/null +++ scroll-reth/crates/scroll/chainspec/res/genesis/scroll.json @@ -0,0 +1 @@ +{"config":{"chainId":534352,"homesteadBlock":0,"eip150Block":0,"eip150Hash":"0x0000000000000000000000000000000000000000000000000000000000000000","eip155Block":0,"eip158Block":0,"byzantiumBlock":0,"constantinopleBlock":0,"petersburgBlock":0,"istanbulBlock":0,"berlinBlock":0,"londonBlock":0,"archimedesBlock":0,"shanghaiTime":0,"bernoulliBlock":5220340,"curieBlock":7096836,"darwinTime":1724227200,"darwinV2Time":1725264000,"clique":{"period":3,"epoch":30000,"relaxed_period":true},"scroll":{"useZktrie":true,"maxTxPerBlock":100,"maxTxPayloadBytesPerBlock":122880,"feeVaultAddress":"0x5300000000000000000000000000000000000005","l1Config":{"l1ChainId":"1","l1MessageQueueAddress":"0x0d7E906BD9cAFa154b048cFa766Cc1E54E39AF9B","scrollChainAddress":"0xa13BAF47339d63B743e7Da8741db5456DAc1E556","numL1MessagesPerBlock":"10"}}},"nonce":"0x0","timestamp":"0x6524e860","extraData":"0x4c61206573746f6e7465636f206573746173206d616c6665726d6974612e0000d2ACF5d16a983DB0d909d9D761B8337Fabd6cBd10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","gasLimit":"10000000","difficulty":"0x1","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","coinbase":"0x0000000000000000000000000000000000000000","alloc":{"0xF9062b8a30e0d7722960e305049FA50b86ba6253":{"balance":"2000000000000000000"},"0x781e90f1c8Fc4611c9b7497C3B47F99Ef6969CbC":{"balance":"226156424291633194186662080095093570025917938800079226637565593765455331328"},"0x5300000000000000000000000000000000000000":{"balance":"0x0","code":"0x608060405234801561001057600080fd5b50600436106100935760003560e01c806383cc76601161006657806383cc7660146100fc5780638da5cb5b1461010f578063c4d66de814610122578063d4b9f4fa14610135578063f2fde38b1461013e57600080fd5b806326aad7b7146100985780633cb747bf146100b4578063600a2e77146100df578063715018a6146100f2575b600080fd5b6100a160015481565b6040519081526020015b60405180910390f35b6053546100c7906001600160a01b031681565b6040516001600160a01b0390911681526020016100ab565b6100a16100ed36600461054a565b610151565b6100fa6101f6565b005b6100a161010a36600461054a565b61022c565b6052546100c7906001600160a01b031681565b6100fa610130366004610563565b610243565b6100a160005481565b6100fa61014c366004610563565b6102db565b6053546000906001600160a01b031633146101a45760405162461bcd60e51b815260206004820152600e60248201526d37b7363c9036b2b9b9b2b733b2b960911b60448201526064015b60405180910390fd5b6000806101b084610367565b60408051838152602081018890529294509092507ffaa617c2d8ce12c62637dbce76efcc18dae60574aa95709bdcedce7e76071693910160405180910390a19392505050565b6052546001600160a01b031633146102205760405162461bcd60e51b815260040161019b90610593565b61022a6000610486565b565b602a816028811061023c57600080fd5b0154905081565b6052546001600160a01b0316331461026d5760405162461bcd60e51b815260040161019b90610593565b600154156102b15760405162461bcd60e51b815260206004820152601160248201527063616e6e6f7420696e697469616c697a6560781b604482015260640161019b565b6102b96104d8565b605380546001600160a01b0319166001600160a01b0392909216919091179055565b6052546001600160a01b031633146103055760405162461bcd60e51b815260040161019b90610593565b6001600160a01b03811661035b5760405162461bcd60e51b815260206004820152601d60248201527f6e6577206f776e657220697320746865207a65726f2061646472657373000000604482015260640161019b565b61036481610486565b50565b60035460009081906103bb5760405162461bcd60e51b815260206004820152601a60248201527f63616c6c206265666f726520696e697469616c697a6174696f6e000000000000604482015260640161019b565b6001548360005b8215610456576103d36002846105e0565b60000361041f5781602a82602881106103ee576103ee6105ca565b01556104188260028360288110610407576104076105ca565b015460009182526020526040902090565b915061044a565b610447602a8260288110610435576104356105ca565b01548360009182526020526040902090565b91505b600192831c92016103c2565b81602a826028811061046a5761046a6105ca565b0155506000819055600180548082019091559590945092505050565b605280546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b60005b60286104e8826001610618565b10156103645761051960028260288110610504576105046105ca565b015460028360288110610407576104076105ca565b6002610526836001610618565b60288110610536576105366105ca565b01558061054281610631565b9150506104db565b60006020828403121561055c57600080fd5b5035919050565b60006020828403121561057557600080fd5b81356001600160a01b038116811461058c57600080fd5b9392505050565b60208082526017908201527f63616c6c6572206973206e6f7420746865206f776e6572000000000000000000604082015260600190565b634e487b7160e01b600052603260045260246000fd5b6000826105fd57634e487b7160e01b600052601260045260246000fd5b500690565b634e487b7160e01b600052601160045260246000fd5b8082018082111561062b5761062b610602565b92915050565b60006001820161064357610643610602565b506001019056fea26469706673582212208fb1cb9933bb17dd0a7c17de7c890919b08d2fd7eb2bede7b41caa32709b30b564736f6c63430008100033","storage":{"0x0000000000000000000000000000000000000000000000000000000000000052":"0xF9062b8a30e0d7722960e305049FA50b86ba6253"}},"0x5300000000000000000000000000000000000002":{"balance":"0x0","code":"0x608060405234801561001057600080fd5b50600436106100cf5760003560e01c8063715018a61161008c578063bede39b511610066578063bede39b51461018d578063de26c4a1146101a0578063f2fde38b146101b3578063f45e65d8146101c657600080fd5b8063715018a6146101475780638da5cb5b1461014f57806393e59dc11461017a57600080fd5b80630c18c162146100d45780633577afc5146100f05780633d0f963e1461010557806349948e0e14610118578063519b4bd31461012b5780637046559714610134575b600080fd5b6100dd60025481565b6040519081526020015b60405180910390f35b6101036100fe366004610671565b6101cf565b005b61010361011336600461068a565b610291565b6100dd6101263660046106d0565b61031c565b6100dd60015481565b610103610142366004610671565b610361565b610103610416565b600054610162906001600160a01b031681565b6040516001600160a01b0390911681526020016100e7565b600454610162906001600160a01b031681565b61010361019b366004610671565b61044c565b6100dd6101ae3660046106d0565b610533565b6101036101c136600461068a565b610595565b6100dd60035481565b6000546001600160a01b031633146102025760405162461bcd60e51b81526004016101f990610781565b60405180910390fd5b621c9c388111156102555760405162461bcd60e51b815260206004820152601760248201527f657863656564206d6178696d756d206f7665726865616400000000000000000060448201526064016101f9565b60028190556040518181527f32740b35c0ea213650f60d44366b4fb211c9033b50714e4a1d34e65d5beb9bb4906020015b60405180910390a150565b6000546001600160a01b031633146102bb5760405162461bcd60e51b81526004016101f990610781565b600480546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527f22d1c35fe072d2e42c3c8f9bd4a0d34aa84a0101d020a62517b33fdb3174e5f7910160405180910390a15050565b60008061032883610533565b905060006001548261033a91906107b8565b9050633b9aca006003548261034f91906107b8565b61035991906107e5565b949350505050565b6000546001600160a01b0316331461038b5760405162461bcd60e51b81526004016101f990610781565b61039b633b9aca006103e86107b8565b8111156103e15760405162461bcd60e51b8152602060048201526014602482015273657863656564206d6178696d756d207363616c6560601b60448201526064016101f9565b60038190556040518181527f3336cd9708eaf2769a0f0dc0679f30e80f15dcd88d1921b5a16858e8b85c591a90602001610286565b6000546001600160a01b031633146104405760405162461bcd60e51b81526004016101f990610781565b61044a6000610621565b565b6004805460405163efc7840160e01b815233928101929092526001600160a01b03169063efc7840190602401602060405180830381865afa158015610495573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104b99190610807565b6104fe5760405162461bcd60e51b81526020600482015260166024820152752737ba103bb434ba32b634b9ba32b21039b2b73232b960511b60448201526064016101f9565b60018190556040518181527f351fb23757bb5ea0546c85b7996ddd7155f96b939ebaa5ff7bc49c75f27f2c4490602001610286565b80516000908190815b818110156105865784818151811061055657610556610829565b01602001516001600160f81b0319166000036105775760048301925061057e565b6010830192505b60010161053c565b50506002540160400192915050565b6000546001600160a01b031633146105bf5760405162461bcd60e51b81526004016101f990610781565b6001600160a01b0381166106155760405162461bcd60e51b815260206004820152601d60248201527f6e6577206f776e657220697320746865207a65726f206164647265737300000060448201526064016101f9565b61061e81610621565b50565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60006020828403121561068357600080fd5b5035919050565b60006020828403121561069c57600080fd5b81356001600160a01b03811681146106b357600080fd5b9392505050565b634e487b7160e01b600052604160045260246000fd5b6000602082840312156106e257600080fd5b813567ffffffffffffffff808211156106fa57600080fd5b818401915084601f83011261070e57600080fd5b813581811115610720576107206106ba565b604051601f8201601f19908116603f01168101908382118183101715610748576107486106ba565b8160405282815287602084870101111561076157600080fd5b826020860160208301376000928101602001929092525095945050505050565b60208082526017908201527f63616c6c6572206973206e6f7420746865206f776e6572000000000000000000604082015260600190565b60008160001904831182151516156107e057634e487b7160e01b600052601160045260246000fd5b500290565b60008261080257634e487b7160e01b600052601260045260246000fd5b500490565b60006020828403121561081957600080fd5b815180151581146106b357600080fd5b634e487b7160e01b600052603260045260246000fdfea26469706673582212205ea335809638809cf032c794fd966e2439020737b1dcc2218435cb438286efcf64736f6c63430008100033","storage":{"0x0000000000000000000000000000000000000000000000000000000000000000":"0xF9062b8a30e0d7722960e305049FA50b86ba6253"}},"0x5300000000000000000000000000000000000003":{"balance":"0x0","code":"0x608060405234801561001057600080fd5b50600436106100575760003560e01c8063715018a61461005c57806379586dd7146100665780638da5cb5b14610079578063efc78401146100a9578063f2fde38b146100e5575b600080fd5b6100646100f8565b005b610064610074366004610356565b610137565b60005461008c906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b6100d56100b736600461042d565b6001600160a01b031660009081526001602052604090205460ff1690565b60405190151581526020016100a0565b6100646100f336600461042d565b610238565b6000546001600160a01b0316331461012b5760405162461bcd60e51b81526004016101229061044f565b60405180910390fd5b61013560006102c4565b565b6000546001600160a01b031633146101615760405162461bcd60e51b81526004016101229061044f565b60005b825181101561023357816001600085848151811061018457610184610486565b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060006101000a81548160ff0219169083151502179055508281815181106101d5576101d5610486565b60200260200101516001600160a01b03167f8daaf060c3306c38e068a75c054bf96ecd85a3db1252712c4d93632744c42e0d83604051610219911515815260200190565b60405180910390a28061022b8161049c565b915050610164565b505050565b6000546001600160a01b031633146102625760405162461bcd60e51b81526004016101229061044f565b6001600160a01b0381166102b85760405162461bcd60e51b815260206004820152601d60248201527f6e6577206f776e657220697320746865207a65726f20616464726573730000006044820152606401610122565b6102c1816102c4565b50565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b634e487b7160e01b600052604160045260246000fd5b80356001600160a01b038116811461034157600080fd5b919050565b8035801515811461034157600080fd5b6000806040838503121561036957600080fd5b823567ffffffffffffffff8082111561038157600080fd5b818501915085601f83011261039557600080fd5b81356020828211156103a9576103a9610314565b8160051b604051601f19603f830116810181811086821117156103ce576103ce610314565b6040529283528183019350848101820192898411156103ec57600080fd5b948201945b83861015610411576104028661032a565b855294820194938201936103f1565b96506104209050878201610346565b9450505050509250929050565b60006020828403121561043f57600080fd5b6104488261032a565b9392505050565b60208082526017908201527f63616c6c6572206973206e6f7420746865206f776e6572000000000000000000604082015260600190565b634e487b7160e01b600052603260045260246000fd5b6000600182016104bc57634e487b7160e01b600052601160045260246000fd5b506001019056fea26469706673582212203414b076e92b618bd7c3437159d7bceb2acc3a5c82f51f383465512d9c52e97064736f6c63430008100033","storage":{"0x0000000000000000000000000000000000000000000000000000000000000000":"0xF9062b8a30e0d7722960e305049FA50b86ba6253"}},"0x5300000000000000000000000000000000000004":{"balance":"0x0","code":"0x6080604052600436106101025760003560e01c806370a0823111610095578063a457c2d711610064578063a457c2d7146102b4578063a9059cbb146102d4578063d0e30db0146102f4578063d505accf146102fc578063dd62ed3e1461031c57600080fd5b806370a08231146102215780637ecebe001461025757806384b0196e1461027757806395d89b411461029f57600080fd5b80632e1a7d4d116100d15780632e1a7d4d146101b0578063313ce567146101d05780633644e515146101ec578063395093511461020157600080fd5b806306fdde0314610116578063095ea7b31461014157806318160ddd1461017157806323b872dd1461019057600080fd5b366101115761010f61033c565b005b600080fd5b34801561012257600080fd5b5061012b61038d565b60405161013891906112fa565b60405180910390f35b34801561014d57600080fd5b5061016161015c366004611330565b61041f565b6040519015158152602001610138565b34801561017d57600080fd5b506002545b604051908152602001610138565b34801561019c57600080fd5b506101616101ab36600461135a565b610439565b3480156101bc57600080fd5b5061010f6101cb366004611396565b61045d565b3480156101dc57600080fd5b5060405160128152602001610138565b3480156101f857600080fd5b5061018261054e565b34801561020d57600080fd5b5061016161021c366004611330565b61055d565b34801561022d57600080fd5b5061018261023c3660046113af565b6001600160a01b031660009081526020819052604090205490565b34801561026357600080fd5b506101826102723660046113af565b61057f565b34801561028357600080fd5b5061028c61059d565b60405161013897969594939291906113ca565b3480156102ab57600080fd5b5061012b610626565b3480156102c057600080fd5b506101616102cf366004611330565b610635565b3480156102e057600080fd5b506101616102ef366004611330565b6106b0565b61010f61033c565b34801561030857600080fd5b5061010f610317366004611460565b6106be565b34801561032857600080fd5b506101826103373660046114d3565b610822565b336103478134610881565b806001600160a01b03167fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c3460405161038291815260200190565b60405180910390a250565b60606003805461039c90611506565b80601f01602080910402602001604051908101604052809291908181526020018280546103c890611506565b80156104155780601f106103ea57610100808354040283529160200191610415565b820191906000526020600020905b8154815290600101906020018083116103f857829003601f168201915b5050505050905090565b60003361042d818585610940565b60019150505b92915050565b600033610447858285610a65565b610452858585610adf565b506001949350505050565b336104688183610c83565b6000816001600160a01b03168360405160006040518083038185875af1925050503d80600081146104b5576040519150601f19603f3d011682016040523d82523d6000602084013e6104ba565b606091505b50509050806105065760405162461bcd60e51b81526020600482015260136024820152721dda5d1a191c985dc81155120819985a5b1959606a1b60448201526064015b60405180910390fd5b816001600160a01b03167f7fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b658460405161054191815260200190565b60405180910390a2505050565b6000610558610db2565b905090565b60003361042d8185856105708383610822565b61057a919061153a565b610940565b6001600160a01b038116600090815260076020526040812054610433565b6000606080828080836105d17f577261707065642045746865720000000000000000000000000000000000000d6005610edd565b6105fc7f31000000000000000000000000000000000000000000000000000000000000016006610edd565b60408051600080825260208201909252600f60f81b9b939a50919850469750309650945092509050565b60606004805461039c90611506565b600033816106438286610822565b9050838110156106a35760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b60648201526084016104fd565b6104528286868403610940565b60003361042d818585610adf565b8342111561070e5760405162461bcd60e51b815260206004820152601d60248201527f45524332305065726d69743a206578706972656420646561646c696e6500000060448201526064016104fd565b60007f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c988888861073d8c610f81565b6040805160208101969096526001600160a01b0394851690860152929091166060840152608083015260a082015260c0810186905260e001604051602081830303815290604052805190602001209050600061079882610fa9565b905060006107a882878787610fd6565b9050896001600160a01b0316816001600160a01b03161461080b5760405162461bcd60e51b815260206004820152601e60248201527f45524332305065726d69743a20696e76616c6964207369676e6174757265000060448201526064016104fd565b6108168a8a8a610940565b50505050505050505050565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b60006020835110156108695761086283610ffe565b9050610433565b8161087484826115bf565b5060ff9050610433565b90565b6001600160a01b0382166108d75760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f20616464726573730060448201526064016104fd565b80600260008282546108e9919061153a565b90915550506001600160a01b038216600081815260208181526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b6001600160a01b0383166109a25760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b60648201526084016104fd565b6001600160a01b038216610a035760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b60648201526084016104fd565b6001600160a01b0383811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3505050565b6000610a718484610822565b90506000198114610ad95781811015610acc5760405162461bcd60e51b815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e636500000060448201526064016104fd565b610ad98484848403610940565b50505050565b6001600160a01b038316610b435760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b60648201526084016104fd565b6001600160a01b038216610ba55760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b60648201526084016104fd565b6001600160a01b03831660009081526020819052604090205481811015610c1d5760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b60648201526084016104fd565b6001600160a01b03848116600081815260208181526040808320878703905593871680835291849020805487019055925185815290927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a3610ad9565b6001600160a01b038216610ce35760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b60648201526084016104fd565b6001600160a01b03821660009081526020819052604090205481811015610d575760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b60648201526084016104fd565b6001600160a01b0383166000818152602081815260408083208686039055600280548790039055518581529192917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9101610a58565b505050565b6000306001600160a01b037f000000000000000000000000530000000000000000000000000000000000000416148015610e0b57507f000000000000000000000000000000000000000000000000000000000008275046145b15610e3557507fe5b117a3cd7ae7ed3508e6e6c5a0794536b2a8dee12533c4d7524eae9c85438f90565b610558604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201527f00cd3d46df44f2cbb950cf84eb2e92aa2ddd23195b1a009173ea59a063357ed3918101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260009060c00160405160208183030381529060405280519060200120905090565b606060ff8314610ef0576108628361103c565b818054610efc90611506565b80601f0160208091040260200160405190810160405280929190818152602001828054610f2890611506565b8015610f755780601f10610f4a57610100808354040283529160200191610f75565b820191906000526020600020905b815481529060010190602001808311610f5857829003601f168201915b50505050509050610433565b6001600160a01b03811660009081526007602052604090208054600181018255905b50919050565b6000610433610fb6610db2565b8360405161190160f01b8152600281019290925260228201526042902090565b6000806000610fe78787878761107b565b91509150610ff48161113f565b5095945050505050565b600080829050601f81511115611029578260405163305a27a960e01b81526004016104fd91906112fa565b80516110348261167f565b179392505050565b606060006110498361128c565b604080516020808252818301909252919250600091906020820181803683375050509182525060208101929092525090565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08311156110b25750600090506003611136565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa158015611106573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b03811661112f57600060019250925050611136565b9150600090505b94509492505050565b6000816004811115611153576111536116a3565b0361115b5750565b600181600481111561116f5761116f6116a3565b036111bc5760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e6174757265000000000000000060448201526064016104fd565b60028160048111156111d0576111d06116a3565b0361121d5760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e6774680060448201526064016104fd565b6003816004811115611231576112316116a3565b036112895760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b60648201526084016104fd565b50565b600060ff8216601f81111561043357604051632cd44ac360e21b815260040160405180910390fd5b6000815180845260005b818110156112da576020818501810151868301820152016112be565b506000602082860101526020601f19601f83011685010191505092915050565b60208152600061130d60208301846112b4565b9392505050565b80356001600160a01b038116811461132b57600080fd5b919050565b6000806040838503121561134357600080fd5b61134c83611314565b946020939093013593505050565b60008060006060848603121561136f57600080fd5b61137884611314565b925061138660208501611314565b9150604084013590509250925092565b6000602082840312156113a857600080fd5b5035919050565b6000602082840312156113c157600080fd5b61130d82611314565b60ff60f81b881681526000602060e0818401526113ea60e084018a6112b4565b83810360408501526113fc818a6112b4565b606085018990526001600160a01b038816608086015260a0850187905284810360c0860152855180825283870192509083019060005b8181101561144e57835183529284019291840191600101611432565b50909c9b505050505050505050505050565b600080600080600080600060e0888a03121561147b57600080fd5b61148488611314565b965061149260208901611314565b95506040880135945060608801359350608088013560ff811681146114b657600080fd5b9699959850939692959460a0840135945060c09093013592915050565b600080604083850312156114e657600080fd5b6114ef83611314565b91506114fd60208401611314565b90509250929050565b600181811c9082168061151a57607f821691505b602082108103610fa357634e487b7160e01b600052602260045260246000fd5b8082018082111561043357634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b601f821115610dad57600081815260208120601f850160051c810160208610156115985750805b601f850160051c820191505b818110156115b7578281556001016115a4565b505050505050565b815167ffffffffffffffff8111156115d9576115d961155b565b6115ed816115e78454611506565b84611571565b602080601f831160018114611622576000841561160a5750858301515b600019600386901b1c1916600185901b1785556115b7565b600085815260208120601f198616915b8281101561165157888601518255948401946001909101908401611632565b508582101561166f5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b80516020808301519190811015610fa35760001960209190910360031b1b16919050565b634e487b7160e01b600052602160045260246000fdfea26469706673582212207f39e33e122e8e2b0381aa6abea46046f56b05ced66c556a06bb1b80be7f55cf64736f6c63430008100033","storage":{"0x0000000000000000000000000000000000000000000000000000000000000003":"0x577261707065642045746865720000000000000000000000000000000000001a","0x0000000000000000000000000000000000000000000000000000000000000004":"0x5745544800000000000000000000000000000000000000000000000000000008"}},"0x5300000000000000000000000000000000000005":{"balance":"0x0","code":"0x6080604052600436106100ab5760003560e01c806384411d651161006457806384411d65146101845780638da5cb5b1461019a5780639e7adc79146101ba578063f2fde38b146101da578063feec756c146101fa578063ff4f35461461021a57600080fd5b80632e1a7d4d146100b75780633cb747bf146100d95780633ccfd60b14610116578063457e1a491461012b57806366d003ac1461014f578063715018a61461016f57600080fd5b366100b257005b600080fd5b3480156100c357600080fd5b506100d76100d2366004610682565b61023a565b005b3480156100e557600080fd5b506002546100f9906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b34801561012257600080fd5b506100d76103ff565b34801561013757600080fd5b5061014160015481565b60405190815260200161010d565b34801561015b57600080fd5b506003546100f9906001600160a01b031681565b34801561017b57600080fd5b506100d761040c565b34801561019057600080fd5b5061014160045481565b3480156101a657600080fd5b506000546100f9906001600160a01b031681565b3480156101c657600080fd5b506100d76101d536600461069b565b610442565b3480156101e657600080fd5b506100d76101f536600461069b565b6104be565b34801561020657600080fd5b506100d761021536600461069b565b610547565b34801561022657600080fd5b506100d7610235366004610682565b6105c3565b6001548110156102ca5760405162461bcd60e51b815260206004820152604a60248201527f4665655661756c743a207769746864726177616c20616d6f756e74206d75737460448201527f2062652067726561746572207468616e206d696e696d756d20776974686472616064820152691dd85b08185b5bdd5b9d60b21b608482015260a4015b60405180910390fd5b478082111561032e5760405162461bcd60e51b815260206004820152602a60248201527f4665655661756c743a20696e73756666696369656e742062616c616e636520746044820152696f20776974686472617760b01b60648201526084016102c1565b6004805483019055600354604080518481526001600160a01b0390921660208301523382820152517fc8a211cc64b6ed1b50595a9fcb1932b6d1e5a6e8ef15b60e5b1f988ea9086bba9181900360600190a1600254600354604080516020810182526000808252915163b2267a7b60e01b81526001600160a01b039485169463b2267a7b9488946103c99491909216928592906004016106cb565b6000604051808303818588803b1580156103e257600080fd5b505af11580156103f6573d6000803e3d6000fd5b50505050505050565b476104098161023a565b50565b6000546001600160a01b031633146104365760405162461bcd60e51b81526004016102c190610737565b6104406000610632565b565b6000546001600160a01b0316331461046c5760405162461bcd60e51b81526004016102c190610737565b600280546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f1c928c417a10a21c3cddad148c5dba5d710e4b1442d6d8a36de345935ad8461290600090a35050565b6000546001600160a01b031633146104e85760405162461bcd60e51b81526004016102c190610737565b6001600160a01b03811661053e5760405162461bcd60e51b815260206004820152601d60248201527f6e6577206f776e657220697320746865207a65726f206164647265737300000060448201526064016102c1565b61040981610632565b6000546001600160a01b031633146105715760405162461bcd60e51b81526004016102c190610737565b600380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f7e1e96961a397c8aa26162fe259cc837afc95e33aad4945ddc61c18dabb7a6ad90600090a35050565b6000546001600160a01b031633146105ed5760405162461bcd60e51b81526004016102c190610737565b600180549082905560408051828152602081018490527f0d3c80219fe57713b9f9c83d1e51426792d0c14d8e330e65b102571816140965910160405180910390a15050565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60006020828403121561069457600080fd5b5035919050565b6000602082840312156106ad57600080fd5b81356001600160a01b03811681146106c457600080fd5b9392505050565b60018060a01b038516815260006020858184015260806040840152845180608085015260005b8181101561070d5786810183015185820160a0015282016106f1565b50600060a0828601015260a0601f19601f8301168501019250505082606083015295945050505050565b60208082526017908201527f63616c6c6572206973206e6f7420746865206f776e657200000000000000000060408201526060019056fea2646970667358221220063c6c384f745ebcacfdd13320e5b9a50687aae43ff14566761f56273111b97e64736f6c63430008100033","storage":{"0x0000000000000000000000000000000000000000000000000000000000000000":"0xF9062b8a30e0d7722960e305049FA50b86ba6253","0x0000000000000000000000000000000000000000000000000000000000000001":"0x8ac7230489e80000","0x0000000000000000000000000000000000000000000000000000000000000002":"0x781e90f1c8Fc4611c9b7497C3B47F99Ef6969CbC","0x0000000000000000000000000000000000000000000000000000000000000003":"0x8FA3b4570B4C96f8036C13b64971BA65867eEB48"}}},"number":"0x0","gasUsed":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","baseFeePerGas":null}
diff --git reth/crates/scroll/chainspec/res/genesis/sepolia_scroll.json scroll-reth/crates/scroll/chainspec/res/genesis/sepolia_scroll.json new file mode 100644 index 0000000000000000000000000000000000000000..64987e5c8c5eed66d6f1927b5f8f0b6d31b18de9 --- /dev/null +++ scroll-reth/crates/scroll/chainspec/res/genesis/sepolia_scroll.json @@ -0,0 +1 @@ +{"config":{"chainId":534351,"homesteadBlock":0,"eip150Block":0,"eip150Hash":"0x0000000000000000000000000000000000000000000000000000000000000000","eip155Block":0,"eip158Block":0,"byzantiumBlock":0,"constantinopleBlock":0,"petersburgBlock":0,"istanbulBlock":0,"berlinBlock":0,"londonBlock":0,"archimedesBlock":0,"shanghaiBlock":0,"bernoulliBlock":3747132,"curieBlock":4740239,"clique":{"period":3,"epoch":30000,"relaxed_period":true},"scroll":{"useZktrie":true,"maxTxPerBlock":100,"maxTxPayloadBytesPerBlock":122880,"feeVaultAddress":"0x5300000000000000000000000000000000000005","enableEIP2718":false,"enableEIP1559":false,"l1Config":{"l1ChainId":"11155111","l1MessageQueueAddress":"0xF0B2293F5D834eAe920c6974D50957A1732de763","scrollChainAddress":"0x2D567EcE699Eabe5afCd141eDB7A4f2D0D6ce8a0","numL1MessagesPerBlock":"10"}}},"nonce":"0x0","timestamp":"0x64cfd015","extraData":"0x000000000000000000000000000000000000000000000000000000000000000048C3F81f3D998b6652900e1C3183736C238Fe4290000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","gasLimit":"8000000","difficulty":"0x1","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","coinbase":"0x0000000000000000000000000000000000000000","alloc":{"0x18960EEc21b1878C581937a14c5c3C43008F6b6B":{"balance":"10000000000000000000"},"0xBa50f5340FB9F3Bd074bD638c9BE13eCB36E603d":{"balance":"226156424291633194186662080095093570025917938800079226629565593765455331328"},"0x5300000000000000000000000000000000000000":{"balance":"0x0","code":"0x608060405234801561001057600080fd5b50600436106100935760003560e01c806383cc76601161006657806383cc7660146100fc5780638da5cb5b1461010f578063c4d66de814610122578063d4b9f4fa14610135578063f2fde38b1461013e57600080fd5b806326aad7b7146100985780633cb747bf146100b4578063600a2e77146100df578063715018a6146100f2575b600080fd5b6100a160015481565b6040519081526020015b60405180910390f35b6053546100c7906001600160a01b031681565b6040516001600160a01b0390911681526020016100ab565b6100a16100ed36600461054a565b610151565b6100fa6101f6565b005b6100a161010a36600461054a565b61022c565b6052546100c7906001600160a01b031681565b6100fa610130366004610563565b610243565b6100a160005481565b6100fa61014c366004610563565b6102db565b6053546000906001600160a01b031633146101a45760405162461bcd60e51b815260206004820152600e60248201526d37b7363c9036b2b9b9b2b733b2b960911b60448201526064015b60405180910390fd5b6000806101b084610367565b60408051838152602081018890529294509092507ffaa617c2d8ce12c62637dbce76efcc18dae60574aa95709bdcedce7e76071693910160405180910390a19392505050565b6052546001600160a01b031633146102205760405162461bcd60e51b815260040161019b90610593565b61022a6000610486565b565b602a816028811061023c57600080fd5b0154905081565b6052546001600160a01b0316331461026d5760405162461bcd60e51b815260040161019b90610593565b600154156102b15760405162461bcd60e51b815260206004820152601160248201527063616e6e6f7420696e697469616c697a6560781b604482015260640161019b565b6102b96104d8565b605380546001600160a01b0319166001600160a01b0392909216919091179055565b6052546001600160a01b031633146103055760405162461bcd60e51b815260040161019b90610593565b6001600160a01b03811661035b5760405162461bcd60e51b815260206004820152601d60248201527f6e6577206f776e657220697320746865207a65726f2061646472657373000000604482015260640161019b565b61036481610486565b50565b60035460009081906103bb5760405162461bcd60e51b815260206004820152601a60248201527f63616c6c206265666f726520696e697469616c697a6174696f6e000000000000604482015260640161019b565b6001548360005b8215610456576103d36002846105e0565b60000361041f5781602a82602881106103ee576103ee6105ca565b01556104188260028360288110610407576104076105ca565b015460009182526020526040902090565b915061044a565b610447602a8260288110610435576104356105ca565b01548360009182526020526040902090565b91505b600192831c92016103c2565b81602a826028811061046a5761046a6105ca565b0155506000819055600180548082019091559590945092505050565b605280546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b60005b60286104e8826001610618565b10156103645761051960028260288110610504576105046105ca565b015460028360288110610407576104076105ca565b6002610526836001610618565b60288110610536576105366105ca565b01558061054281610631565b9150506104db565b60006020828403121561055c57600080fd5b5035919050565b60006020828403121561057557600080fd5b81356001600160a01b038116811461058c57600080fd5b9392505050565b60208082526017908201527f63616c6c6572206973206e6f7420746865206f776e6572000000000000000000604082015260600190565b634e487b7160e01b600052603260045260246000fd5b6000826105fd57634e487b7160e01b600052601260045260246000fd5b500690565b634e487b7160e01b600052601160045260246000fd5b8082018082111561062b5761062b610602565b92915050565b60006001820161064357610643610602565b506001019056fea26469706673582212208fb1cb9933bb17dd0a7c17de7c890919b08d2fd7eb2bede7b41caa32709b30b564736f6c63430008100033","storage":{"0x0000000000000000000000000000000000000000000000000000000000000052":"0x18960EEc21b1878C581937a14c5c3C43008F6b6B"}},"0x5300000000000000000000000000000000000002":{"balance":"0x0","code":"0x608060405234801561001057600080fd5b50600436106100cf5760003560e01c8063715018a61161008c578063bede39b511610066578063bede39b51461018d578063de26c4a1146101a0578063f2fde38b146101b3578063f45e65d8146101c657600080fd5b8063715018a6146101475780638da5cb5b1461014f57806393e59dc11461017a57600080fd5b80630c18c162146100d45780633577afc5146100f05780633d0f963e1461010557806349948e0e14610118578063519b4bd31461012b5780637046559714610134575b600080fd5b6100dd60025481565b6040519081526020015b60405180910390f35b6101036100fe366004610671565b6101cf565b005b61010361011336600461068a565b610291565b6100dd6101263660046106d0565b61031c565b6100dd60015481565b610103610142366004610671565b610361565b610103610416565b600054610162906001600160a01b031681565b6040516001600160a01b0390911681526020016100e7565b600454610162906001600160a01b031681565b61010361019b366004610671565b61044c565b6100dd6101ae3660046106d0565b610533565b6101036101c136600461068a565b610595565b6100dd60035481565b6000546001600160a01b031633146102025760405162461bcd60e51b81526004016101f990610781565b60405180910390fd5b621c9c388111156102555760405162461bcd60e51b815260206004820152601760248201527f657863656564206d6178696d756d206f7665726865616400000000000000000060448201526064016101f9565b60028190556040518181527f32740b35c0ea213650f60d44366b4fb211c9033b50714e4a1d34e65d5beb9bb4906020015b60405180910390a150565b6000546001600160a01b031633146102bb5760405162461bcd60e51b81526004016101f990610781565b600480546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527f22d1c35fe072d2e42c3c8f9bd4a0d34aa84a0101d020a62517b33fdb3174e5f7910160405180910390a15050565b60008061032883610533565b905060006001548261033a91906107b8565b9050633b9aca006003548261034f91906107b8565b61035991906107e5565b949350505050565b6000546001600160a01b0316331461038b5760405162461bcd60e51b81526004016101f990610781565b61039b633b9aca006103e86107b8565b8111156103e15760405162461bcd60e51b8152602060048201526014602482015273657863656564206d6178696d756d207363616c6560601b60448201526064016101f9565b60038190556040518181527f3336cd9708eaf2769a0f0dc0679f30e80f15dcd88d1921b5a16858e8b85c591a90602001610286565b6000546001600160a01b031633146104405760405162461bcd60e51b81526004016101f990610781565b61044a6000610621565b565b6004805460405163efc7840160e01b815233928101929092526001600160a01b03169063efc7840190602401602060405180830381865afa158015610495573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104b99190610807565b6104fe5760405162461bcd60e51b81526020600482015260166024820152752737ba103bb434ba32b634b9ba32b21039b2b73232b960511b60448201526064016101f9565b60018190556040518181527f351fb23757bb5ea0546c85b7996ddd7155f96b939ebaa5ff7bc49c75f27f2c4490602001610286565b80516000908190815b818110156105865784818151811061055657610556610829565b01602001516001600160f81b0319166000036105775760048301925061057e565b6010830192505b60010161053c565b50506002540160400192915050565b6000546001600160a01b031633146105bf5760405162461bcd60e51b81526004016101f990610781565b6001600160a01b0381166106155760405162461bcd60e51b815260206004820152601d60248201527f6e6577206f776e657220697320746865207a65726f206164647265737300000060448201526064016101f9565b61061e81610621565b50565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60006020828403121561068357600080fd5b5035919050565b60006020828403121561069c57600080fd5b81356001600160a01b03811681146106b357600080fd5b9392505050565b634e487b7160e01b600052604160045260246000fd5b6000602082840312156106e257600080fd5b813567ffffffffffffffff808211156106fa57600080fd5b818401915084601f83011261070e57600080fd5b813581811115610720576107206106ba565b604051601f8201601f19908116603f01168101908382118183101715610748576107486106ba565b8160405282815287602084870101111561076157600080fd5b826020860160208301376000928101602001929092525095945050505050565b60208082526017908201527f63616c6c6572206973206e6f7420746865206f776e6572000000000000000000604082015260600190565b60008160001904831182151516156107e057634e487b7160e01b600052601160045260246000fd5b500290565b60008261080257634e487b7160e01b600052601260045260246000fd5b500490565b60006020828403121561081957600080fd5b815180151581146106b357600080fd5b634e487b7160e01b600052603260045260246000fdfea26469706673582212205ea335809638809cf032c794fd966e2439020737b1dcc2218435cb438286efcf64736f6c63430008100033","storage":{"0x0000000000000000000000000000000000000000000000000000000000000000":"0x18960EEc21b1878C581937a14c5c3C43008F6b6B"}},"0x5300000000000000000000000000000000000003":{"balance":"0x0","code":"0x608060405234801561001057600080fd5b50600436106100575760003560e01c8063715018a61461005c57806379586dd7146100665780638da5cb5b14610079578063efc78401146100a9578063f2fde38b146100e5575b600080fd5b6100646100f8565b005b610064610074366004610356565b610137565b60005461008c906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b6100d56100b736600461042d565b6001600160a01b031660009081526001602052604090205460ff1690565b60405190151581526020016100a0565b6100646100f336600461042d565b610238565b6000546001600160a01b0316331461012b5760405162461bcd60e51b81526004016101229061044f565b60405180910390fd5b61013560006102c4565b565b6000546001600160a01b031633146101615760405162461bcd60e51b81526004016101229061044f565b60005b825181101561023357816001600085848151811061018457610184610486565b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060006101000a81548160ff0219169083151502179055508281815181106101d5576101d5610486565b60200260200101516001600160a01b03167f8daaf060c3306c38e068a75c054bf96ecd85a3db1252712c4d93632744c42e0d83604051610219911515815260200190565b60405180910390a28061022b8161049c565b915050610164565b505050565b6000546001600160a01b031633146102625760405162461bcd60e51b81526004016101229061044f565b6001600160a01b0381166102b85760405162461bcd60e51b815260206004820152601d60248201527f6e6577206f776e657220697320746865207a65726f20616464726573730000006044820152606401610122565b6102c1816102c4565b50565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b634e487b7160e01b600052604160045260246000fd5b80356001600160a01b038116811461034157600080fd5b919050565b8035801515811461034157600080fd5b6000806040838503121561036957600080fd5b823567ffffffffffffffff8082111561038157600080fd5b818501915085601f83011261039557600080fd5b81356020828211156103a9576103a9610314565b8160051b604051601f19603f830116810181811086821117156103ce576103ce610314565b6040529283528183019350848101820192898411156103ec57600080fd5b948201945b83861015610411576104028661032a565b855294820194938201936103f1565b96506104209050878201610346565b9450505050509250929050565b60006020828403121561043f57600080fd5b6104488261032a565b9392505050565b60208082526017908201527f63616c6c6572206973206e6f7420746865206f776e6572000000000000000000604082015260600190565b634e487b7160e01b600052603260045260246000fd5b6000600182016104bc57634e487b7160e01b600052601160045260246000fd5b506001019056fea26469706673582212203414b076e92b618bd7c3437159d7bceb2acc3a5c82f51f383465512d9c52e97064736f6c63430008100033","storage":{"0x0000000000000000000000000000000000000000000000000000000000000000":"0x18960EEc21b1878C581937a14c5c3C43008F6b6B"}},"0x5300000000000000000000000000000000000004":{"balance":"0x0","code":"0x6080604052600436106101025760003560e01c806370a0823111610095578063a457c2d711610064578063a457c2d7146102b4578063a9059cbb146102d4578063d0e30db0146102f4578063d505accf146102fc578063dd62ed3e1461031c57600080fd5b806370a08231146102215780637ecebe001461025757806384b0196e1461027757806395d89b411461029f57600080fd5b80632e1a7d4d116100d15780632e1a7d4d146101b0578063313ce567146101d05780633644e515146101ec578063395093511461020157600080fd5b806306fdde0314610116578063095ea7b31461014157806318160ddd1461017157806323b872dd1461019057600080fd5b366101115761010f61033c565b005b600080fd5b34801561012257600080fd5b5061012b61037d565b60405161013891906112cf565b60405180910390f35b34801561014d57600080fd5b5061016161015c366004611305565b61040f565b6040519015158152602001610138565b34801561017d57600080fd5b506002545b604051908152602001610138565b34801561019c57600080fd5b506101616101ab36600461132f565b610429565b3480156101bc57600080fd5b5061010f6101cb36600461136b565b61044d565b3480156101dc57600080fd5b5060405160128152602001610138565b3480156101f857600080fd5b50610182610523565b34801561020d57600080fd5b5061016161021c366004611305565b610532565b34801561022d57600080fd5b5061018261023c366004611384565b6001600160a01b031660009081526020819052604090205490565b34801561026357600080fd5b50610182610272366004611384565b610554565b34801561028357600080fd5b5061028c610572565b604051610138979695949392919061139f565b3480156102ab57600080fd5b5061012b6105fb565b3480156102c057600080fd5b506101616102cf366004611305565b61060a565b3480156102e057600080fd5b506101616102ef366004611305565b610685565b61010f61033c565b34801561030857600080fd5b5061010f610317366004611435565b610693565b34801561032857600080fd5b506101826103373660046114a8565b6107f7565b6103463334610856565b60405134815233907fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c9060200160405180910390a2565b60606003805461038c906114db565b80601f01602080910402602001604051908101604052809291908181526020018280546103b8906114db565b80156104055780601f106103da57610100808354040283529160200191610405565b820191906000526020600020905b8154815290600101906020018083116103e857829003601f168201915b5050505050905090565b60003361041d818585610915565b60019150505b92915050565b600033610437858285610a3a565b610442858585610ab4565b506001949350505050565b6104573382610c58565b604051600090339083908381818185875af1925050503d8060008114610499576040519150601f19603f3d011682016040523d82523d6000602084013e61049e565b606091505b50509050806104ea5760405162461bcd60e51b81526020600482015260136024820152721dda5d1a191c985dc81155120819985a5b1959606a1b60448201526064015b60405180910390fd5b60405182815233907f7fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b659060200160405180910390a25050565b600061052d610d87565b905090565b60003361041d81858561054583836107f7565b61054f919061150f565b610915565b6001600160a01b038116600090815260076020526040812054610423565b6000606080828080836105a67f577261707065642045746865720000000000000000000000000000000000000d6005610eb2565b6105d17f31000000000000000000000000000000000000000000000000000000000000016006610eb2565b60408051600080825260208201909252600f60f81b9b939a50919850469750309650945092509050565b60606004805461038c906114db565b6000338161061882866107f7565b9050838110156106785760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b60648201526084016104e1565b6104428286868403610915565b60003361041d818585610ab4565b834211156106e35760405162461bcd60e51b815260206004820152601d60248201527f45524332305065726d69743a206578706972656420646561646c696e6500000060448201526064016104e1565b60007f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98888886107128c610f56565b6040805160208101969096526001600160a01b0394851690860152929091166060840152608083015260a082015260c0810186905260e001604051602081830303815290604052805190602001209050600061076d82610f7e565b9050600061077d82878787610fab565b9050896001600160a01b0316816001600160a01b0316146107e05760405162461bcd60e51b815260206004820152601e60248201527f45524332305065726d69743a20696e76616c6964207369676e6174757265000060448201526064016104e1565b6107eb8a8a8a610915565b50505050505050505050565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b600060208351101561083e5761083783610fd3565b9050610423565b816108498482611594565b5060ff9050610423565b90565b6001600160a01b0382166108ac5760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f20616464726573730060448201526064016104e1565b80600260008282546108be919061150f565b90915550506001600160a01b038216600081815260208181526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b6001600160a01b0383166109775760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b60648201526084016104e1565b6001600160a01b0382166109d85760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b60648201526084016104e1565b6001600160a01b0383811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3505050565b6000610a4684846107f7565b90506000198114610aae5781811015610aa15760405162461bcd60e51b815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e636500000060448201526064016104e1565b610aae8484848403610915565b50505050565b6001600160a01b038316610b185760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b60648201526084016104e1565b6001600160a01b038216610b7a5760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b60648201526084016104e1565b6001600160a01b03831660009081526020819052604090205481811015610bf25760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b60648201526084016104e1565b6001600160a01b03848116600081815260208181526040808320878703905593871680835291849020805487019055925185815290927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a3610aae565b6001600160a01b038216610cb85760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b60648201526084016104e1565b6001600160a01b03821660009081526020819052604090205481811015610d2c5760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b60648201526084016104e1565b6001600160a01b0383166000818152602081815260408083208686039055600280548790039055518581529192917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9101610a2d565b505050565b6000306001600160a01b037f0000000000000000000000005fbdb2315678afecb367f032d93f642f64180aa316148015610de057507f000000000000000000000000000000000000000000000000000000000008274f46145b15610e0a57507f624453decb4e78ca99c7630ff9f52222ea6f559f0a6c1bb60b935ef006fa159e90565b61052d604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201527f00cd3d46df44f2cbb950cf84eb2e92aa2ddd23195b1a009173ea59a063357ed3918101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260009060c00160405160208183030381529060405280519060200120905090565b606060ff8314610ec55761083783611011565b818054610ed1906114db565b80601f0160208091040260200160405190810160405280929190818152602001828054610efd906114db565b8015610f4a5780601f10610f1f57610100808354040283529160200191610f4a565b820191906000526020600020905b815481529060010190602001808311610f2d57829003601f168201915b50505050509050610423565b6001600160a01b03811660009081526007602052604090208054600181018255905b50919050565b6000610423610f8b610d87565b8360405161190160f01b8152600281019290925260228201526042902090565b6000806000610fbc87878787611050565b91509150610fc981611114565b5095945050505050565b600080829050601f81511115610ffe578260405163305a27a960e01b81526004016104e191906112cf565b805161100982611654565b179392505050565b6060600061101e83611261565b604080516020808252818301909252919250600091906020820181803683375050509182525060208101929092525090565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0831115611087575060009050600361110b565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa1580156110db573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166111045760006001925092505061110b565b9150600090505b94509492505050565b600081600481111561112857611128611678565b036111305750565b600181600481111561114457611144611678565b036111915760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e6174757265000000000000000060448201526064016104e1565b60028160048111156111a5576111a5611678565b036111f25760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e6774680060448201526064016104e1565b600381600481111561120657611206611678565b0361125e5760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b60648201526084016104e1565b50565b600060ff8216601f81111561042357604051632cd44ac360e21b815260040160405180910390fd5b6000815180845260005b818110156112af57602081850181015186830182015201611293565b506000602082860101526020601f19601f83011685010191505092915050565b6020815260006112e26020830184611289565b9392505050565b80356001600160a01b038116811461130057600080fd5b919050565b6000806040838503121561131857600080fd5b611321836112e9565b946020939093013593505050565b60008060006060848603121561134457600080fd5b61134d846112e9565b925061135b602085016112e9565b9150604084013590509250925092565b60006020828403121561137d57600080fd5b5035919050565b60006020828403121561139657600080fd5b6112e2826112e9565b60ff60f81b881681526000602060e0818401526113bf60e084018a611289565b83810360408501526113d1818a611289565b606085018990526001600160a01b038816608086015260a0850187905284810360c0860152855180825283870192509083019060005b8181101561142357835183529284019291840191600101611407565b50909c9b505050505050505050505050565b600080600080600080600060e0888a03121561145057600080fd5b611459886112e9565b9650611467602089016112e9565b95506040880135945060608801359350608088013560ff8116811461148b57600080fd5b9699959850939692959460a0840135945060c09093013592915050565b600080604083850312156114bb57600080fd5b6114c4836112e9565b91506114d2602084016112e9565b90509250929050565b600181811c908216806114ef57607f821691505b602082108103610f7857634e487b7160e01b600052602260045260246000fd5b8082018082111561042357634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b601f821115610d8257600081815260208120601f850160051c8101602086101561156d5750805b601f850160051c820191505b8181101561158c57828155600101611579565b505050505050565b815167ffffffffffffffff8111156115ae576115ae611530565b6115c2816115bc84546114db565b84611546565b602080601f8311600181146115f757600084156115df5750858301515b600019600386901b1c1916600185901b17855561158c565b600085815260208120601f198616915b8281101561162657888601518255948401946001909101908401611607565b50858210156116445787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b80516020808301519190811015610f785760001960209190910360031b1b16919050565b634e487b7160e01b600052602160045260246000fdfea264697066735822122075458b204a41338df799effa8b73c6c1a17e612bc3b3311c0cec123c4da7709964736f6c63430008100033","storage":{"0x0000000000000000000000000000000000000000000000000000000000000003":"0x577261707065642045746865720000000000000000000000000000000000001a","0x0000000000000000000000000000000000000000000000000000000000000004":"0x5745544800000000000000000000000000000000000000000000000000000008"}},"0x5300000000000000000000000000000000000005":{"balance":"0x0","code":"0x6080604052600436106100a05760003560e01c806384411d651161006457806384411d65146101595780638da5cb5b1461016f5780639e7adc791461018f578063f2fde38b146101af578063feec756c146101cf578063ff4f3546146101ef57600080fd5b80633cb747bf146100ac5780633ccfd60b146100e9578063457e1a491461010057806366d003ac14610124578063715018a61461014457600080fd5b366100a757005b600080fd5b3480156100b857600080fd5b506002546100cc906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156100f557600080fd5b506100fe61020f565b005b34801561010c57600080fd5b5061011660015481565b6040519081526020016100e0565b34801561013057600080fd5b506003546100cc906001600160a01b031681565b34801561015057600080fd5b506100fe610371565b34801561016557600080fd5b5061011660045481565b34801561017b57600080fd5b506000546100cc906001600160a01b031681565b34801561019b57600080fd5b506100fe6101aa3660046105ea565b6103a7565b3480156101bb57600080fd5b506100fe6101ca3660046105ea565b610423565b3480156101db57600080fd5b506100fe6101ea3660046105ea565b6104af565b3480156101fb57600080fd5b506100fe61020a36600461061a565b61052b565b60015447908110156102a15760405162461bcd60e51b815260206004820152604a60248201527f4665655661756c743a207769746864726177616c20616d6f756e74206d75737460448201527f2062652067726561746572207468616e206d696e696d756d20776974686472616064820152691dd85b08185b5bdd5b9d60b21b608482015260a4015b60405180910390fd5b6004805482019055600354604080518381526001600160a01b0390921660208301523382820152517fc8a211cc64b6ed1b50595a9fcb1932b6d1e5a6e8ef15b60e5b1f988ea9086bba9181900360600190a1600254600354604080516020810182526000808252915163b2267a7b60e01b81526001600160a01b039485169463b2267a7b94879461033c949190921692859290600401610633565b6000604051808303818588803b15801561035557600080fd5b505af1158015610369573d6000803e3d6000fd5b505050505050565b6000546001600160a01b0316331461039b5760405162461bcd60e51b81526004016102989061069f565b6103a5600061059a565b565b6000546001600160a01b031633146103d15760405162461bcd60e51b81526004016102989061069f565b600280546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f1c928c417a10a21c3cddad148c5dba5d710e4b1442d6d8a36de345935ad8461290600090a35050565b6000546001600160a01b0316331461044d5760405162461bcd60e51b81526004016102989061069f565b6001600160a01b0381166104a35760405162461bcd60e51b815260206004820152601d60248201527f6e6577206f776e657220697320746865207a65726f20616464726573730000006044820152606401610298565b6104ac8161059a565b50565b6000546001600160a01b031633146104d95760405162461bcd60e51b81526004016102989061069f565b600380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f7e1e96961a397c8aa26162fe259cc837afc95e33aad4945ddc61c18dabb7a6ad90600090a35050565b6000546001600160a01b031633146105555760405162461bcd60e51b81526004016102989061069f565b600180549082905560408051828152602081018490527f0d3c80219fe57713b9f9c83d1e51426792d0c14d8e330e65b102571816140965910160405180910390a15050565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6000602082840312156105fc57600080fd5b81356001600160a01b038116811461061357600080fd5b9392505050565b60006020828403121561062c57600080fd5b5035919050565b60018060a01b038516815260006020858184015260806040840152845180608085015260005b818110156106755786810183015185820160a001528201610659565b50600060a0828601015260a0601f19601f8301168501019250505082606083015295945050505050565b60208082526017908201527f63616c6c6572206973206e6f7420746865206f776e657200000000000000000060408201526060019056fea26469706673582212200c5bec0af207d4c7845829d5330f295a5f16702ab8bde670ae90be68974af0a764736f6c63430008100033","storage":{"0x0000000000000000000000000000000000000000000000000000000000000000":"0x18960EEc21b1878C581937a14c5c3C43008F6b6B","0x0000000000000000000000000000000000000000000000000000000000000001":"0x8ac7230489e80000","0x0000000000000000000000000000000000000000000000000000000000000002":"0xBa50f5340FB9F3Bd074bD638c9BE13eCB36E603d","0x0000000000000000000000000000000000000000000000000000000000000003":"0x2351C7aD0c8cFEB25c81301EAC922ab1f1980bbe"}}},"number":"0x0","gasUsed":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","baseFeePerGas":null}
diff --git reth/crates/scroll/chainspec/src/constants.rs scroll-reth/crates/scroll/chainspec/src/constants.rs new file mode 100644 index 0000000000000000000000000000000000000000..458e4ad51638da1301d106566369ab507303bf70 --- /dev/null +++ scroll-reth/crates/scroll/chainspec/src/constants.rs @@ -0,0 +1,74 @@ +use crate::genesis::L1Config; +use alloy_primitives::{address, b256, Address, B256}; + +/// The transaction fee recipient on the L2. +pub const SCROLL_FEE_VAULT_ADDRESS: Address = address!("5300000000000000000000000000000000000005"); + +/// The L1 message queue address for Scroll mainnet. +/// <https://etherscan.io/address/0x0d7E906BD9cAFa154b048cFa766Cc1E54E39AF9B>. +pub const SCROLL_MAINNET_L1_MESSAGE_QUEUE_ADDRESS: Address = + address!("0d7E906BD9cAFa154b048cFa766Cc1E54E39AF9B"); + +/// The L1 proxy address for Scroll mainnet. +/// <https://etherscan.io/address/0xa13BAF47339d63B743e7Da8741db5456DAc1E556>. +pub const SCROLL_MAINNET_L1_PROXY_ADDRESS: Address = + address!("a13BAF47339d63B743e7Da8741db5456DAc1E556"); + +/// The maximum allowed l1 messages per block for Scroll mainnet. +pub const SCROLL_MAINNET_MAX_L1_MESSAGES: u64 = 10; + +/// The L1 configuration for Scroll mainnet. +pub const SCROLL_MAINNET_L1_CONFIG: L1Config = L1Config { + l1_chain_id: alloy_chains::NamedChain::Mainnet as u64, + l1_message_queue_address: SCROLL_MAINNET_L1_MESSAGE_QUEUE_ADDRESS, + scroll_chain_address: SCROLL_MAINNET_L1_PROXY_ADDRESS, + num_l1_messages_per_block: SCROLL_MAINNET_MAX_L1_MESSAGES, +}; + +/// The Scroll Mainnet genesis hash +pub const SCROLL_MAINNET_GENESIS_HASH: B256 = + b256!("bbc05efd412b7cd47a2ed0e5ddfcf87af251e414ea4c801d78b6784513180a80"); + +/// The L1 message queue address for Scroll sepolia. +/// <https://sepolia.etherscan.io/address/0xF0B2293F5D834eAe920c6974D50957A1732de763>. +pub const SCROLL_SEPOLIA_L1_MESSAGE_QUEUE_ADDRESS: Address = + address!("F0B2293F5D834eAe920c6974D50957A1732de763"); + +/// The L1 proxy address for Scroll sepolia. +/// <https://sepolia.etherscan.io/address/0x2D567EcE699Eabe5afCd141eDB7A4f2D0D6ce8a0> +pub const SCROLL_SEPOLIA_L1_PROXY_ADDRESS: Address = + address!("2D567EcE699Eabe5afCd141eDB7A4f2D0D6ce8a0"); + +/// The maximum allowed l1 messages per block for Scroll sepolia. +pub const SCROLL_SEPOLIA_MAX_L1_MESSAGES: u64 = 10; + +/// The L1 configuration for Scroll sepolia. +pub const SCROLL_SEPOLIA_L1_CONFIG: L1Config = L1Config { + l1_chain_id: alloy_chains::NamedChain::Sepolia as u64, + l1_message_queue_address: SCROLL_SEPOLIA_L1_MESSAGE_QUEUE_ADDRESS, + scroll_chain_address: SCROLL_SEPOLIA_L1_PROXY_ADDRESS, + num_l1_messages_per_block: SCROLL_SEPOLIA_MAX_L1_MESSAGES, +}; + +/// The L1 message queue address for Scroll dev. +pub const SCROLL_DEV_L1_MESSAGE_QUEUE_ADDRESS: Address = + address!("0000000000000000000000000000000000000000"); + +/// The L1 proxy address for Scroll dev. +pub const SCROLL_DEV_L1_PROXY_ADDRESS: Address = + address!("0000000000000000000000000000000000000000"); + +/// The maximum allowed l1 messages per block for Scroll dev. +pub const SCROLL_DEV_MAX_L1_MESSAGES: u64 = 10; + +/// The L1 configuration for Scroll dev. +pub const SCROLL_DEV_L1_CONFIG: L1Config = L1Config { + l1_chain_id: alloy_chains::NamedChain::Goerli as u64, + l1_message_queue_address: SCROLL_DEV_L1_MESSAGE_QUEUE_ADDRESS, + scroll_chain_address: SCROLL_DEV_L1_PROXY_ADDRESS, + num_l1_messages_per_block: SCROLL_DEV_MAX_L1_MESSAGES, +}; + +/// The Scroll Sepolia genesis hash +pub const SCROLL_SEPOLIA_GENESIS_HASH: B256 = + b256!("aa62d1a8b2bffa9e5d2368b63aae0d98d54928bd713125e3fd9e5c896c68592c");
diff --git reth/crates/scroll/chainspec/src/dev.rs scroll-reth/crates/scroll/chainspec/src/dev.rs new file mode 100644 index 0000000000000000000000000000000000000000..5ee23d174b684aca08a0c794dd1152f52bd1dc59 --- /dev/null +++ scroll-reth/crates/scroll/chainspec/src/dev.rs @@ -0,0 +1,33 @@ +//! Chain specification in dev mode for custom chain. + +use alloc::sync::Arc; + +use alloy_chains::Chain; +use alloy_primitives::U256; +use reth_chainspec::{BaseFeeParams, BaseFeeParamsKind, ChainSpec}; +use reth_primitives_traits::SealedHeader; +use reth_scroll_forks::DEV_HARDFORKS; + +use crate::{make_genesis_header, LazyLock, ScrollChainConfig, ScrollChainSpec}; + +/// Scroll dev testnet specification +/// +/// Includes 20 prefunded accounts with `10_000` ETH each derived from mnemonic "test test test test +/// test test test test test test test junk". +pub static SCROLL_DEV: LazyLock<Arc<ScrollChainSpec>> = LazyLock::new(|| { + let genesis = serde_json::from_str(include_str!("../res/genesis/dev.json")) + .expect("Can't deserialize Dev testnet genesis json"); + ScrollChainSpec { + inner: ChainSpec { + chain: Chain::dev(), + genesis_header: SealedHeader::new_unhashed(make_genesis_header(&genesis)), + genesis, + paris_block_and_final_difficulty: Some((0, U256::from(0))), + hardforks: DEV_HARDFORKS.clone(), + base_fee_params: BaseFeeParamsKind::Constant(BaseFeeParams::ethereum()), + ..Default::default() + }, + config: ScrollChainConfig::dev(), + } + .into() +});
diff --git reth/crates/scroll/chainspec/src/genesis.rs scroll-reth/crates/scroll/chainspec/src/genesis.rs new file mode 100644 index 0000000000000000000000000000000000000000..96771f0093176fa68eb7aa992757741e8c4bfff4 --- /dev/null +++ scroll-reth/crates/scroll/chainspec/src/genesis.rs @@ -0,0 +1,241 @@ +//! Scroll types for genesis data. + +use crate::{ + constants::{SCROLL_FEE_VAULT_ADDRESS, SCROLL_MAINNET_L1_CONFIG, SCROLL_SEPOLIA_L1_CONFIG}, + SCROLL_DEV_L1_CONFIG, +}; +use alloy_primitives::Address; +use alloy_serde::OtherFields; +use serde::de::Error; + +/// Container type for all Scroll-specific fields in a genesis file. +/// This struct represents the configuration details and metadata +/// that are specific to the Scroll blockchain, used during the chain's initialization. +#[derive(Default, Debug, Clone, Copy, Eq, PartialEq, serde::Serialize, serde::Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ScrollChainInfo { + /// Information about hard forks specific to the Scroll chain. + /// This optional field contains metadata about various hard fork + /// configurations that are specific to the Scroll blockchain. + pub hard_fork_info: Option<ScrollHardforkInfo>, + /// Scroll chain-specific configuration details. + /// Encapsulates special parameters and settings + /// required for Scroll chain functionality, such as fee-related + /// addresses and Layer 1 configuration. + pub scroll_chain_config: ScrollChainConfig, +} + +impl ScrollChainInfo { + /// Extracts the Scroll specific fields from a genesis file. These fields are expected to be + /// contained in the `genesis.config` under `extra_fields` property. + pub fn extract_from(others: &OtherFields) -> Option<Self> { + Self::try_from(others).ok() + } +} + +impl TryFrom<&OtherFields> for ScrollChainInfo { + type Error = serde_json::Error; + + fn try_from(others: &OtherFields) -> Result<Self, Self::Error> { + let hard_fork_info = ScrollHardforkInfo::try_from(others).ok(); + let scroll_chain_config = ScrollChainConfig::try_from(others)?; + + Ok(Self { hard_fork_info, scroll_chain_config }) + } +} + +/// [`ScrollHardforkInfo`] specifies the block numbers and timestamps at which the Scroll hardforks +/// were activated. +#[derive(Default, Debug, Clone, Copy, Eq, PartialEq, serde::Serialize, serde::Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ScrollHardforkInfo { + /// archimedes block number + pub archimedes_block: Option<u64>, + /// bernoulli block number + pub bernoulli_block: Option<u64>, + /// curie block number + pub curie_block: Option<u64>, + /// darwin hardfork timestamp + pub darwin_time: Option<u64>, + /// darwinV2 hardfork timestamp + pub darwin_v2_time: Option<u64>, + /// euclid hardfork timestamp + pub euclid_time: Option<u64>, + /// euclidV2 hardfork timestamp + pub euclid_v2_time: Option<u64>, + /// feynman hardfork timestamp + pub feynman_time: Option<u64>, +} + +impl ScrollHardforkInfo { + /// Extract the Scroll-specific genesis info from a genesis file. + pub fn extract_from(others: &OtherFields) -> Option<Self> { + Self::try_from(others).ok() + } +} + +impl TryFrom<&OtherFields> for ScrollHardforkInfo { + type Error = serde_json::Error; + + fn try_from(others: &OtherFields) -> Result<Self, Self::Error> { + others.deserialize_as() + } +} + +/// The Scroll l1 config +#[derive(Default, Debug, Clone, Copy, Eq, PartialEq, serde::Serialize, serde::Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct L1Config { + /// l1 chain id + pub l1_chain_id: u64, + /// The L1 contract address of the contract that handles the message queue targeting the Scroll + /// rollup. + pub l1_message_queue_address: Address, + /// The L1 contract address of the proxy contract which is responsible for Scroll rollup + /// settlement. + pub scroll_chain_address: Address, + /// The maximum number of L1 messages to be consumed per L2 rollup block. + pub num_l1_messages_per_block: u64, +} + +/// The configuration for the Scroll sequencer chain. +/// This struct holds the configuration details specific to the Scroll chain, +/// including fee-related addresses and L1 chain-specific settings. +#[derive(Default, Debug, Clone, Copy, Eq, PartialEq, serde::Serialize, serde::Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ScrollChainConfig { + /// The address of the L2 transaction fee vault. + /// This is an optional field that, when set, specifies where L2 transaction fees + /// will be sent or stored. + pub fee_vault_address: Option<Address>, + /// The L1 configuration. + /// This field encapsulates specific settings and parameters required for L1 + pub l1_config: L1Config, +} + +impl ScrollChainConfig { + /// Extracts the scroll special info by looking for the `scroll` key. It is intended to be + /// parsed from a genesis file. + pub fn extract_from(others: &OtherFields) -> Option<Self> { + Self::try_from(others).ok() + } + + /// Returns the [`ScrollChainConfig`] for Scroll Mainnet. + pub const fn mainnet() -> Self { + Self { + fee_vault_address: Some(SCROLL_FEE_VAULT_ADDRESS), + l1_config: SCROLL_MAINNET_L1_CONFIG, + } + } + + /// Returns the [`ScrollChainConfig`] for Scroll Sepolia. + pub const fn sepolia() -> Self { + Self { + fee_vault_address: Some(SCROLL_FEE_VAULT_ADDRESS), + l1_config: SCROLL_SEPOLIA_L1_CONFIG, + } + } + + /// Returns the [`ScrollChainConfig`] for Scroll dev. + pub const fn dev() -> Self { + Self { fee_vault_address: Some(SCROLL_FEE_VAULT_ADDRESS), l1_config: SCROLL_DEV_L1_CONFIG } + } +} + +impl TryFrom<&OtherFields> for ScrollChainConfig { + type Error = serde_json::Error; + + fn try_from(others: &OtherFields) -> Result<Self, Self::Error> { + if let Some(Ok(scroll_chain_config)) = others.get_deserialized::<Self>("scroll") { + Ok(scroll_chain_config) + } else { + Err(serde_json::Error::missing_field("scroll")) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use alloy_primitives::address; + + #[test] + fn test_extract_scroll_genesis_info() { + let genesis_info = r#" + { + "archimedesBlock": 0, + "bernoulliBlock": 10, + "curieBlock": 12, + "darwinTime": 0, + "euclidTime": 11, + "feynmanTime": 100 + } + "#; + + let others: OtherFields = serde_json::from_str(genesis_info).unwrap(); + let genesis_info = ScrollHardforkInfo::extract_from(&others).unwrap(); + + assert_eq!( + genesis_info, + ScrollHardforkInfo { + archimedes_block: Some(0), + bernoulli_block: Some(10), + curie_block: Some(12), + darwin_time: Some(0), + darwin_v2_time: None, + euclid_time: Some(11), + euclid_v2_time: None, + feynman_time: Some(100), + } + ); + } + + #[test] + fn test_extract_scroll_chain_info() { + let chain_info_str = r#" + { + "archimedesBlock": 0, + "bernoulliBlock": 10, + "curieBlock": 12, + "darwinTime": 0, + "euclidTime": 11, + "feynmanTime": 100, + "scroll": { + "feeVaultAddress": "0x5300000000000000000000000000000000000005", + "l1Config": { + "l1ChainId": 1, + "l1MessageQueueAddress": "0x0d7E906BD9cAFa154b048cFa766Cc1E54E39AF9B", + "scrollChainAddress": "0xa13BAF47339d63B743e7Da8741db5456DAc1E556", + "numL1MessagesPerBlock": 10 + } + } + } + "#; + + let others: OtherFields = serde_json::from_str(chain_info_str).unwrap(); + let chain_info = ScrollChainInfo::extract_from(&others).unwrap(); + + let expected = ScrollChainInfo { + hard_fork_info: Some(ScrollHardforkInfo { + archimedes_block: Some(0), + bernoulli_block: Some(10), + curie_block: Some(12), + darwin_time: Some(0), + darwin_v2_time: None, + euclid_time: Some(11), + euclid_v2_time: None, + feynman_time: Some(100), + }), + scroll_chain_config: ScrollChainConfig { + fee_vault_address: Some(address!("5300000000000000000000000000000000000005")), + l1_config: L1Config { + l1_chain_id: 1, + l1_message_queue_address: address!("0d7E906BD9cAFa154b048cFa766Cc1E54E39AF9B"), + scroll_chain_address: address!("a13BAF47339d63B743e7Da8741db5456DAc1E556"), + num_l1_messages_per_block: 10, + }, + }, + }; + assert_eq!(chain_info, expected); + } +}
diff --git reth/crates/scroll/chainspec/src/lib.rs scroll-reth/crates/scroll/chainspec/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..776b6b6cde255bb528311f7ae7dbde20bff33596 --- /dev/null +++ scroll-reth/crates/scroll/chainspec/src/lib.rs @@ -0,0 +1,748 @@ +//! Scroll-Reth chain specs. + +#![doc( + html_logo_url = "https://raw.githubusercontent.com/paradigmxyz/reth/main/assets/reth-docs.png", + html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", + issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" +)] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] +#![cfg_attr(not(feature = "std"), no_std)] + +use alloc::{boxed::Box, vec::Vec}; +use alloy_chains::Chain; +use alloy_consensus::Header; +use alloy_genesis::Genesis; +use alloy_primitives::{B256, U256}; +use derive_more::{Constructor, Deref, From, Into}; +use reth_chainspec::{ + BaseFeeParams, ChainSpec, ChainSpecBuilder, DepositContract, EthChainSpec, + EthereumCapabilities, EthereumHardforks, ForkFilter, ForkId, Hardforks, Head, +}; +use reth_ethereum_forks::{ + ChainHardforks, EthereumHardfork, ForkCondition, ForkFilterKey, ForkHash, Hardfork, +}; +use reth_network_peers::NodeRecord; +use scroll_alloy_hardforks::{ScrollHardfork, ScrollHardforks}; + +use alloy_eips::eip7840::BlobParams; +#[cfg(not(feature = "std"))] +use once_cell::sync::Lazy as LazyLock; +#[cfg(feature = "std")] +use std::sync::LazyLock; + +extern crate alloc; + +mod constants; +pub use constants::{ + SCROLL_DEV_L1_CONFIG, SCROLL_DEV_L1_MESSAGE_QUEUE_ADDRESS, SCROLL_DEV_L1_PROXY_ADDRESS, + SCROLL_DEV_MAX_L1_MESSAGES, SCROLL_FEE_VAULT_ADDRESS, SCROLL_MAINNET_GENESIS_HASH, + SCROLL_MAINNET_L1_CONFIG, SCROLL_MAINNET_L1_MESSAGE_QUEUE_ADDRESS, + SCROLL_MAINNET_L1_PROXY_ADDRESS, SCROLL_MAINNET_MAX_L1_MESSAGES, SCROLL_SEPOLIA_GENESIS_HASH, + SCROLL_SEPOLIA_L1_CONFIG, SCROLL_SEPOLIA_L1_MESSAGE_QUEUE_ADDRESS, + SCROLL_SEPOLIA_L1_PROXY_ADDRESS, SCROLL_SEPOLIA_MAX_L1_MESSAGES, +}; + +mod dev; +pub use dev::SCROLL_DEV; + +mod genesis; +pub use genesis::{ScrollChainConfig, ScrollChainInfo}; + +// convenience re-export of the chain spec provider. +pub use reth_chainspec::ChainSpecProvider; +use reth_scroll_forks::SCROLL_MAINNET_HARDFORKS; + +mod scroll; +pub use scroll::SCROLL_MAINNET; + +mod scroll_sepolia; +pub use scroll_sepolia::SCROLL_SEPOLIA; + +/// Chain spec builder for a Scroll chain. +#[derive(Debug, Default, From)] +pub struct ScrollChainSpecBuilder { + /// [`ChainSpecBuilder`] + inner: ChainSpecBuilder, +} + +impl ScrollChainSpecBuilder { + /// Construct a new builder from the scroll mainnet chain spec. + pub fn scroll_mainnet() -> Self { + Self { + inner: ChainSpecBuilder::default() + .chain(SCROLL_MAINNET.chain) + .genesis(SCROLL_MAINNET.genesis.clone()) + .with_forks(SCROLL_MAINNET.hardforks.clone()), + } + } + + /// Construct a new builder from the scroll sepolia chain spec. + pub fn scroll_sepolia() -> Self { + Self { + inner: ChainSpecBuilder::default() + .chain(SCROLL_SEPOLIA.chain) + .genesis(SCROLL_SEPOLIA.genesis.clone()) + .with_forks(SCROLL_SEPOLIA.hardforks.clone()), + } + } +} + +impl ScrollChainSpecBuilder { + /// Set the chain ID + pub fn chain(mut self, chain: Chain) -> Self { + self.inner = self.inner.chain(chain); + self + } + + /// Set the genesis block. + pub fn genesis(mut self, genesis: Genesis) -> Self { + self.inner = self.inner.genesis(genesis); + self + } + + /// Add the given fork with the given activation condition to the spec. + pub fn with_fork<H: Hardfork>(mut self, fork: H, condition: ForkCondition) -> Self { + self.inner = self.inner.with_fork(fork, condition); + self + } + + /// Add the given forks with the given activation condition to the spec. + pub fn with_forks(mut self, forks: ChainHardforks) -> Self { + self.inner = self.inner.with_forks(forks); + self + } + + /// Remove the given fork from the spec. + pub fn without_fork(mut self, fork: ScrollHardfork) -> Self { + self.inner = self.inner.without_fork(fork); + self + } + + /// Enable Archimedes at genesis + pub fn archimedes_activated(mut self) -> Self { + self.inner = self.inner.london_activated(); + self.inner = self.inner.with_fork(ScrollHardfork::Archimedes, ForkCondition::Block(0)); + self + } + + /// Enable Bernoulli at genesis + pub fn bernoulli_activated(mut self) -> Self { + self = self.archimedes_activated(); + self.inner = self.inner.with_fork(EthereumHardfork::Shanghai, ForkCondition::Timestamp(0)); + self.inner = self.inner.with_fork(ScrollHardfork::Bernoulli, ForkCondition::Block(0)); + self + } + + /// Enable Curie at genesis + pub fn curie_activated(mut self) -> Self { + self = self.bernoulli_activated(); + self.inner = self.inner.with_fork(ScrollHardfork::Curie, ForkCondition::Block(0)); + self + } + + /// Enable Darwin at genesis + pub fn darwin_activated(mut self) -> Self { + self = self.curie_activated(); + self.inner = self.inner.with_fork(ScrollHardfork::Darwin, ForkCondition::Timestamp(0)); + self + } + + /// Enable `DarwinV2` at genesis + pub fn darwin_v2_activated(mut self) -> Self { + self = self.darwin_activated(); + self.inner = self.inner.with_fork(ScrollHardfork::DarwinV2, ForkCondition::Timestamp(0)); + self + } + + /// Enable `Euclid` at genesis + pub fn euclid_activated(mut self) -> Self { + self = self.darwin_v2_activated(); + self.inner = self.inner.with_fork(ScrollHardfork::Euclid, ForkCondition::Timestamp(0)); + self + } + + /// Enable `EuclidV2` at genesis + pub fn euclid_v2_activated(mut self) -> Self { + self = self.euclid_activated(); + self.inner = self.inner.with_fork(ScrollHardfork::EuclidV2, ForkCondition::Timestamp(0)); + self + } + + /// Build the resulting [`ScrollChainSpec`]. + /// + /// # Panics + /// + /// This function panics if the chain ID and genesis is not set ([`Self::chain`] and + /// [`Self::genesis`]) + pub fn build(self, config: ScrollChainConfig) -> ScrollChainSpec { + ScrollChainSpec { inner: self.inner.build(), config } + } +} + +/// Returns the chain configuration. +pub trait ChainConfig { + /// The configuration. + type Config; + + /// Returns the chain configuration. + fn chain_config(&self) -> &Self::Config; +} + +impl ChainConfig for ScrollChainSpec { + type Config = ScrollChainConfig; + + fn chain_config(&self) -> &Self::Config { + &self.config + } +} + +/// Scroll chain spec type. +#[derive(Debug, Clone, Deref, Into, Constructor, PartialEq, Eq)] +pub struct ScrollChainSpec { + /// [`ChainSpec`]. + #[deref] + pub inner: ChainSpec, + /// [`ScrollChainConfig`] + pub config: ScrollChainConfig, +} + +impl EthChainSpec for ScrollChainSpec { + type Header = Header; + + fn chain(&self) -> alloy_chains::Chain { + self.inner.chain() + } + + fn base_fee_params_at_block(&self, block_number: u64) -> BaseFeeParams { + // TODO(scroll): need to implement Scroll L2 formula related to https://github.com/scroll-tech/reth/issues/60 + self.inner.base_fee_params_at_block(block_number) + } + + fn base_fee_params_at_timestamp(&self, timestamp: u64) -> BaseFeeParams { + // TODO(scroll): need to implement Scroll L2 formula related to https://github.com/scroll-tech/reth/issues/60 + self.inner.base_fee_params_at_timestamp(timestamp) + } + + fn blob_params_at_timestamp(&self, timestamp: u64) -> Option<BlobParams> { + self.inner.blob_params_at_timestamp(timestamp) + } + + fn deposit_contract(&self) -> Option<&DepositContract> { + self.inner.deposit_contract() + } + + fn genesis_hash(&self) -> B256 { + self.inner.genesis_hash() + } + + fn prune_delete_limit(&self) -> usize { + self.inner.prune_delete_limit() + } + + fn display_hardforks(&self) -> Box<dyn alloc::fmt::Display> { + Box::new(ChainSpec::display_hardforks(self)) + } + + fn genesis_header(&self) -> &Header { + self.inner.genesis_header() + } + + fn genesis(&self) -> &Genesis { + self.inner.genesis() + } + + fn bootnodes(&self) -> Option<Vec<NodeRecord>> { + self.inner.bootnodes() + } + + fn final_paris_total_difficulty(&self) -> Option<U256> { + self.inner.final_paris_total_difficulty() + } +} + +impl EthereumCapabilities for ScrollChainSpec { + fn withdrawals_active(&self, _: u64) -> bool { + // Scroll doesn't activate withdrawals. + false + } +} + +fn make_genesis_header(genesis: &Genesis) -> Header { + Header { + gas_limit: genesis.gas_limit, + difficulty: genesis.difficulty, + nonce: genesis.nonce.into(), + extra_data: genesis.extra_data.clone(), + state_root: reth_trie_common::root::state_root_ref_unhashed(&genesis.alloc), + timestamp: genesis.timestamp, + mix_hash: genesis.mix_hash, + beneficiary: genesis.coinbase, + base_fee_per_gas: None, + withdrawals_root: None, + parent_beacon_block_root: None, + blob_gas_used: None, + excess_blob_gas: None, + requests_hash: None, + ..Default::default() + } +} + +impl Hardforks for ScrollChainSpec { + fn fork<H: Hardfork>(&self, fork: H) -> ForkCondition { + self.inner.fork(fork) + } + + fn forks_iter(&self) -> impl Iterator<Item = (&dyn Hardfork, ForkCondition)> { + self.inner.forks_iter() + } + + fn fork_id(&self, head: &Head) -> ForkId { + // TODO: Geth does not support time based hard forks for its `ForkID` calculation. As such, + // we are only using block based hard forks for now. + // self.inner.fork_id(head) + + // The following code is modified version of self.inner.fork_id(head) to ignore time based + // hard forks. + let mut forkhash = ForkHash::from(self.inner.genesis_hash()); + let mut current_applied = 0; + // handle all block forks before handling timestamp based forks. see: https://eips.ethereum.org/EIPS/eip-6122 + for (_, cond) in self.hardforks.forks_iter() { + // handle block based forks and the sepolia merge netsplit block edge case (TTD + // ForkCondition with Some(block)) + if let ForkCondition::Block(block) | + ForkCondition::TTD { fork_block: Some(block), .. } = cond + { + if head.number >= block { + // skip duplicated hardforks: hardforks enabled at genesis block + if block != current_applied { + forkhash += block; + current_applied = block; + } + } else { + // we can return here because this block fork is not active, so we set the + // `next` value + return ForkId { hash: forkhash, next: block } + } + } + } + ForkId { hash: forkhash, next: 0 } + } + + fn latest_fork_id(&self) -> ForkId { + self.inner.latest_fork_id() + } + + fn fork_filter(&self, head: Head) -> ForkFilter { + let forks = self.inner.hardforks.forks_iter().filter_map(|(_, condition)| { + // We filter out TTD-based forks w/o a pre-known block since those do not show up in the + // fork filter. + Some(match condition { + ForkCondition::Block(block) | + ForkCondition::TTD { fork_block: Some(block), .. } => ForkFilterKey::Block(block), + _ => return None, + }) + }); + + ForkFilter::new(head, self.genesis_hash(), self.genesis_timestamp(), forks) + } +} + +impl EthereumHardforks for ScrollChainSpec { + fn ethereum_fork_activation(&self, fork: EthereumHardfork) -> ForkCondition { + self.fork(fork) + } +} + +impl ScrollHardforks for ScrollChainSpec { + fn scroll_fork_activation(&self, fork: ScrollHardfork) -> ForkCondition { + self.fork(fork) + } +} + +impl From<ChainSpec> for ScrollChainSpec { + fn from(value: ChainSpec) -> Self { + let genesis = value.genesis; + genesis.into() + } +} + +impl From<Genesis> for ScrollChainSpec { + fn from(genesis: Genesis) -> Self { + let scroll_chain_info = ScrollConfigInfo::extract_from(&genesis); + let hard_fork_info = + scroll_chain_info.scroll_chain_info.hard_fork_info.expect("load scroll hard fork info"); + + // Block-based hardforks + let hardfork_opts = [ + (EthereumHardfork::Homestead.boxed(), genesis.config.homestead_block), + (EthereumHardfork::Tangerine.boxed(), genesis.config.eip150_block), + (EthereumHardfork::SpuriousDragon.boxed(), genesis.config.eip155_block), + (EthereumHardfork::Byzantium.boxed(), genesis.config.byzantium_block), + (EthereumHardfork::Constantinople.boxed(), genesis.config.constantinople_block), + (EthereumHardfork::Petersburg.boxed(), genesis.config.petersburg_block), + (EthereumHardfork::Istanbul.boxed(), genesis.config.istanbul_block), + (EthereumHardfork::Berlin.boxed(), genesis.config.berlin_block), + (EthereumHardfork::London.boxed(), genesis.config.london_block), + (ScrollHardfork::Archimedes.boxed(), hard_fork_info.archimedes_block), + (ScrollHardfork::Bernoulli.boxed(), hard_fork_info.bernoulli_block), + (ScrollHardfork::Curie.boxed(), hard_fork_info.curie_block), + ]; + let mut block_hardforks = hardfork_opts + .into_iter() + .filter_map(|(hardfork, opt)| opt.map(|block| (hardfork, ForkCondition::Block(block)))) + .collect::<Vec<_>>(); + + // Time-based hardforks + let time_hardfork_opts = [ + (EthereumHardfork::Shanghai.boxed(), genesis.config.shanghai_time), + (ScrollHardfork::Darwin.boxed(), hard_fork_info.darwin_time), + (ScrollHardfork::DarwinV2.boxed(), hard_fork_info.darwin_v2_time), + (ScrollHardfork::Euclid.boxed(), hard_fork_info.euclid_time), + (ScrollHardfork::EuclidV2.boxed(), hard_fork_info.euclid_v2_time), + (ScrollHardfork::Feynman.boxed(), hard_fork_info.feynman_time), + ]; + + let mut time_hardforks = time_hardfork_opts + .into_iter() + .filter_map(|(hardfork, opt)| { + opt.map(|time| (hardfork, ForkCondition::Timestamp(time))) + }) + .collect::<Vec<_>>(); + + block_hardforks.append(&mut time_hardforks); + + // Ordered Hardforks + let mainnet_hardforks = SCROLL_MAINNET_HARDFORKS.clone(); + let mainnet_order = mainnet_hardforks.forks_iter(); + + let mut ordered_hardforks = Vec::with_capacity(block_hardforks.len()); + for (hardfork, _) in mainnet_order { + if let Some(pos) = block_hardforks.iter().position(|(e, _)| **e == *hardfork) { + ordered_hardforks.push(block_hardforks.remove(pos)); + } + } + + // append the remaining unknown hardforks to ensure we don't filter any out + ordered_hardforks.append(&mut block_hardforks); + + Self { + inner: ChainSpec { + chain: genesis.config.chain_id.into(), + genesis, + hardforks: ChainHardforks::new(ordered_hardforks), + ..Default::default() + }, + config: scroll_chain_info.scroll_chain_info.scroll_chain_config, + } + } +} + +#[derive(Default, Debug)] +struct ScrollConfigInfo { + scroll_chain_info: ScrollChainInfo, +} + +impl ScrollConfigInfo { + fn extract_from(genesis: &Genesis) -> Self { + Self { + scroll_chain_info: ScrollChainInfo::extract_from(&genesis.config.extra_fields) + .expect("extract scroll extra fields failed"), + } + } +} + +#[cfg(test)] +mod tests { + use crate::*; + use alloy_genesis::{ChainConfig, Genesis}; + use alloy_primitives::b256; + use reth_chainspec::{test_fork_ids, ForkFilterKey}; + use reth_ethereum_forks::{EthereumHardfork, ForkHash}; + + #[test] + fn scroll_mainnet_genesis_hash() { + let scroll_mainnet = + ScrollChainSpecBuilder::scroll_mainnet().build(ScrollChainConfig::mainnet()); + assert_eq!( + b256!("908789cb20d00fc6070093f142aa8d02c21cfb0a9b9cfd4621d8cf0255234c0f"), + scroll_mainnet.genesis_hash() + ); + } + + #[test] + fn scroll_sepolia_genesis_hash() { + let scroll_sepolia = + ScrollChainSpecBuilder::scroll_sepolia().build(ScrollChainConfig::sepolia()); + assert_eq!( + b256!("04414a71425e8ef2632e99a4b148c69d69bab8ffa47ee814231331a33d073df2"), + scroll_sepolia.genesis_hash() + ); + } + + #[test] + fn scroll_mainnet_forkids_deref() { + test_fork_ids( + &SCROLL_MAINNET, + &[ + ( + Head { number: 0, ..Default::default() }, + ForkId { hash: ForkHash([0xea, 0x6b, 0x56, 0xca]), next: 5220340 }, + ), + ( + Head { number: 5220340, ..Default::default() }, + ForkId { hash: ForkHash([0xee, 0x46, 0xae, 0x2a]), next: 7096836 }, + ), + ( + Head { number: 7096836, ..Default::default() }, + ForkId { hash: ForkHash([0x18, 0xd3, 0xc8, 0xd9]), next: 1724227200 }, + ), + ( + Head { number: 7096836, timestamp: 1724227200, ..Default::default() }, + ForkId { hash: ForkHash([0xcc, 0xeb, 0x09, 0xb0]), next: 1725264000 }, + ), + ( + Head { number: 7096836, timestamp: 1725264000, ..Default::default() }, + ForkId { hash: ForkHash([0x21, 0xa2, 0x07, 0x54]), next: 1744815600 }, + ), + ( + Head { number: 7096836, timestamp: 1744815600, ..Default::default() }, + ForkId { hash: ForkHash([0xca, 0xc5, 0x80, 0xca]), next: 1745305200 }, + ), + ( + Head { number: 7096836, timestamp: 1745305200, ..Default::default() }, + ForkId { hash: ForkHash([0x0e, 0xcf, 0xb2, 0x31]), next: 18446744073709551615 }, + ), + ( + Head { number: 7096836, timestamp: 18446744073709551615, ..Default::default() }, + ForkId { hash: ForkHash([0x80, 0x47, 0xc0, 0x76]), next: 0 }, + ), + ], + ); + } + + #[test] + fn scroll_mainnet_forkids() { + let cases = [ + ( + Head { number: 0, ..Default::default() }, + ForkId { hash: ForkHash([0xea, 0x6b, 0x56, 0xca]), next: 5220340 }, + ), + ( + Head { number: 5220340, ..Default::default() }, + ForkId { hash: ForkHash([0xee, 0x46, 0xae, 0x2a]), next: 7096836 }, + ), + ( + Head { number: 7096836, ..Default::default() }, + ForkId { hash: ForkHash([0x18, 0xd3, 0xc8, 0xd9]), next: 0 }, + ), + ]; + + for (block, expected_id) in cases { + let computed_id = SCROLL_MAINNET.fork_id(&block); + assert_eq!( + expected_id, computed_id, + "Expected fork ID {:?}, computed fork ID {:?} at block {}", + expected_id, computed_id, block.number + ); + } + } + + #[test] + fn scroll_mainnet_fork_filter_excludes_time_based_forks() { + let head = Default::default(); + let fork_filter = SCROLL_MAINNET.fork_filter(head); + + let forks = vec![ + ForkFilterKey::Block(0), + ForkFilterKey::Block(5220340), + ForkFilterKey::Block(7096836), + ]; + let expected_fork_filter = ForkFilter::new( + head, + SCROLL_MAINNET.genesis_hash(), + SCROLL_MAINNET.genesis_timestamp(), + forks, + ); + + assert_eq!(fork_filter, expected_fork_filter); + } + + #[test] + fn scroll_sepolia_forkids() { + test_fork_ids( + &SCROLL_SEPOLIA, + &[ + ( + Head { number: 0, ..Default::default() }, + ForkId { hash: ForkHash([0x25, 0xfa, 0xe4, 0x54]), next: 3747132 }, + ), + ( + Head { number: 3747132, ..Default::default() }, + ForkId { hash: ForkHash([0xda, 0x76, 0xc2, 0x2d]), next: 4740239 }, + ), + ( + Head { number: 4740239, ..Default::default() }, + ForkId { hash: ForkHash([0x9f, 0xb4, 0x75, 0xf1]), next: 1723622400 }, + ), + ( + Head { number: 4740239, timestamp: 1723622400, ..Default::default() }, + ForkId { hash: ForkHash([0xe9, 0x26, 0xd4, 0x9b]), next: 1724832000 }, + ), + ( + Head { number: 4740239, timestamp: 1724832000, ..Default::default() }, + ForkId { hash: ForkHash([0x69, 0xf3, 0x7e, 0xde]), next: 1741680000 }, + ), + ( + Head { number: 4740239, timestamp: 1741680000, ..Default::default() }, + ForkId { hash: ForkHash([0xf7, 0xac, 0x7e, 0xfc]), next: 1741852800 }, + ), + ( + Head { number: 4740239, timestamp: 1741852800, ..Default::default() }, + ForkId { hash: ForkHash([0x51, 0x7e, 0x0f, 0x1c]), next: 18446744073709551615 }, + ), + ( + Head { number: 4740239, timestamp: 18446744073709551615, ..Default::default() }, + ForkId { hash: ForkHash([0xc8, 0x43, 0x01, 0x5b]), next: 0 }, + ), + ], + ); + } + + #[test] + fn is_bernoulli_active() { + let scroll_mainnet = + ScrollChainSpecBuilder::scroll_mainnet().build(ScrollChainConfig::mainnet()); + assert!(!scroll_mainnet.is_bernoulli_active_at_block(1)) + } + + #[test] + fn parse_scroll_hardforks() { + let geth_genesis = r#" + { + "config": { + "bernoulliBlock": 10, + "curieBlock": 20, + "darwinTime": 30, + "darwinV2Time": 31, + "scroll": { + "feeVaultAddress": "0x5300000000000000000000000000000000000005", + "l1Config": { + "l1ChainId": 1, + "l1MessageQueueAddress": "0x0d7E906BD9cAFa154b048cFa766Cc1E54E39AF9B", + "scrollChainAddress": "0xa13BAF47339d63B743e7Da8741db5456DAc1E556", + "numL1MessagesPerBlock": 10 + } + } + } + } + "#; + let genesis: Genesis = serde_json::from_str(geth_genesis).unwrap(); + + let actual_bernoulli_block = genesis.config.extra_fields.get("bernoulliBlock"); + assert_eq!(actual_bernoulli_block, Some(serde_json::Value::from(10)).as_ref()); + let actual_curie_block = genesis.config.extra_fields.get("curieBlock"); + assert_eq!(actual_curie_block, Some(serde_json::Value::from(20)).as_ref()); + let actual_darwin_timestamp = genesis.config.extra_fields.get("darwinTime"); + assert_eq!(actual_darwin_timestamp, Some(serde_json::Value::from(30)).as_ref()); + let actual_darwin_v2_timestamp = genesis.config.extra_fields.get("darwinV2Time"); + assert_eq!(actual_darwin_v2_timestamp, Some(serde_json::Value::from(31)).as_ref()); + let scroll_object = genesis.config.extra_fields.get("scroll").unwrap(); + assert_eq!( + scroll_object, + &serde_json::json!({ + "feeVaultAddress": "0x5300000000000000000000000000000000000005", + "l1Config": { + "l1ChainId": 1, + "l1MessageQueueAddress": "0x0d7E906BD9cAFa154b048cFa766Cc1E54E39AF9B", + "scrollChainAddress": "0xa13BAF47339d63B743e7Da8741db5456DAc1E556", + "numL1MessagesPerBlock": 10 + } + }) + ); + + let chain_spec: ScrollChainSpec = genesis.into(); + + assert!(!chain_spec.is_fork_active_at_block(ScrollHardfork::Bernoulli, 0)); + assert!(!chain_spec.is_fork_active_at_block(ScrollHardfork::Curie, 0)); + assert!(!chain_spec.is_fork_active_at_timestamp(ScrollHardfork::Darwin, 0)); + assert!(!chain_spec.is_fork_active_at_timestamp(ScrollHardfork::DarwinV2, 0)); + + assert!(chain_spec.is_fork_active_at_block(ScrollHardfork::Bernoulli, 10)); + assert!(chain_spec.is_fork_active_at_block(ScrollHardfork::Curie, 20)); + assert!(chain_spec.is_fork_active_at_timestamp(ScrollHardfork::Darwin, 30)); + assert!(chain_spec.is_fork_active_at_timestamp(ScrollHardfork::DarwinV2, 31)); + } + + #[test] + fn test_fork_order_scroll_mainnet() { + let genesis = Genesis { + config: ChainConfig { + chain_id: 0, + homestead_block: Some(0), + dao_fork_block: Some(0), + dao_fork_support: false, + eip150_block: Some(0), + eip155_block: Some(0), + eip158_block: Some(0), + byzantium_block: Some(0), + constantinople_block: Some(0), + petersburg_block: Some(0), + istanbul_block: Some(0), + berlin_block: Some(0), + london_block: Some(0), + shanghai_time: Some(0), + extra_fields: [ + (String::from("archimedesBlock"), 0.into()), + (String::from("bernoulliBlock"), 0.into()), + (String::from("curieBlock"), 0.into()), + (String::from("darwinTime"), 0.into()), + (String::from("darwinV2Time"), 0.into()), + ( + String::from("scroll"), + serde_json::json!({ + "feeVaultAddress": "0x5300000000000000000000000000000000000005", + "l1Config": { + "l1ChainId": 1, + "l1MessageQueueAddress": "0x0d7E906BD9cAFa154b048cFa766Cc1E54E39AF9B", + "scrollChainAddress": "0xa13BAF47339d63B743e7Da8741db5456DAc1E556", + "numL1MessagesPerBlock": 10 + } + }), + ), + ] + .into_iter() + .collect(), + ..Default::default() + }, + ..Default::default() + }; + + let chain_spec: ScrollChainSpec = genesis.into(); + + let hardforks: Vec<_> = chain_spec.hardforks.forks_iter().map(|(h, _)| h).collect(); + let expected_hardforks = vec![ + EthereumHardfork::Homestead.boxed(), + EthereumHardfork::Tangerine.boxed(), + EthereumHardfork::SpuriousDragon.boxed(), + EthereumHardfork::Byzantium.boxed(), + EthereumHardfork::Constantinople.boxed(), + EthereumHardfork::Petersburg.boxed(), + EthereumHardfork::Istanbul.boxed(), + EthereumHardfork::Berlin.boxed(), + EthereumHardfork::London.boxed(), + ScrollHardfork::Archimedes.boxed(), + EthereumHardfork::Shanghai.boxed(), + ScrollHardfork::Bernoulli.boxed(), + ScrollHardfork::Curie.boxed(), + ScrollHardfork::Darwin.boxed(), + ScrollHardfork::DarwinV2.boxed(), + ]; + + assert!(expected_hardforks + .iter() + .zip(hardforks.iter()) + .all(|(expected, actual)| &**expected == *actual)); + + assert_eq!(expected_hardforks.len(), hardforks.len()); + } +}
diff --git reth/crates/scroll/chainspec/src/scroll.rs scroll-reth/crates/scroll/chainspec/src/scroll.rs new file mode 100644 index 0000000000000000000000000000000000000000..067ca0a5e871fcfa6546f2a0bc047ae38562ac15 --- /dev/null +++ scroll-reth/crates/scroll/chainspec/src/scroll.rs @@ -0,0 +1,32 @@ +//! Chain specification for the Scroll Mainnet network. + +use crate::{ + make_genesis_header, LazyLock, ScrollChainConfig, ScrollChainSpec, SCROLL_MAINNET_GENESIS_HASH, +}; +use alloc::sync::Arc; + +use alloy_chains::{Chain, NamedChain}; +use reth_chainspec::ChainSpec; +use reth_primitives_traits::SealedHeader; +use reth_scroll_forks::SCROLL_MAINNET_HARDFORKS; + +/// The Scroll Mainnet spec +pub static SCROLL_MAINNET: LazyLock<Arc<ScrollChainSpec>> = LazyLock::new(|| { + let genesis = serde_json::from_str(include_str!("../res/genesis/scroll.json")) + .expect("Can't deserialize Scroll Mainnet genesis json"); + ScrollChainSpec { + inner: ChainSpec { + // TODO(scroll): migrate to Chain::scroll() (introduced in https://github.com/alloy-rs/chains/pull/112) when alloy-chains is bumped to version 0.1.48 + chain: Chain::from_named(NamedChain::Scroll), + genesis_header: SealedHeader::new( + make_genesis_header(&genesis), + SCROLL_MAINNET_GENESIS_HASH, + ), + genesis, + hardforks: SCROLL_MAINNET_HARDFORKS.clone(), + ..Default::default() + }, + config: ScrollChainConfig::mainnet(), + } + .into() +});
diff --git reth/crates/scroll/chainspec/src/scroll_sepolia.rs scroll-reth/crates/scroll/chainspec/src/scroll_sepolia.rs new file mode 100644 index 0000000000000000000000000000000000000000..74caaf7dd2465a8454653b7572dc452eb9855019 --- /dev/null +++ scroll-reth/crates/scroll/chainspec/src/scroll_sepolia.rs @@ -0,0 +1,32 @@ +//! Chain specification for the Scroll Sepolia testnet network. + +use crate::{ + make_genesis_header, LazyLock, ScrollChainConfig, ScrollChainSpec, SCROLL_SEPOLIA_GENESIS_HASH, +}; +use alloc::sync::Arc; + +use alloy_chains::{Chain, NamedChain}; +use reth_chainspec::ChainSpec; +use reth_primitives_traits::SealedHeader; +use reth_scroll_forks::SCROLL_SEPOLIA_HARDFORKS; + +/// The Scroll Sepolia spec +pub static SCROLL_SEPOLIA: LazyLock<Arc<ScrollChainSpec>> = LazyLock::new(|| { + let genesis = serde_json::from_str(include_str!("../res/genesis/sepolia_scroll.json")) + .expect("Can't deserialize Scroll Sepolia genesis json"); + ScrollChainSpec { + inner: ChainSpec { + // TODO(scroll): migrate to Chain::scroll_sepolia() (introduced in https://github.com/alloy-rs/chains/pull/112) when alloy-chains is bumped to version 0.1.48 + chain: Chain::from_named(NamedChain::ScrollSepolia), + genesis_header: SealedHeader::new( + make_genesis_header(&genesis), + SCROLL_SEPOLIA_GENESIS_HASH, + ), + genesis, + hardforks: SCROLL_SEPOLIA_HARDFORKS.clone(), + ..Default::default() + }, + config: ScrollChainConfig::sepolia(), + } + .into() +});
diff --git reth/crates/scroll/cli/Cargo.toml scroll-reth/crates/scroll/cli/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..b5c79e24940217b00a3f5bd470492e7d6a212fcb --- /dev/null +++ scroll-reth/crates/scroll/cli/Cargo.toml @@ -0,0 +1,44 @@ +[package] +name = "reth-scroll-cli" +version.workspace = true +edition.workspace = true +rust-version.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +exclude.workspace = true + +[lints] +workspace = true + +[dependencies] +# reth +reth-db = { workspace = true, features = ["scroll-alloy-traits"] } +reth-cli.workspace = true +reth-cli-commands.workspace = true +reth-cli-runner.workspace = true +reth-consensus.workspace = true +reth-node-builder.workspace = true +reth-node-core.workspace = true +reth-node-metrics.workspace = true +reth-tracing.workspace = true + +# scroll +reth-scroll-chainspec.workspace = true +reth-scroll-evm.workspace = true +reth-scroll-node.workspace = true +reth-scroll-primitives.workspace = true +scroll-alloy-consensus = { workspace = true, optional = true } + +# misc +eyre.workspace = true +clap.workspace = true +proptest = { workspace = true, optional = true } +tracing.workspace = true + +[features] +dev = [ + "dep:proptest", + "dep:scroll-alloy-consensus", + "reth-cli-commands/arbitrary", +]
diff --git reth/crates/scroll/cli/src/args.rs scroll-reth/crates/scroll/cli/src/args.rs new file mode 100644 index 0000000000000000000000000000000000000000..d51f83146a06b3387dbd098f63ec1e3d8d46593b --- /dev/null +++ scroll-reth/crates/scroll/cli/src/args.rs @@ -0,0 +1,3 @@ +/// Rollup arguments for the Scroll node. +#[derive(Debug, clap::Args)] +pub struct ScrollRollupArgs;
diff --git reth/crates/scroll/cli/src/commands/mod.rs scroll-reth/crates/scroll/cli/src/commands/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..0690fe3a3123bf9b757ae5c6c2c68f4d1bb2f9a5 --- /dev/null +++ scroll-reth/crates/scroll/cli/src/commands/mod.rs @@ -0,0 +1,56 @@ +#[cfg(feature = "dev")] +mod test_vectors; + +use crate::ScrollChainSpecParser; +use clap::Subcommand; +use reth_cli::chainspec::ChainSpecParser; +use reth_cli_commands::{ + config_cmd, db, dump_genesis, import, init_cmd, init_state, node, node::NoArgs, p2p, prune, + recover, stage, +}; +use std::fmt; + +/// Commands to be executed +#[derive(Debug, Subcommand)] +#[allow(clippy::large_enum_variant)] +pub enum Commands< + Spec: ChainSpecParser = ScrollChainSpecParser, + Ext: clap::Args + fmt::Debug = NoArgs, +> { + /// Start the node + #[command(name = "node")] + Node(Box<node::NodeCommand<Spec, Ext>>), + /// Initialize the database from a genesis file. + #[command(name = "init")] + Init(init_cmd::InitCommand<Spec>), + /// Initialize the database from a state dump file. + #[command(name = "init-state")] + InitState(init_state::InitStateCommand<Spec>), + /// This syncs RLP encoded blocks from a file. + #[command(name = "import")] + Import(import::ImportCommand<Spec>), + /// Dumps genesis block JSON configuration to stdout. + DumpGenesis(dump_genesis::DumpGenesisCommand<Spec>), + /// Database debugging utilities + #[command(name = "db")] + Db(db::Command<Spec>), + /// Manipulate individual stages. + #[command(name = "stage")] + Stage(Box<stage::Command<Spec>>), + /// P2P Debugging utilities + #[command(name = "p2p")] + P2P(p2p::Command<Spec>), + /// Write config to stdout + #[command(name = "config")] + Config(config_cmd::Command), + /// Scripts for node recovery + #[command(name = "recover")] + Recover(recover::Command<Spec>), + /// Prune according to the configuration without any limits + #[command(name = "prune")] + Prune(prune::PruneCommand<Spec>), + /// Generate Test Vectors + #[cfg(feature = "dev")] + #[command(name = "test-vectors")] + TestVectors(test_vectors::Command), +}
diff --git reth/crates/scroll/cli/src/commands/test_vectors.rs scroll-reth/crates/scroll/cli/src/commands/test_vectors.rs new file mode 100644 index 0000000000000000000000000000000000000000..f48c0f0e2e286aa39333ada95107a5450e6b73f1 --- /dev/null +++ scroll-reth/crates/scroll/cli/src/commands/test_vectors.rs @@ -0,0 +1,72 @@ +//! Command for generating test vectors. + +use clap::{Parser, Subcommand}; +use proptest::test_runner::TestRunner; +use reth_cli_commands::{ + compact_types, + test_vectors::{ + compact, + compact::{ + generate_vector, read_vector, GENERATE_VECTORS as ETH_GENERATE_VECTORS, + READ_VECTORS as ETH_READ_VECTORS, + }, + tables, + }, +}; +use scroll_alloy_consensus::TxL1Message; + +/// Generate test-vectors for different data types. +#[derive(Debug, Parser)] +pub struct Command { + #[command(subcommand)] + command: Subcommands, +} + +#[derive(Subcommand, Debug)] +/// `reth test-vectors` subcommands +enum Subcommands { + /// Generates test vectors for specified tables. If no table is specified, generate for all. + Tables { + /// List of table names. Case-sensitive. + names: Vec<String>, + }, + /// Generates test vectors for `Compact` types with `--write`. Reads and checks generated + /// vectors with `--read`. + #[group(multiple = false, required = true)] + Compact { + /// Write test vectors to a file. + #[arg(long)] + write: bool, + + /// Read test vectors from a file. + #[arg(long)] + read: bool, + }, +} + +impl Command { + /// Execute the command + pub async fn execute(self) -> eyre::Result<()> { + match self.command { + Subcommands::Tables { names } => { + tables::generate_vectors(names)?; + } + Subcommands::Compact { write, .. } => { + compact_types!( + regular: [ + TxL1Message + ], identifier: [] + ); + + if write { + compact::generate_vectors_with(ETH_GENERATE_VECTORS)?; + compact::generate_vectors_with(GENERATE_VECTORS)?; + } else { + compact::read_vectors_with(ETH_READ_VECTORS)?; + compact::read_vectors_with(READ_VECTORS)?; + } + } + } + Ok(()) + } +}
diff --git reth/crates/scroll/cli/src/lib.rs scroll-reth/crates/scroll/cli/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..03c748dd6e28b0f8d06299e71acd3aee6b7b3471 --- /dev/null +++ scroll-reth/crates/scroll/cli/src/lib.rs @@ -0,0 +1,159 @@ +//! Scroll CLI implementation. +mod args; +pub use args::ScrollRollupArgs; + +mod commands; +pub use commands::Commands; + +mod spec; +pub use spec::ScrollChainSpecParser; + +use clap::{value_parser, Parser}; +use reth_cli::chainspec::ChainSpecParser; +use reth_cli_commands::{common::CliNodeTypes, launcher::FnLauncher, node::NoArgs}; +use reth_cli_runner::CliRunner; +use reth_consensus::noop::NoopConsensus; +use reth_db::DatabaseEnv; +use reth_node_builder::{NodeBuilder, WithLaunchContext}; +use reth_node_core::{ + args::LogArgs, + version::{LONG_VERSION, SHORT_VERSION}, +}; +use reth_node_metrics::recorder::install_prometheus_recorder; +use reth_scroll_chainspec::ScrollChainSpec; +use reth_scroll_evm::ScrollExecutorProvider; +use reth_scroll_node::ScrollNetworkPrimitives; +use reth_scroll_primitives::ScrollPrimitives; +use reth_tracing::FileWorkerGuard; +use std::{ffi::OsString, fmt, future::Future, sync::Arc}; +use tracing::info; + +/// The main scroll cli interface. +/// +/// This is the entrypoint to the executable. +#[derive(Debug, Parser)] +#[command(author, version = SHORT_VERSION, long_version = LONG_VERSION, about = "Scroll Reth", long_about = None +)] +pub struct Cli<Spec: ChainSpecParser = ScrollChainSpecParser, Ext: clap::Args + fmt::Debug = NoArgs> +{ + /// The command to run + #[command(subcommand)] + command: Commands<Spec, Ext>, + + /// The chain this node is running. + /// + /// Possible values are either a built-in chain or the path to a chain specification file. + #[arg( + long, + value_name = "CHAIN_OR_PATH", + long_help = Spec::help_message(), + default_value = Spec::SUPPORTED_CHAINS[0], + value_parser = Spec::parser(), + global = true, + )] + chain: Arc<Spec::ChainSpec>, + + /// Add a new instance of a node. + /// + /// Configures the ports of the node to avoid conflicts with the defaults. + /// This is useful for running multiple nodes on the same machine. + /// + /// Max number of instances is 200. It is chosen in a way so that it's not possible to have + /// port numbers that conflict with each other. + /// + /// Changes to the following port numbers: + /// - `DISCOVERY_PORT`: default + `instance` - 1 + /// - `AUTH_PORT`: default + `instance` * 100 - 100 + /// - `HTTP_RPC_PORT`: default - `instance` + 1 + /// - `WS_RPC_PORT`: default + `instance` * 2 - 2 + #[arg(long, value_name = "INSTANCE", global = true, default_value_t = 1, value_parser = value_parser!(u16).range(..=200) + )] + instance: u16, + + #[command(flatten)] + logs: LogArgs, +} + +impl Cli { + /// Parsers only the default CLI arguments + pub fn parse_args() -> Self { + Self::parse() + } + + /// Parsers only the default CLI arguments from the given iterator + pub fn try_parse_args_from<I, T>(itr: I) -> Result<Self, clap::error::Error> + where + I: IntoIterator<Item = T>, + T: Into<OsString> + Clone, + { + Self::try_parse_from(itr) + } +} + +impl<C, Ext> Cli<C, Ext> +where + C: ChainSpecParser<ChainSpec = ScrollChainSpec>, + Ext: clap::Args + fmt::Debug, +{ + /// Execute the configured cli command. + /// + /// This accepts a closure that is used to launch the node via the + /// [`NodeCommand`](reth_cli_commands::node::NodeCommand). + pub fn run<L, Fut, Types>(mut self, launcher: L) -> eyre::Result<()> + where + L: FnOnce(WithLaunchContext<NodeBuilder<Arc<DatabaseEnv>, C::ChainSpec>>, Ext) -> Fut, + Fut: Future<Output = eyre::Result<()>>, + Types: CliNodeTypes<ChainSpec = C::ChainSpec, Primitives = ScrollPrimitives>, + { + // add network name to logs dir + self.logs.log_file_directory = + self.logs.log_file_directory.join(self.chain.chain().to_string()); + + let _guard = self.init_tracing()?; + info!(target: "reth::cli", "Initialized tracing, debug log directory: {}", self.logs.log_file_directory); + + // Install the prometheus recorder to be sure to record all metrics + let _ = install_prometheus_recorder(); + let components = |spec: Arc<C::ChainSpec>| { + (ScrollExecutorProvider::scroll(spec), NoopConsensus::default()) + }; + + let runner = CliRunner::try_default_runtime()?; + match self.command { + Commands::Node(command) => runner.run_command_until_exit(|ctx| { + command.execute(ctx, FnLauncher::new::<C, Ext>(launcher)) + }), + Commands::Init(command) => runner.run_blocking_until_ctrl_c(command.execute::<Types>()), + Commands::InitState(command) => { + runner.run_blocking_until_ctrl_c(command.execute::<Types>()) + } + Commands::Import(command) => { + runner.run_blocking_until_ctrl_c(command.execute::<Types, _, _>(components)) + } + Commands::DumpGenesis(command) => runner.run_blocking_until_ctrl_c(command.execute()), + Commands::Db(command) => runner.run_blocking_until_ctrl_c(command.execute::<Types>()), + Commands::Stage(command) => runner.run_command_until_exit(|ctx| { + command.execute::<Types, _, _, ScrollNetworkPrimitives>(ctx, components) + }), + Commands::P2P(command) => { + runner.run_until_ctrl_c(command.execute::<ScrollNetworkPrimitives>()) + } + Commands::Config(command) => runner.run_until_ctrl_c(command.execute()), + Commands::Recover(command) => { + runner.run_command_until_exit(|ctx| command.execute::<Types>(ctx)) + } + Commands::Prune(command) => runner.run_until_ctrl_c(command.execute::<Types>()), + #[cfg(feature = "dev")] + Commands::TestVectors(command) => runner.run_until_ctrl_c(command.execute()), + } + } + + /// Initializes tracing with the configured options. + /// + /// If file logging is enabled, this function returns a guard that must be kept alive to ensure + /// that all logs are flushed to disk. + pub fn init_tracing(&self) -> eyre::Result<Option<FileWorkerGuard>> { + let guard = self.logs.init_tracing()?; + Ok(guard) + } +}
diff --git reth/crates/scroll/cli/src/spec.rs scroll-reth/crates/scroll/cli/src/spec.rs new file mode 100644 index 0000000000000000000000000000000000000000..361029d9d1fc6101bd9f15b0d01b824a3aff04ec --- /dev/null +++ scroll-reth/crates/scroll/cli/src/spec.rs @@ -0,0 +1,21 @@ +use reth_cli::chainspec::{parse_genesis, ChainSpecParser}; +use reth_scroll_chainspec::{ScrollChainSpec, SCROLL_DEV, SCROLL_MAINNET, SCROLL_SEPOLIA}; +use std::sync::Arc; + +/// The parser for the Scroll chain specification. +#[derive(Debug, Clone)] +pub struct ScrollChainSpecParser; + +impl ChainSpecParser for ScrollChainSpecParser { + type ChainSpec = ScrollChainSpec; + const SUPPORTED_CHAINS: &'static [&'static str] = &["dev", "scroll-mainnet", "scroll-sepolia"]; + + fn parse(s: &str) -> eyre::Result<Arc<Self::ChainSpec>> { + Ok(match s { + "dev" => SCROLL_DEV.clone(), + "scroll-mainnet" => SCROLL_MAINNET.clone(), + "scroll-sepolia" => SCROLL_SEPOLIA.clone(), + _ => Arc::new(parse_genesis(s)?.into()), + }) + } +}
diff --git reth/crates/scroll/consensus/Cargo.toml scroll-reth/crates/scroll/consensus/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..f9c6c1319f2931a0c361b46be8eb83f9d1bedc65 --- /dev/null +++ scroll-reth/crates/scroll/consensus/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "reth-scroll-consensus" +version.workspace = true +edition.workspace = true +rust-version.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +exclude.workspace = true + +[lints] +workspace = true + +[dependencies] +# alloy +alloy-consensus.workspace = true +alloy-primitives.workspace = true + +# reth +reth-chainspec.workspace = true +reth-consensus.workspace = true +reth-consensus-common.workspace = true +reth-ethereum-consensus.workspace = true +reth-execution-types.workspace = true +reth-primitives-traits.workspace = true + +# scroll +reth-scroll-primitives = { workspace = true, default-features = false } +scroll-alloy-hardforks.workspace = true + +# misc +thiserror.workspace = true +tracing.workspace = true + +[package.metadata.cargo-udeps.ignore] +normal = ["reth-primitives"]
diff --git reth/crates/scroll/consensus/src/error.rs scroll-reth/crates/scroll/consensus/src/error.rs new file mode 100644 index 0000000000000000000000000000000000000000..39bae3f2eaf0c536b4ea5ed1dd69f4722329a02d --- /dev/null +++ scroll-reth/crates/scroll/consensus/src/error.rs @@ -0,0 +1,15 @@ +use reth_consensus::ConsensusError; + +/// Scroll consensus error. +#[derive(Debug, Clone, thiserror::Error)] +pub enum ScrollConsensusError { + /// L1 [`ConsensusError`], that also occurs on L2. + #[error(transparent)] + Eth(#[from] ConsensusError), + /// Block body has non-empty withdrawals list. + #[error("non-empty block body withdrawals list")] + WithdrawalsNonEmpty, + /// Chain spec yielded unexpected blob params. + #[error("unexpected blob params at timestamp")] + UnexpectedBlobParams, +}
diff --git reth/crates/scroll/consensus/src/lib.rs scroll-reth/crates/scroll/consensus/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..2bf959bc63b6dd26b87504f65437fc4aa2a31826 --- /dev/null +++ scroll-reth/crates/scroll/consensus/src/lib.rs @@ -0,0 +1,10 @@ +//! Scroll consensus implementation. + +extern crate alloc; + +mod error; +pub use error::ScrollConsensusError; + +mod validation; + +pub use validation::ScrollBeaconConsensus;
diff --git reth/crates/scroll/consensus/src/validation.rs scroll-reth/crates/scroll/consensus/src/validation.rs new file mode 100644 index 0000000000000000000000000000000000000000..adcd2706f8504db867092a75d1c97f696fc09d8e --- /dev/null +++ scroll-reth/crates/scroll/consensus/src/validation.rs @@ -0,0 +1,191 @@ +use crate::error::ScrollConsensusError; +use alloc::sync::Arc; +use core::fmt::Debug; + +use alloy_consensus::{BlockHeader as _, TxReceipt, EMPTY_OMMER_ROOT_HASH}; +use alloy_primitives::B256; +use reth_chainspec::{EthChainSpec, EthereumHardforks}; +use reth_consensus::{ + validate_state_root, Consensus, ConsensusError, FullConsensus, HeaderValidator, +}; +use reth_consensus_common::validation::{ + validate_against_parent_hash_number, validate_body_against_header, validate_header_gas, +}; +use reth_execution_types::BlockExecutionResult; +use reth_primitives_traits::{ + receipt::gas_spent_by_transactions, Block, BlockBody, BlockHeader, GotExpected, NodePrimitives, + RecoveredBlock, SealedBlock, SealedHeader, +}; +use reth_scroll_primitives::ScrollReceipt; +use scroll_alloy_hardforks::{ScrollHardfork, ScrollHardforks}; + +/// Scroll consensus implementation. +/// +/// Provides basic checks as outlined in the execution specs. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ScrollBeaconConsensus<ChainSpec> { + /// Configuration + chain_spec: Arc<ChainSpec>, +} + +impl<ChainSpec> ScrollBeaconConsensus<ChainSpec> { + /// Create a new instance of [`ScrollBeaconConsensus`] + pub const fn new(chain_spec: Arc<ChainSpec>) -> Self { + Self { chain_spec } + } +} + +impl<ChainSpec: EthChainSpec + ScrollHardforks, N: NodePrimitives<Receipt = ScrollReceipt>> + FullConsensus<N> for ScrollBeaconConsensus<ChainSpec> +{ + fn validate_block_post_execution( + &self, + block: &RecoveredBlock<N::Block>, + result: &BlockExecutionResult<N::Receipt>, + ) -> Result<(), ConsensusError> { + // verify the block gas used + let cumulative_gas_used = + result.receipts.last().map(|r| r.cumulative_gas_used()).unwrap_or(0); + if block.gas_used() != cumulative_gas_used { + return Err(ConsensusError::BlockGasUsed { + gas: GotExpected { got: cumulative_gas_used, expected: block.gas_used() }, + gas_spent_by_tx: gas_spent_by_transactions(&result.receipts), + }); + } + + // verify the receipts logs bloom and root + if self.chain_spec.is_byzantium_active_at_block(block.header().number()) { + if let Err(error) = reth_ethereum_consensus::verify_receipts( + block.header().receipts_root(), + block.header().logs_bloom(), + &result.receipts, + ) { + tracing::debug!( + %error, + ?result.receipts, + header_receipt_root = ?block.header().receipts_root(), + header_bloom = ?block.header().logs_bloom(), + "failed to verify receipts" + ); + return Err(error); + } + } + + Ok(()) + } +} + +impl<ChainSpec: EthChainSpec + ScrollHardforks, B: Block> Consensus<B> + for ScrollBeaconConsensus<ChainSpec> +{ + type Error = ConsensusError; + + fn validate_body_against_header( + &self, + body: &B::Body, + header: &SealedHeader<B::Header>, + ) -> Result<(), ConsensusError> { + validate_body_against_header(body, header.header()) + } + + fn validate_block_pre_execution(&self, block: &SealedBlock<B>) -> Result<(), ConsensusError> { + // Check ommers hash + let ommers_hash = block.body().calculate_ommers_root(); + if Some(block.ommers_hash()) != ommers_hash { + return Err(ConsensusError::BodyOmmersHashDiff( + GotExpected { + got: ommers_hash.unwrap_or(EMPTY_OMMER_ROOT_HASH), + expected: block.ommers_hash(), + } + .into(), + )) + } + + // Check transaction root + if let Err(error) = block.ensure_transaction_root_valid() { + return Err(ConsensusError::BodyTransactionRootDiff(error.into())) + } + + // Check withdrawals are empty + if block.body().withdrawals().is_some() { + return Err(ConsensusError::Other(ScrollConsensusError::WithdrawalsNonEmpty.to_string())) + } + + Ok(()) + } +} + +impl<ChainSpec: EthChainSpec + ScrollHardforks, H: BlockHeader> HeaderValidator<H> + for ScrollBeaconConsensus<ChainSpec> +{ + fn validate_header(&self, header: &SealedHeader<H>) -> Result<(), ConsensusError> { + if header.ommers_hash() != EMPTY_OMMER_ROOT_HASH { + return Err(ConsensusError::TheMergeOmmerRootIsNotEmpty) + } + + validate_header_gas(header.header())?; + validate_header_base_fee(header.header(), &self.chain_spec) + } + + fn validate_header_against_parent( + &self, + header: &SealedHeader<H>, + parent: &SealedHeader<H>, + ) -> Result<(), ConsensusError> { + validate_against_parent_hash_number(header.header(), parent)?; + validate_against_parent_timestamp(header.header(), parent.header())?; + + // TODO(scroll): we should have a way to validate the base fee from the header + // against the parent header using + // <https://github.com/scroll-tech/go-ethereum/blob/develop/consensus/misc/eip1559.go#L53> + + // ensure that the blob gas fields for this block + if self.chain_spec.blob_params_at_timestamp(header.timestamp()).is_some() { + return Err(ConsensusError::Other( + ScrollConsensusError::UnexpectedBlobParams.to_string(), + )) + } + + Ok(()) + } + + fn validate_state_root(&self, header: &H, root: B256) -> Result<(), ConsensusError> { + if self.chain_spec.is_euclid_active_at_timestamp(header.timestamp()) { + validate_state_root(header, root)?; + } + + Ok(()) + } +} + +/// Ensure the EIP-1559 base fee is set if the Curie hardfork is active. +#[inline] +fn validate_header_base_fee<H: BlockHeader, ChainSpec: ScrollHardforks>( + header: &H, + chain_spec: &ChainSpec, +) -> Result<(), ConsensusError> { + if chain_spec.scroll_fork_activation(ScrollHardfork::Curie).active_at_block(header.number()) && + header.base_fee_per_gas().is_none() + { + return Err(ConsensusError::BaseFeeMissing) + } + Ok(()) +} + +/// Validates the timestamp against the parent to make sure it is in the past. +/// In Scroll, we can have parent.timestamp == header.timestamp which is why +/// we modify this validation compared to +/// [`reth_consensus_common::validation::validate_against_parent_timestamp`]. +#[inline] +fn validate_against_parent_timestamp<H: BlockHeader>( + header: &H, + parent: &H, +) -> Result<(), ConsensusError> { + if header.timestamp() < parent.timestamp() { + return Err(ConsensusError::TimestampIsInPast { + parent_timestamp: parent.timestamp(), + timestamp: header.timestamp(), + }) + } + Ok(()) +}
diff --git reth/crates/scroll/engine-primitives/Cargo.toml scroll-reth/crates/scroll/engine-primitives/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..bb24a42a903ce53184ed40d4698942b1ec69b50b --- /dev/null +++ scroll-reth/crates/scroll/engine-primitives/Cargo.toml @@ -0,0 +1,65 @@ +[package] +name = "reth-scroll-engine-primitives" +version.workspace = true +edition.workspace = true +rust-version.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +exclude.workspace = true + +[lints] +workspace = true + +[dependencies] +# reth +reth-chain-state.workspace = true +reth-chainspec.workspace = true +reth-engine-primitives.workspace = true +reth-payload-builder.workspace = true +reth-payload-primitives = { workspace = true, features = ["scroll-alloy-traits"] } +reth-primitives = { workspace = true, features = ["serde-bincode-compat", "reth-codec"] } +reth-primitives-traits.workspace = true + +# alloy +alloy-consensus.workspace = true +alloy-eips.workspace = true +alloy-primitives.workspace = true +alloy-rlp.workspace = true +alloy-rpc-types-engine.workspace = true + +# scroll +reth-scroll-chainspec.workspace = true +reth-scroll-primitives = { workspace = true, features = ["serde", "serde-bincode-compat", "reth-codec"] } +scroll-alloy-rpc-types-engine.workspace = true +scroll-alloy-hardforks.workspace = true + +# misc +serde.workspace = true +sha2 = { workspace = true, default-features = false } + +[dev-dependencies] +alloy-primitives = { workspace = true, features = ["getrandom"] } +arbitrary.workspace = true +eyre.workspace = true +rand.workspace = true + +[features] +default = ["std"] +std = [ + "alloy-consensus/std", + "alloy-eips/std", + "alloy-primitives/std", + "alloy-rlp/std", + "alloy-rpc-types-engine/std", + "reth-chainspec/std", + "reth-engine-primitives/std", + "reth-primitives/std", + "reth-primitives-traits/std", + "serde/std", + "sha2/std", + "reth-scroll-chainspec/std", + "scroll-alloy-hardforks/std", + "scroll-alloy-rpc-types-engine/std", + "reth-scroll-primitives/std", +]
diff --git reth/crates/scroll/engine-primitives/src/lib.rs scroll-reth/crates/scroll/engine-primitives/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..a82ec2581c09f531783073d2bb2682d94082797d --- /dev/null +++ scroll-reth/crates/scroll/engine-primitives/src/lib.rs @@ -0,0 +1,13 @@ +//! The engine primitives for Scroll. + +#![cfg_attr(not(feature = "std"), no_std)] +#[cfg(not(feature = "std"))] +extern crate alloc as std; + +mod payload; +pub use payload::{ + try_into_block, ScrollBuiltPayload, ScrollEngineTypes, ScrollPayloadBuilderAttributes, + ScrollPayloadTypes, +}; + +extern crate alloc;
diff --git reth/crates/scroll/engine-primitives/src/payload/attributes.rs scroll-reth/crates/scroll/engine-primitives/src/payload/attributes.rs new file mode 100644 index 0000000000000000000000000000000000000000..fa6ed8308b49fce1c1cf5af446d03e4cfd425c0f --- /dev/null +++ scroll-reth/crates/scroll/engine-primitives/src/payload/attributes.rs @@ -0,0 +1,195 @@ +//! Payload related types + +use alloc::vec::Vec; +use std::fmt::Debug; + +use alloy_eips::{eip2718::Decodable2718, eip4895::Withdrawals}; +use alloy_primitives::{keccak256, Address, B256}; +use alloy_rlp::Encodable; +use alloy_rpc_types_engine::PayloadId; +use reth_payload_builder::EthPayloadBuilderAttributes; +use reth_payload_primitives::PayloadBuilderAttributes; +use reth_primitives::transaction::WithEncoded; +use reth_scroll_primitives::ScrollTransactionSigned; +use scroll_alloy_rpc_types_engine::{BlockDataHint, ScrollPayloadAttributes}; + +/// Scroll Payload Builder Attributes +#[derive(Debug, Default, Clone, PartialEq, Eq)] +pub struct ScrollPayloadBuilderAttributes { + /// Inner ethereum payload builder attributes + pub payload_attributes: EthPayloadBuilderAttributes, + /// `NoTxPool` option for the generated payload + pub no_tx_pool: bool, + /// Decoded transactions and the original EIP-2718 encoded bytes as received in the payload + /// attributes. + pub transactions: Vec<WithEncoded<ScrollTransactionSigned>>, + /// The pre-Euclid block data hint, necessary for the block builder to derive the correct block + /// hash. + pub block_data_hint: Option<BlockDataHint>, +} + +impl PayloadBuilderAttributes for ScrollPayloadBuilderAttributes { + type RpcPayloadAttributes = ScrollPayloadAttributes; + type Error = alloy_rlp::Error; + + fn try_new( + parent: B256, + attributes: ScrollPayloadAttributes, + version: u8, + ) -> Result<Self, Self::Error> { + let id = payload_id_scroll(&parent, &attributes, version); + + let transactions = attributes + .transactions + .unwrap_or_default() + .into_iter() + .map(|data| { + let mut buf = data.as_ref(); + let tx = Decodable2718::decode_2718(&mut buf).map_err(alloy_rlp::Error::from)?; + + if !buf.is_empty() { + return Err(alloy_rlp::Error::UnexpectedLength); + } + + Ok(WithEncoded::new(data, tx)) + }) + .collect::<Result<_, _>>()?; + + let payload_attributes = EthPayloadBuilderAttributes { + id, + parent, + timestamp: attributes.payload_attributes.timestamp, + suggested_fee_recipient: attributes.payload_attributes.suggested_fee_recipient, + prev_randao: attributes.payload_attributes.prev_randao, + withdrawals: attributes.payload_attributes.withdrawals.unwrap_or_default().into(), + parent_beacon_block_root: attributes.payload_attributes.parent_beacon_block_root, + }; + + Ok(Self { + payload_attributes, + no_tx_pool: attributes.no_tx_pool, + transactions, + block_data_hint: attributes.block_data_hint, + }) + } + + fn payload_id(&self) -> PayloadId { + self.payload_attributes.id + } + + fn parent(&self) -> B256 { + self.payload_attributes.parent + } + + fn timestamp(&self) -> u64 { + self.payload_attributes.timestamp + } + + fn parent_beacon_block_root(&self) -> Option<B256> { + self.payload_attributes.parent_beacon_block_root + } + + fn suggested_fee_recipient(&self) -> Address { + self.payload_attributes.suggested_fee_recipient + } + + fn prev_randao(&self) -> B256 { + self.payload_attributes.prev_randao + } + + fn withdrawals(&self) -> &Withdrawals { + &self.payload_attributes.withdrawals + } +} + +/// Generates the payload id for the configured payload from the [`ScrollPayloadAttributes`]. +/// +/// Returns an 8-byte identifier by hashing the payload components with sha256 hash. +pub(crate) fn payload_id_scroll( + parent: &B256, + attributes: &ScrollPayloadAttributes, + payload_version: u8, +) -> PayloadId { + use sha2::Digest; + let mut hasher = sha2::Sha256::new(); + hasher.update(parent.as_slice()); + hasher.update(&attributes.payload_attributes.timestamp.to_be_bytes()[..]); + hasher.update(attributes.payload_attributes.prev_randao.as_slice()); + hasher.update(attributes.payload_attributes.suggested_fee_recipient.as_slice()); + if let Some(withdrawals) = &attributes.payload_attributes.withdrawals { + let mut buf = Vec::new(); + withdrawals.encode(&mut buf); + hasher.update(buf); + } + + if let Some(parent_beacon_block) = attributes.payload_attributes.parent_beacon_block_root { + hasher.update(parent_beacon_block); + } + + let no_tx_pool = attributes.no_tx_pool; + if no_tx_pool || attributes.transactions.as_ref().is_some_and(|txs| !txs.is_empty()) { + hasher.update([no_tx_pool as u8]); + let txs_len = attributes.transactions.as_ref().map(|txs| txs.len()).unwrap_or_default(); + hasher.update(&txs_len.to_be_bytes()[..]); + if let Some(txs) = &attributes.transactions { + for tx in txs { + // we have to just hash the bytes here because otherwise we would need to decode + // the transactions here which really isn't ideal + let tx_hash = keccak256(tx); + // maybe we can try just taking the hash and not decoding + hasher.update(tx_hash) + } + } + } + + if let Some(block_data) = &attributes.block_data_hint { + hasher.update(&block_data.extra_data); + hasher.update(block_data.difficulty.to_be_bytes::<32>()); + } + + let mut out = hasher.finalize(); + out[0] = payload_version; + PayloadId::new(out.as_slice()[..8].try_into().expect("sufficient length")) +} + +impl From<EthPayloadBuilderAttributes> for ScrollPayloadBuilderAttributes { + fn from(value: EthPayloadBuilderAttributes) -> Self { + Self { payload_attributes: value, ..Default::default() } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use alloc::str::FromStr; + use alloy_primitives::{address, b256, bytes, FixedBytes, U256}; + use alloy_rpc_types_engine::PayloadAttributes; + use reth_payload_primitives::EngineApiMessageVersion; + + #[test] + fn test_payload_id() { + let expected = + PayloadId::new(FixedBytes::<8>::from_str("0x0322b5f17cf26e85").unwrap().into()); + let attrs = ScrollPayloadAttributes { + payload_attributes: PayloadAttributes { + timestamp: 1728933301, + prev_randao: b256!("9158595abbdab2c90635087619aa7042bbebe47642dfab3c9bfb934f6b082765"), + suggested_fee_recipient: address!("4200000000000000000000000000000000000011"), + withdrawals: Some([].into()), + parent_beacon_block_root: b256!("8fe0193b9bf83cb7e5a08538e494fecc23046aab9a497af3704f4afdae3250ff").into(), + }, + transactions: Some([bytes!("7ef8f8a0dc19cfa777d90980e4875d0a548a881baaa3f83f14d1bc0d3038bc329350e54194deaddeaddeaddeaddeaddeaddeaddeaddead00019442000000000000000000000000000000000000158080830f424080b8a4440a5e20000f424000000000000000000000000300000000670d6d890000000000000125000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000014bf9181db6e381d4384bbf69c48b0ee0eed23c6ca26143c6d2544f9d39997a590000000000000000000000007f83d659683caf2767fd3c720981d51f5bc365bc")].into()), + no_tx_pool: false, + block_data_hint: Some(BlockDataHint{ extra_data: bytes!("476574682f76312e302e302f6c696e75782f676f312e342e32"), difficulty: U256::from(10) } ), + }; + + assert_eq!( + expected, + payload_id_scroll( + &b256!("3533bf30edaf9505d0810bf475cbe4e5f4b9889904b9845e83efdeab4e92eb1e"), + &attrs, + EngineApiMessageVersion::V3 as u8 + ) + ); + } +}
diff --git reth/crates/scroll/engine-primitives/src/payload/built.rs scroll-reth/crates/scroll/engine-primitives/src/payload/built.rs new file mode 100644 index 0000000000000000000000000000000000000000..cea13302aa1c9cfacba619cebd2b2f90613e6593 --- /dev/null +++ scroll-reth/crates/scroll/engine-primitives/src/payload/built.rs @@ -0,0 +1,135 @@ +//! Outcome of a Scroll block building task with payload attributes provided via the Engine API. + +use core::iter; +use std::sync::Arc; + +use alloy_eips::eip7685::Requests; +use alloy_primitives::U256; +use alloy_rpc_types_engine::{ + BlobsBundleV1, ExecutionPayloadEnvelopeV2, ExecutionPayloadEnvelopeV3, + ExecutionPayloadEnvelopeV4, ExecutionPayloadFieldV2, ExecutionPayloadV1, ExecutionPayloadV3, + PayloadId, +}; +use reth_chain_state::ExecutedBlockWithTrieUpdates; +use reth_payload_primitives::BuiltPayload; +use reth_primitives_traits::SealedBlock; +use reth_scroll_primitives::{ScrollBlock, ScrollPrimitives}; + +/// Contains the built payload. +#[derive(Debug, Clone, Default)] +pub struct ScrollBuiltPayload { + /// Identifier of the payload + pub(crate) id: PayloadId, + /// Sealed block + pub(crate) block: Arc<SealedBlock<ScrollBlock>>, + /// Block execution data for the payload + pub(crate) executed_block: Option<ExecutedBlockWithTrieUpdates<ScrollPrimitives>>, + /// The fees of the block + pub(crate) fees: U256, +} + +impl ScrollBuiltPayload { + /// Initializes the payload with the given initial block. + pub const fn new( + id: PayloadId, + block: Arc<SealedBlock<ScrollBlock>>, + executed_block: Option<ExecutedBlockWithTrieUpdates<ScrollPrimitives>>, + fees: U256, + ) -> Self { + Self { id, block, executed_block, fees } + } + + /// Returns the identifier of the payload. + pub const fn id(&self) -> PayloadId { + self.id + } + + /// Returns the built block(sealed) + pub fn block(&self) -> &SealedBlock<ScrollBlock> { + &self.block + } + + /// Fees of the block + pub const fn fees(&self) -> U256 { + self.fees + } + + /// Converts the value into [`SealedBlock`]. + pub fn into_sealed_block(self) -> SealedBlock<ScrollBlock> { + Arc::unwrap_or_clone(self.block) + } +} + +impl BuiltPayload for ScrollBuiltPayload { + type Primitives = ScrollPrimitives; + + fn block(&self) -> &SealedBlock<ScrollBlock> { + self.block() + } + + fn fees(&self) -> U256 { + self.fees + } + + fn executed_block(&self) -> Option<ExecutedBlockWithTrieUpdates<Self::Primitives>> { + self.executed_block.clone() + } + + fn requests(&self) -> Option<Requests> { + None + } +} + +// V1 engine_getPayloadV1 response +impl From<ScrollBuiltPayload> for ExecutionPayloadV1 { + fn from(value: ScrollBuiltPayload) -> Self { + Self::from_block_unchecked( + value.block().hash(), + &Arc::unwrap_or_clone(value.block).into_block(), + ) + } +} + +// V2 engine_getPayloadV2 response +impl From<ScrollBuiltPayload> for ExecutionPayloadEnvelopeV2 { + fn from(value: ScrollBuiltPayload) -> Self { + let ScrollBuiltPayload { block, fees, .. } = value; + + Self { + block_value: fees, + execution_payload: ExecutionPayloadFieldV2::from_block_unchecked( + block.hash(), + &Arc::unwrap_or_clone(block).into_block(), + ), + } + } +} + +impl From<ScrollBuiltPayload> for ExecutionPayloadEnvelopeV3 { + fn from(value: ScrollBuiltPayload) -> Self { + let ScrollBuiltPayload { block, fees, .. } = value; + + Self { + execution_payload: ExecutionPayloadV3::from_block_unchecked( + block.hash(), + &Arc::unwrap_or_clone(block).into_block(), + ), + block_value: fees, + // From the engine API spec: + // + // > Client software **MAY** use any heuristics to decide whether to set + // `shouldOverrideBuilder` flag or not. If client software does not implement any + // heuristic this flag **SHOULD** be set to `false`. + // + // Spec: + // <https://github.com/ethereum/execution-apis/blob/fe8e13c288c592ec154ce25c534e26cb7ce0530d/src/engine/cancun.md#specification-2> + should_override_builder: false, + blobs_bundle: BlobsBundleV1::new(iter::empty()), + } + } +} +impl From<ScrollBuiltPayload> for ExecutionPayloadEnvelopeV4 { + fn from(value: ScrollBuiltPayload) -> Self { + Self { envelope_inner: value.into(), execution_requests: Default::default() } + } +}
diff --git reth/crates/scroll/engine-primitives/src/payload/mod.rs scroll-reth/crates/scroll/engine-primitives/src/payload/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..c7fd48170173de33c7b2cab42bad7690f31c62c2 --- /dev/null +++ scroll-reth/crates/scroll/engine-primitives/src/payload/mod.rs @@ -0,0 +1,331 @@ +//! Engine API Payload types. + +mod attributes; +pub use attributes::ScrollPayloadBuilderAttributes; + +mod built; +pub use built::ScrollBuiltPayload; + +use alloc::{sync::Arc, vec::Vec}; +use core::marker::PhantomData; + +use alloy_consensus::{proofs, EMPTY_OMMER_ROOT_HASH}; +use alloy_eips::eip2718::Decodable2718; +use alloy_primitives::U256; +use alloy_rlp::BufMut; +use alloy_rpc_types_engine::{ + ExecutionData, ExecutionPayload, ExecutionPayloadEnvelopeV2, ExecutionPayloadEnvelopeV3, + ExecutionPayloadEnvelopeV4, ExecutionPayloadV1, ExecutionPayloadV2, ExecutionPayloadV3, + PayloadError, +}; +use reth_engine_primitives::EngineTypes; +use reth_payload_primitives::{BuiltPayload, PayloadTypes}; +use reth_primitives::{Block, BlockBody, Header}; +use reth_primitives_traits::{NodePrimitives, SealedBlock}; +use reth_scroll_primitives::ScrollBlock; +use scroll_alloy_hardforks::ScrollHardforks; +use scroll_alloy_rpc_types_engine::ScrollPayloadAttributes; + +/// The types used in the default Scroll beacon consensus engine. +#[derive(Debug, Default, Clone, serde::Deserialize, serde::Serialize)] +#[non_exhaustive] +pub struct ScrollEngineTypes<T: PayloadTypes = ScrollPayloadTypes> { + _marker: PhantomData<T>, +} + +impl< + T: PayloadTypes< + ExecutionData = ExecutionData, + BuiltPayload: BuiltPayload<Primitives: NodePrimitives<Block = ScrollBlock>>, + >, + > PayloadTypes for ScrollEngineTypes<T> +{ + type ExecutionData = T::ExecutionData; + type BuiltPayload = T::BuiltPayload; + type PayloadAttributes = T::PayloadAttributes; + type PayloadBuilderAttributes = T::PayloadBuilderAttributes; + + fn block_to_payload( + block: SealedBlock< + <<Self::BuiltPayload as BuiltPayload>::Primitives as NodePrimitives>::Block, + >, + ) -> ExecutionData { + let (payload, sidecar) = + ExecutionPayload::from_block_unchecked(block.hash(), &block.into_block()); + ExecutionData { payload, sidecar } + } +} + +impl<T> EngineTypes for ScrollEngineTypes<T> +where + T: PayloadTypes<ExecutionData = ExecutionData>, + T::BuiltPayload: BuiltPayload<Primitives: NodePrimitives<Block = ScrollBlock>> + + TryInto<ExecutionPayloadV1> + + TryInto<ExecutionPayloadEnvelopeV2> + + TryInto<ExecutionPayloadEnvelopeV3> + + TryInto<ExecutionPayloadEnvelopeV4>, +{ + type ExecutionPayloadEnvelopeV1 = ExecutionPayloadV1; + type ExecutionPayloadEnvelopeV2 = ExecutionPayloadEnvelopeV2; + type ExecutionPayloadEnvelopeV3 = ExecutionPayloadEnvelopeV3; + type ExecutionPayloadEnvelopeV4 = ExecutionPayloadEnvelopeV4; + type ExecutionPayloadEnvelopeV5 = ExecutionPayloadEnvelopeV4; +} + +/// A default payload type for [`ScrollEngineTypes`] +#[derive(Debug, Default, Clone, serde::Deserialize, serde::Serialize)] +#[non_exhaustive] +pub struct ScrollPayloadTypes; + +impl PayloadTypes for ScrollPayloadTypes { + type ExecutionData = ExecutionData; + type BuiltPayload = ScrollBuiltPayload; + type PayloadAttributes = ScrollPayloadAttributes; + type PayloadBuilderAttributes = ScrollPayloadBuilderAttributes; + + fn block_to_payload( + block: SealedBlock< + <<Self::BuiltPayload as BuiltPayload>::Primitives as NodePrimitives>::Block, + >, + ) -> Self::ExecutionData { + let (payload, sidecar) = + ExecutionPayload::from_block_unchecked(block.hash(), &block.into_block()); + ExecutionData { payload, sidecar } + } +} + +/// Tries to create a new unsealed block from the given payload, sidecar and chain specification. +/// Sets the base fee of the block to `None` before the Curie hardfork. +/// Scroll implementation of the [`ExecutionPayload::try_into_block`], which will fail with +/// [`PayloadError::ExtraData`] due to the Scroll blocks containing extra data for the Clique +/// consensus. +pub fn try_into_block<T: Decodable2718, CS: ScrollHardforks>( + value: ExecutionData, + chainspec: Arc<CS>, +) -> Result<Block<T>, PayloadError> { + let mut block = match value.payload { + ExecutionPayload::V1(payload) => try_payload_v1_to_block(payload, chainspec)?, + ExecutionPayload::V2(payload) => try_payload_v2_to_block(payload, chainspec)?, + ExecutionPayload::V3(payload) => try_payload_v3_to_block(payload, chainspec)?, + }; + + block.header.parent_beacon_block_root = value.sidecar.parent_beacon_block_root(); + block.header.requests_hash = value.sidecar.requests_hash(); + + Ok(block) +} + +/// Tries to convert an [`ExecutionPayloadV1`] to [`Block`]. +fn try_payload_v1_to_block<T: Decodable2718, CS: ScrollHardforks>( + payload: ExecutionPayloadV1, + chainspec: CS, +) -> Result<Block<T>, PayloadError> { + // WARNING: It’s allowed for a base fee in EIP1559 to increase unbounded. We assume that + // it will fit in an u64. This is not always necessarily true, although it is extremely + // unlikely not to be the case, a u64 maximum would have 2^64 which equates to 18 ETH per + // gas. + let basefee = chainspec + .is_curie_active_at_block(payload.block_number) + .then_some(payload.base_fee_per_gas) + .map(|b| b.try_into()) + .transpose() + .map_err(|_| PayloadError::BaseFee(payload.base_fee_per_gas))?; + + let transactions = payload + .transactions + .iter() + .map(|tx| { + let mut buf = tx.as_ref(); + + let tx = T::decode_2718(&mut buf).map_err(alloy_rlp::Error::from)?; + + if !buf.is_empty() { + return Err(alloy_rlp::Error::UnexpectedLength); + } + + Ok(tx) + }) + .collect::<Result<Vec<_>, _>>()?; + + // Reuse the encoded bytes for root calculation + let transactions_root = + proofs::ordered_trie_root_with_encoder(&payload.transactions, |item, buf| { + buf.put_slice(item) + }); + + let header = Header { + parent_hash: payload.parent_hash, + beneficiary: payload.fee_recipient, + state_root: payload.state_root, + transactions_root, + receipts_root: payload.receipts_root, + withdrawals_root: None, + logs_bloom: payload.logs_bloom, + number: payload.block_number, + gas_limit: payload.gas_limit, + gas_used: payload.gas_used, + timestamp: payload.timestamp, + mix_hash: payload.prev_randao, + base_fee_per_gas: basefee, + blob_gas_used: None, + excess_blob_gas: None, + parent_beacon_block_root: None, + requests_hash: None, + extra_data: payload.extra_data, + // Defaults + ommers_hash: EMPTY_OMMER_ROOT_HASH, + difficulty: U256::ONE, + nonce: Default::default(), + }; + + Ok(Block { header, body: BlockBody { transactions, ..Default::default() } }) +} + +/// Tries to convert an [`ExecutionPayloadV2`] to [`Block`]. +fn try_payload_v2_to_block<T: Decodable2718, CS: ScrollHardforks>( + payload: ExecutionPayloadV2, + chainspec: CS, +) -> Result<Block<T>, PayloadError> { + // this performs the same conversion as the underlying V1 payload, but calculates the + // withdrawals root and adds withdrawals + let mut base_sealed_block = try_payload_v1_to_block(payload.payload_inner, chainspec)?; + let withdrawals_root = proofs::calculate_withdrawals_root(&payload.withdrawals); + base_sealed_block.body.withdrawals = Some(payload.withdrawals.into()); + base_sealed_block.header.withdrawals_root = Some(withdrawals_root); + Ok(base_sealed_block) +} + +/// Tries to convert an [`ExecutionPayloadV3`] to [`Block`]. +fn try_payload_v3_to_block<T: Decodable2718, CS: ScrollHardforks>( + payload: ExecutionPayloadV3, + chainspec: CS, +) -> Result<Block<T>, PayloadError> { + // this performs the same conversion as the underlying V2 payload, but inserts the blob gas + // used and excess blob gas + let mut base_block = try_payload_v2_to_block(payload.payload_inner, chainspec)?; + + base_block.header.blob_gas_used = Some(payload.blob_gas_used); + base_block.header.excess_blob_gas = Some(payload.excess_blob_gas); + + Ok(base_block) +} + +#[cfg(test)] +mod tests { + use super::*; + use alloy_primitives::{Address, Bloom, B256, U256}; + use alloy_rpc_types_engine::ExecutionPayloadV1; + use arbitrary::{Arbitrary, Unstructured}; + use rand::Rng; + use reth_scroll_chainspec::SCROLL_MAINNET; + use reth_scroll_primitives::ScrollTransactionSigned; + + #[test] + fn test_can_convert_execution_v1_payload_into_block() -> eyre::Result<()> { + let mut bytes = [0u8; 1024]; + rand::rng().fill(bytes.as_mut_slice()); + let mut u = Unstructured::new(&bytes); + + let mut extra_data = [0u8; 64]; + rand::rng().fill(extra_data.as_mut_slice()); + + let execution_payload = ExecutionPayload::V1(ExecutionPayloadV1 { + parent_hash: B256::random(), + fee_recipient: Address::random(), + state_root: B256::random(), + receipts_root: B256::random(), + logs_bloom: Bloom::random(), + prev_randao: B256::random(), + block_number: u64::arbitrary(&mut u)?, + gas_limit: u64::arbitrary(&mut u)?, + gas_used: u64::arbitrary(&mut u)?, + timestamp: u64::arbitrary(&mut u)?, + extra_data: extra_data.into(), + base_fee_per_gas: U256::from(u64::arbitrary(&mut u)?), + block_hash: B256::random(), + transactions: vec![], + }); + let execution_data = ExecutionData::new(execution_payload, Default::default()); + + let _: Block<ScrollTransactionSigned> = + try_into_block(execution_data, SCROLL_MAINNET.clone())?; + + Ok(()) + } + + #[test] + fn test_can_convert_execution_v2_payload_into_block() -> eyre::Result<()> { + let mut bytes = [0u8; 1024]; + rand::rng().fill(bytes.as_mut_slice()); + let mut u = Unstructured::new(&bytes); + + let mut extra_data = [0u8; 64]; + rand::rng().fill(extra_data.as_mut_slice()); + + let execution_payload = ExecutionPayload::V2(ExecutionPayloadV2 { + payload_inner: ExecutionPayloadV1 { + parent_hash: B256::random(), + fee_recipient: Address::random(), + state_root: B256::random(), + receipts_root: B256::random(), + logs_bloom: Bloom::random(), + prev_randao: B256::random(), + block_number: u64::arbitrary(&mut u)?, + gas_limit: u64::arbitrary(&mut u)?, + gas_used: u64::arbitrary(&mut u)?, + timestamp: u64::arbitrary(&mut u)?, + extra_data: extra_data.into(), + base_fee_per_gas: U256::from(u64::arbitrary(&mut u)?), + block_hash: B256::random(), + transactions: vec![], + }, + withdrawals: vec![], + }); + let execution_data = ExecutionData::new(execution_payload, Default::default()); + + let _: Block<ScrollTransactionSigned> = + try_into_block(execution_data, SCROLL_MAINNET.clone())?; + + Ok(()) + } + + #[test] + fn test_can_convert_execution_v3_payload_into_block() -> eyre::Result<()> { + let mut bytes = [0u8; 1024]; + rand::rng().fill(bytes.as_mut_slice()); + let mut u = Unstructured::new(&bytes); + + let mut extra_data = [0u8; 64]; + rand::rng().fill(extra_data.as_mut_slice()); + + let execution_payload = ExecutionPayload::V3(ExecutionPayloadV3 { + payload_inner: ExecutionPayloadV2 { + payload_inner: ExecutionPayloadV1 { + parent_hash: B256::random(), + fee_recipient: Address::random(), + state_root: B256::random(), + receipts_root: B256::random(), + logs_bloom: Bloom::random(), + prev_randao: B256::random(), + block_number: u64::arbitrary(&mut u)?, + gas_limit: u64::arbitrary(&mut u)?, + gas_used: u64::arbitrary(&mut u)?, + timestamp: u64::arbitrary(&mut u)?, + extra_data: extra_data.into(), + base_fee_per_gas: U256::from(u64::arbitrary(&mut u)?), + block_hash: B256::random(), + transactions: vec![], + }, + withdrawals: vec![], + }, + blob_gas_used: 0, + excess_blob_gas: 0, + }); + let execution_data = ExecutionData::new(execution_payload, Default::default()); + + let _: Block<ScrollTransactionSigned> = + try_into_block(execution_data, SCROLL_MAINNET.clone())?; + + Ok(()) + } +}
diff --git reth/crates/scroll/evm/Cargo.toml scroll-reth/crates/scroll/evm/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..e7f5d9c840b48632ef77e47543c485ed69cf90b6 --- /dev/null +++ scroll-reth/crates/scroll/evm/Cargo.toml @@ -0,0 +1,76 @@ +[package] +name = "reth-scroll-evm" +version.workspace = true +edition.workspace = true +rust-version.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +exclude.workspace = true + +[lints] +workspace = true + +[dependencies] +# reth +reth-chainspec.workspace = true +reth-evm = { workspace = true, features = ["scroll-alloy-traits"] } +reth-execution-types.workspace = true +reth-primitives = { workspace = true, features = ["serde-bincode-compat"] } +reth-primitives-traits.workspace = true + +# revm +revm = { workspace = true, features = ["optional_no_base_fee"] } +revm-primitives.workspace = true +revm-scroll.workspace = true + +# scroll +reth-scroll-chainspec.workspace = true +reth-scroll-forks.workspace = true +reth-scroll-primitives = { workspace = true, features = ["serde", "serde-bincode-compat"], default-features = false } + +# alloy +alloy-consensus.workspace = true +alloy-eips.workspace = true +alloy-evm.workspace = true +alloy-primitives.workspace = true + +# scroll +scroll-alloy-consensus.workspace = true +scroll-alloy-evm.workspace = true +scroll-alloy-hardforks.workspace = true + +# misc +derive_more.workspace = true +thiserror.workspace = true +tracing.workspace = true + +[dev-dependencies] +eyre.workspace = true +alloy-primitives = { workspace = true, features = ["getrandom"] } + +[features] +default = ["std"] +std = [ + "scroll-alloy-consensus/std", + "scroll-alloy-evm/std", + "alloy-consensus/std", + "alloy-evm/std", + "alloy-eips/std", + "alloy-primitives/std", + "derive_more/std", + "reth-chainspec/std", + "reth-evm/std", + "reth-execution-types/std", + "reth-primitives-traits/std", + "reth-primitives/std", + "reth-scroll-chainspec/std", + "reth-scroll-forks/std", + "reth-scroll-primitives/std", + "revm-primitives/std", + "revm-scroll/std", + "revm/std", + "thiserror/std", + "tracing/std", + "scroll-alloy-hardforks/std", +]
diff --git reth/crates/scroll/evm/src/build.rs scroll-reth/crates/scroll/evm/src/build.rs new file mode 100644 index 0000000000000000000000000000000000000000..58c8e31d00b2d980bd9fec9eac26c293955cd35a --- /dev/null +++ scroll-reth/crates/scroll/evm/src/build.rs @@ -0,0 +1,94 @@ +use alloc::sync::Arc; +use alloy_consensus::{proofs, BlockBody, Header, TxReceipt, EMPTY_OMMER_ROOT_HASH}; +use alloy_eips::merge::BEACON_NONCE; +use alloy_evm::block::{BlockExecutionError, BlockExecutorFactory}; +use alloy_primitives::{logs_bloom, Address}; +use reth_evm::execute::{BlockAssembler, BlockAssemblerInput}; +use reth_execution_types::BlockExecutionResult; +use reth_primitives_traits::SignedTransaction; +use reth_scroll_primitives::ScrollReceipt; +use scroll_alloy_evm::ScrollBlockExecutionCtx; +use scroll_alloy_hardforks::ScrollHardforks; + +/// Block builder for Scroll. +#[derive(Debug)] +pub struct ScrollBlockAssembler<ChainSpec> { + chain_spec: Arc<ChainSpec>, +} + +impl<ChainSpec> ScrollBlockAssembler<ChainSpec> { + /// Creates a new [`ScrollBlockAssembler`]. + pub const fn new(chain_spec: Arc<ChainSpec>) -> Self { + Self { chain_spec } + } +} + +impl<ChainSpec> Clone for ScrollBlockAssembler<ChainSpec> { + fn clone(&self) -> Self { + Self { chain_spec: self.chain_spec.clone() } + } +} + +impl<F, ChainSpec> BlockAssembler<F> for ScrollBlockAssembler<ChainSpec> +where + ChainSpec: ScrollHardforks, + F: for<'a> BlockExecutorFactory< + ExecutionCtx<'a> = ScrollBlockExecutionCtx, + Transaction: SignedTransaction, + Receipt = ScrollReceipt, + >, +{ + type Block = alloy_consensus::Block<F::Transaction>; + + fn assemble_block( + &self, + input: BlockAssemblerInput<'_, '_, F>, + ) -> Result<Self::Block, BlockExecutionError> { + let BlockAssemblerInput { + evm_env, + execution_ctx: ctx, + transactions, + output: BlockExecutionResult { receipts, gas_used, .. }, + state_root, + .. + } = input; + + let timestamp = evm_env.block_env.timestamp; + + let transactions_root = proofs::calculate_transaction_root(&transactions); + let receipts_root = ScrollReceipt::calculate_receipt_root_no_memo(receipts); + let logs_bloom = logs_bloom(receipts.iter().flat_map(|r| r.logs())); + + let header = Header { + parent_hash: ctx.parent_hash, + ommers_hash: EMPTY_OMMER_ROOT_HASH, + beneficiary: Address::ZERO, + state_root, + transactions_root, + receipts_root, + withdrawals_root: None, + logs_bloom, + timestamp, + mix_hash: evm_env.block_env.prevrandao.unwrap_or_default(), + nonce: BEACON_NONCE.into(), + base_fee_per_gas: self + .chain_spec + .is_curie_active_at_block(evm_env.block_env.number) + .then_some(evm_env.block_env.basefee), + number: evm_env.block_env.number, + gas_limit: evm_env.block_env.gas_limit, + difficulty: evm_env.block_env.difficulty, + gas_used: *gas_used, + extra_data: Default::default(), + parent_beacon_block_root: None, + blob_gas_used: None, + excess_blob_gas: None, + requests_hash: None, + }; + + Ok(alloy_consensus::Block::new( + header, + BlockBody { transactions, ommers: Default::default(), withdrawals: None }, + )) + } +}
diff --git reth/crates/scroll/evm/src/config.rs scroll-reth/crates/scroll/evm/src/config.rs new file mode 100644 index 0000000000000000000000000000000000000000..3c1a23bcd8a02cf11f4bf4f2f371d15157ad4099 --- /dev/null +++ scroll-reth/crates/scroll/evm/src/config.rs @@ -0,0 +1,301 @@ +use crate::{build::ScrollBlockAssembler, ScrollEvmConfig, ScrollNextBlockEnvAttributes}; +use alloc::sync::Arc; +use alloy_consensus::{BlockHeader, Header}; +use alloy_evm::{FromRecoveredTx, FromTxWithEncoded}; +use alloy_primitives::B256; +use core::convert::Infallible; +use reth_chainspec::EthChainSpec; +use reth_evm::{ConfigureEvm, EvmEnv, ExecutionCtxFor}; +use reth_primitives_traits::{ + BlockTy, NodePrimitives, SealedBlock, SealedHeader, SignedTransaction, +}; +use reth_scroll_chainspec::{ChainConfig, ScrollChainConfig}; +use reth_scroll_primitives::ScrollReceipt; +use revm::{ + context::{BlockEnv, CfgEnv, TxEnv}, + primitives::U256, +}; +use revm_scroll::ScrollSpecId; +use scroll_alloy_evm::{ + ScrollBlockExecutionCtx, ScrollBlockExecutorFactory, ScrollReceiptBuilder, + ScrollTransactionIntoTxEnv, +}; +use scroll_alloy_hardforks::ScrollHardforks; + +impl<ChainSpec, N, R> ConfigureEvm for ScrollEvmConfig<ChainSpec, N, R> +where + ChainSpec: EthChainSpec + ChainConfig<Config = ScrollChainConfig> + ScrollHardforks, + N: NodePrimitives< + Receipt = R::Receipt, + SignedTx = R::Transaction, + BlockHeader = Header, + BlockBody = alloy_consensus::BlockBody<R::Transaction>, + Block = alloy_consensus::Block<R::Transaction>, + >, + ScrollTransactionIntoTxEnv<TxEnv>: + FromRecoveredTx<N::SignedTx> + FromTxWithEncoded<N::SignedTx>, + R: ScrollReceiptBuilder<Receipt = ScrollReceipt, Transaction: SignedTransaction>, + Self: Send + Sync + Unpin + Clone + 'static, +{ + type Primitives = N; + type Error = Infallible; + type NextBlockEnvCtx = ScrollNextBlockEnvAttributes; + type BlockExecutorFactory = ScrollBlockExecutorFactory<R, Arc<ChainSpec>>; + type BlockAssembler = ScrollBlockAssembler<ChainSpec>; + + fn block_executor_factory(&self) -> &Self::BlockExecutorFactory { + &self.executor_factory + } + + fn block_assembler(&self) -> &Self::BlockAssembler { + &self.block_assembler + } + + fn evm_env(&self, header: &N::BlockHeader) -> EvmEnv<ScrollSpecId> { + let chain_spec = self.chain_spec(); + let spec_id = self.spec_id_at_timestamp_and_number(header.timestamp(), header.number()); + + let cfg_env = CfgEnv::<ScrollSpecId>::default() + .with_spec(spec_id) + .with_chain_id(chain_spec.chain().id()); + + // get coinbase from chain spec + let coinbase = if let Some(vault_address) = chain_spec.chain_config().fee_vault_address { + vault_address + } else { + header.beneficiary() + }; + + let block_env = BlockEnv { + number: header.number(), + beneficiary: coinbase, + timestamp: header.timestamp(), + difficulty: header.difficulty(), + prevrandao: header.mix_hash(), + gas_limit: header.gas_limit(), + basefee: header.base_fee_per_gas().unwrap_or_default(), + // EIP-4844 excess blob gas of this block, introduced in Cancun + blob_excess_gas_and_price: None, + }; + + EvmEnv { cfg_env, block_env } + } + + fn next_evm_env( + &self, + parent: &N::BlockHeader, + attributes: &Self::NextBlockEnvCtx, + ) -> Result<EvmEnv<ScrollSpecId>, Self::Error> { + // ensure we're not missing any timestamp based hardforks + let spec_id = + self.spec_id_at_timestamp_and_number(attributes.timestamp, parent.number() + 1); + + let chain_spec = self.chain_spec(); + + // configure evm env based on parent block + let cfg_env = CfgEnv::<ScrollSpecId>::default() + .with_chain_id(chain_spec.chain().id()) + .with_spec(spec_id); + + // get coinbase from chain spec + let coinbase = if let Some(vault_address) = chain_spec.chain_config().fee_vault_address { + vault_address + } else { + attributes.suggested_fee_recipient + }; + + let block_env = BlockEnv { + number: parent.number() + 1, + beneficiary: coinbase, + timestamp: attributes.timestamp, + difficulty: U256::ONE, + prevrandao: Some(B256::ZERO), + gas_limit: attributes.gas_limit, + basefee: attributes.base_fee, + blob_excess_gas_and_price: None, + }; + + Ok(EvmEnv { cfg_env, block_env }) + } + + fn context_for_block<'a>( + &self, + block: &'a SealedBlock<BlockTy<Self::Primitives>>, + ) -> ExecutionCtxFor<'a, Self> { + ScrollBlockExecutionCtx { parent_hash: block.header().parent_hash() } + } + + fn context_for_next_block( + &self, + parent: &SealedHeader<N::BlockHeader>, + _attributes: Self::NextBlockEnvCtx, + ) -> ExecutionCtxFor<'_, Self> { + ScrollBlockExecutionCtx { parent_hash: parent.hash() } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::ScrollRethReceiptBuilder; + use alloy_consensus::Header; + use reth_chainspec::{Head, NamedChain::Scroll}; + use reth_scroll_chainspec::{ScrollChainConfig, ScrollChainSpecBuilder}; + use reth_scroll_primitives::ScrollPrimitives; + use revm::primitives::B256; + use revm_primitives::Address; + + #[test] + fn test_spec_at_head() { + let config = ScrollEvmConfig::<_, ScrollPrimitives, _>::new( + ScrollChainSpecBuilder::scroll_mainnet().build(ScrollChainConfig::mainnet()).into(), + ScrollRethReceiptBuilder::default(), + ); + + // prepare all fork heads + let curie_head = &Head { number: 7096836, ..Default::default() }; + let bernouilli_head = &Head { number: 5220340, ..Default::default() }; + let pre_bernouilli_head = &Head { number: 0, ..Default::default() }; + + // check correct spec id + assert_eq!( + config.spec_id_at_timestamp_and_number(curie_head.timestamp, curie_head.number), + ScrollSpecId::CURIE + ); + assert_eq!( + config + .spec_id_at_timestamp_and_number(bernouilli_head.timestamp, bernouilli_head.number), + ScrollSpecId::BERNOULLI + ); + assert_eq!( + config.spec_id_at_timestamp_and_number( + pre_bernouilli_head.timestamp, + pre_bernouilli_head.number + ), + ScrollSpecId::SHANGHAI + ); + } + + #[test] + fn test_fill_cfg_env() { + let config = ScrollEvmConfig::<_, ScrollPrimitives, _>::new( + ScrollChainSpecBuilder::scroll_mainnet().build(ScrollChainConfig::mainnet()).into(), + ScrollRethReceiptBuilder::default(), + ); + + // curie + let curie_header = Header { number: 7096836, ..Default::default() }; + + // fill cfg env + let env = config.evm_env(&curie_header); + + // check correct cfg env + assert_eq!(env.cfg_env.chain_id, Scroll as u64); + assert_eq!(env.cfg_env.spec, ScrollSpecId::CURIE); + + // bernoulli + let bernouilli_header = Header { number: 5220340, ..Default::default() }; + + // fill cfg env + let env = config.evm_env(&bernouilli_header); + + // check correct cfg env + assert_eq!(env.cfg_env.chain_id, Scroll as u64); + assert_eq!(env.cfg_env.spec, ScrollSpecId::BERNOULLI); + + // pre-bernoulli + let pre_bernouilli_header = Header { number: 0, ..Default::default() }; + + // fill cfg env + let env = config.evm_env(&pre_bernouilli_header); + + // check correct cfg env + assert_eq!(env.cfg_env.chain_id, Scroll as u64); + assert_eq!(env.cfg_env.spec, ScrollSpecId::SHANGHAI); + } + + #[test] + fn test_fill_block_env() { + let config = ScrollEvmConfig::<_, ScrollPrimitives, _>::new( + ScrollChainSpecBuilder::scroll_mainnet().build(ScrollChainConfig::mainnet()).into(), + ScrollRethReceiptBuilder::default(), + ); + + // curie header + let header = Header { + number: 7096836, + beneficiary: Address::random(), + timestamp: 1719994277, + mix_hash: B256::random(), + base_fee_per_gas: Some(155157341), + gas_limit: 10000000, + ..Default::default() + }; + + // fill block env + let env = config.evm_env(&header); + + // verify block env correctly updated + let expected = BlockEnv { + number: header.number, + beneficiary: config.chain_spec().config.fee_vault_address.unwrap(), + timestamp: header.timestamp, + prevrandao: Some(header.mix_hash), + difficulty: U256::ZERO, + basefee: header.base_fee_per_gas.unwrap_or_default(), + gas_limit: header.gas_limit, + blob_excess_gas_and_price: None, + }; + assert_eq!(env.block_env, expected) + } + + #[test] + fn test_next_cfg_and_block_env() -> eyre::Result<()> { + let config = ScrollEvmConfig::<_, ScrollPrimitives, _>::new( + ScrollChainSpecBuilder::scroll_mainnet().build(ScrollChainConfig::mainnet()).into(), + ScrollRethReceiptBuilder::default(), + ); + + // pre curie header + let header = Header { + number: 7096835, + beneficiary: Address::random(), + timestamp: 1719994274, + mix_hash: B256::random(), + base_fee_per_gas: None, + gas_limit: 10000000, + ..Default::default() + }; + + // curie block attributes + let attributes = ScrollNextBlockEnvAttributes { + timestamp: 1719994277, + suggested_fee_recipient: Address::random(), + gas_limit: 10000000, + base_fee: 155157341, + }; + + // get next cfg env and block env + let env = config.next_evm_env(&header, &attributes)?; + let (cfg_env, block_env, spec) = (env.cfg_env.clone(), env.block_env, env.cfg_env.spec); + + // verify cfg env + assert_eq!(cfg_env.chain_id, Scroll as u64); + assert_eq!(spec, ScrollSpecId::CURIE); + + // verify block env + let expected = BlockEnv { + number: header.number + 1, + beneficiary: config.chain_spec().config.fee_vault_address.unwrap(), + timestamp: attributes.timestamp, + prevrandao: Some(B256::ZERO), + difficulty: U256::ONE, + basefee: 155157341, + gas_limit: header.gas_limit, + blob_excess_gas_and_price: None, + }; + assert_eq!(block_env, expected); + + Ok(()) + } +}
diff --git reth/crates/scroll/evm/src/execute.rs scroll-reth/crates/scroll/evm/src/execute.rs new file mode 100644 index 0000000000000000000000000000000000000000..a96452a082b87807729becd70eb1c0507eabf011 --- /dev/null +++ scroll-reth/crates/scroll/evm/src/execute.rs @@ -0,0 +1,662 @@ +//! Execution primitives for EVM. + +use crate::ScrollEvmConfig; +use core::fmt::Debug; + +use alloy_consensus::BlockHeader; +use alloy_primitives::{Address, B256}; +use reth_primitives::SealedBlock; +use reth_primitives_traits::Block; + +/// Input for block execution. +#[derive(Debug, Clone, Copy)] +pub struct ScrollBlockExecutionInput { + /// Block number. + pub number: u64, + /// Block timestamp. + pub timestamp: u64, + /// Parent block hash. + pub parent_hash: B256, + /// Block gas limit. + pub gas_limit: u64, + /// Block beneficiary. + pub beneficiary: Address, +} + +impl<B: Block> From<&SealedBlock<B>> for ScrollBlockExecutionInput { + fn from(block: &SealedBlock<B>) -> Self { + Self { + number: block.header().number(), + timestamp: block.header().timestamp(), + parent_hash: block.header().parent_hash(), + gas_limit: block.header().gas_limit(), + beneficiary: block.header().beneficiary(), + } + } +} + +/// Helper type with backwards compatible methods to obtain Scroll executor +/// providers. +pub type ScrollExecutorProvider = ScrollEvmConfig; + +#[cfg(test)] +mod tests { + use crate::{ScrollEvmConfig, ScrollRethReceiptBuilder}; + use std::{convert::Infallible, sync::Arc}; + + use alloy_consensus::{ + transaction::{Recovered, SignerRecoverable}, + Block, BlockBody, Header, SignableTransaction, Signed, TxLegacy, + }; + use alloy_eips::{ + eip7702::{constants::PER_EMPTY_ACCOUNT_COST, Authorization, SignedAuthorization}, + Encodable2718, Typed2718, + }; + use alloy_evm::{ + block::{BlockExecutionResult, BlockExecutor}, + precompiles::PrecompilesMap, + Evm, + }; + use alloy_primitives::Sealed; + use reth_chainspec::MIN_TRANSACTION_GAS; + use reth_evm::ConfigureEvm; + use reth_primitives_traits::{NodePrimitives, RecoveredBlock, SignedTransaction}; + use reth_scroll_chainspec::{ScrollChainConfig, ScrollChainSpec, ScrollChainSpecBuilder}; + use reth_scroll_primitives::{ + ScrollBlock, ScrollPrimitives, ScrollReceipt, ScrollTransactionSigned, + }; + use revm::{ + bytecode::Bytecode, + database::{ + states::{bundle_state::BundleRetention, StorageSlot}, + EmptyDBTyped, State, + }, + inspector::NoOpInspector, + primitives::{Address, TxKind, B256, U256}, + state::AccountInfo, + }; + use scroll_alloy_consensus::{ScrollTransactionReceipt, ScrollTxEnvelope, ScrollTxType}; + use scroll_alloy_evm::{ + compute_compression_ratio, + curie::{ + BLOB_SCALAR_SLOT, COMMIT_SCALAR_SLOT, CURIE_L1_GAS_PRICE_ORACLE_BYTECODE, + CURIE_L1_GAS_PRICE_ORACLE_STORAGE, IS_CURIE_SLOT, L1_BLOB_BASE_FEE_SLOT, + L1_GAS_PRICE_ORACLE_ADDRESS, + }, + feynman::{IS_FEYNMAN_SLOT, PENALTY_FACTOR_SLOT, PENALTY_THRESHOLD_SLOT}, + ScrollBlockExecutionCtx, ScrollBlockExecutor, ScrollEvm, + }; + use scroll_alloy_hardforks::ScrollHardforks; + + const BLOCK_GAS_LIMIT: u64 = 10_000_000; + const SCROLL_CHAIN_ID: u64 = 534352; + const NOT_CURIE_BLOCK_NUMBER: u64 = 7096835; + const CURIE_BLOCK_NUMBER: u64 = 7096837; + const EUCLID_V2_BLOCK_NUMBER: u64 = 14907015; + const EUCLID_V2_BLOCK_TIMESTAMP: u64 = 1745305200; + const FEYNMAN_BLOCK_TIMESTAMP: u64 = u64::MAX; + + const L1_BASE_FEE_SLOT: U256 = U256::from_limbs([1, 0, 0, 0]); + const OVER_HEAD_SLOT: U256 = U256::from_limbs([2, 0, 0, 0]); + const SCALAR_SLOT: U256 = U256::from_limbs([3, 0, 0, 0]); + + fn state() -> State<EmptyDBTyped<Infallible>> { + let db = EmptyDBTyped::<Infallible>::new(); + State::builder().with_database(db).with_bundle_update().without_state_clear().build() + } + + #[allow(clippy::type_complexity)] + fn executor<'a>( + block: &RecoveredBlock<ScrollBlock>, + state: &'a mut State<EmptyDBTyped<Infallible>>, + ) -> ScrollBlockExecutor< + ScrollEvm<&'a mut State<EmptyDBTyped<Infallible>>, NoOpInspector, PrecompilesMap>, + ScrollRethReceiptBuilder, + Arc<ScrollChainSpec>, + > { + let chain_spec = + Arc::new(ScrollChainSpecBuilder::scroll_mainnet().build(ScrollChainConfig::mainnet())); + let evm_config = ScrollEvmConfig::scroll(chain_spec.clone()); + + let evm = evm_config.evm_for_block(state, block.header()); + let receipt_builder = ScrollRethReceiptBuilder::default(); + ScrollBlockExecutor::new( + evm, + ScrollBlockExecutionCtx { parent_hash: block.parent_hash }, + chain_spec, + receipt_builder, + ) + } + + fn block( + number: u64, + timestamp: u64, + transactions: Vec<ScrollTransactionSigned>, + ) -> RecoveredBlock<<ScrollPrimitives as NodePrimitives>::Block> { + let senders = transactions.iter().map(|t| t.recover_signer().unwrap()).collect(); + RecoveredBlock::new_unhashed( + Block { + header: Header { + number, + timestamp, + gas_limit: BLOCK_GAS_LIMIT, + ..Default::default() + }, + body: BlockBody { transactions, ..Default::default() }, + }, + senders, + ) + } + + fn transaction(typ: ScrollTxType, gas_limit: u64) -> ScrollTxEnvelope { + let pk = B256::random(); + match typ { + ScrollTxType::Legacy => { + let tx = TxLegacy { + to: TxKind::Call(Address::ZERO), + chain_id: Some(SCROLL_CHAIN_ID), + gas_limit, + ..Default::default() + }; + let signature = reth_primitives::sign_message(pk, tx.signature_hash()).unwrap(); + ScrollTxEnvelope::Legacy(Signed::new_unhashed(tx, signature)) + } + ScrollTxType::Eip2930 => { + let tx = alloy_consensus::TxEip2930 { + to: TxKind::Call(Address::ZERO), + chain_id: SCROLL_CHAIN_ID, + gas_limit, + ..Default::default() + }; + let signature = reth_primitives::sign_message(pk, tx.signature_hash()).unwrap(); + ScrollTxEnvelope::Eip2930(Signed::new_unhashed(tx, signature)) + } + ScrollTxType::Eip1559 => { + let tx = alloy_consensus::TxEip1559 { + to: TxKind::Call(Address::ZERO), + chain_id: SCROLL_CHAIN_ID, + gas_limit, + ..Default::default() + }; + let signature = reth_primitives::sign_message(pk, tx.signature_hash()).unwrap(); + ScrollTxEnvelope::Eip1559(Signed::new_unhashed(tx, signature)) + } + ScrollTxType::Eip7702 => { + let authorization = Authorization { + chain_id: Default::default(), + address: Address::random(), + nonce: 0, + }; + let signature = + reth_primitives::sign_message(B256::random(), authorization.signature_hash()) + .unwrap(); + + let tx = alloy_consensus::TxEip7702 { + to: Address::ZERO, + chain_id: SCROLL_CHAIN_ID, + gas_limit: gas_limit + PER_EMPTY_ACCOUNT_COST, + authorization_list: vec![SignedAuthorization::new_unchecked( + authorization, + signature.v() as u8, + signature.r(), + signature.s(), + )], + ..Default::default() + }; + let signature = reth_primitives::sign_message(pk, tx.signature_hash()).unwrap(); + ScrollTxEnvelope::Eip7702(Signed::new_unhashed(tx, signature)) + } + ScrollTxType::L1Message => { + ScrollTxEnvelope::L1Message(Sealed::new(scroll_alloy_consensus::TxL1Message { + sender: Address::random(), + to: Address::ZERO, + gas_limit, + ..Default::default() + })) + } + } + } + + fn execute_block( + transactions: Vec<ScrollTxEnvelope>, + block_number: u64, + block_timestamp: u64, + compression_ratios: Option<Vec<U256>>, + ) -> eyre::Result<BlockExecutionResult<ScrollReceipt>> { + let block = block(block_number, block_timestamp, transactions); + + let mut state = state(); + let mut strategy = executor(&block, &mut state); + + // determine l1 gas oracle storage + let l1_gas_oracle_storage = + if strategy.spec().is_feynman_active_at_timestamp(block_timestamp) { + vec![ + (L1_BLOB_BASE_FEE_SLOT, U256::from(1000)), + (OVER_HEAD_SLOT, U256::from(1000)), + (SCALAR_SLOT, U256::from(1000)), + (L1_BLOB_BASE_FEE_SLOT, U256::from(10000)), + (COMMIT_SCALAR_SLOT, U256::from(1000)), + (BLOB_SCALAR_SLOT, U256::from(10000)), + (IS_CURIE_SLOT, U256::from(1)), + (PENALTY_THRESHOLD_SLOT, U256::from(1_000_000_000u64)), + (PENALTY_FACTOR_SLOT, U256::from(1_000_000_000u64)), + (IS_FEYNMAN_SLOT, U256::from(1)), + ] + } else if strategy.spec().is_curie_active_at_block(block_number) { + vec![ + (L1_BLOB_BASE_FEE_SLOT, U256::from(1000)), + (OVER_HEAD_SLOT, U256::from(1000)), + (SCALAR_SLOT, U256::from(1000)), + (L1_BLOB_BASE_FEE_SLOT, U256::from(10000)), + (COMMIT_SCALAR_SLOT, U256::from(1000)), + (BLOB_SCALAR_SLOT, U256::from(10000)), + (IS_CURIE_SLOT, U256::from(1)), + ] + } else { + vec![ + (L1_BASE_FEE_SLOT, U256::from(1000)), + (OVER_HEAD_SLOT, U256::from(1000)), + (SCALAR_SLOT, U256::from(1000)), + ] + } + .into_iter() + .collect(); + + // load accounts in state + strategy.evm_mut().db_mut().insert_account_with_storage( + L1_GAS_PRICE_ORACLE_ADDRESS, + Default::default(), + l1_gas_oracle_storage, + ); + for add in block.senders() { + strategy + .evm_mut() + .db_mut() + .insert_account(*add, AccountInfo { balance: U256::MAX, ..Default::default() }); + } + + if let Some(compression_ratios) = compression_ratios { + Ok(strategy.execute_block_with_compression_cache( + block.transactions_recovered(), + compression_ratios, + )?) + } else { + Ok(strategy.execute_block(block.transactions_recovered())?) + } + } + + fn execute_transaction( + tx_type: ScrollTxType, + block_number: u64, + block_timestamp: u64, + expected_l1_fee: U256, + expected_error: Option<&str>, + ) -> eyre::Result<()> { + // prepare transaction + let transaction = transaction(tx_type, MIN_TRANSACTION_GAS); + let block = block(block_number, block_timestamp, vec![transaction.clone()]); + + // init strategy + let mut state = state(); + let mut strategy = executor(&block, &mut state); + + // determine l1 gas oracle storage + let l1_gas_oracle_storage = + if strategy.spec().is_feynman_active_at_timestamp(block_timestamp) { + vec![ + (L1_BLOB_BASE_FEE_SLOT, U256::from(1000)), + (OVER_HEAD_SLOT, U256::from(1000)), + (SCALAR_SLOT, U256::from(1000)), + (L1_BLOB_BASE_FEE_SLOT, U256::from(10000)), + (COMMIT_SCALAR_SLOT, U256::from(1000)), + (BLOB_SCALAR_SLOT, U256::from(10000)), + (IS_CURIE_SLOT, U256::from(1)), + (PENALTY_THRESHOLD_SLOT, U256::from(2_000_000_000u64)), // penalty if <2x + (PENALTY_FACTOR_SLOT, U256::from(10_000_000_000u64)), // 10x penalty + (IS_FEYNMAN_SLOT, U256::from(1)), + ] + } else if strategy.spec().is_curie_active_at_block(block_number) { + vec![ + (L1_BLOB_BASE_FEE_SLOT, U256::from(1000)), + (OVER_HEAD_SLOT, U256::from(1000)), + (SCALAR_SLOT, U256::from(1000)), + (L1_BLOB_BASE_FEE_SLOT, U256::from(10000)), + (COMMIT_SCALAR_SLOT, U256::from(1000)), + (BLOB_SCALAR_SLOT, U256::from(10000)), + (IS_CURIE_SLOT, U256::from(1)), + ] + } else { + vec![ + (L1_BASE_FEE_SLOT, U256::from(1000)), + (OVER_HEAD_SLOT, U256::from(1000)), + (SCALAR_SLOT, U256::from(1000)), + ] + } + .into_iter() + .collect(); + + // load accounts in state + strategy.evm_mut().db_mut().insert_account_with_storage( + L1_GAS_PRICE_ORACLE_ADDRESS, + Default::default(), + l1_gas_oracle_storage, + ); + for add in block.senders() { + strategy + .evm_mut() + .db_mut() + .insert_account(*add, AccountInfo { balance: U256::MAX, ..Default::default() }); + } + + // execute and verify output + let sender = transaction.try_recover()?; + let tx = Recovered::new_unchecked(transaction, sender); + let res = strategy.execute_transaction(&tx); + + // check for error or execution outcome + let output = strategy.apply_post_execution_changes()?; + if let Some(error) = expected_error { + assert!(res.unwrap_err().to_string().contains(error)); + } else { + let BlockExecutionResult { receipts, .. } = output; + let gas_used = + MIN_TRANSACTION_GAS + if tx_type.is_eip7702() { PER_EMPTY_ACCOUNT_COST } else { 0 }; + let inner = alloy_consensus::Receipt { + cumulative_gas_used: gas_used, + status: true.into(), + ..Default::default() + }; + let into_scroll_receipt = |inner: alloy_consensus::Receipt| { + ScrollTransactionReceipt::new(inner, expected_l1_fee) + }; + let receipt = match tx_type { + ScrollTxType::Legacy => ScrollReceipt::Legacy(into_scroll_receipt(inner)), + ScrollTxType::Eip2930 => ScrollReceipt::Eip2930(into_scroll_receipt(inner)), + ScrollTxType::Eip1559 => ScrollReceipt::Eip1559(into_scroll_receipt(inner)), + ScrollTxType::Eip7702 => ScrollReceipt::Eip7702(into_scroll_receipt(inner)), + ScrollTxType::L1Message => ScrollReceipt::L1Message(inner), + }; + let expected = vec![receipt]; + + assert_eq!(receipts, expected); + } + + Ok(()) + } + + #[test] + fn test_apply_pre_execution_changes_curie_block() -> eyre::Result<()> { + // init curie transition block + let curie_block = block(CURIE_BLOCK_NUMBER - 1, 0, vec![]); + + // init strategy + let mut state = state(); + let mut strategy = executor(&curie_block, &mut state); + + // apply pre execution change + strategy.apply_pre_execution_changes()?; + + // take bundle + let state = strategy.evm_mut().db_mut(); + state.merge_transitions(BundleRetention::Reverts); + let bundle = state.take_bundle(); + + // assert oracle contract contains updated bytecode + let oracle = bundle.state.get(&L1_GAS_PRICE_ORACLE_ADDRESS).unwrap().clone(); + let bytecode = Bytecode::new_raw(CURIE_L1_GAS_PRICE_ORACLE_BYTECODE); + assert_eq!(oracle.info.unwrap().code.unwrap(), bytecode); + + // check oracle contract contains storage changeset + let mut storage = oracle.storage.into_iter().collect::<Vec<(U256, StorageSlot)>>(); + storage.sort_by(|(a, _), (b, _)| a.cmp(b)); + for (got, expected) in storage.into_iter().zip(CURIE_L1_GAS_PRICE_ORACLE_STORAGE) { + assert_eq!(got.0, expected.0); + assert_eq!(got.1, StorageSlot { present_value: expected.1, ..Default::default() }); + } + + Ok(()) + } + + #[test] + fn test_apply_pre_execution_changes_not_curie_block() -> eyre::Result<()> { + // init block + let not_curie_block = block(NOT_CURIE_BLOCK_NUMBER, 0, vec![]); + + // init strategy + let mut state = state(); + let mut strategy = executor(&not_curie_block, &mut state); + + // apply pre execution change + strategy.apply_pre_execution_changes()?; + + // take bundle + let state = strategy.evm_mut().db_mut(); + state.merge_transitions(BundleRetention::Reverts); + let bundle = state.take_bundle(); + + // assert oracle contract is empty + let oracle = bundle.state.get(&L1_GAS_PRICE_ORACLE_ADDRESS); + assert!(oracle.is_none()); + + Ok(()) + } + + #[test] + fn test_execute_transactions_exceeds_block_gas_limit() -> eyre::Result<()> { + // prepare transaction exceeding block gas limit + let transaction = transaction(ScrollTxType::Legacy, BLOCK_GAS_LIMIT + 1); + let block = block(7096837, 0, vec![transaction.clone()]); + + // init strategy + let mut state = state(); + let mut strategy = executor(&block, &mut state); + + // execute and verify error + let sender = transaction.try_recover()?; + let tx = Recovered::new_unchecked(transaction, sender); + let res = strategy.execute_transaction(&tx); + assert_eq!( + res.unwrap_err().to_string(), + "transaction gas limit 10000001 is more than blocks available gas 10000000" + ); + + Ok(()) + } + + #[test] + fn test_execute_transactions_l1_message() -> eyre::Result<()> { + // Execute l1 message on curie block + let expected_l1_fee = U256::ZERO; + execute_transaction(ScrollTxType::L1Message, CURIE_BLOCK_NUMBER, 0, expected_l1_fee, None)?; + Ok(()) + } + + #[test] + fn test_execute_transaction_l1_message_feynman_fork() -> eyre::Result<()> { + // Execute L1 message on feynman block + let expected_l1_fee = U256::ZERO; + execute_transaction( + ScrollTxType::L1Message, + CURIE_BLOCK_NUMBER + 1, + FEYNMAN_BLOCK_TIMESTAMP, + expected_l1_fee, + None, + )?; + Ok(()) + } + + #[test] + fn test_execute_transactions_legacy_curie_fork() -> eyre::Result<()> { + // Execute legacy transaction on curie block + let expected_l1_fee = U256::from(10); + execute_transaction(ScrollTxType::Legacy, CURIE_BLOCK_NUMBER, 0, expected_l1_fee, None)?; + Ok(()) + } + + #[test] + fn test_execute_transactions_legacy_not_curie_fork() -> eyre::Result<()> { + // Execute legacy before curie block + let expected_l1_fee = U256::from(2); + execute_transaction( + ScrollTxType::Legacy, + NOT_CURIE_BLOCK_NUMBER, + 0, + expected_l1_fee, + None, + )?; + Ok(()) + } + + #[test] + fn test_execute_transactions_legacy_feynman_fork() -> eyre::Result<()> { + // Execute legacy transaction on feynman block + let expected_l1_fee = U256::from(100); + execute_transaction( + ScrollTxType::Legacy, + CURIE_BLOCK_NUMBER + 1, + FEYNMAN_BLOCK_TIMESTAMP, + expected_l1_fee, + None, + )?; + Ok(()) + } + + #[test] + fn test_execute_transactions_eip2930_curie_fork() -> eyre::Result<()> { + // Execute eip2930 transaction on curie block + let expected_l1_fee = U256::from(10); + execute_transaction(ScrollTxType::Eip2930, CURIE_BLOCK_NUMBER, 0, expected_l1_fee, None)?; + Ok(()) + } + + #[test] + fn test_execute_transactions_eip2930_not_curie_fork() -> eyre::Result<()> { + // Execute eip2930 transaction before curie block + execute_transaction( + ScrollTxType::Eip2930, + NOT_CURIE_BLOCK_NUMBER, + 0, + U256::ZERO, + Some("Eip2930 is not supported"), + )?; + Ok(()) + } + + #[test] + fn test_execute_transactions_eip2930_feynman_fork() -> eyre::Result<()> { + // Execute eip2930 transaction on feynman block + let expected_l1_fee = U256::from(103); + execute_transaction( + ScrollTxType::Eip2930, + CURIE_BLOCK_NUMBER + 1, + FEYNMAN_BLOCK_TIMESTAMP, + expected_l1_fee, + None, + )?; + Ok(()) + } + + #[test] + fn test_execute_transactions_eip1559_curie_fork() -> eyre::Result<()> { + // Execute eip1559 transaction on curie block + let expected_l1_fee = U256::from(10); + execute_transaction(ScrollTxType::Eip1559, CURIE_BLOCK_NUMBER, 0, expected_l1_fee, None)?; + Ok(()) + } + + #[test] + fn test_execute_transactions_eip1559_not_curie_fork() -> eyre::Result<()> { + // Execute eip1559 transaction before curie block + execute_transaction( + ScrollTxType::Eip1559, + NOT_CURIE_BLOCK_NUMBER, + 0, + U256::ZERO, + Some("Eip1559 is not supported"), + )?; + Ok(()) + } + + #[test] + fn test_execute_transaction_eip1559_feynman_fork() -> eyre::Result<()> { + // Execute eip1559 transaction on feynman block + let expected_l1_fee = U256::from(104); + execute_transaction( + ScrollTxType::Eip1559, + CURIE_BLOCK_NUMBER + 1, + FEYNMAN_BLOCK_TIMESTAMP, + expected_l1_fee, + None, + )?; + Ok(()) + } + + #[test] + fn test_execute_transactions_eip7702_euclid_v2_fork() -> eyre::Result<()> { + // Execute eip7702 transaction on euclid v2 block. + let expected_l1_fee = U256::from(19); + execute_transaction( + ScrollTxType::Eip7702, + EUCLID_V2_BLOCK_NUMBER, + EUCLID_V2_BLOCK_TIMESTAMP, + expected_l1_fee, + None, + )?; + Ok(()) + } + + #[test] + fn test_execute_transactions_eip7702_not_euclid_v2_fork() -> eyre::Result<()> { + // Execute eip7702 transaction before euclid v2 block + execute_transaction( + ScrollTxType::Eip7702, + EUCLID_V2_BLOCK_NUMBER - 1, + EUCLID_V2_BLOCK_TIMESTAMP - 1, + U256::ZERO, + Some("Eip7702 is not supported"), + )?; + Ok(()) + } + + #[test] + fn test_execute_transactions_eip7702_feynman_fork() -> eyre::Result<()> { + // Execute eip7702 transaction on feynman block + let expected_l1_fee = U256::from(198); + execute_transaction( + ScrollTxType::Eip7702, + CURIE_BLOCK_NUMBER + 1, + FEYNMAN_BLOCK_TIMESTAMP, + expected_l1_fee, + None, + )?; + Ok(()) + } + + #[test] + fn test_consistency_with_provided_compression_ratio() -> eyre::Result<()> { + let transactions = vec![ + transaction(ScrollTxType::Legacy, MIN_TRANSACTION_GAS), + transaction(ScrollTxType::Eip2930, MIN_TRANSACTION_GAS), + transaction(ScrollTxType::Eip1559, MIN_TRANSACTION_GAS), + transaction(ScrollTxType::Eip7702, MIN_TRANSACTION_GAS), + ]; + let compression_ratios = transactions + .iter() + .map(|tx| { + let encoded = tx.encoded_2718(); + compute_compression_ratio(&encoded) + }) + .collect::<Vec<_>>(); + let with_compression_ratios = execute_block( + transactions.clone(), + CURIE_BLOCK_NUMBER + 1, + FEYNMAN_BLOCK_TIMESTAMP, + Some(compression_ratios), + )?; + let without_compression_ratios = + execute_block(transactions, CURIE_BLOCK_NUMBER + 1, FEYNMAN_BLOCK_TIMESTAMP, None)?; + assert_eq!(without_compression_ratios, with_compression_ratios); + Ok(()) + } +}
diff --git reth/crates/scroll/evm/src/l1.rs scroll-reth/crates/scroll/evm/src/l1.rs new file mode 100644 index 0000000000000000000000000000000000000000..2ad06a3839c666b2cb90f11b948401b472e179f5 --- /dev/null +++ scroll-reth/crates/scroll/evm/src/l1.rs @@ -0,0 +1,46 @@ +use super::spec_id_at_timestamp_and_number; +use reth_evm::block::BlockExecutionError; +use revm_primitives::U256; +use revm_scroll::l1block::L1BlockInfo; +use scroll_alloy_hardforks::ScrollHardforks; + +/// An extension trait for [`L1BlockInfo`] that allows us to calculate the L1 cost of a transaction +/// based off of the chain spec's activated hardfork. +pub trait RethL1BlockInfo { + /// Forwards an L1 transaction calculation to revm and returns the gas cost. + /// + /// ### Takes + /// - `chain_spec`: The chain spec for the node. + /// - `timestamp`: The timestamp of the current block. + /// - `block`: The block number of the current block. + /// - `input`: The calldata of the transaction. + /// - `is_l1_message`: Whether or not the transaction is a l1 message. + fn l1_tx_data_fee( + &mut self, + chain_spec: impl ScrollHardforks, + timestamp: u64, + block: u64, + input: &[u8], + compression_ratio: Option<U256>, + is_l1_message: bool, + ) -> Result<U256, BlockExecutionError>; +} + +impl RethL1BlockInfo for L1BlockInfo { + fn l1_tx_data_fee( + &mut self, + chain_spec: impl ScrollHardforks, + timestamp: u64, + block_number: u64, + input: &[u8], + compression_ratio: Option<U256>, + is_l1_message: bool, + ) -> Result<U256, BlockExecutionError> { + if is_l1_message { + return Ok(U256::ZERO); + } + + let spec_id = spec_id_at_timestamp_and_number(timestamp, block_number, chain_spec); + Ok(self.calculate_tx_l1_cost(input, spec_id, compression_ratio)) + } +}
diff --git reth/crates/scroll/evm/src/lib.rs scroll-reth/crates/scroll/evm/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..ab7c9fdb7719eca586221e546a3ffc0f45bdbb0f --- /dev/null +++ scroll-reth/crates/scroll/evm/src/lib.rs @@ -0,0 +1,149 @@ +//! Scroll evm execution implementation. + +#![cfg_attr(not(feature = "std"), no_std)] + +extern crate alloc; + +mod build; + +mod config; + +pub use execute::{ScrollBlockExecutionInput, ScrollExecutorProvider}; +mod execute; + +mod l1; +pub use l1::RethL1BlockInfo; + +pub use receipt::ScrollRethReceiptBuilder; +mod receipt; + +use crate::build::ScrollBlockAssembler; +use alloc::sync::Arc; + +use alloy_primitives::{Address, BlockNumber, BlockTimestamp}; +use reth_primitives_traits::NodePrimitives; +use reth_scroll_chainspec::ScrollChainSpec; +use reth_scroll_primitives::ScrollPrimitives; +use revm_scroll::ScrollSpecId; +pub use scroll_alloy_evm::{ + compute_compression_ratio, ScrollBlockExecutorFactory, ScrollEvmFactory, + ScrollTxCompressionRatios, +}; +pub use scroll_alloy_hardforks::{ScrollHardfork, ScrollHardforks}; + +/// Scroll EVM configuration. +#[derive(Debug)] +pub struct ScrollEvmConfig< + ChainSpec = ScrollChainSpec, + N: NodePrimitives = ScrollPrimitives, + R = ScrollRethReceiptBuilder, +> { + /// Executor factory. + executor_factory: ScrollBlockExecutorFactory<R, Arc<ChainSpec>>, + /// Block assembler. + block_assembler: ScrollBlockAssembler<ChainSpec>, + /// Node primitives marker. + _pd: core::marker::PhantomData<N>, +} + +impl<ChainSpec: ScrollHardforks> ScrollEvmConfig<ChainSpec> { + /// Creates a new [`ScrollEvmConfig`] with the given chain spec for Scroll chains. + pub fn scroll(chain_spec: Arc<ChainSpec>) -> Self { + Self::new(chain_spec, ScrollRethReceiptBuilder::default()) + } +} + +impl<ChainSpec, N: NodePrimitives, R: Clone> Clone for ScrollEvmConfig<ChainSpec, N, R> { + fn clone(&self) -> Self { + Self { + executor_factory: self.executor_factory.clone(), + block_assembler: self.block_assembler.clone(), + _pd: self._pd, + } + } +} + +impl<ChainSpec: ScrollHardforks, N: NodePrimitives, R> ScrollEvmConfig<ChainSpec, N, R> { + /// Creates a new [`ScrollEvmConfig`] with the given chain spec. + pub fn new(chain_spec: Arc<ChainSpec>, receipt_builder: R) -> Self { + Self { + block_assembler: ScrollBlockAssembler::new(chain_spec.clone()), + executor_factory: ScrollBlockExecutorFactory::new( + receipt_builder, + chain_spec, + ScrollEvmFactory::default(), + ), + _pd: core::marker::PhantomData, + } + } + + /// Returns the chain spec associated with this configuration. + pub const fn chain_spec(&self) -> &Arc<ChainSpec> { + self.executor_factory.spec() + } + + /// Returns the spec id at the given head. + pub fn spec_id_at_timestamp_and_number( + &self, + timestamp: BlockTimestamp, + number: BlockNumber, + ) -> ScrollSpecId { + let chain_spec = self.chain_spec(); + spec_id_at_timestamp_and_number(timestamp, number, chain_spec) + } +} + +/// Returns the spec id at the given timestamp and block number for the provided chain spec. +pub fn spec_id_at_timestamp_and_number( + timestamp: u64, + number: u64, + chain_spec: impl ScrollHardforks, +) -> ScrollSpecId { + if chain_spec + .scroll_fork_activation(ScrollHardfork::Feynman) + .active_at_timestamp_or_number(timestamp, number) + { + ScrollSpecId::FEYNMAN + } else if chain_spec + .scroll_fork_activation(ScrollHardfork::EuclidV2) + .active_at_timestamp_or_number(timestamp, number) + { + ScrollSpecId::EUCLID + } else if chain_spec + .scroll_fork_activation(ScrollHardfork::Euclid) + .active_at_timestamp_or_number(timestamp, number) || + chain_spec + .scroll_fork_activation(ScrollHardfork::DarwinV2) + .active_at_timestamp_or_number(timestamp, number) || + chain_spec + .scroll_fork_activation(ScrollHardfork::Darwin) + .active_at_timestamp_or_number(timestamp, number) + { + ScrollSpecId::DARWIN + } else if chain_spec + .scroll_fork_activation(ScrollHardfork::Curie) + .active_at_timestamp_or_number(timestamp, number) + { + ScrollSpecId::CURIE + } else if chain_spec + .scroll_fork_activation(ScrollHardfork::Bernoulli) + .active_at_timestamp_or_number(timestamp, number) + { + ScrollSpecId::BERNOULLI + } else { + ScrollSpecId::SHANGHAI + } +} + +/// The attributes for the next block env. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ScrollNextBlockEnvAttributes { + /// The timestamp of the next block. + pub timestamp: u64, + /// The suggested fee recipient for the next block. + pub suggested_fee_recipient: Address, + /// Block gas limit. + pub gas_limit: u64, + /// The base fee of the next block. + pub base_fee: u64, +}
diff --git reth/crates/scroll/evm/src/receipt.rs scroll-reth/crates/scroll/evm/src/receipt.rs new file mode 100644 index 0000000000000000000000000000000000000000..062ebc99e096d5c7df330f1ee1040769febf37f5 --- /dev/null +++ scroll-reth/crates/scroll/evm/src/receipt.rs @@ -0,0 +1,37 @@ +use alloy_consensus::{Eip658Value, Receipt}; +use alloy_evm::Evm; +use reth_scroll_primitives::{ScrollReceipt, ScrollTransactionSigned}; +use scroll_alloy_consensus::{ScrollTransactionReceipt, ScrollTxType}; +use scroll_alloy_evm::{ReceiptBuilderCtx, ScrollReceiptBuilder}; + +/// Basic builder for receipts of [`ScrollTransactionSigned`]. +#[derive(Debug, Default, Clone, Copy)] +#[non_exhaustive] +pub struct ScrollRethReceiptBuilder; + +impl ScrollReceiptBuilder for ScrollRethReceiptBuilder { + type Transaction = ScrollTransactionSigned; + type Receipt = ScrollReceipt; + + fn build_receipt<E: Evm>( + &self, + ctx: ReceiptBuilderCtx<'_, ScrollTransactionSigned, E>, + ) -> Self::Receipt { + let inner = Receipt { + // Success flag was added in `EIP-658: Embedding transaction status code in + // receipts`. + status: Eip658Value::Eip658(ctx.result.is_success()), + cumulative_gas_used: ctx.cumulative_gas_used, + logs: ctx.result.into_logs(), + }; + let into_scroll_receipt = |inner: Receipt| ScrollTransactionReceipt::new(inner, ctx.l1_fee); + + match ctx.tx.tx_type() { + ScrollTxType::Legacy => ScrollReceipt::Legacy(into_scroll_receipt(inner)), + ScrollTxType::Eip2930 => ScrollReceipt::Eip2930(into_scroll_receipt(inner)), + ScrollTxType::Eip1559 => ScrollReceipt::Eip1559(into_scroll_receipt(inner)), + ScrollTxType::Eip7702 => ScrollReceipt::Eip7702(into_scroll_receipt(inner)), + ScrollTxType::L1Message => ScrollReceipt::L1Message(inner), + } + } +}
diff --git reth/crates/scroll/hardforks/Cargo.toml scroll-reth/crates/scroll/hardforks/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..bd69a2a489a6663f42d2a38975dba913856252f5 --- /dev/null +++ scroll-reth/crates/scroll/hardforks/Cargo.toml @@ -0,0 +1,46 @@ +[package] +name = "reth-scroll-forks" +version.workspace = true +edition.workspace = true +rust-version.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +description = "Scroll hardforks used in reth" + +[lints] +workspace = true + +[dependencies] +# reth +reth-ethereum-forks.workspace = true + +# ethereum +scroll-alloy-hardforks.workspace = true +alloy-chains.workspace = true +alloy-primitives.workspace = true + +# io +serde = { workspace = true, optional = true } + +# misc +auto_impl.workspace = true +once_cell.workspace = true + +[features] +default = ["std"] +std = [ + "alloy-primitives/std", + "once_cell/std", + "serde?/std", + "alloy-chains/std", + "reth-ethereum-forks/std", + "scroll-alloy-hardforks/std", +] +serde = [ + "dep:serde", + "alloy-chains/serde", + "alloy-primitives/serde", + "reth-ethereum-forks/serde", + "scroll-alloy-hardforks/serde", +]
diff --git reth/crates/scroll/hardforks/docs/hardforks.md scroll-reth/crates/scroll/hardforks/docs/hardforks.md new file mode 100644 index 0000000000000000000000000000000000000000..8f00794c94799714b14719be3292d93e159bbd53 --- /dev/null +++ scroll-reth/crates/scroll/hardforks/docs/hardforks.md @@ -0,0 +1,433 @@ +--- +section: technology +date: Last Modified +title: "Scroll Upgrades" +lang: "en" +permalink: "technology/overview/scroll-upgrades" +--- + +As the team continues to progress on Scroll's roadmap, we will be upgrading the Scroll network to include new features +and improvements. + +The following contracts are used to initiate upgrades and execute upgrades after the two-week timelock period: + +| Contract | Network | Address | +|--------------------|----------|---------------------------------------------------------------------------------------------------------------------------| +| L1 Scroll Multisig | Ethereum | [`0xEfc9D1096fb65c832207E5e7F13C2D1102244dbe`](https://etherscan.io/address/0xEfc9D1096fb65c832207E5e7F13C2D1102244dbe) | +| L1 Timelock | Ethereum | [`0x1A658B88fD0a3c82fa1a0609fCDbD32e7dd4aB9C`](https://etherscan.io/address/0x1A658B88fD0a3c82fa1a0609fCDbD32e7dd4aB9C) | +| L2 Scroll Multisig | Scroll | [`0xEfc9D1096fb65c832207E5e7F13C2D1102244dbe`](https://scrollscan.com/address/0xEfc9D1096fb65c832207E5e7F13C2D1102244dbe) | +| L2 Timelock | Scroll | [`0xf6069DB81239E5194bb53f83aF564d282357bc99`](https://scrollscan.com/address/0xf6069DB81239E5194bb53f83aF564d282357bc99) | + +You can join our [Telegram channel for technical updates](https://t.me/scroll_tech_updates), which includes future +upgrade announcements and on-chain operation events. + +## `DarwinV2` Upgrade + +### Overview + +During internal testing, we identified that blocks may not always be compressible under certain conditions, which leads +to unprovable chunks and batches. +To fix this issue, a minor upgrade has been conducted so that uncompressed blobs will be enabled when this special case +is detected. + +### Timeline + +As this is a security related patch, we bypassed the 7-day timelock mechanism. + +- **Scroll Sepolia**: August 28th, 2024 +- **Scroll Mainnet**: September 2nd, 2024 + +### Compatibility + +#### Sequencer and Follower Nodes (l2geth) + +The new node version is `v5.7.0`. See +the [release notes](https://github.com/scroll-tech/go-ethereum/releases/tag/scroll-v5.7.0) for more information. + +This upgrade does not change Scroll's state transition function, so it is backward compatible. However, the format of +the batch data committed to Ethereum changes. As a result, nodes that enabled rollup verification (`--rollup.verify`) +must upgrade to be able to follow the chain. + +#### Dapps and Indexers + +A change has been implemented to Scroll Mainnet to enhance sequencer throughput, which adjusted the maximum reorg depth +to 17 blocks. Previously, the system performed thorough capacity checks within the signer thread to determine whether +transactions exceed the circuit limit. While this ensures that all transactions within a block are compliant, it also +requires additional CPU resources. +We introduced a new circuit capacity checking scheme on Mainnet. The sequencer thread now will continue to perform +capacity checks, but in a more approximate manner. In parallel, 16 worker threads will accurately verify the capacity of +previous blocks. As a result, a reorg could occur with a maximum depth of 17 blocks, although the likelihood of this is +low. + +For indexers, the `BatchHeader` version has been upgraded to 4. This is backward compatible (the only exception is for +developers decoding the blob payload, which has changed slightly). + +## Darwin Upgrade + +### Overview + +This upgrade will reduce gas fees by 34% by using a single aggregated proof for multiple batches, eliminating the need +to finalize each batch individually. + +- Darwin uses a new [V3 batch codec](https://github.com/scroll-tech/da-codec/tree/main/encoding/codecv3). +- In addition to the previous notions of `chunk` and `batch`, we have introduced a new concept called `bundle`. + - `Chunk`: A unit of zkEVM proving, consisting of a list of L2 blocks. + - `Batch`: A collection of chunks encoded into one EIP-4844 blob, serving as the unit of Data Availability. + - `Bundle`: A series of batches that functions as the unit of finalization. + + The main difference compared to Curie is that Scroll will now finalize multiple batches using a single aggregated + bundle proof. + +- The on-chain bundle proof verifier uses a new public input layout. + +### Timeline + +- **Scroll Sepolia** + - Network Upgrade: August 14th, 2024 +- **Scroll Mainnet** + - Upgrade Initiation: August 5th, 2024 + - Timelock Completion & Upgrade: August 21st, 2024 + +### Technical Details + +#### Contract Changes + +*Note: Since the previous Curie upgrade, we have migrated the Scroll contracts to a new repo +at [scroll-contracts](https://github.com/scroll-tech/scroll-contracts).* + +The code changes for this upgrade are implemented in [this PR](https://github.com/scroll-tech/scroll-contracts/pull/4). +The key changes are as follows: + +- We have introduced a new `BatchHeaderV3Codec`. +- We have changed how messages are processed in the `L1MessageQueue` contract. Prior to Darwin, we would process + messages when a batch is finalized. After Darwin, most of this processing is moved to the commit step. +- We have introduced a new public input format for bundle proofs. This is implemented in a new contract + `IZkEvmVerifierV2`, which is in turn added to `MultipleVersionRollupVerifier`. +- In the `ScrollChain` contract `version=3` batches will now be committed through a new function called + `commitBatchWithBlobProof`. Bundles will be finalized using a new function called `finalizeBundleWithProof`. + +See the contract [release notes](https://github.com/scroll-tech/scroll-contracts/releases/tag/v1.0.0) for more +information. + +#### Node Changes + +The new node version is `v5.6.0`. See +the [release notes](https://github.com/scroll-tech/go-ethereum/releases/tag/scroll-v5.6.0) for more information. + +The main changes are: + +- Implementation of timestamp-based hard forks. +- Processing V3 batch codec in rollup-verifier. + +#### zkEVM circuit changes + +The new version of zkevm circuits is `v0.12.0`. +See [here](https://github.com/scroll-tech/zkevm-circuits/releases/tag/v0.12.0) for the release log. + +We have introduced a `RecursionCircuit` that will bundle multiple sequential batches by recursively aggregating the +SNARKs from the `BatchCircuit` (previously `AggregationCircuit`). The previously 5 layer proving system is now 7 layers +as we introduce: + +- 6th Layer (layer5): `RecursionCircuit` that recursively aggregates `BatchCircuit` SNARKs. +- 7th Layer (layer6): `CompressionCircuit` that compresses the `RecursionCircuit` SNARK and produce an EVM-verifiable + validity proof. + +The public input to the `BatchCircuit` is now context-aware of the “previous” `batch`, which allows us to implement the +recursion scheme we adopted ( +described [here](https://scrollzkp.notion.site/Upgrade-4-Darwin-Documentation-05a3ecb59e9d4f288254701f8c888173) +in-depth). + +#### Audits + +- TrailofBits: coming soon! + +### Compatibility + +#### Sequencer and Follower Nodes (l2geth) + +This upgrade does not alter the state transition function and is therefore backward-compatible. However, we strongly +recommend node operators to upgrade to [v5.6.0](https://github.com/scroll-tech/go-ethereum/releases/tag/scroll-v5.6.0). + +#### Dapps and Indexers + +There are some major changes to how we commit and finalize batches after Darwin. + +- Batches will be encoded using the + new [V3 batch codec](https://github.com/scroll-tech/da-codec/tree/main/encoding/codecv3). This version adds two new + fields: + 1. `lastBlockTimestamp` (the timestamp of the last block in this batch). + 2. `blobDataProof` (the KZG challenge point evaluation proof). + + This version removes `skippedL1MessageBitmap`. There will be no changes to how the blob data is encoded and + compressed. +- Batches will be committed using the `commitBatchWithBlobProof` function (instead of the previous `commitBatch`). + + New function signature: + + ```solidity + function commitBatchWithBlobProof(uint8 _version, bytes calldata _parentBatchHeader, bytes[] memory _chunks, bytes calldata _skippedL1MessageBitmap, bytes calldata _blobDataProof) + ``` + +- Batches will be finalized using the `finalizeBundleWithProof` function (instead of the previous + `finalizeBatchWithProof4844`). + + New function signature: + + ```solidity + function finalizeBundleWithProof(bytes calldata _batchHeader, bytes32 _postStateRoot, bytes32 _withdrawRoot, bytes calldata _aggrProof) + ``` + +- The semantics of the `FinalizeBatch` event will change: It will now mean that all batches between the last finalized + batch and the event’s `_batchIndex` have been finalized. The event’s stateRoot and withdrawRoot values belong to the + last finalized batch in the bundle. Finalized roots for intermediate batches are no longer available. + + The semantics of the `CommitBatch` and `RevertBatch` events will not change. + +Recommendations: + +- Indexers that decode committed batch data should be adjusted to use the new codec and the new function signature. +- Indexers that track batch finalization status should be adjusted to consider the new event semantics. + +## Curie Upgrade + +### Overview + +This significant upgrade will reduce gas fees on the Scroll chain by 1.5x. Highlights include: + +- Compresses the data stored in blobs using the [zstd](https://github.com/scroll-tech/da-codec/tree/main/libzstd) + algorithm. This compression reduces the data size, allowing each blob to store more transactions, thereby reducing + data availability cost per transaction. +- Adopts a modified version of the EIP-1559 pricing model which is compatible with the EIP-1559 transaction interface, + bringing beneftis such as more accurate transaction pricing and a more predictable and stable fee structure. +- Support for new EVM opcodes `TLOAD`, `TSTORE`, and `MCOPY`. Users can safely use the latest Solidity compiler version + `0.8.26` to build the contracts. +- Introduces a dynamic block time. During periods of traffic congestion, a block will be packed when the number of + transactions reaches the circuit limit instead of waiting for the 3-second interval. + +### Timeline + +- **Scroll Sepolia** + - Network Upgrade: June 17th, 2024 +- **Scroll Mainnet** + - Upgrade Initiation: June 20th, 2024 + - Timelock Completion & Upgrade: July 3rd, 2024 + +### Technical Details + +#### Contract Changes + +The code changes for this upgrade are documented in the following PRs: + +- [Accept compressed batches](https://github.com/scroll-tech/scroll/pull/1317) +- [Update `L1GasPriceOracle`](https://github.com/scroll-tech/scroll/pull/1343) +- [Change `MAX_COMMIT_SCALAR` and `MAX_BLOB_SCALAR` to 1e18](https://github.com/scroll-tech/scroll/pull/1354) +- [Remove batch index check when updating a verifier](https://github.com/scroll-tech/scroll/pull/1372) + +The main changes are as follows: + +- The rollup contract (`ScrollChain`) will now accept batches with both versions 1 and + 2. [Version 1](https://github.com/scroll-tech/da-codec/tree/main/encoding/codecv1) is used for uncompressed blobs ( + pre-Curie), while [version 2](https://github.com/scroll-tech/da-codec/tree/main/encoding/codecv2) is used for + compressed blobs (post-Curie). +- The `L1GasPriceOracle` contract will be updated to change the data fee formula to account for blob DA, providing a + more accurate estimation of DA costs: + - Original formula: `(l1GasUsed(txRlp) + overhead) * l1BaseFee * scalar` + - New formula: `l1BaseFee * commitScalar + len(txRlp) * l1BlobBaseFee * blobScalar` + +#### Node Changes + +The new node version is `v5.5.0`. See +the [release notes](https://github.com/scroll-tech/go-ethereum/releases/tag/scroll-v5.5.0) for the list of changes. + +#### zkEVM circuit changes + +The new version of zkevm circuits is `v0.11.4`. +See [here](https://github.com/scroll-tech/zkevm-circuits/releases/tag/v0.11.4) for the release log. + +#### Audits + +- TrailofBits: coming soon! +- [Zellic](https://github.com/Zellic/publications/blob/master/Scroll%20zkEVM%20-%20Zellic%20Audit%20Report.pdf) + +### Compatibility + +#### Sequencer and Follower Nodes (l2geth) + +This upgrade is a hard fork, introducing the `TLOAD`, `TSTORE`, and `MCOPY` opcodes. Operators running an `l2geth` node +are required to upgrade before the hard fork block. For more information, see +the [node release note](https://github.com/scroll-tech/go-ethereum/releases/tag/scroll-v5.4.2). + +#### Dapps and Indexers + +For dApps, this upgrade is backward compatible. Developers should adjust the gas fee settings to incorporate the +EIP-1559 pricing model. Note that dApps can no longer rely on the fixed 3-second block time in the application logic. + +For indexers, the [data format](https://docs.scroll.io/en/technology/chain/rollup/#codec) remains the same. The will be +however changes to the data content: + +- The `version` field in `BatchHeader` will be changed to 2 since Curie block. +- The data stored in blob will be compressed and can be decompressed + by [zstd v1.5.6](https://github.com/facebook/zstd/releases/tag/v1.5.6). + +## Bernoulli Upgrade + +### Overview + +This upgrade features a significant reduction in transaction costs by introducing support for EIP-4844 data blobs and +supporting the SHA2-256 precompile. + +### Timeline + +- **Scroll Sepolia** + - Network Upgrade: April 15th, 2024 +- **Scroll Mainnet** + - Upgrade Initiation: April 15th, 2024 + - Timelock Completion & Upgrade: April 29th, 2024 + +### Technical Details + +#### Contract changes + +The contract changes for this upgrade are in [this PR](https://github.com/scroll-tech/scroll/pull/1179), along with the +audit +fixes [here](https://github.com/scroll-tech/scroll/pulls?q=is%3Apr+created%3A2024-04-10..2024-04-11+fix+in%3Atitle+label%3Abug). +The main changes are as follows: + +- `ScrollChain` now accepts batches with either calldata or blob encoding in `commitBatch`. +- `ScrollChain` now supports finalizing blob-encoded batches through `finalizeBatchWithProof4844`. +- `MultipleVersionRollupVerifier` can now manage different on-chain verifiers for each batch encoding version. + +#### Node changes + +The new node version is `v5.3.0`. See [here](https://github.com/scroll-tech/go-ethereum/releases/tag/scroll-v5.3.0) for +the release log. + +#### zkEVM circuit changes + +The new version of zkevm circuits is `v0.10.3`. +See [here](https://github.com/scroll-tech/zkevm-circuits/releases/tag/v0.10.3) for the release log. + +#### Audits + +- [OpenZeppelin](https://blog.openzeppelin.com/scroll-eip-4844-support-audit) +- [TrailofBits](https://github.com/trailofbits/publications/blob/master/reviews/2024-04-scroll-4844-blob-securityreview.pdf) + +### Compatibility + +#### Sequencer and follower nodes (l2geth) + +This upgrade is a hard fork as it introduces the new blob data type and the SHA2-256 precompiled contract. Operators +running an `l2geth` node are required to upgrade before the hard fork block. See +the [node releases](https://github.com/scroll-tech/go-ethereum/releases) for more information. + +#### Indexers and Bridges + +This upgrade changes the format that Scroll uses to publish data to Ethereum. Projects that rely on this data should +carefully review [the new data format](/en/technology/chain/rollup/#codec), and check whether their decoders need to be +adjusted. A summary of the new format: + +- The format of [ + `BlockContext`](https://github.com/scroll-tech/scroll/blob/5362e28f744093495c1c09a6b68fc96a3264278b/common/types/encoding/codecv1/codecv1.go#L125) + will not change. +- `Chunks` + will [no longer include](https://github.com/scroll-tech/scroll/blob/5362e28f744093495c1c09a6b68fc96a3264278b/common/types/encoding/codecv1/codecv1.go#L162) + the L2 transaction data. This will instead + be [stored in a blob](https://github.com/scroll-tech/scroll/blob/5362e28f744093495c1c09a6b68fc96a3264278b/common/types/encoding/codecv1/codecv1.go#L284) + attached to the `commitBatch` transaction. +- `BatchHeader` now contains one new field, [ + `BlobVersionedHash`](https://github.com/scroll-tech/scroll/blob/5362e28f744093495c1c09a6b68fc96a3264278b/common/types/encoding/codecv1/codecv1.go#L405). + +#### Provers + +This upgrade involves a breaking change in [zkevm-circuits](https://github.com/scroll-tech/zkevm-circuits). Operators +running a prover node are required to upgrade. + +## Bridge Upgrade + +### Overview + +To reduce bridging costs, we implemented several gas optimizations on our bridge and rollup contract suite. The +optimization techniques used include the following: + +- We will now use constants to store some companion contract addresses, instead of using storage variables. This is + possible since these values should (almost) never change. With this change we can save on a few storage load + operations. +- We updated the intrinsic gas estimation in `L1MessageQueue` to use a simple upper bound instead of an exact + calculation. The two results will be similar for most bridge transactions but the new implementation is significantly + cheaper. +- We merged two contracts `L1MessageQueue` and `L2GasPriceOracle` to save on call costs from one contract to the other. + +### Timeline + +- **Scroll Sepolia:** + - Network Upgrade: January 19, 2024 +- **Scroll Mainnet:** + - Upgrade Initiation: February 7, 2024 + - Timelock Completion & Upgrade: February 21, 2024 + +### Technical Details + +#### Code Changes + +- [Bridge Cost Optimization](https://github.com/scroll-tech/scroll/pull/1011) +- [Audit Fixes](https://github.com/scroll-tech/scroll/pulls?q=OZ+is%3Apr+created%3A2024-01-27..2024-02-10) +- [Previously deployed version](https://github.com/scroll-tech/scroll/tree/ff380141a8cbcc214dc65f17ffa44faf4be646b6) ( + commit `ff380141a8cbcc214dc65f17ffa44faf4be646b6`) +- [Version deployed](https://github.com/scroll-tech/scroll/tree/6030927680a92d0285c2c13e6bb27ed27d1f32d1) (commit + `6030927680a92d0285c2c13e6bb27ed27d1f32d1`) + +#### Audits + +- [OpenZeppelin](https://blog.openzeppelin.com/scroll-bridge-gas-optimizations-audit) + +#### List of Changes + +**Changes to L1 contracts:** + +- In `ScrollChain`, change `messageQueue` and `verifier` to `immutable`. +- In `L1ScrollMessenger`, change `counterpart`, `rollup`, and `messageQueue` to `immutable`. +- In all token gateways, change `counterpart`, `router`, and `messenger` to `immutable`. +- Merge `L1MessageQueue` and `L2GasPriceOracle` into a single contract `L1MessageQueueWithGasPriceOracle` (deployed on + the same address as the previous `L1MessageQueue`). In this contract, we also change `messenger` and `scrollChain` to + `immutable`, and simplify `calculateIntrinsicGasFee`. + +**Changes to L2 contracts:** + +- In `L2ScrollMessenger`, change `counterpart` to `immutable`. +- In all token gateways, change `counterpart`, `router`, and `messenger` to `immutable`. + +**Contracts affected:** + +- **L1:** `L1MessageQueue`, `L2GasPriceOracle`, `ScrollChain`, `L1WETHGateway`, `L1StandardERC20Gateway`, + `L1GatewayRouter`, `L1ScrollMessenger`, `L1CustomERC20Gateway`, `L1ERC721Gateway`, `L1ERC1155Gateway`. +- **L2:** `L2ScrollMessenger`, `L2WETHGateway`, `L2StandardERC20Gateway`, `L2GatewayRouter`, `L2CustomERC20Gateway`, + `L2ERC721Gateway`, `L2ERC1155Gateway`. + +#### Compatibility + +##### Sequencer and follower nodes (l2geth) + +Operators running an `l2geth` node do not need to upgrade. The changes in this upgrade will not affect `l2geth`. + +##### Dapps and indexers + +Dapps and indexers (and similar off-chain infrastructure) that query contracts or rely on contract interfaces would, in +most cases, not need to be changed. The majority of the contract changes are internal and/or backward compatible. + +If your application depends on [ +`L2GasPriceOracle`](https://etherscan.io/address/0x987e300fDfb06093859358522a79098848C33852) to monitor how Scroll keeps +track of the L2 gas price on L1, from the upgrade block number you will need to start monitoring [ +`L1MessageQueueWithGasPriceOracle`](https://etherscan.io/address/0x0d7E906BD9cAFa154b048cFa766Cc1E54E39AF9B). + +The original gas price oracle contract will be deprecated: it will no longer be updated or used by the Scroll bridge. + +- Ethereum: + - `L2GasPriceOracle`: [ + `0x987e300fDfb06093859358522a79098848C33852`](https://etherscan.io/address/0x987e300fDfb06093859358522a79098848C33852) + - `L1MessageQueueWithGasPriceOracle`: [ + `0x0d7E906BD9cAFa154b048cFa766Cc1E54E39AF9B`](https://etherscan.io/address/0x0d7E906BD9cAFa154b048cFa766Cc1E54E39AF9B) +- Sepolia: + - `L2GasPriceOracle`: [ + `0x247969F4fad93a33d4826046bc3eAE0D36BdE548`](https://sepolia.etherscan.io/address/0x247969F4fad93a33d4826046bc3eAE0D36BdE548) + - `L1MessageQueueWithGasPriceOracle`: [ + `0xF0B2293F5D834eAe920c6974D50957A1732de763`](https://sepolia.etherscan.io/address/0xF0B2293F5D834eAe920c6974D50957A1732de763) \ No newline at end of file
diff --git reth/crates/scroll/hardforks/src/lib.rs scroll-reth/crates/scroll/hardforks/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..597e4b246355a41347702de314fcf91e6b19e0a5 --- /dev/null +++ scroll-reth/crates/scroll/hardforks/src/lib.rs @@ -0,0 +1,94 @@ +//! Scroll-Reth hard forks. + +#![cfg_attr(not(feature = "std"), no_std)] +#![doc = include_str!("../docs/hardforks.md")] +#[cfg(not(feature = "std"))] +extern crate alloc as std; + +use reth_ethereum_forks::{ChainHardforks, EthereumHardfork, ForkCondition, Hardfork}; + +// Re-export scroll-alloy-hardforks types. +pub use scroll_alloy_hardforks::{ScrollHardfork, ScrollHardforks}; + +#[cfg(not(feature = "std"))] +use once_cell::sync::Lazy as LazyLock; +#[cfg(feature = "std")] +use std::sync::LazyLock; +use std::vec; + +/// Scroll mainnet hardforks +pub static SCROLL_MAINNET_HARDFORKS: LazyLock<ChainHardforks> = LazyLock::new(|| { + ChainHardforks::new(vec![ + (EthereumHardfork::Homestead.boxed(), ForkCondition::Block(0)), + (EthereumHardfork::Dao.boxed(), ForkCondition::Never), + (EthereumHardfork::Tangerine.boxed(), ForkCondition::Block(0)), + (EthereumHardfork::SpuriousDragon.boxed(), ForkCondition::Block(0)), + (EthereumHardfork::Byzantium.boxed(), ForkCondition::Block(0)), + (EthereumHardfork::Constantinople.boxed(), ForkCondition::Block(0)), + (EthereumHardfork::Petersburg.boxed(), ForkCondition::Block(0)), + (EthereumHardfork::Istanbul.boxed(), ForkCondition::Block(0)), + (EthereumHardfork::MuirGlacier.boxed(), ForkCondition::Never), + (EthereumHardfork::Berlin.boxed(), ForkCondition::Block(0)), + (EthereumHardfork::London.boxed(), ForkCondition::Never), + (EthereumHardfork::ArrowGlacier.boxed(), ForkCondition::Never), + (ScrollHardfork::Archimedes.boxed(), ForkCondition::Block(0)), + (EthereumHardfork::Shanghai.boxed(), ForkCondition::Block(0)), + (ScrollHardfork::Bernoulli.boxed(), ForkCondition::Block(5220340)), + (ScrollHardfork::Curie.boxed(), ForkCondition::Block(7096836)), + (ScrollHardfork::Darwin.boxed(), ForkCondition::Timestamp(1724227200)), + (ScrollHardfork::DarwinV2.boxed(), ForkCondition::Timestamp(1725264000)), + (ScrollHardfork::Euclid.boxed(), ForkCondition::Timestamp(1744815600)), + (ScrollHardfork::EuclidV2.boxed(), ForkCondition::Timestamp(1745305200)), + // TODO: update + (ScrollHardfork::Feynman.boxed(), ForkCondition::Timestamp(u64::MAX)), + ]) +}); + +/// Scroll sepolia hardforks +pub static SCROLL_SEPOLIA_HARDFORKS: LazyLock<ChainHardforks> = LazyLock::new(|| { + ChainHardforks::new(vec![ + (EthereumHardfork::Homestead.boxed(), ForkCondition::Block(0)), + (EthereumHardfork::Tangerine.boxed(), ForkCondition::Block(0)), + (EthereumHardfork::SpuriousDragon.boxed(), ForkCondition::Block(0)), + (EthereumHardfork::Byzantium.boxed(), ForkCondition::Block(0)), + (EthereumHardfork::Constantinople.boxed(), ForkCondition::Block(0)), + (EthereumHardfork::Petersburg.boxed(), ForkCondition::Block(0)), + (EthereumHardfork::Istanbul.boxed(), ForkCondition::Block(0)), + (EthereumHardfork::Berlin.boxed(), ForkCondition::Block(0)), + (EthereumHardfork::London.boxed(), ForkCondition::Block(0)), + (ScrollHardfork::Archimedes.boxed(), ForkCondition::Block(0)), + (EthereumHardfork::Shanghai.boxed(), ForkCondition::Block(0)), + (ScrollHardfork::Bernoulli.boxed(), ForkCondition::Block(3747132)), + (ScrollHardfork::Curie.boxed(), ForkCondition::Block(4740239)), + (ScrollHardfork::Darwin.boxed(), ForkCondition::Timestamp(1723622400)), + (ScrollHardfork::DarwinV2.boxed(), ForkCondition::Timestamp(1724832000)), + (ScrollHardfork::Euclid.boxed(), ForkCondition::Timestamp(1741680000)), + (ScrollHardfork::EuclidV2.boxed(), ForkCondition::Timestamp(1741852800)), + // TODO: update + (ScrollHardfork::Feynman.boxed(), ForkCondition::Timestamp(u64::MAX)), + ]) +}); + +/// Dev hardforks +pub static DEV_HARDFORKS: LazyLock<ChainHardforks> = LazyLock::new(|| { + ChainHardforks::new(vec![ + (EthereumHardfork::Homestead.boxed(), ForkCondition::Block(0)), + (EthereumHardfork::Tangerine.boxed(), ForkCondition::Block(0)), + (EthereumHardfork::SpuriousDragon.boxed(), ForkCondition::Block(0)), + (EthereumHardfork::Byzantium.boxed(), ForkCondition::Block(0)), + (EthereumHardfork::Constantinople.boxed(), ForkCondition::Block(0)), + (EthereumHardfork::Petersburg.boxed(), ForkCondition::Block(0)), + (EthereumHardfork::Istanbul.boxed(), ForkCondition::Block(0)), + (EthereumHardfork::Berlin.boxed(), ForkCondition::Block(0)), + (EthereumHardfork::London.boxed(), ForkCondition::Block(0)), + (ScrollHardfork::Archimedes.boxed(), ForkCondition::Block(0)), + (EthereumHardfork::Shanghai.boxed(), ForkCondition::Timestamp(0)), + (ScrollHardfork::Bernoulli.boxed(), ForkCondition::Block(0)), + (ScrollHardfork::Curie.boxed(), ForkCondition::Block(0)), + (ScrollHardfork::Darwin.boxed(), ForkCondition::Timestamp(0)), + (ScrollHardfork::DarwinV2.boxed(), ForkCondition::Timestamp(0)), + (ScrollHardfork::Euclid.boxed(), ForkCondition::Timestamp(0)), + (ScrollHardfork::EuclidV2.boxed(), ForkCondition::Timestamp(0)), + (ScrollHardfork::Feynman.boxed(), ForkCondition::Timestamp(0)), + ]) +});
diff --git reth/crates/scroll/node/Cargo.toml scroll-reth/crates/scroll/node/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..b94b463aa01e2dfbef83da9e6cab74960fd31869 --- /dev/null +++ scroll-reth/crates/scroll/node/Cargo.toml @@ -0,0 +1,124 @@ +[package] +name = "reth-scroll-node" +version.workspace = true +edition.workspace = true +rust-version.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +exclude.workspace = true + +[lints] +workspace = true + +[dependencies] +# reth +reth-basic-payload-builder.workspace = true +reth-chainspec.workspace = true +reth-db = { workspace = true, features = ["scroll-alloy-traits"] } +reth-engine-local = { workspace = true, features = ["scroll-alloy-traits"] } +reth-eth-wire-types.workspace = true +reth-evm = { workspace = true, features = ["scroll-alloy-traits"] } +reth-e2e-test-utils = { workspace = true, optional = true } +reth-network.workspace = true +reth-node-api.workspace = true +reth-node-core = { workspace = true, optional = true } +reth-node-types.workspace = true +reth-node-builder.workspace = true +reth-payload-builder.workspace = true +reth-primitives = { workspace = true, features = ["c-kzg"] } +reth-primitives-traits.workspace = true +reth-provider.workspace = true +reth-rpc-eth-types.workspace = true +reth-rpc-server-types = { workspace = true, optional = true } +reth-tasks = { workspace = true, optional = true } +reth-tracing.workspace = true +reth-transaction-pool.workspace = true +reth-trie-db.workspace = true + +# revm +revm = { workspace = true, features = ["c-kzg"] } + +# alloy +alloy-consensus.workspace = true +alloy-eips.workspace = true +alloy-genesis = { workspace = true, optional = true } +alloy-primitives.workspace = true +alloy-rpc-types-engine.workspace = true + +# scroll-reth +reth-scroll-chainspec.workspace = true +reth-scroll-consensus.workspace = true +reth-scroll-engine-primitives.workspace = true +reth-scroll-evm.workspace = true +reth-scroll-payload.workspace = true +reth-scroll-primitives = { workspace = true, features = ["serde", "serde-bincode-compat", "reth-codec"] } +reth-scroll-rpc.workspace = true +reth-scroll-txpool.workspace = true + +# scroll-alloy +scroll-alloy-consensus.workspace = true +scroll-alloy-evm.workspace = true +scroll-alloy-hardforks.workspace = true +scroll-alloy-rpc-types-engine.workspace = true + +# misc +eyre.workspace = true +serde_json = { workspace = true, optional = true } +tracing.workspace = true +thiserror.workspace = true +tokio.workspace = true + +[dev-dependencies] +reth-scroll-node = { workspace = true, features = ["test-utils"] } +reth-db.workspace = true +reth-node-core.workspace = true +reth-node-builder = { workspace = true, features = ["test-utils"] } +reth-provider = { workspace = true, features = ["test-utils"] } +reth-revm = { workspace = true, features = ["test-utils"] } +reth-tasks.workspace = true + +alloy-primitives.workspace = true +scroll-alloy-consensus.workspace = true +alloy-network.workspace = true +alloy-consensus.workspace = true +futures.workspace = true + +[features] +default = ["reth-codec", "scroll-alloy-traits"] +reth-codec = [ + "reth-scroll-primitives/reth-codec", +] +skip-state-root-validation = [ + "reth-provider/skip-state-root-validation", + "reth-node-builder/skip-state-root-validation", +] +test-utils = [ + "dep:alloy-genesis", + "dep:reth-e2e-test-utils", + "dep:reth-node-core", + "dep:reth-rpc-server-types", + "dep:reth-tasks", + "dep:serde_json", + "reth-chainspec/test-utils", + "reth-evm/test-utils", + "reth-network/test-utils", + "reth-node-builder/test-utils", + "reth-payload-builder/test-utils", + "reth-primitives/test-utils", + "reth-primitives-traits/test-utils", + "reth-provider/test-utils", + "reth-scroll-payload/test-utils", + "reth-transaction-pool/test-utils", + "reth-trie-db/test-utils", + "reth-db/test-utils", + "reth-revm/test-utils", + "reth-scroll-node/test-utils", +] +scroll-alloy-traits = [ + "reth-db/scroll-alloy-traits", + "reth-evm/scroll-alloy-traits", + "reth-primitives-traits/scroll-alloy-traits", + "reth-scroll-node/scroll-alloy-traits", + "reth-engine-local/scroll-alloy-traits", +]
diff --git reth/crates/scroll/node/src/addons.rs scroll-reth/crates/scroll/node/src/addons.rs new file mode 100644 index 0000000000000000000000000000000000000000..880ee6a50ce687abadcb476bb14a6405b63974aa --- /dev/null +++ scroll-reth/crates/scroll/node/src/addons.rs @@ -0,0 +1,140 @@ +use crate::{ScrollEngineValidator, ScrollEngineValidatorBuilder, ScrollStorage}; +use reth_evm::{ConfigureEvm, EvmFactory, EvmFactoryFor}; +use reth_node_api::{AddOnsContext, NodeAddOns}; +use reth_node_builder::{ + rpc::{ + BasicEngineApiBuilder, EngineValidatorAddOn, EngineValidatorBuilder, EthApiBuilder, + RethRpcAddOns, RpcAddOns, RpcHandle, + }, + FullNodeComponents, +}; +use reth_node_types::NodeTypes; +use reth_rpc_eth_types::error::FromEvmError; +use reth_scroll_chainspec::ScrollChainSpec; +use reth_scroll_engine_primitives::ScrollEngineTypes; +use reth_scroll_evm::ScrollNextBlockEnvAttributes; +use reth_scroll_primitives::ScrollPrimitives; +use reth_scroll_rpc::{eth::ScrollEthApiBuilder, ScrollEthApi, ScrollEthApiError}; +use revm::context::TxEnv; +use scroll_alloy_evm::ScrollTransactionIntoTxEnv; + +/// Add-ons for the Scroll follower node. +#[derive(Debug)] +pub struct ScrollAddOns<N> +where + N: FullNodeComponents, + ScrollEthApiBuilder: EthApiBuilder<N>, +{ + /// Rpc add-ons responsible for launching the RPC servers and instantiating the RPC handlers + /// and eth-api. + pub rpc_add_ons: RpcAddOns< + N, + ScrollEthApiBuilder, + ScrollEngineValidatorBuilder, + BasicEngineApiBuilder<ScrollEngineValidatorBuilder>, + >, +} + +impl<N> Default for ScrollAddOns<N> +where + N: FullNodeComponents<Types: NodeTypes<Primitives = ScrollPrimitives>>, + ScrollEthApiBuilder: EthApiBuilder<N>, +{ + fn default() -> Self { + Self::builder().build() + } +} + +impl<N> ScrollAddOns<N> +where + N: FullNodeComponents<Types: NodeTypes<Primitives = ScrollPrimitives>>, + ScrollEthApiBuilder: EthApiBuilder<N>, +{ + /// Build a [`ScrollAddOns`] using [`ScrollAddOnsBuilder`]. + pub fn builder() -> ScrollAddOnsBuilder { + ScrollAddOnsBuilder::default() + } +} + +impl<N> NodeAddOns<N> for ScrollAddOns<N> +where + N: FullNodeComponents< + Types: NodeTypes< + ChainSpec = ScrollChainSpec, + Primitives = ScrollPrimitives, + Storage = ScrollStorage, + Payload = ScrollEngineTypes, + >, + Evm: ConfigureEvm<NextBlockEnvCtx = ScrollNextBlockEnvAttributes>, + >, + ScrollEthApiError: FromEvmError<N::Evm>, + EvmFactoryFor<N::Evm>: EvmFactory<Tx = ScrollTransactionIntoTxEnv<TxEnv>>, +{ + type Handle = RpcHandle<N, ScrollEthApi<N>>; + + async fn launch_add_ons(self, ctx: AddOnsContext<'_, N>) -> eyre::Result<Self::Handle> { + let Self { rpc_add_ons } = self; + rpc_add_ons.launch_add_ons_with(ctx, |_| Ok(())).await + } +} + +impl<N> RethRpcAddOns<N> for ScrollAddOns<N> +where + N: FullNodeComponents< + Types: NodeTypes< + ChainSpec = ScrollChainSpec, + Primitives = ScrollPrimitives, + Storage = ScrollStorage, + Payload = ScrollEngineTypes, + >, + Evm: ConfigureEvm<NextBlockEnvCtx = ScrollNextBlockEnvAttributes>, + >, + ScrollEthApiError: FromEvmError<N::Evm>, + EvmFactoryFor<N::Evm>: EvmFactory<Tx = ScrollTransactionIntoTxEnv<TxEnv>>, +{ + type EthApi = ScrollEthApi<N>; + + fn hooks_mut(&mut self) -> &mut reth_node_builder::rpc::RpcHooks<N, Self::EthApi> { + self.rpc_add_ons.hooks_mut() + } +} + +impl<N> EngineValidatorAddOn<N> for ScrollAddOns<N> +where + N: FullNodeComponents< + Types: NodeTypes< + ChainSpec = ScrollChainSpec, + Primitives = ScrollPrimitives, + Payload = ScrollEngineTypes, + >, + >, + ScrollEthApiBuilder: EthApiBuilder<N>, +{ + type Validator = ScrollEngineValidator; + + async fn engine_validator(&self, ctx: &AddOnsContext<'_, N>) -> eyre::Result<Self::Validator> { + ScrollEngineValidatorBuilder.build(ctx).await + } +} + +/// A regular scroll evm and executor builder. +#[derive(Debug, Default, Clone)] +#[non_exhaustive] +pub struct ScrollAddOnsBuilder {} + +impl ScrollAddOnsBuilder { + /// Builds an instance of [`ScrollAddOns`]. + pub fn build<N>(self) -> ScrollAddOns<N> + where + N: FullNodeComponents<Types: NodeTypes<Primitives = ScrollPrimitives>>, + ScrollEthApiBuilder: EthApiBuilder<N>, + { + ScrollAddOns { + rpc_add_ons: RpcAddOns::new( + ScrollEthApi::<N>::builder(), + Default::default(), + Default::default(), + ), + } + } +}
diff --git reth/crates/scroll/node/src/builder/consensus.rs scroll-reth/crates/scroll/node/src/builder/consensus.rs new file mode 100644 index 0000000000000000000000000000000000000000..fcb25235ad1dba7d64ac554e8832c5f865d6054b --- /dev/null +++ scroll-reth/crates/scroll/node/src/builder/consensus.rs @@ -0,0 +1,28 @@ +use reth_chainspec::EthChainSpec; +use reth_node_builder::{components::ConsensusBuilder, BuilderContext, FullNodeTypes}; +use reth_node_types::NodeTypes; +use reth_primitives_traits::NodePrimitives; +use reth_scroll_consensus::ScrollBeaconConsensus; +use reth_scroll_primitives::ScrollReceipt; +use scroll_alloy_hardforks::ScrollHardforks; +use std::sync::Arc; + +/// The consensus builder for Scroll. +#[derive(Debug, Default, Clone, Copy)] +pub struct ScrollConsensusBuilder; + +impl<Node> ConsensusBuilder<Node> for ScrollConsensusBuilder +where + Node: FullNodeTypes< + Types: NodeTypes< + ChainSpec: EthChainSpec + ScrollHardforks, + Primitives: NodePrimitives<Receipt = ScrollReceipt>, + >, + >, +{ + type Consensus = Arc<ScrollBeaconConsensus<<Node::Types as NodeTypes>::ChainSpec>>; + + async fn build_consensus(self, ctx: &BuilderContext<Node>) -> eyre::Result<Self::Consensus> { + Ok(Arc::new(ScrollBeaconConsensus::new(ctx.chain_spec()))) + } +}
diff --git reth/crates/scroll/node/src/builder/engine.rs scroll-reth/crates/scroll/node/src/builder/engine.rs new file mode 100644 index 0000000000000000000000000000000000000000..7b2560b5b1eaf800bcc0e1cb54165c31c7099e13 --- /dev/null +++ scroll-reth/crates/scroll/node/src/builder/engine.rs @@ -0,0 +1,166 @@ +use alloy_consensus::BlockHeader; +use alloy_primitives::U256; +use alloy_rpc_types_engine::{ExecutionData, PayloadError}; +use reth_node_api::{ + InvalidPayloadAttributesError, MessageValidationKind, NewPayloadError, PayloadAttributes, + PayloadTypes, PayloadValidator, VersionSpecificValidationError, +}; +use reth_node_builder::{ + rpc::EngineValidatorBuilder, AddOnsContext, EngineApiMessageVersion, + EngineObjectValidationError, EngineTypes, EngineValidator, ExecutionPayload, + FullNodeComponents, PayloadOrAttributes, +}; +use reth_node_types::NodeTypes; +use reth_primitives_traits::{Block, RecoveredBlock}; +use reth_scroll_chainspec::ScrollChainSpec; +use reth_scroll_engine_primitives::{try_into_block, ScrollEngineTypes}; +use reth_scroll_primitives::{ScrollBlock, ScrollPrimitives}; +use scroll_alloy_hardforks::ScrollHardforks; +use scroll_alloy_rpc_types_engine::ScrollPayloadAttributes; +use std::sync::Arc; + +/// The block difficulty for in turn signing in the Clique consensus. +const CLIQUE_IN_TURN_DIFFICULTY: U256 = U256::from_limbs([2, 0, 0, 0]); +/// The block difficulty for out of turn signing in the Clique consensus. +const CLIQUE_NO_TURN_DIFFICULTY: U256 = U256::from_limbs([1, 0, 0, 0]); + +/// Builder for [`ScrollEngineValidator`]. +#[derive(Debug, Default, Clone, Copy)] +pub struct ScrollEngineValidatorBuilder; + +impl<Node, Types> EngineValidatorBuilder<Node> for ScrollEngineValidatorBuilder +where + Types: NodeTypes< + ChainSpec = ScrollChainSpec, + Primitives = ScrollPrimitives, + Payload = ScrollEngineTypes, + >, + Node: FullNodeComponents<Types = Types>, +{ + type Validator = ScrollEngineValidator; + + async fn build(self, ctx: &AddOnsContext<'_, Node>) -> eyre::Result<Self::Validator> { + let chainspec = ctx.config.chain.clone(); + Ok(ScrollEngineValidator { chainspec }) + } +} + +/// Scroll engine validator. +#[derive(Debug, Clone)] +pub struct ScrollEngineValidator { + chainspec: Arc<ScrollChainSpec>, +} + +impl ScrollEngineValidator { + /// Returns a new [`ScrollEngineValidator`]. + pub const fn new(chainspec: Arc<ScrollChainSpec>) -> Self { + Self { chainspec } + } +} + +impl<Types> EngineValidator<Types> for ScrollEngineValidator +where + Types: EngineTypes<PayloadAttributes = ScrollPayloadAttributes, ExecutionData = ExecutionData>, +{ + fn validate_version_specific_fields( + &self, + _version: EngineApiMessageVersion, + payload_or_attrs: PayloadOrAttributes<'_, Self::ExecutionData, ScrollPayloadAttributes>, + ) -> Result<(), EngineObjectValidationError> { + validate_scroll_payload_or_attributes( + &payload_or_attrs, + payload_or_attrs.message_validation_kind(), + )?; + Ok(()) + } + + fn ensure_well_formed_attributes( + &self, + _version: EngineApiMessageVersion, + attributes: &ScrollPayloadAttributes, + ) -> Result<(), EngineObjectValidationError> { + validate_scroll_payload_or_attributes( + &PayloadOrAttributes::PayloadAttributes::<'_, ExecutionData, _>(attributes), + MessageValidationKind::PayloadAttributes, + )?; + + // ensure block data hint is present pre euclid. + let is_euclid_active = + self.chainspec.is_euclid_active_at_timestamp(attributes.payload_attributes.timestamp); + if !is_euclid_active && attributes.block_data_hint.is_none() { + return Err(EngineObjectValidationError::InvalidParams( + "Missing block data hint Pre-Euclid".to_string().into(), + )); + } + + Ok(()) + } + + fn validate_payload_attributes_against_header( + &self, + attr: &<Types as PayloadTypes>::PayloadAttributes, + header: &<Self::Block as Block>::Header, + ) -> Result<(), InvalidPayloadAttributesError> { + if attr.timestamp() < header.timestamp() { + return Err(InvalidPayloadAttributesError::InvalidTimestamp); + } + Ok(()) + } +} + +/// Validates the payload or attributes for Scroll. +fn validate_scroll_payload_or_attributes<Payload: ExecutionPayload>( + payload_or_attributes: &PayloadOrAttributes<'_, Payload, ScrollPayloadAttributes>, + message_validation_kind: MessageValidationKind, +) -> Result<(), EngineObjectValidationError> { + if payload_or_attributes.parent_beacon_block_root().is_some() { + return Err(message_validation_kind + .to_error(VersionSpecificValidationError::ParentBeaconBlockRootNotSupportedBeforeV3)); + } + if payload_or_attributes.withdrawals().is_some() { + return Err(message_validation_kind + .to_error(VersionSpecificValidationError::HasWithdrawalsPreShanghai)); + } + + Ok(()) +} + +impl PayloadValidator for ScrollEngineValidator { + type Block = ScrollBlock; + type ExecutionData = ExecutionData; + + fn ensure_well_formed_payload( + &self, + payload: ExecutionData, + ) -> Result<RecoveredBlock<Self::Block>, NewPayloadError> { + let expected_hash = payload.payload.block_hash(); + + // First parse the block + let mut block = try_into_block(payload, self.chainspec.clone())?; + + // Seal the block with the no-turn difficulty and return if hashes match. + // We guess the difficulty, which should always be 1 or 2 on Scroll. + // CLIQUE_NO_TURN_DIFFICULTY is used starting at Euclid, so we test this value first. + block.header.difficulty = CLIQUE_NO_TURN_DIFFICULTY; + let block_hash_no_turn = block.hash_slow(); + if block_hash_no_turn == expected_hash { + return block + .seal_unchecked(block_hash_no_turn) + .try_recover() + .map_err(|err| NewPayloadError::Other(err.into())); + } + + // Seal the block with the in-turn difficulty and return if hashes match + block.header.difficulty = CLIQUE_IN_TURN_DIFFICULTY; + let block_hash_in_turn = block.hash_slow(); + if block_hash_in_turn == expected_hash { + return block + .seal_unchecked(block_hash_in_turn) + .try_recover() + .map_err(|err| NewPayloadError::Other(err.into())); + } + + Err(PayloadError::BlockHash { execution: block_hash_no_turn, consensus: expected_hash } + .into()) + } +}
diff --git reth/crates/scroll/node/src/builder/execution.rs scroll-reth/crates/scroll/node/src/builder/execution.rs new file mode 100644 index 0000000000000000000000000000000000000000..2e56b699cab9ae2e1bd5e4169404b90555ec40ba --- /dev/null +++ scroll-reth/crates/scroll/node/src/builder/execution.rs @@ -0,0 +1,24 @@ +use reth_node_builder::{components::ExecutorBuilder, BuilderContext, FullNodeTypes}; +use reth_node_types::NodeTypes; +use reth_scroll_chainspec::ScrollChainSpec; +use reth_scroll_evm::ScrollEvmConfig; +use reth_scroll_primitives::ScrollPrimitives; + +/// Executor builder for Scroll. +#[derive(Debug, Default, Clone, Copy)] +#[non_exhaustive] +pub struct ScrollExecutorBuilder; + +impl<Node> ExecutorBuilder<Node> for ScrollExecutorBuilder +where + Node: FullNodeTypes, + Node::Types: NodeTypes<ChainSpec = ScrollChainSpec, Primitives = ScrollPrimitives>, +{ + type EVM = ScrollEvmConfig; + + async fn build_evm(self, ctx: &BuilderContext<Node>) -> eyre::Result<Self::EVM> { + let evm_config = ScrollEvmConfig::scroll(ctx.chain_spec()); + + Ok(evm_config) + } +}
diff --git reth/crates/scroll/node/src/builder/mod.rs scroll-reth/crates/scroll/node/src/builder/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..86ed6d848601ad99def48042f8331f48e688b84d --- /dev/null +++ scroll-reth/crates/scroll/node/src/builder/mod.rs @@ -0,0 +1,6 @@ +pub(crate) mod consensus; +pub(crate) mod engine; +pub(crate) mod execution; +pub(crate) mod network; +pub(crate) mod payload; +pub(crate) mod pool;
diff --git reth/crates/scroll/node/src/builder/network.rs scroll-reth/crates/scroll/node/src/builder/network.rs new file mode 100644 index 0000000000000000000000000000000000000000..c69adf6ca2778d4f6afc1f015446b966f123ec53 --- /dev/null +++ scroll-reth/crates/scroll/node/src/builder/network.rs @@ -0,0 +1,92 @@ +use reth_eth_wire_types::BasicNetworkPrimitives; +use reth_network::{ + config::NetworkMode, transform::header::HeaderTransform, NetworkConfig, NetworkHandle, + NetworkManager, PeersInfo, +}; +use reth_node_api::TxTy; +use reth_node_builder::{components::NetworkBuilder, BuilderContext, FullNodeTypes}; +use reth_node_types::NodeTypes; +use reth_primitives_traits::BlockHeader; +use reth_scroll_chainspec::ScrollChainSpec; +use reth_scroll_primitives::ScrollPrimitives; +use reth_tracing::tracing::info; +use reth_transaction_pool::{PoolTransaction, TransactionPool}; +use scroll_alloy_hardforks::ScrollHardforks; +use std::fmt::Debug; + +/// The network builder for Scroll. +#[derive(Debug, Default, Clone, Copy)] +pub struct ScrollNetworkBuilder; + +impl<Node, Pool> NetworkBuilder<Node, Pool> for ScrollNetworkBuilder +where + Node: + FullNodeTypes<Types: NodeTypes<ChainSpec = ScrollChainSpec, Primitives = ScrollPrimitives>>, + Pool: TransactionPool< + Transaction: PoolTransaction< + Consensus = TxTy<Node::Types>, + Pooled = scroll_alloy_consensus::ScrollPooledTransaction, + >, + > + Unpin + + 'static, +{ + type Network = NetworkHandle<ScrollNetworkPrimitives>; + + async fn build_network( + self, + ctx: &BuilderContext<Node>, + pool: Pool, + ) -> eyre::Result<Self::Network> { + // get the header transform. + let chain_spec = ctx.chain_spec(); + let transform = ScrollHeaderTransform { chain_spec }; + + // set the network mode to work. + let config = ctx.network_config()?; + let config = NetworkConfig { + network_mode: NetworkMode::Work, + header_transform: Box::new(transform), + ..config + }; + + let network = NetworkManager::builder(config).await?; + let handle = ctx.start_network(network, pool); + info!(target: "reth::cli", enode=%handle.local_node_record(), "P2P networking initialized"); + Ok(handle) + } +} + +/// Network primitive types used by Scroll networks. +pub type ScrollNetworkPrimitives = + BasicNetworkPrimitives<ScrollPrimitives, scroll_alloy_consensus::ScrollPooledTransaction>; + +/// An implementation of a [`HeaderTransform`] for Scroll. +#[derive(Debug, Clone)] +#[non_exhaustive] +pub struct ScrollHeaderTransform<ChainSpec> { + chain_spec: ChainSpec, +} + +impl<ChainSpec: ScrollHardforks + Debug + Send + Sync + 'static> ScrollHeaderTransform<ChainSpec> { + /// Returns a new instance of the [`ScrollHeaderTransform`] from the provider chain spec. + pub const fn new(chain_spec: ChainSpec) -> Self { + Self { chain_spec } + } + + /// Returns a new [`ScrollHeaderTransform`] as a [`HeaderTransform`] trait object. + pub fn boxed<H: BlockHeader>(chain_spec: ChainSpec) -> Box<dyn HeaderTransform<H>> { + Box::new(Self { chain_spec }) + } +} + +impl<H: BlockHeader, ChainSpec: ScrollHardforks + Debug + Send + Sync> HeaderTransform<H> + for ScrollHeaderTransform<ChainSpec> +{ + fn map(&self, mut header: H) -> H { + if self.chain_spec.is_euclid_v2_active_at_timestamp(header.timestamp()) { + // clear the extra data field. + *header.extra_data_mut() = Default::default() + } + header + } +}
diff --git reth/crates/scroll/node/src/builder/payload.rs scroll-reth/crates/scroll/node/src/builder/payload.rs new file mode 100644 index 0000000000000000000000000000000000000000..c2bfa547b353241f53d772d5e480935c3f70f071 --- /dev/null +++ scroll-reth/crates/scroll/node/src/builder/payload.rs @@ -0,0 +1,105 @@ +use reth_evm::ConfigureEvm; +use reth_node_api::PrimitivesTy; +use reth_node_builder::{ + components::PayloadBuilderBuilder, BuilderContext, FullNodeTypes, PayloadBuilderConfig, +}; +use reth_node_types::{NodeTypes, TxTy}; +use reth_scroll_chainspec::ScrollChainSpec; +use reth_scroll_engine_primitives::ScrollEngineTypes; +use reth_scroll_evm::ScrollNextBlockEnvAttributes; +use reth_scroll_payload::{ScrollBuilderConfig, ScrollPayloadTransactions}; +use reth_scroll_primitives::{ScrollPrimitives, ScrollTransactionSigned}; +use reth_transaction_pool::{PoolTransaction, TransactionPool}; +use std::time::Duration; + +/// Payload builder for Scroll. +#[derive(Debug, Clone, Copy)] +pub struct ScrollPayloadBuilderBuilder<Txs = ()> { + /// Returns the current best transactions from the mempool. + pub best_transactions: Txs, + /// The payload building time limit. + pub payload_building_time_limit: Duration, +} + +impl Default for ScrollPayloadBuilderBuilder { + fn default() -> Self { + Self { + best_transactions: (), + payload_building_time_limit: SCROLL_PAYLOAD_BUILDING_DURATION, + } + } +} + +const SCROLL_GAS_LIMIT: u64 = 20_000_000; +const SCROLL_PAYLOAD_BUILDING_DURATION: Duration = Duration::from_secs(1); + +impl<Txs> ScrollPayloadBuilderBuilder<Txs> { + /// A helper method to initialize [`reth_scroll_payload::ScrollPayloadBuilder`] with the + /// given EVM config. + pub fn build<Node, Evm, Pool>( + self, + evm_config: Evm, + ctx: &BuilderContext<Node>, + pool: Pool, + ) -> eyre::Result<reth_scroll_payload::ScrollPayloadBuilder<Pool, Node::Provider, Evm, Txs>> + where + Node: FullNodeTypes< + Types: NodeTypes< + Payload = ScrollEngineTypes, + ChainSpec = ScrollChainSpec, + Primitives = ScrollPrimitives, + >, + >, + Pool: TransactionPool<Transaction: PoolTransaction<Consensus = TxTy<Node::Types>>> + + Unpin + + 'static, + Evm: ConfigureEvm<Primitives = PrimitivesTy<Node::Types>>, + Txs: ScrollPayloadTransactions<Pool::Transaction>, + { + let gas_limit = ctx.payload_builder_config().gas_limit().unwrap_or_else (|| { + tracing::warn!(target: "reth::cli", "Using {SCROLL_GAS_LIMIT} gas limit for ScrollPayloadBuilder. Configure with --builder.gaslimit"); + SCROLL_GAS_LIMIT + }); + + let payload_builder = reth_scroll_payload::ScrollPayloadBuilder::new( + pool, + evm_config, + ctx.provider().clone(), + ScrollBuilderConfig::new(gas_limit, self.payload_building_time_limit), + ) + .with_transactions(self.best_transactions); + + Ok(payload_builder) + } +} + +impl<Node, Pool, Txs, Evm> PayloadBuilderBuilder<Node, Pool, Evm> + for ScrollPayloadBuilderBuilder<Txs> +where + Node: FullNodeTypes< + Types: NodeTypes< + Payload = ScrollEngineTypes, + ChainSpec = ScrollChainSpec, + Primitives = ScrollPrimitives, + >, + >, + Evm: ConfigureEvm< + Primitives = PrimitivesTy<Node::Types>, + NextBlockEnvCtx = ScrollNextBlockEnvAttributes, + > + 'static, + Pool: TransactionPool<Transaction: PoolTransaction<Consensus = ScrollTransactionSigned>> + + Unpin + + 'static, + Txs: ScrollPayloadTransactions<Pool::Transaction>, +{ + type PayloadBuilder = reth_scroll_payload::ScrollPayloadBuilder<Pool, Node::Provider, Evm, Txs>; + + async fn build_payload_builder( + self, + ctx: &BuilderContext<Node>, + pool: Pool, + evm_config: Evm, + ) -> eyre::Result<Self::PayloadBuilder> { + self.build(evm_config, ctx, pool) + } +}
diff --git reth/crates/scroll/node/src/builder/pool.rs scroll-reth/crates/scroll/node/src/builder/pool.rs new file mode 100644 index 0000000000000000000000000000000000000000..81e2f9215035f995db1dbfb2af98fb3f98c44869 --- /dev/null +++ scroll-reth/crates/scroll/node/src/builder/pool.rs @@ -0,0 +1,120 @@ +use reth_node_api::{FullNodeTypes, NodeTypes}; +use reth_node_builder::{ + components::{PoolBuilder, PoolBuilderConfigOverrides}, + BuilderContext, TxTy, +}; + +use reth_provider::CanonStateSubscriptions; +use reth_scroll_txpool::{ScrollTransactionPool, ScrollTransactionValidator}; +use reth_transaction_pool::{ + blobstore::DiskFileBlobStore, CoinbaseTipOrdering, EthPoolTransaction, + TransactionValidationTaskExecutor, +}; +use scroll_alloy_hardforks::ScrollHardforks; + +/// A basic scroll transaction pool. +/// +/// This contains various settings that can be configured and take precedence over the node's +/// config. +#[derive(Debug, Clone)] +pub struct ScrollPoolBuilder<T = reth_scroll_txpool::ScrollPooledTransaction> { + /// Enforced overrides that are applied to the pool config. + pub pool_config_overrides: PoolBuilderConfigOverrides, + + /// Marker for the pooled transaction type. + _pd: core::marker::PhantomData<T>, +} + +impl<T> Default for ScrollPoolBuilder<T> { + fn default() -> Self { + Self { pool_config_overrides: Default::default(), _pd: Default::default() } + } +} + +impl<T> ScrollPoolBuilder<T> { + /// Sets the [`PoolBuilderConfigOverrides`] on the pool builder. + pub fn with_pool_config_overrides( + mut self, + pool_config_overrides: PoolBuilderConfigOverrides, + ) -> Self { + self.pool_config_overrides = pool_config_overrides; + self + } +} + +impl<Node, T> PoolBuilder<Node> for ScrollPoolBuilder<T> +where + Node: FullNodeTypes<Types: NodeTypes<ChainSpec: ScrollHardforks>>, + T: EthPoolTransaction<Consensus = TxTy<Node::Types>>, +{ + type Pool = ScrollTransactionPool<Node::Provider, DiskFileBlobStore, T>; + + async fn build_pool(self, ctx: &BuilderContext<Node>) -> eyre::Result<Self::Pool> { + let Self { pool_config_overrides, .. } = self; + let data_dir = ctx.config().datadir(); + let blob_store = DiskFileBlobStore::open(data_dir.blobstore(), Default::default())?; + + let validator = TransactionValidationTaskExecutor::eth_builder(ctx.provider().clone()) + .no_eip4844() + .with_head_timestamp(ctx.head().timestamp) + .kzg_settings(ctx.kzg_settings()?) + .with_additional_tasks( + pool_config_overrides + .additional_validation_tasks + .unwrap_or_else(|| ctx.config().txpool.additional_validation_tasks), + ) + .build_with_tasks(ctx.task_executor().clone(), blob_store.clone()) + .map(|validator| { + ScrollTransactionValidator::new(validator) + // In --dev mode we can't require gas fees because we're unable to decode + // the L1 block info + .require_l1_data_gas_fee(!ctx.config().dev.dev) + }); + + let transaction_pool = reth_transaction_pool::Pool::new( + validator, + CoinbaseTipOrdering::default(), + blob_store, + pool_config_overrides.apply(ctx.pool_config()), + ); + tracing::info!(target: "reth::cli", "Transaction pool initialized"); + let transactions_path = data_dir.txpool_transactions(); + + // spawn txpool maintenance tasks + { + let chain_events = ctx.provider().canonical_state_stream(); + let client = ctx.provider().clone(); + let transactions_backup_config = + reth_transaction_pool::maintain::LocalTransactionBackupConfig::with_local_txs_backup(transactions_path); + + ctx.task_executor().spawn_critical_with_graceful_shutdown_signal( + "local transactions backup task", + |shutdown| { + reth_transaction_pool::maintain::backup_local_transactions_task( + shutdown, + transaction_pool.clone(), + transactions_backup_config, + ) + }, + ); + + // spawn the main maintenance task + ctx.task_executor().spawn_critical( + "txpool maintenance task", + reth_transaction_pool::maintain::maintain_transaction_pool_future( + client, + transaction_pool.clone(), + chain_events, + ctx.task_executor().clone(), + reth_transaction_pool::maintain::MaintainPoolConfig { + max_tx_lifetime: transaction_pool.config().max_queued_lifetime, + ..Default::default() + }, + ), + ); + tracing::debug!(target: "reth::cli", "Spawned txpool maintenance task"); + } + + Ok(transaction_pool) + } +}
diff --git reth/crates/scroll/node/src/lib.rs scroll-reth/crates/scroll/node/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..2d262086661ef3b1901bffb9d7dbfb5e9f6e7bf7 --- /dev/null +++ scroll-reth/crates/scroll/node/src/lib.rs @@ -0,0 +1,25 @@ +//! Node specific implementations for Scroll. +mod builder; +pub use builder::{ + consensus::ScrollConsensusBuilder, + engine::{ScrollEngineValidator, ScrollEngineValidatorBuilder}, + execution::ScrollExecutorBuilder, + network::{ScrollHeaderTransform, ScrollNetworkBuilder, ScrollNetworkPrimitives}, + payload::ScrollPayloadBuilderBuilder, + pool::ScrollPoolBuilder, +}; + +mod addons; +pub use addons::{ScrollAddOns, ScrollAddOnsBuilder}; + +mod node; +pub use node::ScrollNode; + +mod storage; +pub use storage::ScrollStorage; + +/// Helpers for running test node instances. +#[cfg(feature = "test-utils")] +pub mod test_utils; + +pub use reth_scroll_engine_primitives::{ScrollBuiltPayload, ScrollPayloadBuilderAttributes};
diff --git reth/crates/scroll/node/src/node.rs scroll-reth/crates/scroll/node/src/node.rs new file mode 100644 index 0000000000000000000000000000000000000000..7e1da81bfefa9fd9aa1f9ebf1aeefde675c5e3f7 --- /dev/null +++ scroll-reth/crates/scroll/node/src/node.rs @@ -0,0 +1,83 @@ +//! Node specific implementations for Scroll. + +use crate::{ + ScrollAddOns, ScrollConsensusBuilder, ScrollExecutorBuilder, ScrollNetworkBuilder, + ScrollPayloadBuilderBuilder, ScrollPoolBuilder, ScrollStorage, +}; +use reth_node_builder::{ + components::{BasicPayloadServiceBuilder, ComponentsBuilder}, + node::{FullNodeTypes, NodeTypes}, + Node, NodeAdapter, NodeComponentsBuilder, +}; +use reth_scroll_chainspec::ScrollChainSpec; +use reth_scroll_engine_primitives::ScrollEngineTypes; +use reth_scroll_primitives::ScrollPrimitives; +use reth_trie_db::MerklePatriciaTrie; + +/// The Scroll node implementation. +#[derive(Clone, Debug, Default)] +pub struct ScrollNode; + +impl ScrollNode { + /// Returns a [`ComponentsBuilder`] configured for a regular Ethereum node. + pub fn components<Node>() -> ComponentsBuilder< + Node, + ScrollPoolBuilder, + BasicPayloadServiceBuilder<ScrollPayloadBuilderBuilder>, + ScrollNetworkBuilder, + ScrollExecutorBuilder, + ScrollConsensusBuilder, + > + where + Node: FullNodeTypes< + Types: NodeTypes< + ChainSpec = ScrollChainSpec, + Primitives = ScrollPrimitives, + Payload = ScrollEngineTypes, + >, + >, + { + ComponentsBuilder::default() + .node_types::<Node>() + .pool(ScrollPoolBuilder::default()) + .executor(ScrollExecutorBuilder::default()) + .payload(BasicPayloadServiceBuilder::new(ScrollPayloadBuilderBuilder::default())) + .network(ScrollNetworkBuilder) + .executor(ScrollExecutorBuilder) + .consensus(ScrollConsensusBuilder) + } +} + +impl<N> Node<N> for ScrollNode +where + N: FullNodeTypes<Types = Self>, +{ + type ComponentsBuilder = ComponentsBuilder< + N, + ScrollPoolBuilder, + BasicPayloadServiceBuilder<ScrollPayloadBuilderBuilder>, + ScrollNetworkBuilder, + ScrollExecutorBuilder, + ScrollConsensusBuilder, + >; + + type AddOns = ScrollAddOns< + NodeAdapter<N, <Self::ComponentsBuilder as NodeComponentsBuilder<N>>::Components>, + >; + + fn components_builder(&self) -> Self::ComponentsBuilder { + Self::components() + } + + fn add_ons(&self) -> Self::AddOns { + ScrollAddOns::default() + } +} + +impl NodeTypes for ScrollNode { + type Primitives = ScrollPrimitives; + type ChainSpec = ScrollChainSpec; + type StateCommitment = MerklePatriciaTrie; + type Storage = ScrollStorage; + type Payload = ScrollEngineTypes; +}
diff --git reth/crates/scroll/node/src/storage.rs scroll-reth/crates/scroll/node/src/storage.rs new file mode 100644 index 0000000000000000000000000000000000000000..4347044a74eb5a3533092a1252ad830da37917da --- /dev/null +++ scroll-reth/crates/scroll/node/src/storage.rs @@ -0,0 +1,5 @@ +use reth_provider::EthStorage; +use reth_scroll_primitives::ScrollTransactionSigned; + +/// The storage implementation for Scroll. +pub type ScrollStorage = EthStorage<ScrollTransactionSigned>;
diff --git reth/crates/scroll/node/src/test_utils.rs scroll-reth/crates/scroll/node/src/test_utils.rs new file mode 100644 index 0000000000000000000000000000000000000000..f69ba9f1a633062b1186a313e6fd100b2cd27ac5 --- /dev/null +++ scroll-reth/crates/scroll/node/src/test_utils.rs @@ -0,0 +1,83 @@ +use crate::{ScrollBuiltPayload, ScrollNode as OtherScrollNode, ScrollPayloadBuilderAttributes}; +use alloy_genesis::Genesis; +use alloy_primitives::{Address, B256}; +use alloy_rpc_types_engine::PayloadAttributes; +use reth_e2e_test_utils::{ + transaction::TransactionTestContext, wallet::Wallet, NodeHelperType, TmpDB, +}; +use reth_node_api::NodeTypesWithDBAdapter; + +use reth_payload_builder::EthPayloadBuilderAttributes; +use reth_provider::providers::BlockchainProvider; +use reth_scroll_chainspec::ScrollChainSpecBuilder; +use reth_tasks::TaskManager; +use std::sync::Arc; +use tokio::sync::Mutex; + +/// Scroll Node Helper type +pub(crate) type ScrollNode = NodeHelperType< + OtherScrollNode, + BlockchainProvider<NodeTypesWithDBAdapter<OtherScrollNode, TmpDB>>, +>; + +/// Creates the initial setup with `num_nodes` of the node config, started and connected. +pub async fn setup( + num_nodes: usize, + is_dev: bool, +) -> eyre::Result<(Vec<ScrollNode>, TaskManager, Wallet)> { + let genesis: Genesis = + serde_json::from_str(include_str!("../tests/assets/genesis.json")).unwrap(); + reth_e2e_test_utils::setup_engine( + num_nodes, + Arc::new( + ScrollChainSpecBuilder::scroll_mainnet() + .genesis(genesis) + .euclid_v2_activated() + .build(Default::default()), + ), + is_dev, + Default::default(), + scroll_payload_attributes, + ) + .await +} + +/// Advance the chain with sequential payloads returning them in the end. +pub async fn advance_chain( + length: usize, + node: &mut ScrollNode, + wallet: Arc<Mutex<Wallet>>, +) -> eyre::Result<Vec<ScrollBuiltPayload>> { + node.advance(length as u64, |_| { + let wallet = wallet.clone(); + Box::pin(async move { + let mut wallet = wallet.lock().await; + let tx_fut = TransactionTestContext::transfer_tx_nonce_bytes( + wallet.chain_id, + wallet.inner.clone(), + wallet.inner_nonce, + ); + wallet.inner_nonce += 1; + tx_fut.await + }) + }) + .await +} + +/// Helper function to create a new scroll payload attributes +pub fn scroll_payload_attributes(timestamp: u64) -> ScrollPayloadBuilderAttributes { + let attributes = PayloadAttributes { + timestamp, + prev_randao: B256::ZERO, + suggested_fee_recipient: Address::ZERO, + withdrawals: None, + parent_beacon_block_root: Some(B256::ZERO), + }; + + ScrollPayloadBuilderAttributes { + payload_attributes: EthPayloadBuilderAttributes::new(B256::ZERO, attributes), + transactions: vec![], + no_tx_pool: false, + block_data_hint: None, + } +}
diff --git reth/crates/scroll/node/tests/assets/genesis.json scroll-reth/crates/scroll/node/tests/assets/genesis.json new file mode 100644 index 0000000000000000000000000000000000000000..6f1737c9a93156f4207621047723f02b2c7937e8 --- /dev/null +++ scroll-reth/crates/scroll/node/tests/assets/genesis.json @@ -0,0 +1,108 @@ +{ + "config": { + "chainId": 8453, + "homesteadBlock": 0, + "eip150Block": 0, + "eip155Block": 0, + "eip158Block": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 0, + "petersburgBlock": 0, + "istanbulBlock": 0, + "muirGlacierBlock": 0, + "berlinBlock": 0, + "londonBlock": 0, + "arrowGlacierBlock": 0, + "grayGlacierBlock": 0, + "mergeNetsplitBlock": 0, + "bedrockBlock": 0, + "regolithTime": 0, + "terminalTotalDifficulty": 0, + "terminalTotalDifficultyPassed": true + }, + "nonce": "0x0", + "timestamp": "0x0", + "extraData": "0x00", + "gasLimit": "0x1c9c380", + "difficulty": "0x0", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "coinbase": "0x0000000000000000000000000000000000000000", + "alloc": { + "0x14dc79964da2c08b23698b3d3cc7ca32193d9955": { + "balance": "0xd3c21bcecceda1000000" + }, + "0x15d34aaf54267db7d7c367839aaf71a00a2c6a65": { + "balance": "0xd3c21bcecceda1000000" + }, + "0x1cbd3b2770909d4e10f157cabc84c7264073c9ec": { + "balance": "0xd3c21bcecceda1000000" + }, + "0x23618e81e3f5cdf7f54c3d65f7fbc0abf5b21e8f": { + "balance": "0xd3c21bcecceda1000000" + }, + "0x2546bcd3c84621e976d8185a91a922ae77ecec30": { + "balance": "0xd3c21bcecceda1000000" + }, + "0x3c44cdddb6a900fa2b585dd299e03d12fa4293bc": { + "balance": "0xd3c21bcecceda1000000" + }, + "0x70997970c51812dc3a010c7d01b50e0d17dc79c8": { + "balance": "0xd3c21bcecceda1000000" + }, + "0x71be63f3384f5fb98995898a86b02fb2426c5788": { + "balance": "0xd3c21bcecceda1000000" + }, + "0x8626f6940e2eb28930efb4cef49b2d1f2c9c1199": { + "balance": "0xd3c21bcecceda1000000" + }, + "0x90f79bf6eb2c4f870365e785982e1f101e93b906": { + "balance": "0xd3c21bcecceda1000000" + }, + "0x976ea74026e726554db657fa54763abd0c3a0aa9": { + "balance": "0xd3c21bcecceda1000000" + }, + "0x9965507d1a55bcc2695c58ba16fb37d819b0a4dc": { + "balance": "0xd3c21bcecceda1000000" + }, + "0x9c41de96b2088cdc640c6182dfcf5491dc574a57": { + "balance": "0xd3c21bcecceda1000000" + }, + "0xa0ee7a142d267c1f36714e4a8f75612f20a79720": { + "balance": "0xd3c21bcecceda1000000" + }, + "0xbcd4042de499d14e55001ccbb24a551f3b954096": { + "balance": "0xd3c21bcecceda1000000" + }, + "0xbda5747bfd65f08deb54cb465eb87d40e51b197e": { + "balance": "0xd3c21bcecceda1000000" + }, + "0xcd3b766ccdd6ae721141f452c550ca635964ce71": { + "balance": "0xd3c21bcecceda1000000" + }, + "0xdd2fd4581271e230360230f9337d5c0430bf44c0": { + "balance": "0xd3c21bcecceda1000000" + }, + "0xdf3e18d64bc6a983f673ab319ccae4f1a57c7097": { + "balance": "0xd3c21bcecceda1000000" + }, + "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266": { + "balance": "0xd3c21bcecceda1000000" + }, + "0xfabb0ac9d68b0b445fb7357272ff202c5651694a": { + "balance": "0xd3c21bcecceda1000000" + }, + "0x5300000000000000000000000000000000000002": { + "balance": "0xd3c21bcecceda1000000", + "storage": { + "0x01": "0x000000000000000000000000000000000000000000000000000000003758e6b0", + "0x02": "0x0000000000000000000000000000000000000000000000000000000000000038", + "0x03": "0x000000000000000000000000000000000000000000000000000000003e95ba80", + "0x04": "0x0000000000000000000000005300000000000000000000000000000000000003", + "0x05": "0x000000000000000000000000000000000000000000000000000000008390c2c1", + "0x06": "0x00000000000000000000000000000000000000000000000000000069cf265bfe", + "0x07": "0x00000000000000000000000000000000000000000000000000000000168b9aa3" + } + } + }, + "number": "0x0" +} \ No newline at end of file
diff --git reth/crates/scroll/node/tests/e2e/main.rs scroll-reth/crates/scroll/node/tests/e2e/main.rs new file mode 100644 index 0000000000000000000000000000000000000000..1402b98661a6946e2c62d2d11a158468546f98e5 --- /dev/null +++ scroll-reth/crates/scroll/node/tests/e2e/main.rs @@ -0,0 +1,3 @@ +#![allow(missing_docs)] + +mod payload;
diff --git reth/crates/scroll/node/tests/e2e/payload.rs scroll-reth/crates/scroll/node/tests/e2e/payload.rs new file mode 100644 index 0000000000000000000000000000000000000000..81e2bf4b45ad99460863aaf84ccfb2abd4ebcc68 --- /dev/null +++ scroll-reth/crates/scroll/node/tests/e2e/payload.rs @@ -0,0 +1,19 @@ +use reth_scroll_node::test_utils::{advance_chain, setup}; +use std::sync::Arc; +use tokio::sync::Mutex; + +#[tokio::test] +async fn can_sync() -> eyre::Result<()> { + reth_tracing::init_test_tracing(); + + let (mut node, _tasks, wallet) = setup(1, false).await?; + let mut node = node.pop().unwrap(); + let wallet = Arc::new(Mutex::new(wallet)); + + let tip: usize = 90; + + // Create a chain of 90 blocks + let _canonical_payload_chain = advance_chain(tip, &mut node, wallet.clone()).await?; + + Ok(()) +}
diff --git reth/crates/scroll/openvm-compat/Cargo.lock scroll-reth/crates/scroll/openvm-compat/Cargo.lock new file mode 100644 index 0000000000000000000000000000000000000000..64cee04aeec8535f84bffd1d191642123bd7bd57 --- /dev/null +++ scroll-reth/crates/scroll/openvm-compat/Cargo.lock @@ -0,0 +1,4098 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "alloy-chains" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19a9cc9d81ace3da457883b0bdf76776e55f1b84219a9e9d55c27ad308548d3f" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "num_enum", + "serde", + "strum", +] + +[[package]] +name = "alloy-consensus" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bcb57295c4b632b6b3941a089ee82d00ff31ff9eb3eac801bf605ffddc81041" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "alloy-trie", + "alloy-tx-macros", + "auto_impl", + "c-kzg", + "derive_more", + "either", + "k256", + "once_cell", + "rand 0.8.5", + "secp256k1", + "serde", + "serde_with", + "thiserror", +] + +[[package]] +name = "alloy-consensus-any" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ab669be40024565acb719daf1b2a050e6dc065fc0bec6050d97a81cdb860bd7" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "serde", +] + +[[package]] +name = "alloy-eip2124" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "741bdd7499908b3aa0b159bba11e71c8cddd009a2c2eb7a06e825f1ec87900a5" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "crc", + "serde", + "thiserror", +] + +[[package]] +name = "alloy-eip2930" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b82752a889170df67bbb36d42ca63c531eb16274f0d7299ae2a680facba17bd" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "serde", +] + +[[package]] +name = "alloy-eip7702" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d4769c6ffddca380b0070d71c8b7f30bed375543fe76bb2f74ec0acf4b7cd16" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "k256", + "serde", + "serde_with", + "thiserror", +] + +[[package]] +name = "alloy-eips" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f853de9ca1819f54de80de5d03bfc1bb7c9fafcf092b480a654447141bc354d" +dependencies = [ + "alloy-eip2124", + "alloy-eip2930", + "alloy-eip7702", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "auto_impl", + "c-kzg", + "derive_more", + "either", + "serde", + "sha2", +] + +[[package]] +name = "alloy-evm" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "394b09cf3a32773eedf11828987f9c72dfa74545040be0422e3f5f09a2a3fab9" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-hardforks", + "alloy-primitives", + "alloy-sol-types", + "auto_impl", + "derive_more", + "revm", + "thiserror", +] + +[[package]] +name = "alloy-genesis" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8500bcc1037901953771c25cb77e0d4ec0bffd938d93a04715390230d21a612d" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "alloy-serde", + "alloy-trie", + "serde", +] + +[[package]] +name = "alloy-hardforks" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "977d2492ce210e34baf7b36afaacea272c96fbe6774c47e23f97d14033c0e94f" +dependencies = [ + "alloy-chains", + "alloy-eip2124", + "alloy-primitives", + "auto_impl", + "dyn-clone", +] + +[[package]] +name = "alloy-network-primitives" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eef189583f4c53d231dd1297b28a675ff842b551fb34715f562868a1937431a" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-serde", + "serde", +] + +[[package]] +name = "alloy-primitives" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6177ed26655d4e84e00b65cb494d4e0b8830e7cae7ef5d63087d445a2600fb55" +dependencies = [ + "alloy-rlp", + "bytes", + "cfg-if", + "const-hex", + "derive_more", + "foldhash", + "hashbrown 0.15.4", + "indexmap 2.9.0", + "itoa", + "k256", + "keccak-asm", + "paste", + "proptest", + "rand 0.9.1", + "ruint", + "rustc-hash", + "serde", + "sha3", + "tiny-keccak", +] + +[[package]] +name = "alloy-rlp" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f70d83b765fdc080dbcd4f4db70d8d23fe4761f2f02ebfa9146b833900634b4" +dependencies = [ + "alloy-rlp-derive", + "arrayvec", + "bytes", +] + +[[package]] +name = "alloy-rlp-derive" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64b728d511962dda67c1bc7ea7c03736ec275ed2cf4c35d9585298ac9ccf3b73" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "alloy-rpc-types-engine" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e5b09d86d0c015cb8400c5d1d0483425670bef4fc1260336aea9ef6d4b9540c" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "derive_more", + "strum", +] + +[[package]] +name = "alloy-rpc-types-eth" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1826285e4ffc2372a8c061d5cc145858e67a0be3309b768c5b77ddb6b9e6cbc7" +dependencies = [ + "alloy-consensus", + "alloy-consensus-any", + "alloy-eips", + "alloy-network-primitives", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "alloy-sol-types", + "itertools 0.14.0", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "alloy-serde" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "906ce0190afeded19cb2e963cb8507c975a7862216b9e74f39bf91ddee6ae74b" +dependencies = [ + "alloy-primitives", + "serde", + "serde_json", +] + +[[package]] +name = "alloy-sol-macro" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a14f21d053aea4c6630687c2f4ad614bed4c81e14737a9b904798b24f30ea849" +dependencies = [ + "alloy-sol-macro-expander", + "alloy-sol-macro-input", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "alloy-sol-macro-expander" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34d99282e7c9ef14eb62727981a985a01869e586d1dec729d3bb33679094c100" +dependencies = [ + "alloy-sol-macro-input", + "const-hex", + "heck", + "indexmap 2.9.0", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.104", + "syn-solidity", + "tiny-keccak", +] + +[[package]] +name = "alloy-sol-macro-input" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eda029f955b78e493360ee1d7bd11e1ab9f2a220a5715449babc79d6d0a01105" +dependencies = [ + "const-hex", + "dunce", + "heck", + "macro-string", + "proc-macro2", + "quote", + "syn 2.0.104", + "syn-solidity", +] + +[[package]] +name = "alloy-sol-types" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58377025a47d8b8426b3e4846a251f2c1991033b27f517aade368146f6ab1dfe" +dependencies = [ + "alloy-primitives", + "alloy-sol-macro", +] + +[[package]] +name = "alloy-trie" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "983d99aa81f586cef9dae38443245e585840fcf0fc58b09aee0b1f27aed1d500" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "arrayvec", + "derive_more", + "nybbles", + "serde", + "smallvec", + "tracing", +] + +[[package]] +name = "alloy-tx-macros" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b75ef8609ea2b31c799b0a56c724dca4c73105c5ccc205d9dfeb1d038df6a1da" +dependencies = [ + "alloy-primitives", + "darling", + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "ark-bls12-381" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3df4dcc01ff89867cd86b0da835f23c3f02738353aaee7dde7495af71363b8d5" +dependencies = [ + "ark-ec", + "ark-ff 0.5.0", + "ark-serialize 0.5.0", + "ark-std 0.5.0", +] + +[[package]] +name = "ark-bn254" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d69eab57e8d2663efa5c63135b2af4f396d66424f88954c21104125ab6b3e6bc" +dependencies = [ + "ark-ec", + "ark-ff 0.5.0", + "ark-std 0.5.0", +] + +[[package]] +name = "ark-ec" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d68f2d516162846c1238e755a7c4d131b892b70cc70c471a8e3ca3ed818fce" +dependencies = [ + "ahash", + "ark-ff 0.5.0", + "ark-poly", + "ark-serialize 0.5.0", + "ark-std 0.5.0", + "educe", + "fnv", + "hashbrown 0.15.4", + "itertools 0.13.0", + "num-bigint", + "num-integer", + "num-traits", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b3235cc41ee7a12aaaf2c575a2ad7b46713a8a50bda2fc3b003a04845c05dd6" +dependencies = [ + "ark-ff-asm 0.3.0", + "ark-ff-macros 0.3.0", + "ark-serialize 0.3.0", + "ark-std 0.3.0", + "derivative", + "num-bigint", + "num-traits", + "paste", + "rustc_version 0.3.3", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm 0.4.2", + "ark-ff-macros 0.4.2", + "ark-serialize 0.4.2", + "ark-std 0.4.0", + "derivative", + "digest 0.10.7", + "itertools 0.10.5", + "num-bigint", + "num-traits", + "paste", + "rustc_version 0.4.1", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a177aba0ed1e0fbb62aa9f6d0502e9b46dad8c2eab04c14258a1212d2557ea70" +dependencies = [ + "ark-ff-asm 0.5.0", + "ark-ff-macros 0.5.0", + "ark-serialize 0.5.0", + "ark-std 0.5.0", + "arrayvec", + "digest 0.10.7", + "educe", + "itertools 0.13.0", + "num-bigint", + "num-traits", + "paste", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db02d390bf6643fb404d3d22d31aee1c4bc4459600aef9113833d17e786c6e44" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-asm" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60" +dependencies = [ + "quote", + "syn 2.0.104", +] + +[[package]] +name = "ark-ff-macros" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" +dependencies = [ + "num-bigint", + "num-traits", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09be120733ee33f7693ceaa202ca41accd5653b779563608f1234f78ae07c4b3" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "ark-poly" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "579305839da207f02b89cd1679e50e67b4331e2f9294a57693e5051b7703fe27" +dependencies = [ + "ahash", + "ark-ff 0.5.0", + "ark-serialize 0.5.0", + "ark-std 0.5.0", + "educe", + "fnv", + "hashbrown 0.15.4", +] + +[[package]] +name = "ark-serialize" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6c2b318ee6e10f8c2853e73a83adc0ccb88995aa978d8a3408d492ab2ee671" +dependencies = [ + "ark-std 0.3.0", + "digest 0.9.0", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-std 0.4.0", + "digest 0.10.7", + "num-bigint", +] + +[[package]] +name = "ark-serialize" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f4d068aaf107ebcd7dfb52bc748f8030e0fc930ac8e360146ca54c1203088f7" +dependencies = [ + "ark-serialize-derive", + "ark-std 0.5.0", + "arrayvec", + "digest 0.10.7", + "num-bigint", +] + +[[package]] +name = "ark-serialize-derive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213888f660fddcca0d257e88e54ac05bca01885f258ccdf695bafd77031bb69d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "ark-std" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" +dependencies = [ + "num-traits", + "rand 0.8.5", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand 0.8.5", +] + +[[package]] +name = "ark-std" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "246a225cc6131e9ee4f24619af0f19d67761fff15d7ccc22e42b80846e69449a" +dependencies = [ + "num-traits", + "rand 0.8.5", +] + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +dependencies = [ + "serde", +] + +[[package]] +name = "aurora-engine-modexp" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "518bc5745a6264b5fd7b09dffb9667e400ee9e2bbe18555fac75e1fe9afa0df9" +dependencies = [ + "hex", + "num", +] + +[[package]] +name = "auto_impl" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdcb70bdbc4d478427380519163274ac86e52916e10f0a8889adf0f96d3fee7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" + +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + +[[package]] +name = "bitcoin-io" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b47c4ab7a93edb0c7198c5535ed9b52b63095f4e9b45279c6736cec4b856baf" + +[[package]] +name = "bitcoin_hashes" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16" +dependencies = [ + "bitcoin-io", + "hex-conservative", +] + +[[package]] +name = "bitflags" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +dependencies = [ + "serde", +] + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "serde", + "tap", + "wyz", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "blst" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fd49896f12ac9b6dcd7a5998466b9b58263a695a3dd1ecc1aaca2e12a90b080" +dependencies = [ + "cc", + "glob", + "threadpool", + "zeroize", +] + +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + +[[package]] +name = "byte-slice-cast" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7575182f7272186991736b70173b0ea045398f984bf5ebbb3804736ce1330c9d" + +[[package]] +name = "bytemuck" +version = "1.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +dependencies = [ + "serde", +] + +[[package]] +name = "c-kzg" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7318cfa722931cb5fe0838b98d3ce5621e75f6a6408abc21721d80de9223f2e4" +dependencies = [ + "blst", + "cc", + "glob", + "hex", + "libc", + "once_cell", + "serde", +] + +[[package]] +name = "cc" +version = "1.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc" +dependencies = [ + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" + +[[package]] +name = "chrono" +version = "0.4.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "serde", + "windows-link", +] + +[[package]] +name = "const-hex" +version = "1.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83e22e0ed40b96a48d3db274f72fd365bd78f67af39b6bbd47e8a15e1c6207ff" +dependencies = [ + "cfg-if", + "cpufeatures", + "hex", + "proptest", + "serde", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "const_format" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "126f97965c8ad46d6d9163268ff28432e8f6a1196a55578867832e3049df63dd" +dependencies = [ + "const_format_proc_macros", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "convert_case" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crc" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.104", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +dependencies = [ + "powerfmt", + "serde", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive-where" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "510c292c8cf384b1a340b816a9a6cf2599eb8f566a44949024af88418000c50b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "derive_more" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "syn 2.0.104", + "unicode-xid", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "dyn-clone" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest 0.10.7", + "elliptic-curve", + "rfc6979", + "serdect", + "signature", + "spki", +] + +[[package]] +name = "educe" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7bc049e1bd8cdeb31b68bbd586a9464ecf9f3944af3958a7a9d0f8b9799417" +dependencies = [ + "enum-ordinalize", + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +dependencies = [ + "serde", +] + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest 0.10.7", + "ff", + "generic-array", + "group", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "serdect", + "subtle", + "zeroize", +] + +[[package]] +name = "enum-ordinalize" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea0dcfa4e54eeb516fe454635a95753ddd39acda650ce703031c6973e315dd5" +dependencies = [ + "enum-ordinalize-derive", +] + +[[package]] +name = "enum-ordinalize-derive" +version = "4.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d28318a75d4aead5c4db25382e8ef717932d0346600cacae6357eb5941bc5ff" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "enumn" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "fastrlp" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139834ddba373bbdd213dffe02c8d110508dcf1726c2be27e8d1f7d7e1856418" +dependencies = [ + "arrayvec", + "auto_impl", + "bytes", +] + +[[package]] +name = "fastrlp" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce8dba4714ef14b8274c371879b175aa55b16b30f269663f19d576f380018dc4" +dependencies = [ + "arrayvec", + "auto_impl", + "bytes", +] + +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "fixed-hash" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" +dependencies = [ + "byteorder", + "rand 0.8.5", + "rustc-hex", + "static_assertions", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.1+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", +] + +[[package]] +name = "glob" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" +dependencies = [ + "allocator-api2", + "foldhash", + "serde", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +dependencies = [ + "serde", +] + +[[package]] +name = "hex-conservative" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5313b072ce3c597065a808dbf612c4c8e8590bdbf8b579508bf7a762c5eae6cd" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "icu_collections" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" + +[[package]] +name = "icu_properties" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "potential_utf", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" + +[[package]] +name = "icu_provider" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +dependencies = [ + "displaydoc", + "icu_locale_core", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "impl-codec" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +dependencies = [ + "equivalent", + "hashbrown 0.15.4", + "serde", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "jobserver" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" +dependencies = [ + "getrandom 0.3.3", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "k256" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "once_cell", + "serdect", + "sha2", +] + +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "keccak-asm" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "505d1856a39b200489082f90d897c3f07c455563880bc5952e38eabf731c83b6" +dependencies = [ + "digest 0.10.7", + "sha3-asm", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.174" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" + +[[package]] +name = "libm" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" + +[[package]] +name = "linux-raw-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" + +[[package]] +name = "litemap" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "macro-string" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b27834086c65ec3f9387b096d66e99f221cf081c2b738042aa252bcd41204e3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "memchr" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" + +[[package]] +name = "modular-bitfield" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a53d79ba8304ac1c4f9eb3b9d281f21f7be9d4626f72ce7df4ad8fbde4f38a74" +dependencies = [ + "modular-bitfield-impl", + "static_assertions", +] + +[[package]] +name = "modular-bitfield-impl" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a7d5f7076603ebc68de2dc6a650ec331a062a13abaa346975be747bbfa4b789" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "num_cpus" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "num_enum" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a" +dependencies = [ + "num_enum_derive", + "rustversion", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "nybbles" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8983bb634df7248924ee0c4c3a749609b5abcb082c28fffe3254b3eb3602b307" +dependencies = [ + "alloy-rlp", + "const-hex", + "proptest", + "serde", + "smallvec", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +dependencies = [ + "critical-section", + "portable-atomic", +] + +[[package]] +name = "op-alloy-consensus" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2423a125ef2daa0d15dacc361805a0b6f76d6acfc6e24a1ff6473582087fe75" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "derive_more", + "serde", + "serde_with", + "thiserror", +] + +[[package]] +name = "openvm" +version = "1.2.0" +source = "git+https://github.com/openvm-org/openvm.git?tag=v1.2.0#bdb4831fefed13b0741d3a052d434a9c995c6d5d" +dependencies = [ + "bytemuck", + "num-bigint", + "openvm-custom-insn", + "openvm-platform", + "openvm-rv32im-guest", + "serde", +] + +[[package]] +name = "openvm-compat" +version = "0.0.1" +dependencies = [ + "openvm", + "reth-chainspec", + "reth-ethereum-forks", + "reth-evm", + "reth-evm-ethereum", + "reth-execution-types", + "reth-primitives", + "reth-primitives-traits", + "reth-scroll-chainspec", + "reth-scroll-evm", + "reth-scroll-forks", + "reth-scroll-primitives", + "reth-storage-errors", + "reth-trie", + "reth-trie-sparse", + "scroll-alloy-consensus", + "scroll-alloy-rpc-types", +] + +[[package]] +name = "openvm-custom-insn" +version = "0.1.0" +source = "git+https://github.com/openvm-org/openvm.git?tag=v1.2.0#bdb4831fefed13b0741d3a052d434a9c995c6d5d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "openvm-platform" +version = "1.2.0" +source = "git+https://github.com/openvm-org/openvm.git?tag=v1.2.0#bdb4831fefed13b0741d3a052d434a9c995c6d5d" +dependencies = [ + "getrandom 0.2.16", + "libm", + "openvm-custom-insn", + "openvm-rv32im-guest", +] + +[[package]] +name = "openvm-rv32im-guest" +version = "1.2.0" +source = "git+https://github.com/openvm-org/openvm.git?tag=v1.2.0#bdb4831fefed13b0741d3a052d434a9c995c6d5d" +dependencies = [ + "openvm-custom-insn", + "strum_macros 0.26.4", +] + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "parity-scale-codec" +version = "3.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799781ae679d79a948e13d4824a40970bfa500058d245760dd857301059810fa" +dependencies = [ + "arrayvec", + "bitvec", + "byte-slice-cast", + "const_format", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "rustversion", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34b4653168b563151153c9e4c08ebed57fb8262bebfa79711552fa983c623e7a" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pest" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1db05f56d34358a8b1066f67cbb203ee3e7ed2ba674a6263a1d5ec6db2204323" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] + +[[package]] +name = "phf" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +dependencies = [ + "phf_macros", + "phf_shared", + "serde", +] + +[[package]] +name = "phf_generator" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" +dependencies = [ + "phf_shared", + "rand 0.8.5", +] + +[[package]] +name = "phf_macros" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "portable-atomic" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" + +[[package]] +name = "potential_utf" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +dependencies = [ + "zerovec", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + +[[package]] +name = "primitive-types" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" +dependencies = [ + "fixed-hash", + "impl-codec", + "uint", +] + +[[package]] +name = "proc-macro-crate" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proptest" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fcdab19deb5195a31cf7726a210015ff1496ba1464fd42cb4f537b8b01b471f" +dependencies = [ + "bit-set", + "bit-vec", + "bitflags", + "lazy_static", + "num-traits", + "rand 0.9.1", + "rand_chacha 0.9.0", + "rand_xorshift", + "regex-syntax", + "rusty-fork", + "tempfile", + "unarray", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", + "serde", +] + +[[package]] +name = "rand" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", + "serde", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.16", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.3", + "serde", +] + +[[package]] +name = "rand_xorshift" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" +dependencies = [ + "rand_core 0.9.3", +] + +[[package]] +name = "ref-cast" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "reth-chainspec" +version = "1.4.8" +dependencies = [ + "alloy-chains", + "alloy-consensus", + "alloy-eips", + "alloy-evm", + "alloy-genesis", + "alloy-primitives", + "alloy-trie", + "auto_impl", + "derive_more", + "reth-ethereum-forks", + "reth-network-peers", + "reth-primitives-traits", + "serde_json", +] + +[[package]] +name = "reth-codecs" +version = "1.4.8" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-genesis", + "alloy-primitives", + "alloy-trie", + "bytes", + "modular-bitfield", + "op-alloy-consensus", + "reth-codecs-derive", + "reth-zstd-compressors", + "serde", +] + +[[package]] +name = "reth-codecs-derive" +version = "1.4.8" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "reth-db-models" +version = "1.4.8" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "reth-primitives-traits", +] + +[[package]] +name = "reth-ethereum-forks" +version = "1.4.8" +dependencies = [ + "alloy-eip2124", + "alloy-hardforks", + "alloy-primitives", + "auto_impl", + "once_cell", +] + +[[package]] +name = "reth-ethereum-primitives" +version = "1.4.8" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "reth-codecs", + "reth-primitives-traits", + "serde", + "serde_with", +] + +[[package]] +name = "reth-evm" +version = "1.4.8" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-evm", + "alloy-primitives", + "auto_impl", + "derive_more", + "futures-util", + "reth-execution-errors", + "reth-execution-types", + "reth-primitives-traits", + "reth-storage-api", + "reth-storage-errors", + "reth-trie-common", + "revm", + "scroll-alloy-evm", +] + +[[package]] +name = "reth-evm-ethereum" +version = "1.4.8" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-evm", + "alloy-primitives", + "reth-chainspec", + "reth-ethereum-forks", + "reth-ethereum-primitives", + "reth-evm", + "reth-execution-types", + "reth-primitives-traits", + "revm", +] + +[[package]] +name = "reth-execution-errors" +version = "1.4.8" +dependencies = [ + "alloy-evm", + "alloy-primitives", + "alloy-rlp", + "nybbles", + "reth-storage-errors", + "thiserror", +] + +[[package]] +name = "reth-execution-types" +version = "1.4.8" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-evm", + "alloy-primitives", + "derive_more", + "reth-ethereum-primitives", + "reth-primitives-traits", + "reth-trie-common", + "revm", +] + +[[package]] +name = "reth-network-peers" +version = "1.4.8" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "serde_with", + "thiserror", + "url", +] + +[[package]] +name = "reth-primitives" +version = "1.4.8" +dependencies = [ + "alloy-consensus", + "once_cell", + "reth-ethereum-forks", + "reth-ethereum-primitives", + "reth-primitives-traits", + "reth-static-file-types", +] + +[[package]] +name = "reth-primitives-traits" +version = "1.4.8" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-genesis", + "alloy-primitives", + "alloy-rlp", + "alloy-trie", + "auto_impl", + "bytes", + "derive_more", + "once_cell", + "op-alloy-consensus", + "reth-codecs", + "revm-bytecode", + "revm-primitives", + "revm-state", + "scroll-alloy-consensus", + "secp256k1", + "serde", + "serde_with", + "thiserror", +] + +[[package]] +name = "reth-prune-types" +version = "1.4.8" +dependencies = [ + "alloy-primitives", + "derive_more", + "thiserror", +] + +[[package]] +name = "reth-scroll-chainspec" +version = "1.4.8" +dependencies = [ + "alloy-chains", + "alloy-consensus", + "alloy-eips", + "alloy-genesis", + "alloy-primitives", + "alloy-serde", + "derive_more", + "once_cell", + "reth-chainspec", + "reth-ethereum-forks", + "reth-network-peers", + "reth-primitives-traits", + "reth-scroll-forks", + "reth-trie-common", + "scroll-alloy-hardforks", + "serde", + "serde_json", +] + +[[package]] +name = "reth-scroll-evm" +version = "1.4.8" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-evm", + "alloy-primitives", + "derive_more", + "reth-chainspec", + "reth-evm", + "reth-execution-types", + "reth-primitives", + "reth-primitives-traits", + "reth-scroll-chainspec", + "reth-scroll-forks", + "reth-scroll-primitives", + "revm", + "revm-primitives", + "revm-scroll", + "scroll-alloy-consensus", + "scroll-alloy-evm", + "scroll-alloy-hardforks", + "thiserror", + "tracing", +] + +[[package]] +name = "reth-scroll-forks" +version = "1.4.8" +dependencies = [ + "alloy-chains", + "alloy-primitives", + "auto_impl", + "once_cell", + "reth-ethereum-forks", + "scroll-alloy-hardforks", +] + +[[package]] +name = "reth-scroll-primitives" +version = "1.4.8" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "bytes", + "once_cell", + "reth-codecs", + "reth-primitives-traits", + "scroll-alloy-consensus", + "serde", +] + +[[package]] +name = "reth-stages-types" +version = "1.4.8" +dependencies = [ + "alloy-primitives", + "reth-trie-common", +] + +[[package]] +name = "reth-static-file-types" +version = "1.4.8" +dependencies = [ + "alloy-primitives", + "derive_more", + "serde", + "strum", +] + +[[package]] +name = "reth-storage-api" +version = "1.4.8" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rpc-types-engine", + "auto_impl", + "reth-chainspec", + "reth-db-models", + "reth-ethereum-primitives", + "reth-execution-types", + "reth-primitives-traits", + "reth-prune-types", + "reth-stages-types", + "reth-storage-errors", + "reth-trie-common", + "revm-database", +] + +[[package]] +name = "reth-storage-errors" +version = "1.4.8" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "derive_more", + "reth-primitives-traits", + "reth-prune-types", + "reth-static-file-types", + "revm-database-interface", + "thiserror", +] + +[[package]] +name = "reth-trie" +version = "1.4.8" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "alloy-trie", + "auto_impl", + "itertools 0.14.0", + "reth-execution-errors", + "reth-primitives-traits", + "reth-stages-types", + "reth-storage-errors", + "reth-trie-common", + "reth-trie-sparse", + "revm-database", + "tracing", +] + +[[package]] +name = "reth-trie-common" +version = "1.4.8" +dependencies = [ + "alloy-consensus", + "alloy-primitives", + "alloy-rlp", + "alloy-trie", + "derive_more", + "itertools 0.14.0", + "nybbles", + "reth-primitives-traits", + "revm-database", +] + +[[package]] +name = "reth-trie-sparse" +version = "1.4.8" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "alloy-trie", + "auto_impl", + "reth-execution-errors", + "reth-primitives-traits", + "reth-trie-common", + "smallvec", + "tracing", +] + +[[package]] +name = "reth-zstd-compressors" +version = "1.4.8" +dependencies = [ + "zstd", +] + +[[package]] +name = "revm" +version = "24.0.0" +source = "git+https://github.com/scroll-tech/revm?branch=feat%2Freth-v74#774616019e9562b12cbe1c3f1cdd110793f8084c" +dependencies = [ + "revm-bytecode", + "revm-context", + "revm-context-interface", + "revm-database", + "revm-database-interface", + "revm-handler", + "revm-inspector", + "revm-interpreter", + "revm-precompile", + "revm-primitives", + "revm-state", +] + +[[package]] +name = "revm-bytecode" +version = "4.0.1" +source = "git+https://github.com/scroll-tech/revm?branch=feat%2Freth-v74#774616019e9562b12cbe1c3f1cdd110793f8084c" +dependencies = [ + "bitvec", + "once_cell", + "phf", + "revm-primitives", + "serde", +] + +[[package]] +name = "revm-context" +version = "5.0.0" +source = "git+https://github.com/scroll-tech/revm?branch=feat%2Freth-v74#774616019e9562b12cbe1c3f1cdd110793f8084c" +dependencies = [ + "cfg-if", + "derive-where", + "revm-bytecode", + "revm-context-interface", + "revm-database-interface", + "revm-primitives", + "revm-state", +] + +[[package]] +name = "revm-context-interface" +version = "5.0.0" +source = "git+https://github.com/scroll-tech/revm?branch=feat%2Freth-v74#774616019e9562b12cbe1c3f1cdd110793f8084c" +dependencies = [ + "alloy-eip2930", + "alloy-eip7702", + "auto_impl", + "either", + "revm-database-interface", + "revm-primitives", + "revm-state", +] + +[[package]] +name = "revm-database" +version = "4.0.1" +source = "git+https://github.com/scroll-tech/revm?branch=feat%2Freth-v74#774616019e9562b12cbe1c3f1cdd110793f8084c" +dependencies = [ + "revm-bytecode", + "revm-database-interface", + "revm-primitives", + "revm-state", +] + +[[package]] +name = "revm-database-interface" +version = "4.0.1" +source = "git+https://github.com/scroll-tech/revm?branch=feat%2Freth-v74#774616019e9562b12cbe1c3f1cdd110793f8084c" +dependencies = [ + "auto_impl", + "revm-primitives", + "revm-state", +] + +[[package]] +name = "revm-handler" +version = "5.0.0" +source = "git+https://github.com/scroll-tech/revm?branch=feat%2Freth-v74#774616019e9562b12cbe1c3f1cdd110793f8084c" +dependencies = [ + "auto_impl", + "revm-bytecode", + "revm-context", + "revm-context-interface", + "revm-database-interface", + "revm-interpreter", + "revm-precompile", + "revm-primitives", + "revm-state", +] + +[[package]] +name = "revm-inspector" +version = "5.0.0" +source = "git+https://github.com/scroll-tech/revm?branch=feat%2Freth-v74#774616019e9562b12cbe1c3f1cdd110793f8084c" +dependencies = [ + "auto_impl", + "revm-context", + "revm-database-interface", + "revm-handler", + "revm-interpreter", + "revm-primitives", + "revm-state", +] + +[[package]] +name = "revm-interpreter" +version = "20.0.0" +source = "git+https://github.com/scroll-tech/revm?branch=feat%2Freth-v74#774616019e9562b12cbe1c3f1cdd110793f8084c" +dependencies = [ + "revm-bytecode", + "revm-context-interface", + "revm-primitives", +] + +[[package]] +name = "revm-precompile" +version = "21.0.0" +source = "git+https://github.com/scroll-tech/revm?branch=feat%2Freth-v74#774616019e9562b12cbe1c3f1cdd110793f8084c" +dependencies = [ + "ark-bls12-381", + "ark-bn254", + "ark-ec", + "ark-ff 0.5.0", + "ark-serialize 0.5.0", + "aurora-engine-modexp", + "cfg-if", + "k256", + "once_cell", + "p256", + "revm-primitives", + "ripemd", + "sha2", +] + +[[package]] +name = "revm-primitives" +version = "19.1.0" +source = "git+https://github.com/scroll-tech/revm?branch=feat%2Freth-v74#774616019e9562b12cbe1c3f1cdd110793f8084c" +dependencies = [ + "alloy-primitives", + "num_enum", + "serde", +] + +[[package]] +name = "revm-scroll" +version = "0.1.0" +source = "git+https://github.com/scroll-tech/scroll-revm#0195a04190cef78901ed67c7bc3048114034f366" +dependencies = [ + "auto_impl", + "enumn", + "once_cell", + "revm", + "revm-inspector", + "revm-primitives", +] + +[[package]] +name = "revm-state" +version = "4.0.1" +source = "git+https://github.com/scroll-tech/revm?branch=feat%2Freth-v74#774616019e9562b12cbe1c3f1cdd110793f8084c" +dependencies = [ + "bitflags", + "revm-bytecode", + "revm-primitives", + "serde", +] + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "ripemd" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "rlp" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" +dependencies = [ + "bytes", + "rustc-hex", +] + +[[package]] +name = "ruint" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11256b5fe8c68f56ac6f39ef0720e592f33d2367a4782740d9c9142e889c7fb4" +dependencies = [ + "alloy-rlp", + "ark-ff 0.3.0", + "ark-ff 0.4.2", + "bytes", + "fastrlp 0.3.1", + "fastrlp 0.4.0", + "num-bigint", + "num-integer", + "num-traits", + "parity-scale-codec", + "primitive-types", + "proptest", + "rand 0.8.5", + "rand 0.9.1", + "rlp", + "ruint-macro", + "serde", + "valuable", + "zeroize", +] + +[[package]] +name = "ruint-macro" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "rustc_version" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +dependencies = [ + "semver 0.11.0", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver 1.0.26", +] + +[[package]] +name = "rustix" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustversion" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" + +[[package]] +name = "rusty-fork" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" +dependencies = [ + "fnv", + "quick-error", + "tempfile", + "wait-timeout", +] + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "scroll-alloy-consensus" +version = "1.4.8" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "derive_more", + "reth-codecs", + "serde", + "serde_with", +] + +[[package]] +name = "scroll-alloy-evm" +version = "1.4.8" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-evm", + "alloy-primitives", + "auto_impl", + "revm", + "revm-scroll", + "scroll-alloy-consensus", + "scroll-alloy-hardforks", +] + +[[package]] +name = "scroll-alloy-hardforks" +version = "1.4.8" +dependencies = [ + "alloy-hardforks", + "auto_impl", +] + +[[package]] +name = "scroll-alloy-rpc-types" +version = "1.4.8" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-network-primitives", + "alloy-primitives", + "alloy-rpc-types-eth", + "alloy-serde", + "derive_more", + "scroll-alloy-consensus", + "serde", + "serde_json", +] + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "serdect", + "subtle", + "zeroize", +] + +[[package]] +name = "secp256k1" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b50c5943d326858130af85e049f2661ba3c78b26589b8ab98e65e80ae44a1252" +dependencies = [ + "bitcoin_hashes", + "rand 0.8.5", + "secp256k1-sys", + "serde", +] + +[[package]] +name = "secp256k1-sys" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4387882333d3aa8cb20530a17c69a3752e97837832f34f6dccc760e715001d9" +dependencies = [ + "cc", +] + +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" + +[[package]] +name = "semver-parser" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9900206b54a3527fdc7b8a938bffd94a568bac4f4aa8113b209df75a09c0dec2" +dependencies = [ + "pest", +] + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "serde_json" +version = "1.0.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf65a400f8f66fb7b0552869ad70157166676db75ed8181f8104ea91cf9d0b42" +dependencies = [ + "base64", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.9.0", + "schemars", + "serde", + "serde_derive", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81679d9ed988d5e9a5e6531dc3f2c28efbd639cbd1dfb628df08edea6004da77" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "serdect" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a84f14a19e9a014bb9f4512488d9829a68e04ecabffb0f9904cd1ace94598177" +dependencies = [ + "base16ct", + "serde", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest 0.10.7", + "keccak", +] + +[[package]] +name = "sha3-asm" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28efc5e327c837aa837c59eae585fc250715ef939ac32881bcc11677cd02d46" +dependencies = [ + "cc", + "cfg-if", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest 0.10.7", + "rand_core 0.6.4", +] + +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +dependencies = [ + "serde", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "strum" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f64def088c51c9510a8579e3c5d67c65349dcf755e5479ad3d010aa6454e2c32" +dependencies = [ + "strum_macros 0.27.1", +] + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.104", +] + +[[package]] +name = "strum_macros" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c77a8c5abcaf0f9ce05d62342b7d298c346515365c36b673df4ebe3ced01fde8" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.104", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn-solidity" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ac494e7266fcdd2ad80bf4375d55d27a117ea5c866c26d0e97fe5b3caeeb75" +dependencies = [ + "paste", + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "tempfile" +version = "3.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" +dependencies = [ + "fastrand", + "getrandom 0.3.3", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", +] + +[[package]] +name = "time" +version = "0.3.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" + +[[package]] +name = "time-macros" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "tinystr" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" + +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +dependencies = [ + "indexmap 2.9.0", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "tracing-core" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +dependencies = [ + "once_cell", +] + +[[package]] +name = "typenum" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" + +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + +[[package]] +name = "uint" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "url" +version = "2.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wait-timeout" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" +dependencies = [ + "libc", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn 2.0.104", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "windows-core" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.2", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef" +dependencies = [ + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + +[[package]] +name = "winnow" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd" +dependencies = [ + "memchr", +] + +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags", +] + +[[package]] +name = "writeable" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "yoke" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "zerotrie" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "zstd" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "7.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.15+zstd.1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb81183ddd97d0c74cedf1d50d85c8d08c1b8b68ee863bdee9e706eedba1a237" +dependencies = [ + "cc", + "pkg-config", +]
diff --git reth/crates/scroll/openvm-compat/Cargo.toml scroll-reth/crates/scroll/openvm-compat/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..067990ba5fb48c724cac1996b368a1b3ae3eed9a --- /dev/null +++ scroll-reth/crates/scroll/openvm-compat/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "openvm-compat" +version = "0.0.1" +edition = "2024" + +[workspace] + +[dependencies] +openvm = { git = "https://github.com/openvm-org/openvm.git", tag = "v1.2.0", default-features = false } + +reth-chainspec = { path = "../../chainspec", default-features = false } +reth-evm = { path = "../../evm/evm", default-features = false } +reth-evm-ethereum = { path = "../../ethereum/evm", default-features = false } +reth-ethereum-forks = { path = "../../ethereum/hardforks", default-features = false } +reth-execution-types = { path = "../../evm/execution-types", default-features = false } +reth-primitives = { path = "../../primitives", default-features = false } +reth-primitives-traits = { path = "../../primitives-traits", default-features = false } +reth-storage-errors = { path = "../../storage/errors", default-features = false } +reth-trie = { path = "../../trie/trie", default-features = false } +reth-trie-sparse = { path = "../../trie/sparse", default-features = false } + +reth-scroll-chainspec = { path = "../chainspec", default-features = false } +reth-scroll-evm = { path = "../evm", default-features = false } +reth-scroll-forks = { path = "../hardforks", default-features = false } +reth-scroll-primitives = { path = "../primitives", default-features = false } + +scroll-alloy-consensus = { path = "../alloy/consensus", default-features = false } +scroll-alloy-rpc-types = { path = "../alloy/rpc-types", default-features = false } + +[patch.crates-io] +revm = { git = "https://github.com/scroll-tech/revm", branch = "feat/reth-v74" }
diff --git reth/crates/scroll/openvm-compat/src/main.rs scroll-reth/crates/scroll/openvm-compat/src/main.rs new file mode 100644 index 0000000000000000000000000000000000000000..8fd9e8ae9c79267a45b20339a75ce2ed5de8f199 --- /dev/null +++ scroll-reth/crates/scroll/openvm-compat/src/main.rs @@ -0,0 +1,9 @@ +//! `OpenVM` compatibility bin. + +#![no_main] +#![no_std] + +openvm::entry!(main); + +#[allow(dead_code)] +const fn main() {}
diff --git reth/crates/scroll/payload/Cargo.toml scroll-reth/crates/scroll/payload/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..8cf2e616f42fb3b2135610b0851c925460bef66b --- /dev/null +++ scroll-reth/crates/scroll/payload/Cargo.toml @@ -0,0 +1,61 @@ +[package] +name = "reth-scroll-payload" +version.workspace = true +edition.workspace = true +rust-version.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +exclude.workspace = true + +[lints] +workspace = true + +[dependencies] +# alloy +alloy-rlp.workspace = true +alloy-consensus.workspace = true +alloy-primitives.workspace = true + +# scroll-alloy +reth-scroll-evm.workspace = true +scroll-alloy-hardforks.workspace = true +scroll-alloy-evm.workspace = true + +# revm +revm.workspace = true + +# reth +reth-basic-payload-builder.workspace = true +reth-chainspec.workspace = true +reth-chain-state.workspace = true +reth-evm.workspace = true +reth-execution-types.workspace = true +reth-payload-builder.workspace = true +reth-payload-primitives.workspace = true +reth-primitives-traits.workspace = true +reth-revm.workspace = true +reth-storage-api.workspace = true +reth-transaction-pool.workspace = true +reth-payload-util.workspace = true + +# scroll +reth-scroll-primitives.workspace = true +reth-scroll-engine-primitives.workspace = true + +# misc +futures-util = { workspace = true, optional = true } +thiserror.workspace = true +tracing.workspace = true + +[features] +test-utils = [ + "dep:futures-util", + "reth-payload-builder/test-utils", + "reth-primitives-traits/test-utils", + "reth-transaction-pool/test-utils", + "reth-chain-state/test-utils", + "reth-chainspec/test-utils", + "reth-evm/test-utils", + "reth-revm/test-utils", +]
diff --git reth/crates/scroll/payload/src/base_fee.rs scroll-reth/crates/scroll/payload/src/base_fee.rs new file mode 100644 index 0000000000000000000000000000000000000000..978a89662d5985a95240eba5125b76c539f4c06a --- /dev/null +++ scroll-reth/crates/scroll/payload/src/base_fee.rs @@ -0,0 +1,97 @@ +use alloy_primitives::U256; +use revm::{database::State, Database}; +use scroll_alloy_evm::curie::L1_GAS_PRICE_ORACLE_ADDRESS; + +/// L1 gas price oracle base fee slot. +pub const L1_BASE_FEE_SLOT: U256 = U256::from_limbs([1, 0, 0, 0]); + +/// Protocol-enforced maximum L2 base fee. +pub const MAX_L2_BASE_FEE: U256 = U256::from_limbs([10_000_000_000, 0, 0, 0]); + +/// The base fee overhead. +pub const L1_BASE_FEE_OVERHEAD: U256 = U256::from_limbs([15_680_000, 0, 0, 0]); + +/// The scalar applied on the L1 base fee. +pub const L1_BASE_FEE_SCALAR: U256 = U256::from_limbs([34_000_000_000_000, 0, 0, 0]); + +/// The precision of the L1 base fee. +pub const L1_BASE_FEE_PRECISION: U256 = U256::from_limbs([1_000_000_000_000_000_000, 0, 0, 0]); + +/// An instance of the trait can return the current base fee for block building. +pub trait PayloadBuildingBaseFeeProvider { + /// The error type. + type Error; + + /// Returns the base fee for block building. + fn payload_building_base_fee(&mut self) -> Result<U256, Self::Error>; +} + +impl<DB> PayloadBuildingBaseFeeProvider for State<DB> +where + DB: Database, +{ + type Error = DB::Error; + + fn payload_building_base_fee(&mut self) -> Result<U256, Self::Error> { + // load account into cache. + let _ = self.load_cache_account(L1_GAS_PRICE_ORACLE_ADDRESS)?; + + // query storage. + let parent_l1_base_fee = self.storage(L1_GAS_PRICE_ORACLE_ADDRESS, L1_BASE_FEE_SLOT)?; + + // l1 base fee * scalar / precision + overhead. + let mut base_fee = + parent_l1_base_fee * L1_BASE_FEE_SCALAR / L1_BASE_FEE_PRECISION + L1_BASE_FEE_OVERHEAD; + + if base_fee > MAX_L2_BASE_FEE { + base_fee = MAX_L2_BASE_FEE; + } + + Ok(base_fee) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::boxed::Box; + + use reth_revm::db::states::plain_account::PlainStorage; + use revm::database::EmptyDB; + + #[test] + fn test_should_return_correct_base_fee() -> Result<(), Box<dyn core::error::Error>> { + let db = EmptyDB::new(); + let mut state = + State::builder().with_database(db).with_bundle_update().without_state_clear().build(); + + let test_values = [ + (0u64, 15680000u64), + (1000000000, 15714000), + (2000000000, 15748000), + (100000000000, 19080000), + (111111111111, 19457777), + (2164000000000, 89256000), + (644149677419355, 10000000000), + (0x1c3c0f442u64, 15937691), + ]; + + for (l1_base_fee, expected_base_fee) in test_values { + // insert the l1 base fee. + let oracle_storage_pre_fork = + PlainStorage::from_iter([(U256::from(1), U256::from(l1_base_fee))]); + state.insert_account_with_storage( + L1_GAS_PRICE_ORACLE_ADDRESS, + Default::default(), + oracle_storage_pre_fork, + ); + + // fetch base fee from db. + let base_fee = state.payload_building_base_fee()?; + let expected_base_fee = U256::from(expected_base_fee); + assert_eq!(base_fee, expected_base_fee); + } + + Ok(()) + } +}
diff --git reth/crates/scroll/payload/src/builder.rs scroll-reth/crates/scroll/payload/src/builder.rs new file mode 100644 index 0000000000000000000000000000000000000000..166e9cfc372e669324830b39504ca34f326d3704 --- /dev/null +++ scroll-reth/crates/scroll/payload/src/builder.rs @@ -0,0 +1,569 @@ +//! Scroll's payload builder implementation. + +use super::{PayloadBuildingBaseFeeProvider, ScrollPayloadBuilderError}; +use crate::config::{PayloadBuildingBreaker, ScrollBuilderConfig}; + +use alloy_consensus::{Transaction, Typed2718}; +use alloy_primitives::{B256, U256}; +use alloy_rlp::Encodable; +use core::fmt::Debug; +use reth_basic_payload_builder::{ + is_better_payload, BuildArguments, BuildOutcome, BuildOutcomeKind, MissingPayloadBehaviour, + PayloadBuilder, PayloadConfig, +}; +use reth_chain_state::{ExecutedBlock, ExecutedBlockWithTrieUpdates, ExecutedTrieUpdates}; +use reth_chainspec::{ChainSpecProvider, EthChainSpec}; +use reth_evm::{ + block::{BlockExecutionError, BlockValidationError}, + execute::{BlockBuilder, BlockBuilderOutcome, ProviderError}, + ConfigureEvm, Database, Evm, +}; +use reth_execution_types::ExecutionOutcome; +use reth_payload_builder::PayloadId; +use reth_payload_primitives::{PayloadBuilderAttributes, PayloadBuilderError}; +use reth_payload_util::{BestPayloadTransactions, NoopPayloadTransactions, PayloadTransactions}; +use reth_primitives_traits::{ + NodePrimitives, RecoveredBlock, SealedHeader, SignedTransaction, TxTy, +}; +use reth_revm::{cancelled::CancelOnDrop, database::StateProviderDatabase, db::State}; +use reth_scroll_engine_primitives::{ScrollBuiltPayload, ScrollPayloadBuilderAttributes}; +use reth_scroll_evm::ScrollNextBlockEnvAttributes; +use reth_scroll_primitives::{ScrollPrimitives, ScrollTransactionSigned}; +use reth_storage_api::{StateProvider, StateProviderFactory}; +use reth_transaction_pool::{BestTransactionsAttributes, PoolTransaction, TransactionPool}; +use revm::context::{Block, BlockEnv}; +use scroll_alloy_hardforks::ScrollHardforks; +use std::{boxed::Box, sync::Arc, vec, vec::Vec}; + +/// A type that returns the [`PayloadTransactions`] that should be included in the pool. +pub trait ScrollPayloadTransactions<Transaction>: Clone + Send + Sync + Unpin + 'static { + /// Returns an iterator that yields the transaction in the order they should get included in the + /// new payload. + fn best_transactions<Pool: TransactionPool<Transaction = Transaction>>( + &self, + pool: Pool, + attr: BestTransactionsAttributes, + ) -> impl PayloadTransactions<Transaction = Transaction>; +} + +impl<T: PoolTransaction> ScrollPayloadTransactions<T> for () { + fn best_transactions<Pool: TransactionPool<Transaction = T>>( + &self, + pool: Pool, + attr: BestTransactionsAttributes, + ) -> impl PayloadTransactions<Transaction = T> { + BestPayloadTransactions::new(pool.best_transactions_with_attributes(attr)) + } +} + +/// Scroll's payload builder. +#[derive(Clone, Debug)] +pub struct ScrollPayloadBuilder<Pool, Client, Evm, Txs = ()> { + /// The type responsible for creating the evm. + pub evm_config: Evm, + /// Transaction pool. + pub pool: Pool, + /// Node client + pub client: Client, + /// The type responsible for yielding the best transactions to include in a payload. + pub best_transactions: Txs, + /// Payload builder configuration. + pub builder_config: ScrollBuilderConfig, +} + +impl<Pool, Evm, Client> ScrollPayloadBuilder<Pool, Client, Evm> { + /// Creates a new [`ScrollPayloadBuilder`]. + pub const fn new( + pool: Pool, + evm_config: Evm, + client: Client, + builder_config: ScrollBuilderConfig, + ) -> Self { + Self { evm_config, pool, client, best_transactions: (), builder_config } + } +} + +impl<Pool, Client, Evm, Txs> ScrollPayloadBuilder<Pool, Client, Evm, Txs> { + /// Configures the type responsible for yielding the transactions that should be included in the + /// payload. + pub fn with_transactions<T>( + self, + best_transactions: T, + ) -> ScrollPayloadBuilder<Pool, Client, Evm, T> { + let Self { evm_config, pool, client, builder_config, .. } = self; + ScrollPayloadBuilder { evm_config, pool, client, best_transactions, builder_config } + } +} + +impl<Pool, Client, Evm, T> ScrollPayloadBuilder<Pool, Client, Evm, T> +where + Pool: TransactionPool<Transaction: PoolTransaction<Consensus = ScrollTransactionSigned>>, + Client: StateProviderFactory + ChainSpecProvider<ChainSpec: EthChainSpec + ScrollHardforks>, + Evm: + ConfigureEvm<Primitives = ScrollPrimitives, NextBlockEnvCtx = ScrollNextBlockEnvAttributes>, +{ + /// Constructs a Scroll payload from the transactions sent via the + /// Payload attributes by the sequencer. If the `no_tx_pool` argument is passed in + /// the payload attributes, the transaction pool will be ignored and the only transactions + /// included in the payload will be those sent through the attributes. + /// + /// Given build arguments including a Scroll client, transaction pool, + /// and configuration, this function creates a transaction payload. Returns + /// a result indicating success with the payload or an error in case of failure. + fn build_payload<'a, Txs>( + &self, + args: BuildArguments<ScrollPayloadBuilderAttributes, ScrollBuiltPayload>, + best: impl FnOnce(BestTransactionsAttributes) -> Txs + Send + Sync + 'a, + ) -> Result<BuildOutcome<ScrollBuiltPayload>, PayloadBuilderError> + where + Txs: PayloadTransactions<Transaction: PoolTransaction<Consensus = ScrollTransactionSigned>>, + { + let BuildArguments { mut cached_reads, config, cancel, best_payload } = args; + + let ctx = ScrollPayloadBuilderCtx { + evm_config: self.evm_config.clone(), + chain_spec: self.client.chain_spec(), + config, + cancel, + best_payload, + }; + + let builder = ScrollBuilder::new(best); + + let state_provider = self.client.state_by_block_hash(ctx.parent().hash())?; + let state = StateProviderDatabase::new(&state_provider); + + if ctx.attributes().no_tx_pool { + builder.build(state, &state_provider, ctx, &self.builder_config) + } else { + // sequencer mode we can reuse cachedreads from previous runs + builder.build(cached_reads.as_db_mut(state), &state_provider, ctx, &self.builder_config) + } + .map(|out| out.with_cached_reads(cached_reads)) + } +} + +/// Implementation of the [`PayloadBuilder`] trait for [`ScrollPayloadBuilder`]. +impl<Pool, Client, Evm, Txs> PayloadBuilder for ScrollPayloadBuilder<Pool, Client, Evm, Txs> +where + Client: + StateProviderFactory + ChainSpecProvider<ChainSpec: EthChainSpec + ScrollHardforks> + Clone, + Pool: TransactionPool<Transaction: PoolTransaction<Consensus = ScrollTransactionSigned>>, + Evm: + ConfigureEvm<Primitives = ScrollPrimitives, NextBlockEnvCtx = ScrollNextBlockEnvAttributes>, + Txs: ScrollPayloadTransactions<Pool::Transaction>, +{ + type Attributes = ScrollPayloadBuilderAttributes; + type BuiltPayload = ScrollBuiltPayload; + + fn try_build( + &self, + args: BuildArguments<Self::Attributes, Self::BuiltPayload>, + ) -> Result<BuildOutcome<Self::BuiltPayload>, PayloadBuilderError> { + let pool = self.pool.clone(); + self.build_payload(args, |attrs| self.best_transactions.best_transactions(pool, attrs)) + } + + fn on_missing_payload( + &self, + _args: BuildArguments<Self::Attributes, Self::BuiltPayload>, + ) -> MissingPayloadBehaviour<Self::BuiltPayload> { + // we want to await the job that's already in progress because that should be returned as + // is, there's no benefit in racing another job + MissingPayloadBehaviour::AwaitInProgress + } + + // NOTE: this should only be used for testing purposes because this doesn't have access to L1 + // system txs, hence on_missing_payload we return [MissingPayloadBehaviour::AwaitInProgress]. + fn build_empty_payload( + &self, + config: PayloadConfig<Self::Attributes>, + ) -> Result<Self::BuiltPayload, PayloadBuilderError> { + let args = BuildArguments { + config, + cached_reads: Default::default(), + cancel: Default::default(), + best_payload: None, + }; + self.build_payload(args, |_| NoopPayloadTransactions::<Pool::Transaction>::default())? + .into_payload() + .ok_or_else(|| PayloadBuilderError::MissingPayload) + } +} + +/// A builder for a new payload. +pub struct ScrollBuilder<'a, Txs> { + /// Yields the best transaction to include if transactions from the mempool are allowed. + best: Box<dyn FnOnce(BestTransactionsAttributes) -> Txs + 'a>, +} + +impl<'a, Txs> ScrollBuilder<'a, Txs> { + /// Creates a new [`ScrollBuilder`]. + pub fn new(best: impl FnOnce(BestTransactionsAttributes) -> Txs + Send + Sync + 'a) -> Self { + Self { best: Box::new(best) } + } +} + +impl<'a, Txs> std::fmt::Debug for ScrollBuilder<'a, Txs> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ScrollBuilder").finish() + } +} + +impl<Txs> ScrollBuilder<'_, Txs> { + /// Builds the payload on top of the state. + pub fn build<EvmConfig, ChainSpec>( + self, + db: impl Database<Error = ProviderError>, + state_provider: impl StateProvider, + ctx: ScrollPayloadBuilderCtx<EvmConfig, ChainSpec>, + builder_config: &ScrollBuilderConfig, + ) -> Result<BuildOutcomeKind<ScrollBuiltPayload>, PayloadBuilderError> + where + EvmConfig: ConfigureEvm< + Primitives = ScrollPrimitives, + NextBlockEnvCtx = ScrollNextBlockEnvAttributes, + >, + ChainSpec: EthChainSpec + ScrollHardforks, + Txs: PayloadTransactions<Transaction: PoolTransaction<Consensus = ScrollTransactionSigned>>, + { + let Self { best } = self; + tracing::debug!(target: "payload_builder", id=%ctx.payload_id(), parent_header = ?ctx.parent().hash(), parent_number = ctx.parent().number, "building new payload"); + let breaker = builder_config.breaker(); + + let mut db = State::builder().with_database(db).with_bundle_update().build(); + + let mut builder = ctx.block_builder(&mut db, builder_config)?; + + // 1. apply pre-execution changes + builder.apply_pre_execution_changes().map_err(|err| { + tracing::warn!(target: "payload_builder", %err, "failed to apply pre-execution changes"); + PayloadBuilderError::Internal(err.into()) + })?; + + // 2. execute sequencer transactions + let mut info = ctx.execute_sequencer_transactions(&mut builder)?; + + // 3. if mem pool transactions are requested we execute them + if !ctx.attributes().no_tx_pool { + let best_txs = best(ctx.best_transaction_attributes(builder.evm_mut().block())); + if ctx.execute_best_transactions(&mut info, &mut builder, best_txs, breaker)?.is_some() + { + return Ok(BuildOutcomeKind::Cancelled); + } + + // check if the new payload is even more valuable + if !ctx.is_better_payload(info.total_fees) { + // can skip building the block + return Ok(BuildOutcomeKind::Aborted { fees: info.total_fees }) + } + } + + let BlockBuilderOutcome { execution_result, hashed_state, trie_updates, mut block } = + builder.finish(state_provider)?; + + // set the block extra data and difficulty fields using the payload attributes. + if let Some(block_data) = &ctx.config.attributes.block_data_hint { + let (mut scroll_block, senders) = block.split(); + scroll_block = scroll_block.map_header(|mut header| { + header.extra_data = block_data.extra_data.clone(); + header.difficulty = block_data.difficulty; + header + }); + block = RecoveredBlock::new_unhashed(scroll_block, senders) + } + + let sealed_block = Arc::new(block.sealed_block().clone()); + tracing::debug!(target: "payload_builder", id=%ctx.attributes().payload_id(), sealed_block_header = ?sealed_block.header(), "sealed built block"); + + let execution_outcome = ExecutionOutcome::new( + db.take_bundle(), + vec![execution_result.receipts], + block.number, + Vec::new(), + ); + + // create the executed block data + let executed: ExecutedBlockWithTrieUpdates<ScrollPrimitives> = + ExecutedBlockWithTrieUpdates { + block: ExecutedBlock { + recovered_block: Arc::new(block), + execution_output: Arc::new(execution_outcome), + hashed_state: Arc::new(hashed_state), + }, + trie: ExecutedTrieUpdates::Present(Arc::new(trie_updates)), + }; + + let no_tx_pool = ctx.attributes().no_tx_pool; + + let payload = ScrollBuiltPayload::new( + ctx.payload_id(), + sealed_block, + Some(executed), + info.total_fees, + ); + + if no_tx_pool { + // if `no_tx_pool` is set only transactions from the payload attributes will be included + // in the payload. In other words, the payload is deterministic and we can + // freeze it once we've successfully built it. + Ok(BuildOutcomeKind::Freeze(payload)) + } else { + Ok(BuildOutcomeKind::Better { payload }) + } + } +} + +/// Container type that holds all necessities to build a new payload. +#[derive(Debug)] +pub struct ScrollPayloadBuilderCtx<Evm: ConfigureEvm, ChainSpec> { + /// The type that knows how to perform system calls and configure the evm. + pub evm_config: Evm, + /// The chainspec + pub chain_spec: ChainSpec, + /// How to build the payload. + pub config: PayloadConfig<ScrollPayloadBuilderAttributes>, + /// Marker to check whether the job has been cancelled. + pub cancel: CancelOnDrop, + /// The currently best payload. + pub best_payload: Option<ScrollBuiltPayload>, +} + +impl<Evm, ChainSpec> ScrollPayloadBuilderCtx<Evm, ChainSpec> +where + Evm: + ConfigureEvm<Primitives = ScrollPrimitives, NextBlockEnvCtx = ScrollNextBlockEnvAttributes>, + ChainSpec: EthChainSpec + ScrollHardforks, +{ + /// Returns the parent block the payload will be build on. + #[allow(clippy::missing_const_for_fn)] + pub fn parent(&self) -> &SealedHeader { + &self.config.parent_header + } + + /// Returns the builder attributes. + pub const fn attributes(&self) -> &ScrollPayloadBuilderAttributes { + &self.config.attributes + } + + /// Returns the current fee settings for transactions from the mempool + pub fn best_transaction_attributes(&self, block_env: &BlockEnv) -> BestTransactionsAttributes { + BestTransactionsAttributes::new( + block_env.basefee, + block_env.blob_gasprice().map(|p| p as u64), + ) + } + + /// Returns the unique id for this payload job. + pub fn payload_id(&self) -> PayloadId { + self.attributes().payload_id() + } + + /// Returns true if the fees are higher than the previous payload. + pub fn is_better_payload(&self, total_fees: U256) -> bool { + is_better_payload(self.best_payload.as_ref(), total_fees) + } + + /// Prepares a [`BlockBuilder`] for the next block. + pub fn block_builder<'a, DB: Database>( + &'a self, + db: &'a mut State<DB>, + builder_config: &ScrollBuilderConfig, + ) -> Result<impl BlockBuilder<Primitives = Evm::Primitives> + 'a, PayloadBuilderError> { + // get the base fee for the attributes. + let base_fee: u64 = if self.chain_spec.is_curie_active_at_block(self.parent().number + 1) { + db.payload_building_base_fee() + .map_err(|err| PayloadBuilderError::Other(Box::new(err)))? + .try_into() + .expect("base fee limited to 10_000_000_000") + } else { + 0 + }; + + self.evm_config + .builder_for_next_block( + db, + self.parent(), + ScrollNextBlockEnvAttributes { + timestamp: self.attributes().timestamp(), + suggested_fee_recipient: self.attributes().suggested_fee_recipient(), + gas_limit: builder_config.gas_limit, + base_fee, + }, + ) + .map_err(PayloadBuilderError::other) + } +} + +impl<Evm, ChainSpec> ScrollPayloadBuilderCtx<Evm, ChainSpec> +where + Evm: + ConfigureEvm<Primitives = ScrollPrimitives, NextBlockEnvCtx = ScrollNextBlockEnvAttributes>, + ChainSpec: EthChainSpec + ScrollHardforks, +{ + /// Executes all sequencer transactions that are included in the payload attributes. + pub fn execute_sequencer_transactions( + &self, + builder: &mut impl BlockBuilder<Primitives = Evm::Primitives>, + ) -> Result<ExecutionInfo, PayloadBuilderError> { + let mut info = ExecutionInfo::new(); + + for sequencer_tx in &self.attributes().transactions { + // A sequencer's block should never contain blob transactions. + if sequencer_tx.value().is_eip4844() { + return Err(PayloadBuilderError::other( + ScrollPayloadBuilderError::BlobTransactionRejected, + )) + } + + // Convert the transaction to a [RecoveredTx]. This is + // purely for the purposes of utilizing the `evm_config.tx_env`` function. + // Deposit transactions do not have signatures, so if the tx is a deposit, this + // will just pull in its `from` address. + let sequencer_tx = sequencer_tx.value().try_clone_into_recovered().map_err(|_| { + PayloadBuilderError::other(ScrollPayloadBuilderError::TransactionEcRecoverFailed) + })?; + + let gas_used = match builder.execute_transaction(sequencer_tx.clone()) { + Ok(gas_used) => gas_used, + Err(BlockExecutionError::Validation(BlockValidationError::InvalidTx { + error, + .. + })) => { + tracing::trace!(target: "payload_builder", %error, ?sequencer_tx, "Error in sequencer transaction, skipping."); + continue + } + Err(err) => { + // this is an error that we should treat as fatal for this attempt + return Err(PayloadBuilderError::EvmExecutionError(Box::new(err))) + } + }; + + // add gas used by the transaction to cumulative gas used, before creating the receipt + info.cumulative_gas_used += gas_used; + } + + Ok(info) + } + + /// Executes the given best transactions and updates the execution info. + /// + /// Returns `Ok(Some(())` if the job was cancelled. + pub fn execute_best_transactions( + &self, + info: &mut ExecutionInfo, + builder: &mut impl BlockBuilder<Primitives = Evm::Primitives>, + mut best_txs: impl PayloadTransactions< + Transaction: PoolTransaction<Consensus = TxTy<Evm::Primitives>>, + >, + breaker: PayloadBuildingBreaker, + ) -> Result<Option<()>, PayloadBuilderError> { + let block_gas_limit = builder.evm_mut().block().gas_limit; + let base_fee = builder.evm_mut().block().basefee; + + while let Some(tx) = best_txs.next(()) { + let tx = tx.into_consensus(); + if info.is_tx_over_limits(tx.inner(), block_gas_limit) { + // we can't fit this transaction into the block, so we need to mark it as + // invalid which also removes all dependent transaction from + // the iterator before we can continue + best_txs.mark_invalid(tx.signer(), tx.nonce()); + continue + } + + // A sequencer's block should never contain blob or deposit transactions from the pool. + if tx.is_eip4844() || tx.is_l1_message() { + best_txs.mark_invalid(tx.signer(), tx.nonce()); + continue + } + + // check if the job was cancelled, if so we can exit early + if self.cancel.is_cancelled() { + return Ok(Some(())) + } + + // check if the execution needs to be halted. + if breaker.should_break(info.cumulative_gas_used) { + tracing::trace!(target: "scroll::payload_builder", ?info, "breaking execution loop"); + return Ok(None); + } + + let gas_used = match builder.execute_transaction(tx.clone()) { + Ok(gas_used) => gas_used, + Err(BlockExecutionError::Validation(BlockValidationError::InvalidTx { + error, + .. + })) => { + if error.is_nonce_too_low() { + // if the nonce is too low, we can skip this transaction + tracing::trace!(target: "payload_builder", %error, ?tx, "skipping nonce too low transaction"); + } else { + // if the transaction is invalid, we can skip it and all of its + // descendants + tracing::trace!(target: "payload_builder", %error, ?tx, "skipping invalid transaction and its descendants"); + best_txs.mark_invalid(tx.signer(), tx.nonce()); + } + continue + } + Err(err) => { + // this is an error that we should treat as fatal for this attempt + return Err(PayloadBuilderError::EvmExecutionError(Box::new(err))) + } + }; + + // add gas used by the transaction to cumulative gas used, before creating the + // receipt + info.cumulative_gas_used += gas_used; + info.cumulative_da_bytes_used += tx.length() as u64; + + // update add to total fees + let miner_fee = tx + .effective_tip_per_gas(base_fee) + .expect("fee is always valid; execution succeeded"); + info.total_fees += U256::from(miner_fee) * U256::from(gas_used); + } + + Ok(None) + } +} + +/// Holds the state after execution +#[derive(Debug)] +pub struct ExecutedPayload<N: NodePrimitives> { + /// Tracked execution info + pub info: ExecutionInfo, + /// Withdrawal hash. + pub withdrawals_root: Option<B256>, + /// The transaction receipts. + pub receipts: Vec<N::Receipt>, + /// The block env used during execution. + pub block_env: BlockEnv, +} + +/// This acts as the container for executed transactions and its byproducts (receipts, gas used) +#[derive(Default, Debug)] +pub struct ExecutionInfo { + /// All gas used so far + pub cumulative_gas_used: u64, + /// Estimated DA size + pub cumulative_da_bytes_used: u64, + /// Tracks fees from executed mempool transactions + pub total_fees: U256, +} + +impl ExecutionInfo { + /// Create a new instance with allocated slots. + pub const fn new() -> Self { + Self { cumulative_gas_used: 0, cumulative_da_bytes_used: 0, total_fees: U256::ZERO } + } + + /// Returns true if the transaction would exceed the block limits: + /// - block gas limit: ensures the transaction still fits into the block. + pub fn is_tx_over_limits( + &self, + tx: &(impl Encodable + Transaction), + block_gas_limit: u64, + ) -> bool { + self.cumulative_gas_used + tx.gas_limit() > block_gas_limit + } +}
diff --git reth/crates/scroll/payload/src/config.rs scroll-reth/crates/scroll/payload/src/config.rs new file mode 100644 index 0000000000000000000000000000000000000000..d51c59bf1d63961a812b8eff723706c0267090cb --- /dev/null +++ scroll-reth/crates/scroll/payload/src/config.rs @@ -0,0 +1,68 @@ +//! Configuration for the payload builder. + +use core::time::Duration; +use reth_chainspec::MIN_TRANSACTION_GAS; +use std::{fmt::Debug, time::Instant}; + +/// Settings for the Scroll builder. +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct ScrollBuilderConfig { + /// Gas limit. + pub gas_limit: u64, + /// Time limit for payload building. + pub time_limit: Duration, +} + +impl ScrollBuilderConfig { + /// Returns a new instance of [`ScrollBuilderConfig`]. + pub const fn new(gas_limit: u64, time_limit: Duration) -> Self { + Self { gas_limit, time_limit } + } + + /// Returns the [`PayloadBuildingBreaker`] for the config. + pub(super) fn breaker(&self) -> PayloadBuildingBreaker { + PayloadBuildingBreaker::new(self.time_limit, self.gas_limit) + } +} + +/// Used in the [`super::ScrollPayloadBuilder`] to exit the transactions execution loop early. +#[derive(Debug, Clone)] +pub struct PayloadBuildingBreaker { + start: Instant, + time_limit: Duration, + gas_limit: u64, +} + +impl PayloadBuildingBreaker { + /// Returns a new instance of the [`PayloadBuildingBreaker`]. + fn new(time_limit: Duration, gas_limit: u64) -> Self { + Self { start: Instant::now(), time_limit, gas_limit } + } + + /// Returns whether the payload building should stop. + pub(super) fn should_break(&self, cumulative_gas_used: u64) -> bool { + self.start.elapsed() >= self.time_limit || + cumulative_gas_used > self.gas_limit.saturating_sub(MIN_TRANSACTION_GAS) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_should_break_on_time_limit() { + let breaker = + PayloadBuildingBreaker::new(Duration::from_millis(200), 2 * MIN_TRANSACTION_GAS); + assert!(!breaker.should_break(MIN_TRANSACTION_GAS)); + std::thread::sleep(Duration::from_millis(201)); + assert!(breaker.should_break(MIN_TRANSACTION_GAS)); + } + + #[test] + fn test_should_break_on_gas_limit() { + let breaker = PayloadBuildingBreaker::new(Duration::from_secs(1), 2 * MIN_TRANSACTION_GAS); + assert!(!breaker.should_break(MIN_TRANSACTION_GAS)); + assert!(breaker.should_break(MIN_TRANSACTION_GAS + 1)); + } +}
diff --git reth/crates/scroll/payload/src/error.rs scroll-reth/crates/scroll/payload/src/error.rs new file mode 100644 index 0000000000000000000000000000000000000000..0dbbd321af008a79b596aab44dc9a08cf23c961f --- /dev/null +++ scroll-reth/crates/scroll/payload/src/error.rs @@ -0,0 +1,11 @@ +/// Scroll specific payload building errors. +#[derive(Debug, thiserror::Error)] +pub enum ScrollPayloadBuilderError { + /// Thrown when a transaction fails to convert to a + /// [`alloy_consensus::transaction::Recovered`]. + #[error("failed to convert deposit transaction to RecoveredTx")] + TransactionEcRecoverFailed, + /// Thrown when a blob transaction is included in a sequencer's block. + #[error("blob transaction included in sequencer block")] + BlobTransactionRejected, +}
diff --git reth/crates/scroll/payload/src/lib.rs scroll-reth/crates/scroll/payload/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..9e2720e72513bb3d632716ac60a72fbfb1b64298 --- /dev/null +++ scroll-reth/crates/scroll/payload/src/lib.rs @@ -0,0 +1,22 @@ +//! Engine Payload related types. + +pub use base_fee::{ + PayloadBuildingBaseFeeProvider, L1_BASE_FEE_OVERHEAD, L1_BASE_FEE_PRECISION, + L1_BASE_FEE_SCALAR, L1_BASE_FEE_SLOT, MAX_L2_BASE_FEE, +}; +mod base_fee; + +pub mod builder; +pub use builder::{ScrollPayloadBuilder, ScrollPayloadTransactions}; + +pub mod config; +pub use config::ScrollBuilderConfig; + +mod error; +pub use error::ScrollPayloadBuilderError; + +#[cfg(feature = "test-utils")] +mod test_utils; + +#[cfg(feature = "test-utils")] +pub use test_utils::{NoopPayloadJob, NoopPayloadJobGenerator};
diff --git reth/crates/scroll/payload/src/test_utils.rs scroll-reth/crates/scroll/payload/src/test_utils.rs new file mode 100644 index 0000000000000000000000000000000000000000..a9d494f40fdaeef1faa719cff26319224f974cf9 --- /dev/null +++ scroll-reth/crates/scroll/payload/src/test_utils.rs @@ -0,0 +1,70 @@ +use core::{ + fmt::Debug, + future::Future, + pin::Pin, + task::{Context, Poll}, +}; +use reth_payload_builder::{KeepPayloadJobAlive, PayloadJob, PayloadJobGenerator}; +use reth_payload_primitives::{ + BuiltPayload, PayloadBuilderAttributes, PayloadBuilderError, PayloadKind, +}; + +/// A [`PayloadJobGenerator`] that doesn't produce any useful payload. +#[derive(Debug, Default)] +#[non_exhaustive] +pub struct NoopPayloadJobGenerator<PA, BP> { + _types: core::marker::PhantomData<(PA, BP)>, +} + +impl<PA, BP> PayloadJobGenerator for NoopPayloadJobGenerator<PA, BP> +where + PA: PayloadBuilderAttributes + Default + Debug + Send + Sync, + BP: BuiltPayload + Default + Clone + Debug + Send + Sync + 'static, +{ + type Job = NoopPayloadJob<PA, BP>; + + fn new_payload_job(&self, _attr: PA) -> Result<Self::Job, PayloadBuilderError> { + Ok(NoopPayloadJob::<PA, BP>::default()) + } +} + +/// A [`PayloadJobGenerator`] that doesn't produce any payload. +#[derive(Debug, Default)] +pub struct NoopPayloadJob<PA, BP> { + _types: core::marker::PhantomData<(PA, BP)>, +} + +impl<PA, BP> Future for NoopPayloadJob<PA, BP> { + type Output = Result<(), PayloadBuilderError>; + + fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> { + Poll::Pending + } +} + +impl<PA, BP> PayloadJob for NoopPayloadJob<PA, BP> +where + PA: PayloadBuilderAttributes + Default + Debug, + BP: BuiltPayload + Default + Clone + Debug + 'static, +{ + type PayloadAttributes = PA; + type ResolvePayloadFuture = + futures_util::future::Ready<Result<Self::BuiltPayload, PayloadBuilderError>>; + type BuiltPayload = BP; + + fn best_payload(&self) -> Result<Self::BuiltPayload, PayloadBuilderError> { + Ok(Self::BuiltPayload::default()) + } + + fn payload_attributes(&self) -> Result<Self::PayloadAttributes, PayloadBuilderError> { + Ok(Self::PayloadAttributes::default()) + } + + fn resolve_kind( + &mut self, + _kind: PayloadKind, + ) -> (Self::ResolvePayloadFuture, KeepPayloadJobAlive) { + let fut = futures_util::future::ready(self.best_payload()); + (fut, KeepPayloadJobAlive::No) + } +}
diff --git reth/crates/scroll/primitives/Cargo.toml scroll-reth/crates/scroll/primitives/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..d9ee3c03a129a2b3869adcdc28bc570adf16315a --- /dev/null +++ scroll-reth/crates/scroll/primitives/Cargo.toml @@ -0,0 +1,95 @@ +[package] +name = "reth-scroll-primitives" +version.workspace = true +edition.workspace = true +rust-version.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true + +[lints] +workspace = true + +[dependencies] +# reth +reth-codecs = { workspace = true, optional = true } +reth-primitives-traits = { workspace = true, features = ["scroll-alloy-traits"] } +reth-zstd-compressors = { workspace = true, optional = true } + +# alloy +alloy-consensus.workspace = true +alloy-eips.workspace = true +alloy-primitives.workspace = true +alloy-rlp.workspace = true + +# scroll +scroll-alloy-consensus.workspace = true + +# codec +bytes = { workspace = true, optional = true } +modular-bitfield = { workspace = true, optional = true } +serde = { workspace = true, optional = true } + +# misc +once_cell.workspace = true + +# test +arbitrary = { workspace = true, features = ["derive"], optional = true } + +[dev-dependencies] +reth-codecs = { workspace = true, features = ["test-utils"] } +rstest.workspace = true +rand.workspace = true + +[features] +default = ["std"] +std = [ + "serde?/std", + "scroll-alloy-consensus/std", + "alloy-consensus/std", + "alloy-eips/std", + "alloy-primitives/std", + "alloy-rlp/std", + "bytes?/std", + "reth-primitives-traits/std", + "reth-zstd-compressors?/std", + "reth-codecs?/std", + "once_cell/std", + "serde?/std", +] +reth-codec = [ + "dep:reth-codecs", + "std", + "dep:arbitrary", + "reth-primitives-traits/reth-codec", + "scroll-alloy-consensus/reth-codec", + "dep:bytes", + "dep:modular-bitfield", + "dep:reth-zstd-compressors", +] +serde = [ + "dep:serde", + "scroll-alloy-consensus/serde", + "alloy-consensus/serde", + "alloy-eips/serde", + "alloy-primitives/serde", + "bytes?/serde", + "reth-codecs?/serde", + "reth-primitives-traits/serde", + "rand/serde", +] +serde-bincode-compat = [ + "alloy-consensus/serde-bincode-compat", + "alloy-eips/serde-bincode-compat", + "reth-primitives-traits/serde-bincode-compat", + "scroll-alloy-consensus/serde-bincode-compat", +] +arbitrary = [ + "dep:arbitrary", + "alloy-consensus/arbitrary", + "alloy-eips/arbitrary", + "alloy-primitives/arbitrary", + "reth-codecs?/arbitrary", + "reth-primitives-traits/arbitrary", + "scroll-alloy-consensus/arbitrary", +]
diff --git reth/crates/scroll/primitives/src/lib.rs scroll-reth/crates/scroll/primitives/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..232cfc9a79fc56221b92db63e9cee28369ac19ce --- /dev/null +++ scroll-reth/crates/scroll/primitives/src/lib.rs @@ -0,0 +1,39 @@ +//! Commonly used types in Scroll. + +#![doc( + html_logo_url = "https://raw.githubusercontent.com/paradigmxyz/reth/main/assets/reth-docs.png", + html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", + issue_tracker_base_url = "https://github.com/scroll-tech/reth/issues/" +)] +#![cfg_attr(not(test), warn(unused_crate_dependencies))] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] +#![cfg_attr(not(feature = "std"), no_std)] + +use once_cell as _; + +pub mod transaction; +pub use transaction::{tx_type::ScrollTxType, ScrollTransactionSigned}; + +use reth_primitives_traits::Block; + +mod receipt; +pub use receipt::ScrollReceipt; + +/// Scroll-specific block type. +pub type ScrollBlock = alloy_consensus::Block<ScrollTransactionSigned>; + +/// Scroll-specific block body type. +pub type ScrollBlockBody = <ScrollBlock as Block>::Body; + +/// Primitive types for Scroll Node. +#[derive(Debug, Default, Clone, PartialEq, Eq)] +pub struct ScrollPrimitives; + +#[cfg(feature = "serde-bincode-compat")] +impl reth_primitives_traits::NodePrimitives for ScrollPrimitives { + type Block = ScrollBlock; + type BlockHeader = alloy_consensus::Header; + type BlockBody = ScrollBlockBody; + type SignedTx = ScrollTransactionSigned; + type Receipt = ScrollReceipt; +}
diff --git reth/crates/scroll/primitives/src/receipt.rs scroll-reth/crates/scroll/primitives/src/receipt.rs new file mode 100644 index 0000000000000000000000000000000000000000..405e33eba23b699bce5daac1b653467a91680319 --- /dev/null +++ scroll-reth/crates/scroll/primitives/src/receipt.rs @@ -0,0 +1,443 @@ +use alloy_consensus::{ + proofs::ordered_trie_root_with_encoder, Eip2718EncodableReceipt, Eip658Value, Receipt, + ReceiptWithBloom, RlpDecodableReceipt, RlpEncodableReceipt, TxReceipt, Typed2718, +}; +use alloy_eips::{ + eip2718::{Eip2718Result, Encodable2718}, + Decodable2718, +}; +use alloy_primitives::{Bloom, Log, B256, U256}; +use alloy_rlp::{BufMut, Decodable, Encodable, Header}; +use reth_primitives_traits::InMemorySize; +use scroll_alloy_consensus::{ScrollTransactionReceipt, ScrollTxType}; + +/// Typed ethereum transaction receipt. +/// Receipt containing result of transaction execution. +#[derive(Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub enum ScrollReceipt { + /// Legacy receipt + Legacy(ScrollTransactionReceipt), + /// EIP-2930 receipt + Eip2930(ScrollTransactionReceipt), + /// EIP-1559 receipt + Eip1559(ScrollTransactionReceipt), + /// EIP-7702 receipt + Eip7702(ScrollTransactionReceipt), + /// L1 message receipt + L1Message(Receipt), +} + +impl ScrollReceipt { + /// Returns [`ScrollTxType`] of the receipt. + pub const fn tx_type(&self) -> ScrollTxType { + match self { + Self::Legacy(_) => ScrollTxType::Legacy, + Self::Eip2930(_) => ScrollTxType::Eip2930, + Self::Eip1559(_) => ScrollTxType::Eip1559, + Self::Eip7702(_) => ScrollTxType::Eip7702, + Self::L1Message(_) => ScrollTxType::L1Message, + } + } + + /// Returns inner [`Receipt`], + pub const fn as_receipt(&self) -> &Receipt { + match self { + Self::Legacy(receipt) | + Self::Eip2930(receipt) | + Self::Eip1559(receipt) | + Self::Eip7702(receipt) => &receipt.inner, + Self::L1Message(receipt) => receipt, + } + } + + /// Returns length of RLP-encoded receipt fields with the given [`Bloom`] without an RLP header. + pub fn rlp_encoded_fields_length(&self, bloom: &Bloom) -> usize { + match self { + Self::Legacy(receipt) | + Self::Eip2930(receipt) | + Self::Eip1559(receipt) | + Self::Eip7702(receipt) => receipt.rlp_encoded_fields_length_with_bloom(bloom), + Self::L1Message(receipt) => receipt.rlp_encoded_fields_length_with_bloom(bloom), + } + } + + /// RLP-encodes receipt fields with the given [`Bloom`] without an RLP header. + pub fn rlp_encode_fields(&self, bloom: &Bloom, out: &mut dyn BufMut) { + match self { + Self::Legacy(receipt) | + Self::Eip2930(receipt) | + Self::Eip1559(receipt) | + Self::Eip7702(receipt) => receipt.rlp_encode_fields_with_bloom(bloom, out), + Self::L1Message(receipt) => receipt.rlp_encode_fields_with_bloom(bloom, out), + } + } + + /// Returns RLP header for inner encoding. + pub fn rlp_header_inner(&self, bloom: &Bloom) -> Header { + Header { list: true, payload_length: self.rlp_encoded_fields_length(bloom) } + } + + /// Returns RLP header for inner encoding without bloom. + pub fn rlp_header_inner_without_bloom(&self) -> Header { + Header { list: true, payload_length: self.rlp_encoded_fields_length_without_bloom() } + } + + /// RLP-decodes the receipt from the provided buffer. This does not expect a type byte or + /// network header. + pub fn rlp_decode_inner( + buf: &mut &[u8], + tx_type: ScrollTxType, + ) -> alloy_rlp::Result<ReceiptWithBloom<Self>> { + match tx_type { + ScrollTxType::Legacy => { + let ReceiptWithBloom { receipt, logs_bloom } = + RlpDecodableReceipt::rlp_decode_with_bloom(buf)?; + Ok(ReceiptWithBloom { receipt: Self::Legacy(receipt), logs_bloom }) + } + ScrollTxType::Eip2930 => { + let ReceiptWithBloom { receipt, logs_bloom } = + RlpDecodableReceipt::rlp_decode_with_bloom(buf)?; + Ok(ReceiptWithBloom { receipt: Self::Eip2930(receipt), logs_bloom }) + } + ScrollTxType::Eip1559 => { + let ReceiptWithBloom { receipt, logs_bloom } = + RlpDecodableReceipt::rlp_decode_with_bloom(buf)?; + Ok(ReceiptWithBloom { receipt: Self::Eip1559(receipt), logs_bloom }) + } + ScrollTxType::Eip7702 => { + let ReceiptWithBloom { receipt, logs_bloom } = + RlpDecodableReceipt::rlp_decode_with_bloom(buf)?; + Ok(ReceiptWithBloom { receipt: Self::Eip7702(receipt), logs_bloom }) + } + ScrollTxType::L1Message => { + let ReceiptWithBloom { receipt, logs_bloom } = + RlpDecodableReceipt::rlp_decode_with_bloom(buf)?; + Ok(ReceiptWithBloom { receipt: Self::L1Message(receipt), logs_bloom }) + } + } + } + + /// RLP-encodes receipt fields without an RLP header. + pub fn rlp_encode_fields_without_bloom(&self, out: &mut dyn BufMut) { + match self { + Self::Legacy(receipt) | + Self::Eip2930(receipt) | + Self::Eip1559(receipt) | + Self::Eip7702(receipt) => { + receipt.inner.status.encode(out); + receipt.inner.cumulative_gas_used.encode(out); + receipt.inner.logs.encode(out); + } + Self::L1Message(receipt) => { + receipt.status.encode(out); + receipt.cumulative_gas_used.encode(out); + receipt.logs.encode(out); + } + } + } + + /// Returns length of RLP-encoded receipt fields without an RLP header. + pub fn rlp_encoded_fields_length_without_bloom(&self) -> usize { + match self { + Self::Legacy(receipt) | + Self::Eip2930(receipt) | + Self::Eip1559(receipt) | + Self::Eip7702(receipt) => { + receipt.inner.status.length() + + receipt.inner.cumulative_gas_used.length() + + receipt.inner.logs.length() + } + Self::L1Message(receipt) => { + receipt.status.length() + + receipt.cumulative_gas_used.length() + + receipt.logs.length() + } + } + } + + /// RLP-decodes the receipt from the provided buffer without bloom. + pub fn rlp_decode_inner_without_bloom( + buf: &mut &[u8], + tx_type: ScrollTxType, + ) -> alloy_rlp::Result<Self> { + let header = Header::decode(buf)?; + if !header.list { + return Err(alloy_rlp::Error::UnexpectedString); + } + + let remaining = buf.len(); + let status = Decodable::decode(buf)?; + let cumulative_gas_used = Decodable::decode(buf)?; + let logs = Decodable::decode(buf)?; + + if buf.len() + header.payload_length != remaining { + return Err(alloy_rlp::Error::UnexpectedLength); + } + + let inner = Receipt { status, cumulative_gas_used, logs }; + + match tx_type { + ScrollTxType::Legacy => { + Ok(Self::Legacy(ScrollTransactionReceipt { inner, l1_fee: Default::default() })) + } + ScrollTxType::Eip2930 => { + Ok(Self::Eip2930(ScrollTransactionReceipt { inner, l1_fee: Default::default() })) + } + ScrollTxType::Eip1559 => { + Ok(Self::Eip1559(ScrollTransactionReceipt { inner, l1_fee: Default::default() })) + } + ScrollTxType::Eip7702 => { + Ok(Self::Eip7702(ScrollTransactionReceipt { inner, l1_fee: Default::default() })) + } + ScrollTxType::L1Message => Ok(Self::L1Message(inner)), + } + } + + /// Returns the l1 fee for the transaction receipt. + pub const fn l1_fee(&self) -> U256 { + match self { + Self::Legacy(receipt) | + Self::Eip2930(receipt) | + Self::Eip1559(receipt) | + Self::Eip7702(receipt) => receipt.l1_fee, + Self::L1Message(_) => U256::ZERO, + } + } + + /// Calculates the receipt root for a header for the reference type of [Receipt]. + /// + /// NOTE: Prefer `proofs::calculate_receipt_root` if you have log blooms memoized. + pub fn calculate_receipt_root_no_memo(receipts: &[Self]) -> B256 { + ordered_trie_root_with_encoder(receipts, |r, buf| r.with_bloom_ref().encode_2718(buf)) + } +} + +impl Eip2718EncodableReceipt for ScrollReceipt { + fn eip2718_encoded_length_with_bloom(&self, bloom: &Bloom) -> usize { + !self.tx_type().is_legacy() as usize + self.rlp_header_inner(bloom).length_with_payload() + } + + fn eip2718_encode_with_bloom(&self, bloom: &Bloom, out: &mut dyn BufMut) { + if !self.tx_type().is_legacy() { + out.put_u8(self.tx_type() as u8); + } + self.rlp_header_inner(bloom).encode(out); + self.rlp_encode_fields(bloom, out); + } +} + +impl RlpEncodableReceipt for ScrollReceipt { + fn rlp_encoded_length_with_bloom(&self, bloom: &Bloom) -> usize { + let mut len = self.eip2718_encoded_length_with_bloom(bloom); + if !self.tx_type().is_legacy() { + len += Header { + list: false, + payload_length: self.eip2718_encoded_length_with_bloom(bloom), + } + .length(); + } + + len + } + + fn rlp_encode_with_bloom(&self, bloom: &Bloom, out: &mut dyn BufMut) { + if !self.tx_type().is_legacy() { + Header { list: false, payload_length: self.eip2718_encoded_length_with_bloom(bloom) } + .encode(out); + } + self.eip2718_encode_with_bloom(bloom, out); + } +} + +impl RlpDecodableReceipt for ScrollReceipt { + fn rlp_decode_with_bloom(buf: &mut &[u8]) -> alloy_rlp::Result<ReceiptWithBloom<Self>> { + let header_buf = &mut &**buf; + let header = Header::decode(header_buf)?; + + // Legacy receipt, reuse initial buffer without advancing + if header.list { + return Self::rlp_decode_inner(buf, ScrollTxType::Legacy) + } + + // Otherwise, advance the buffer and try decoding type flag followed by receipt + *buf = *header_buf; + + let remaining = buf.len(); + let tx_type = ScrollTxType::decode(buf)?; + let this = Self::rlp_decode_inner(buf, tx_type)?; + + if buf.len() + header.payload_length != remaining { + return Err(alloy_rlp::Error::UnexpectedLength); + } + + Ok(this) + } +} + +impl Encodable2718 for ScrollReceipt { + fn encode_2718_len(&self) -> usize { + !self.tx_type().is_legacy() as usize + + self.rlp_header_inner_without_bloom().length_with_payload() + } + + fn encode_2718(&self, out: &mut dyn BufMut) { + if !self.tx_type().is_legacy() { + out.put_u8(self.tx_type() as u8); + } + self.rlp_header_inner_without_bloom().encode(out); + self.rlp_encode_fields_without_bloom(out); + } +} + +impl Decodable2718 for ScrollReceipt { + fn typed_decode(ty: u8, buf: &mut &[u8]) -> Eip2718Result<Self> { + Ok(Self::rlp_decode_inner_without_bloom(buf, ScrollTxType::try_from(ty)?)?) + } + + fn fallback_decode(buf: &mut &[u8]) -> Eip2718Result<Self> { + Ok(Self::rlp_decode_inner_without_bloom(buf, ScrollTxType::Legacy)?) + } +} + +impl Encodable for ScrollReceipt { + fn encode(&self, out: &mut dyn BufMut) { + self.network_encode(out); + } + + fn length(&self) -> usize { + self.network_len() + } +} + +impl Decodable for ScrollReceipt { + fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> { + Ok(Self::network_decode(buf)?) + } +} + +impl TxReceipt for ScrollReceipt { + type Log = Log; + + fn status_or_post_state(&self) -> Eip658Value { + self.as_receipt().status_or_post_state() + } + + fn status(&self) -> bool { + self.as_receipt().status() + } + + fn bloom(&self) -> Bloom { + self.as_receipt().bloom() + } + + fn cumulative_gas_used(&self) -> u64 { + self.as_receipt().cumulative_gas_used() + } + + fn logs(&self) -> &[Log] { + self.as_receipt().logs() + } +} + +impl Typed2718 for ScrollReceipt { + fn ty(&self) -> u8 { + self.tx_type().into() + } +} + +impl InMemorySize for ScrollReceipt { + fn size(&self) -> usize { + self.as_receipt().size() + } +} + +impl reth_primitives_traits::Receipt for ScrollReceipt {} + +#[cfg(feature = "serde-bincode-compat")] +impl reth_primitives_traits::serde_bincode_compat::SerdeBincodeCompat for ScrollReceipt { + type BincodeRepr<'a> = Self; + + fn as_repr(&self) -> Self::BincodeRepr<'_> { + self.clone() + } + + fn from_repr(repr: Self::BincodeRepr<'_>) -> Self { + repr + } +} + +#[cfg(feature = "reth-codec")] +mod compact { + use super::*; + use alloy_primitives::U256; + use reth_codecs::Compact; + use std::borrow::Cow; + + #[derive(reth_codecs::CompactZstd)] + #[reth_zstd( + compressor = reth_zstd_compressors::RECEIPT_COMPRESSOR, + decompressor = reth_zstd_compressors::RECEIPT_DECOMPRESSOR + )] + struct CompactScrollReceipt<'a> { + tx_type: ScrollTxType, + success: bool, + cumulative_gas_used: u64, + #[allow(clippy::owned_cow)] + logs: Cow<'a, Vec<Log>>, + l1_fee: Option<U256>, + } + + impl<'a> From<&'a ScrollReceipt> for CompactScrollReceipt<'a> { + fn from(receipt: &'a ScrollReceipt) -> Self { + Self { + tx_type: receipt.tx_type(), + success: receipt.status(), + cumulative_gas_used: receipt.cumulative_gas_used(), + logs: Cow::Borrowed(&receipt.as_receipt().logs), + l1_fee: (receipt.l1_fee() != U256::ZERO).then_some(receipt.l1_fee()), + } + } + } + + impl From<CompactScrollReceipt<'_>> for ScrollReceipt { + fn from(receipt: CompactScrollReceipt<'_>) -> Self { + let CompactScrollReceipt { tx_type, success, cumulative_gas_used, logs, l1_fee } = + receipt; + + let inner = + Receipt { status: success.into(), cumulative_gas_used, logs: logs.into_owned() }; + + match tx_type { + ScrollTxType::Legacy => { + Self::Legacy(ScrollTransactionReceipt::new(inner, l1_fee.unwrap_or_default())) + } + ScrollTxType::Eip2930 => { + Self::Eip2930(ScrollTransactionReceipt::new(inner, l1_fee.unwrap_or_default())) + } + ScrollTxType::Eip1559 => { + Self::Eip1559(ScrollTransactionReceipt::new(inner, l1_fee.unwrap_or_default())) + } + ScrollTxType::Eip7702 => { + Self::Eip7702(ScrollTransactionReceipt::new(inner, l1_fee.unwrap_or_default())) + } + ScrollTxType::L1Message => Self::L1Message(inner), + } + } + } + + impl Compact for ScrollReceipt { + fn to_compact<B>(&self, buf: &mut B) -> usize + where + B: bytes::BufMut + AsMut<[u8]>, + { + CompactScrollReceipt::from(self).to_compact(buf) + } + + fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8]) { + let (receipt, buf) = CompactScrollReceipt::from_compact(buf, len); + (receipt.into(), buf) + } + } +}
diff --git reth/crates/scroll/primitives/src/transaction/mod.rs scroll-reth/crates/scroll/primitives/src/transaction/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..87471e507e9013fa44fb153d98f5553d1bc74e19 --- /dev/null +++ scroll-reth/crates/scroll/primitives/src/transaction/mod.rs @@ -0,0 +1,6 @@ +//! Scroll primitives transaction types. + +pub mod tx_type; + +/// Signed transaction. +pub type ScrollTransactionSigned = scroll_alloy_consensus::ScrollTxEnvelope;
diff --git reth/crates/scroll/primitives/src/transaction/tx_type.rs scroll-reth/crates/scroll/primitives/src/transaction/tx_type.rs new file mode 100644 index 0000000000000000000000000000000000000000..e549b81bd97a09f15f1b00dc99f1f2a34cb292b0 --- /dev/null +++ scroll-reth/crates/scroll/primitives/src/transaction/tx_type.rs @@ -0,0 +1,47 @@ +//! Scroll transaction type. + +pub use scroll_alloy_consensus::ScrollTxType; + +#[cfg(test)] +mod tests { + use super::*; + use reth_codecs::{txtype::*, Compact}; + use rstest::rstest; + use scroll_alloy_consensus::L1_MESSAGE_TX_TYPE_ID; + + #[rstest] + #[case(ScrollTxType::Legacy, COMPACT_IDENTIFIER_LEGACY, vec![])] + #[case(ScrollTxType::Eip2930, COMPACT_IDENTIFIER_EIP2930, vec![])] + #[case(ScrollTxType::Eip1559, COMPACT_IDENTIFIER_EIP1559, vec![])] + #[case(ScrollTxType::L1Message, COMPACT_EXTENDED_IDENTIFIER_FLAG, vec![L1_MESSAGE_TX_TYPE_ID])] + fn test_txtype_to_compact( + #[case] tx_type: ScrollTxType, + #[case] expected_identifier: usize, + #[case] expected_buf: Vec<u8>, + ) { + let mut buf = vec![]; + let identifier = tx_type.to_compact(&mut buf); + + assert_eq!( + identifier, expected_identifier, + "Unexpected identifier for ScrollTxType {tx_type:?}", + ); + assert_eq!(buf, expected_buf, "Unexpected buffer for ScrollTxType {tx_type:?}",); + } + + #[rstest] + #[case(ScrollTxType::Legacy, COMPACT_IDENTIFIER_LEGACY, vec![])] + #[case(ScrollTxType::Eip2930, COMPACT_IDENTIFIER_EIP2930, vec![])] + #[case(ScrollTxType::Eip1559, COMPACT_IDENTIFIER_EIP1559, vec![])] + #[case(ScrollTxType::L1Message, COMPACT_EXTENDED_IDENTIFIER_FLAG, vec![L1_MESSAGE_TX_TYPE_ID])] + fn test_txtype_from_compact( + #[case] expected_type: ScrollTxType, + #[case] identifier: usize, + #[case] buf: Vec<u8>, + ) { + let (actual_type, remaining_buf) = ScrollTxType::from_compact(&buf, identifier); + + assert_eq!(actual_type, expected_type, "Unexpected TxType for identifier {identifier}"); + assert!(remaining_buf.is_empty(), "Buffer not fully consumed for identifier {identifier}"); + } +}
diff --git reth/crates/scroll/rpc/Cargo.toml scroll-reth/crates/scroll/rpc/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..f1fbbc9289c814c4abf597f52b9150cad3669f02 --- /dev/null +++ scroll-reth/crates/scroll/rpc/Cargo.toml @@ -0,0 +1,55 @@ +[package] +name = "reth-scroll-rpc" +version.workspace = true +edition.workspace = true +rust-version.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +description = "Ethereum RPC implementation for scroll." + +[lints] +workspace = true + +[dependencies] +# reth +reth-evm.workspace = true +reth-primitives.workspace = true +reth-primitives-traits.workspace = true +reth-provider.workspace = true +reth-rpc-eth-api.workspace = true +reth-rpc-eth-types.workspace = true +reth-tasks = { workspace = true, features = ["rayon"] } +reth-transaction-pool.workspace = true +reth-rpc.workspace = true +reth-node-api.workspace = true +reth-node-builder.workspace = true +reth-network-api.workspace = true +reth-chainspec.workspace = true + +# scroll +reth-scroll-chainspec.workspace = true +reth-scroll-evm.workspace = true +reth-scroll-primitives = { workspace = true, features = ["serde", "serde-bincode-compat", "reth-codec"] } +scroll-alloy-consensus.workspace = true +scroll-alloy-evm.workspace = true +scroll-alloy-hardforks.workspace = true +scroll-alloy-network.workspace = true +scroll-alloy-rpc-types.workspace = true + +# ethereum +alloy-primitives.workspace = true +alloy-rpc-types-eth.workspace = true +alloy-consensus.workspace = true +revm.workspace = true + +# async +parking_lot.workspace = true +tokio.workspace = true + +# rpc +jsonrpsee-types.workspace = true + +# misc +eyre.workspace = true +thiserror.workspace = true
diff --git reth/crates/scroll/rpc/src/error.rs scroll-reth/crates/scroll/rpc/src/error.rs new file mode 100644 index 0000000000000000000000000000000000000000..6357053166906da3f45527553013a137fcca00a3 --- /dev/null +++ scroll-reth/crates/scroll/rpc/src/error.rs @@ -0,0 +1,64 @@ +//! RPC errors specific to Scroll. + +use alloy_rpc_types_eth::BlockError; +use reth_evm::execute::ProviderError; +use reth_rpc_eth_api::{AsEthApiError, TransactionConversionError}; +use reth_rpc_eth_types::{error::api::FromEvmHalt, EthApiError}; +use revm::context::result::{EVMError, HaltReason}; + +/// Scroll specific errors, that extend [`EthApiError`]. +#[derive(Debug, thiserror::Error)] +pub enum ScrollEthApiError { + /// L1 ethereum error. + #[error(transparent)] + Eth(#[from] EthApiError), +} + +impl AsEthApiError for ScrollEthApiError { + fn as_err(&self) -> Option<&EthApiError> { + match self { + Self::Eth(err) => Some(err), + } + } +} + +impl From<ScrollEthApiError> for jsonrpsee_types::error::ErrorObject<'static> { + fn from(err: ScrollEthApiError) -> Self { + match err { + ScrollEthApiError::Eth(err) => err.into(), + } + } +} + +impl From<BlockError> for ScrollEthApiError { + fn from(error: BlockError) -> Self { + Self::Eth(error.into()) + } +} + +impl<DB> From<EVMError<DB>> for ScrollEthApiError +where + EthApiError: From<EVMError<DB>>, +{ + fn from(error: EVMError<DB>) -> Self { + Self::Eth(error.into()) + } +} + +impl FromEvmHalt<HaltReason> for ScrollEthApiError { + fn from_evm_halt(halt: HaltReason, gas_limit: u64) -> Self { + EthApiError::from_evm_halt(halt, gas_limit).into() + } +} + +impl From<TransactionConversionError> for ScrollEthApiError { + fn from(value: TransactionConversionError) -> Self { + Self::Eth(EthApiError::from(value)) + } +} + +impl From<ProviderError> for ScrollEthApiError { + fn from(value: ProviderError) -> Self { + Self::Eth(EthApiError::from(value)) + } +}
diff --git reth/crates/scroll/rpc/src/eth/block.rs scroll-reth/crates/scroll/rpc/src/eth/block.rs new file mode 100644 index 0000000000000000000000000000000000000000..49bcd5b29e27f2cba176efd45acbd4f45e310c39 --- /dev/null +++ scroll-reth/crates/scroll/rpc/src/eth/block.rs @@ -0,0 +1,81 @@ +//! Loads and formats Scroll block RPC response. + +use crate::{eth::ScrollNodeCore, ScrollEthApi, ScrollEthApiError, ScrollReceiptBuilder}; + +use alloy_consensus::BlockHeader; +use alloy_rpc_types_eth::BlockId; +use reth_chainspec::ChainSpecProvider; +use reth_node_api::BlockBody; +use reth_primitives::TransactionMeta; +use reth_primitives_traits::SignedTransaction; +use reth_provider::{BlockReader, HeaderProvider, ProviderTx}; +use reth_rpc_eth_api::{ + helpers::{EthBlocks, LoadBlock, LoadPendingBlock, LoadReceipt, SpawnBlocking}, + types::RpcTypes, + RpcReceipt, +}; +use reth_scroll_chainspec::ScrollChainSpec; +use reth_scroll_primitives::{ScrollReceipt, ScrollTransactionSigned}; +use reth_transaction_pool::{PoolTransaction, TransactionPool}; +use scroll_alloy_rpc_types::ScrollTransactionReceipt; + +impl<N> EthBlocks for ScrollEthApi<N> +where + Self: LoadBlock< + Error = ScrollEthApiError, + NetworkTypes: RpcTypes<Receipt = ScrollTransactionReceipt>, + Provider: BlockReader<Receipt = ScrollReceipt, Transaction = ScrollTransactionSigned>, + >, + N: ScrollNodeCore<Provider: ChainSpecProvider<ChainSpec = ScrollChainSpec> + HeaderProvider>, +{ + async fn block_receipts( + &self, + block_id: BlockId, + ) -> Result<Option<Vec<RpcReceipt<Self::NetworkTypes>>>, Self::Error> + where + Self: LoadReceipt, + { + if let Some((block, receipts)) = self.load_block_and_receipts(block_id).await? { + let block_number = block.number(); + let base_fee = block.base_fee_per_gas(); + let block_hash = block.hash(); + let excess_blob_gas = block.excess_blob_gas(); + let timestamp = block.timestamp(); + + return block + .body() + .transactions() + .iter() + .zip(receipts.iter()) + .enumerate() + .map(|(idx, (tx, receipt))| -> Result<_, _> { + let meta = TransactionMeta { + tx_hash: *tx.tx_hash(), + index: idx as u64, + block_hash, + block_number, + base_fee, + excess_blob_gas, + timestamp, + }; + ScrollReceiptBuilder::new(tx, meta, receipt, &receipts) + .map(|builder| builder.build()) + }) + .collect::<Result<Vec<_>, Self::Error>>() + .map(Some) + } + + Ok(None) + } +} + +impl<N> LoadBlock for ScrollEthApi<N> +where + Self: LoadPendingBlock< + Pool: TransactionPool< + Transaction: PoolTransaction<Consensus = ProviderTx<Self::Provider>>, + >, + > + SpawnBlocking, + N: ScrollNodeCore, +{ +}
diff --git reth/crates/scroll/rpc/src/eth/call.rs scroll-reth/crates/scroll/rpc/src/eth/call.rs new file mode 100644 index 0000000000000000000000000000000000000000..2edd64d7e0abfa696fbf59024249958f2fd1369a --- /dev/null +++ scroll-reth/crates/scroll/rpc/src/eth/call.rs @@ -0,0 +1,161 @@ +use super::ScrollNodeCore; +use crate::{ScrollEthApi, ScrollEthApiError}; +use alloy_consensus::transaction::Either; + +use alloy_primitives::{TxKind, U256}; +use alloy_rpc_types_eth::transaction::TransactionRequest; +use reth_evm::{block::BlockExecutorFactory, ConfigureEvm, EvmEnv, EvmFactory, SpecFor}; +use reth_primitives_traits::NodePrimitives; +use reth_provider::{ProviderHeader, ProviderTx}; +use reth_rpc_eth_api::{ + helpers::{estimate::EstimateCall, Call, EthCall, LoadBlock, LoadState, SpawnBlocking}, + FromEthApiError, FullEthApiTypes, IntoEthApiError, +}; +use reth_rpc_eth_types::{ + error::FromEvmError, revm_utils::CallFees, EthApiError, RpcInvalidTransactionError, +}; +use revm::{ + context::{Block, TxEnv}, + Database, +}; +use scroll_alloy_evm::{ScrollTransactionIntoTxEnv, TX_L1_FEE_PRECISION_U256}; + +impl<N> EthCall for ScrollEthApi<N> +where + Self: EstimateCall + LoadBlock + FullEthApiTypes, + N: ScrollNodeCore, +{ +} + +impl<N> EstimateCall for ScrollEthApi<N> +where + Self: Call, + Self::Error: From<ScrollEthApiError>, + N: ScrollNodeCore, +{ +} + +impl<N> Call for ScrollEthApi<N> +where + Self: LoadState< + Evm: ConfigureEvm< + Primitives: NodePrimitives< + BlockHeader = ProviderHeader<Self::Provider>, + SignedTx = ProviderTx<Self::Provider>, + >, + BlockExecutorFactory: BlockExecutorFactory< + EvmFactory: EvmFactory<Tx = ScrollTransactionIntoTxEnv<TxEnv>>, + >, + >, + Error: FromEvmError<Self::Evm>, + > + SpawnBlocking, + Self::Error: From<ScrollEthApiError>, + N: ScrollNodeCore, +{ + #[inline] + fn call_gas_limit(&self) -> u64 { + self.inner.eth_api.gas_cap() + } + + #[inline] + fn max_simulate_blocks(&self) -> u64 { + self.inner.eth_api.max_simulate_blocks() + } + + fn create_txn_env( + &self, + evm_env: &EvmEnv<SpecFor<Self::Evm>>, + request: TransactionRequest, + mut db: impl Database<Error: Into<EthApiError>>, + ) -> Result<ScrollTransactionIntoTxEnv<TxEnv>, Self::Error> { + // Ensure that if versioned hashes are set, they're not empty + if request.blob_versioned_hashes.as_ref().is_some_and(|hashes| hashes.is_empty()) { + return Err(RpcInvalidTransactionError::BlobTransactionMissingBlobHashes.into_eth_err()) + } + + let tx_type = request.preferred_type() as u8; + + let TransactionRequest { + from, + to, + gas_price, + max_fee_per_gas, + max_priority_fee_per_gas, + gas, + value, + input, + nonce, + access_list, + chain_id, + blob_versioned_hashes, + max_fee_per_blob_gas, + authorization_list, + transaction_type: _, + sidecar: _, + } = request; + + let CallFees { max_priority_fee_per_gas, gas_price, max_fee_per_blob_gas } = + CallFees::ensure_fees( + gas_price.map(U256::from), + max_fee_per_gas.map(U256::from), + max_priority_fee_per_gas.map(U256::from), + U256::from(evm_env.block_env.basefee), + blob_versioned_hashes.as_deref(), + max_fee_per_blob_gas.map(U256::from), + evm_env.block_env.blob_gasprice().map(U256::from), + )?; + + let gas_limit = gas.unwrap_or( + // Use maximum allowed gas limit. The reason for this + // is that both Erigon and Geth use pre-configured gas cap even if + // it's possible to derive the gas limit from the block: + // <https://github.com/ledgerwatch/erigon/blob/eae2d9a79cb70dbe30b3a6b79c436872e4605458/cmd/rpcdaemon/commands/trace_adhoc.go#L956 + // https://github.com/ledgerwatch/erigon/blob/eae2d9a79cb70dbe30b3a6b79c436872e4605458/eth/ethconfig/config.go#L94> + evm_env.block_env.gas_limit, + ); + + let chain_id = chain_id.unwrap_or(evm_env.cfg_env.chain_id); + + let caller = from.unwrap_or_default(); + + let nonce = if let Some(nonce) = nonce { + nonce + } else { + db.basic(caller).map_err(Into::into)?.map(|acc| acc.nonce).unwrap_or_default() + }; + + let base = TxEnv { + tx_type, + gas_limit, + nonce, + caller, + gas_price: gas_price.saturating_to(), + gas_priority_fee: max_priority_fee_per_gas.map(|v| v.saturating_to()), + kind: to.unwrap_or(TxKind::Create), + value: value.unwrap_or_default(), + data: input + .try_into_unique_input() + .map_err(Self::Error::from_eth_err)? + .unwrap_or_default(), + chain_id: Some(chain_id), + access_list: access_list.unwrap_or_default(), + // EIP-4844 fields + blob_hashes: blob_versioned_hashes.unwrap_or_default(), + max_fee_per_blob_gas: max_fee_per_blob_gas + .map(|v| v.saturating_to()) + .unwrap_or_default(), + // EIP-7702 fields + authorization_list: authorization_list + .unwrap_or_default() + .into_iter() + .map(Either::Left) + .collect(), + }; + + Ok(ScrollTransactionIntoTxEnv::new( + base, + Some(Default::default()), + Some(TX_L1_FEE_PRECISION_U256), + )) + } +}
diff --git reth/crates/scroll/rpc/src/eth/mod.rs scroll-reth/crates/scroll/rpc/src/eth/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..c89c1509fbb5a4ac90fdaa677fbed52b6546d06d --- /dev/null +++ scroll-reth/crates/scroll/rpc/src/eth/mod.rs @@ -0,0 +1,350 @@ +//! Scroll-Reth `eth_` endpoint implementation. + +use alloy_primitives::U256; +use reth_chainspec::{EthChainSpec, EthereumHardforks}; +use reth_evm::ConfigureEvm; +use reth_network_api::NetworkInfo; +use reth_node_api::FullNodeComponents; +use reth_provider::{ + BlockNumReader, BlockReader, BlockReaderIdExt, CanonStateSubscriptions, ChainSpecProvider, + ProviderBlock, ProviderHeader, ProviderReceipt, ProviderTx, StageCheckpointReader, + StateProviderFactory, +}; +use reth_rpc::eth::{core::EthApiInner, DevSigner}; +use reth_rpc_eth_api::{ + helpers::{ + AddDevSigners, EthApiSpec, EthFees, EthSigner, EthState, LoadBlock, LoadFee, LoadState, + SpawnBlocking, Trace, + }, + EthApiTypes, FullEthApiServer, RpcConverter, RpcNodeCore, RpcNodeCoreExt, +}; +use reth_rpc_eth_types::{EthStateCache, FeeHistoryCache, GasPriceOracle}; +use reth_tasks::{ + pool::{BlockingTaskGuard, BlockingTaskPool}, + TaskSpawner, +}; +use reth_transaction_pool::TransactionPool; +use std::{fmt, marker::PhantomData, sync::Arc}; + +use crate::{eth::transaction::ScrollTxInfoMapper, ScrollEthApiError}; +pub use receipt::ScrollReceiptBuilder; +use reth_node_builder::rpc::{EthApiBuilder, EthApiCtx}; +use reth_primitives_traits::NodePrimitives; +use reth_rpc_eth_types::error::FromEvmError; +use reth_scroll_primitives::ScrollPrimitives; +use scroll_alloy_network::{Network, Scroll}; + +mod block; +mod call; +mod pending_block; +pub mod receipt; +pub mod transaction; + +/// Adapter for [`EthApiInner`], which holds all the data required to serve core `eth_` API. +pub type EthApiNodeBackend<N> = EthApiInner< + <N as RpcNodeCore>::Provider, + <N as RpcNodeCore>::Pool, + <N as RpcNodeCore>::Network, + <N as RpcNodeCore>::Evm, +>; + +/// A helper trait with requirements for [`RpcNodeCore`] to be used in [`ScrollEthApi`]. +pub trait ScrollNodeCore: RpcNodeCore<Provider: BlockReader> {} +impl<T> ScrollNodeCore for T where T: RpcNodeCore<Provider: BlockReader> {} + +/// Scroll-Reth `Eth` API implementation. +/// +/// This type provides the functionality for handling `eth_` related requests. +/// +/// This wraps a default `Eth` implementation, and provides additional functionality where the +/// scroll spec deviates from the default (ethereum) spec, e.g. transaction forwarding to the +/// receipts, additional RPC fields for transaction receipts. +/// +/// This type implements the [`FullEthApi`](reth_rpc_eth_api::helpers::FullEthApi) by implemented +/// all the `Eth` helper traits and prerequisite traits. +#[derive(Clone)] +pub struct ScrollEthApi<N: ScrollNodeCore, NetworkT = Scroll> { + /// Gateway to node's core components. + inner: Arc<ScrollEthApiInner<N>>, + /// Marker for the network types. + _nt: PhantomData<NetworkT>, + tx_resp_builder: + RpcConverter<N::Primitives, NetworkT, ScrollEthApiError, ScrollTxInfoMapper<N>>, +} + +impl<N: ScrollNodeCore, NetworkT> ScrollEthApi<N, NetworkT> { + /// Creates a new [`ScrollEthApi`]. + pub fn new(eth_api: EthApiNodeBackend<N>) -> Self { + let inner = Arc::new(ScrollEthApiInner { eth_api }); + Self { + inner: inner.clone(), + _nt: PhantomData, + tx_resp_builder: RpcConverter::with_mapper(ScrollTxInfoMapper::new(inner)), + } + } +} + +impl<N> ScrollEthApi<N> +where + N: ScrollNodeCore< + Provider: BlockReaderIdExt + + ChainSpecProvider + + CanonStateSubscriptions<Primitives = ScrollPrimitives> + + Clone + + 'static, + >, +{ + /// Returns a reference to the [`EthApiNodeBackend`]. + pub fn eth_api(&self) -> &EthApiNodeBackend<N> { + self.inner.eth_api() + } + + /// Return a builder for the [`ScrollEthApi`]. + pub const fn builder() -> ScrollEthApiBuilder { + ScrollEthApiBuilder::new() + } +} + +impl<N, NetworkT> EthApiTypes for ScrollEthApi<N, NetworkT> +where + Self: Send + Sync + fmt::Debug, + N: ScrollNodeCore, + NetworkT: Network + Clone + fmt::Debug, + <N as RpcNodeCore>::Primitives: fmt::Debug, +{ + type Error = ScrollEthApiError; + type NetworkTypes = Scroll; + type TransactionCompat = + RpcConverter<N::Primitives, NetworkT, ScrollEthApiError, ScrollTxInfoMapper<N>>; + + fn tx_resp_builder(&self) -> &Self::TransactionCompat { + &self.tx_resp_builder + } +} + +impl<N, NetworkT> RpcNodeCore for ScrollEthApi<N, NetworkT> +where + N: ScrollNodeCore, + NetworkT: Network, +{ + type Primitives = N::Primitives; + type Provider = N::Provider; + type Pool = N::Pool; + type Evm = <N as RpcNodeCore>::Evm; + type Network = <N as RpcNodeCore>::Network; + type PayloadBuilder = (); + + #[inline] + fn pool(&self) -> &Self::Pool { + self.inner.eth_api.pool() + } + + #[inline] + fn evm_config(&self) -> &Self::Evm { + self.inner.eth_api.evm_config() + } + + #[inline] + fn network(&self) -> &Self::Network { + self.inner.eth_api.network() + } + + #[inline] + fn payload_builder(&self) -> &Self::PayloadBuilder { + &() + } + + #[inline] + fn provider(&self) -> &Self::Provider { + self.inner.eth_api.provider() + } +} + +impl<N, NetworkT> RpcNodeCoreExt for ScrollEthApi<N, NetworkT> +where + N: ScrollNodeCore, + NetworkT: Network, +{ + #[inline] + fn cache(&self) -> &EthStateCache<ProviderBlock<N::Provider>, ProviderReceipt<N::Provider>> { + self.inner.eth_api.cache() + } +} + +impl<N, NetworkT> EthApiSpec for ScrollEthApi<N, NetworkT> +where + N: ScrollNodeCore< + Provider: ChainSpecProvider<ChainSpec: EthereumHardforks> + + BlockNumReader + + StageCheckpointReader, + Network: NetworkInfo, + >, + NetworkT: Network, +{ + type Transaction = ProviderTx<Self::Provider>; + + #[inline] + fn starting_block(&self) -> U256 { + self.inner.eth_api.starting_block() + } + + #[inline] + fn signers(&self) -> &parking_lot::RwLock<Vec<Box<dyn EthSigner<ProviderTx<Self::Provider>>>>> { + self.inner.eth_api.signers() + } +} + +impl<N, NetworkT> SpawnBlocking for ScrollEthApi<N, NetworkT> +where + Self: Send + Sync + Clone + 'static, + N: ScrollNodeCore, + NetworkT: Network, + <N as RpcNodeCore>::Primitives: fmt::Debug, +{ + #[inline] + fn io_task_spawner(&self) -> impl TaskSpawner { + self.inner.eth_api.task_spawner() + } + + #[inline] + fn tracing_task_pool(&self) -> &BlockingTaskPool { + self.inner.eth_api.blocking_task_pool() + } + + #[inline] + fn tracing_task_guard(&self) -> &BlockingTaskGuard { + self.inner.eth_api.blocking_task_guard() + } +} + +impl<N, NetworkT> LoadFee for ScrollEthApi<N, NetworkT> +where + Self: LoadBlock<Provider = N::Provider>, + N: ScrollNodeCore< + Provider: BlockReaderIdExt + + ChainSpecProvider<ChainSpec: EthChainSpec + EthereumHardforks> + + StateProviderFactory, + >, +{ + #[inline] + fn gas_oracle(&self) -> &GasPriceOracle<Self::Provider> { + self.inner.eth_api.gas_oracle() + } + + #[inline] + fn fee_history_cache(&self) -> &FeeHistoryCache { + self.inner.eth_api.fee_history_cache() + } +} + +impl<N, NetworkT> LoadState for ScrollEthApi<N, NetworkT> +where + N: ScrollNodeCore< + Provider: StateProviderFactory + ChainSpecProvider<ChainSpec: EthereumHardforks>, + Pool: TransactionPool, + >, + NetworkT: Network, + <N as RpcNodeCore>::Primitives: fmt::Debug, +{ +} + +impl<N, NetworkT> EthState for ScrollEthApi<N, NetworkT> +where + Self: LoadState + SpawnBlocking, + N: ScrollNodeCore, +{ + #[inline] + fn max_proof_window(&self) -> u64 { + self.inner.eth_api.eth_proof_window() + } +} + +impl<N, NetworkT> EthFees for ScrollEthApi<N, NetworkT> +where + Self: LoadFee, + N: ScrollNodeCore, +{ +} + +impl<N, NetworkT> Trace for ScrollEthApi<N, NetworkT> +where + Self: RpcNodeCore<Provider: BlockReader> + + LoadState< + Evm: ConfigureEvm< + Primitives: NodePrimitives< + BlockHeader = ProviderHeader<Self::Provider>, + SignedTx = ProviderTx<Self::Provider>, + >, + >, + Error: FromEvmError<Self::Evm>, + >, + N: ScrollNodeCore, +{ +} + +impl<N, NetworkT> AddDevSigners for ScrollEthApi<N, NetworkT> +where + N: ScrollNodeCore, +{ + fn with_dev_accounts(&self) { + *self.inner.eth_api.signers().write() = DevSigner::random_signers(20) + } +} + +impl<N: ScrollNodeCore, NetworkT> fmt::Debug for ScrollEthApi<N, NetworkT> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ScrollEthApi").finish_non_exhaustive() + } +} + +/// Container type `ScrollEthApi` +#[allow(missing_debug_implementations)] +pub struct ScrollEthApiInner<N: ScrollNodeCore> { + /// Gateway to node's core components. + pub eth_api: EthApiNodeBackend<N>, +} + +impl<N: ScrollNodeCore> ScrollEthApiInner<N> { + /// Returns a reference to the [`EthApiNodeBackend`]. + const fn eth_api(&self) -> &EthApiNodeBackend<N> { + &self.eth_api + } +} + +/// A type that knows how to build a [`ScrollEthApi`]. +#[derive(Debug, Default)] +pub struct ScrollEthApiBuilder {} + +impl ScrollEthApiBuilder { + /// Creates a [`ScrollEthApiBuilder`] instance. + pub const fn new() -> Self { + Self {} + } +} + +impl<N> EthApiBuilder<N> for ScrollEthApiBuilder +where + N: FullNodeComponents, + ScrollEthApi<N>: FullEthApiServer<Provider = N::Provider, Pool = N::Pool>, +{ + type EthApi = ScrollEthApi<N>; + + async fn build_eth_api(self, ctx: EthApiCtx<'_, N>) -> eyre::Result<Self::EthApi> { + let eth_api = reth_rpc::EthApiBuilder::new( + ctx.components.provider().clone(), + ctx.components.pool().clone(), + ctx.components.network().clone(), + ctx.components.evm_config().clone(), + ) + .eth_cache(ctx.cache) + .task_spawner(ctx.components.task_executor().clone()) + .gas_cap(ctx.config.rpc_gas_cap.into()) + .max_simulate_blocks(ctx.config.rpc_max_simulate_blocks) + .eth_proof_window(ctx.config.eth_proof_window) + .fee_history_cache_config(ctx.config.fee_history_cache) + .proof_permits(ctx.config.proof_permits) + .build_inner(); + + Ok(ScrollEthApi::new(eth_api)) + } +}
diff --git reth/crates/scroll/rpc/src/eth/pending_block.rs scroll-reth/crates/scroll/rpc/src/eth/pending_block.rs new file mode 100644 index 0000000000000000000000000000000000000000..9bdb6b9b7d448e095a69d6af870cd806b0d53245 --- /dev/null +++ scroll-reth/crates/scroll/rpc/src/eth/pending_block.rs @@ -0,0 +1,74 @@ +//! Loads Scroll pending block for an RPC response. + +use crate::ScrollEthApi; + +use alloy_consensus::{BlockHeader, Header}; +use reth_chainspec::EthChainSpec; +use reth_evm::ConfigureEvm; +use reth_primitives_traits::{NodePrimitives, SealedHeader}; +use reth_provider::{ + BlockReaderIdExt, ChainSpecProvider, ProviderBlock, ProviderHeader, ProviderReceipt, + ProviderTx, StateProviderFactory, +}; +use reth_rpc_eth_api::{ + helpers::{LoadPendingBlock, SpawnBlocking}, + types::RpcTypes, + EthApiTypes, RpcNodeCore, +}; +use reth_rpc_eth_types::{error::FromEvmError, PendingBlock}; +use reth_scroll_evm::ScrollNextBlockEnvAttributes; +use reth_scroll_primitives::{ScrollBlock, ScrollReceipt, ScrollTransactionSigned}; +use reth_transaction_pool::{PoolTransaction, TransactionPool}; +use scroll_alloy_hardforks::ScrollHardforks; + +impl<N> LoadPendingBlock for ScrollEthApi<N> +where + Self: SpawnBlocking + + EthApiTypes< + NetworkTypes: RpcTypes< + Header = alloy_rpc_types_eth::Header<ProviderHeader<Self::Provider>>, + >, + Error: FromEvmError<Self::Evm>, + >, + N: RpcNodeCore< + Provider: BlockReaderIdExt< + Transaction = ScrollTransactionSigned, + Block = ScrollBlock, + Receipt = ScrollReceipt, + Header = Header, + > + ChainSpecProvider<ChainSpec: EthChainSpec + ScrollHardforks> + + StateProviderFactory, + Pool: TransactionPool<Transaction: PoolTransaction<Consensus = ProviderTx<N::Provider>>>, + Evm: ConfigureEvm< + Primitives = <Self as RpcNodeCore>::Primitives, + NextBlockEnvCtx = ScrollNextBlockEnvAttributes, + >, + Primitives: NodePrimitives< + BlockHeader = ProviderHeader<Self::Provider>, + SignedTx = ProviderTx<Self::Provider>, + Receipt = ProviderReceipt<Self::Provider>, + Block = ProviderBlock<Self::Provider>, + >, + >, +{ + #[inline] + fn pending_block( + &self, + ) -> &tokio::sync::Mutex< + Option<PendingBlock<ProviderBlock<Self::Provider>, ProviderReceipt<Self::Provider>>>, + > { + self.inner.eth_api.pending_block() + } + + fn next_env_attributes( + &self, + parent: &SealedHeader<ProviderHeader<Self::Provider>>, + ) -> Result<<Self::Evm as ConfigureEvm>::NextBlockEnvCtx, Self::Error> { + Ok(ScrollNextBlockEnvAttributes { + timestamp: parent.timestamp().saturating_add(3), + suggested_fee_recipient: parent.beneficiary(), + gas_limit: parent.gas_limit(), + base_fee: parent.base_fee_per_gas().unwrap_or_default(), + }) + } +}
diff --git reth/crates/scroll/rpc/src/eth/receipt.rs scroll-reth/crates/scroll/rpc/src/eth/receipt.rs new file mode 100644 index 0000000000000000000000000000000000000000..254fb1f546b71d852ed75236d33b61766f1c73af --- /dev/null +++ scroll-reth/crates/scroll/rpc/src/eth/receipt.rs @@ -0,0 +1,97 @@ +//! Loads and formats Scroll receipt RPC response. + +use crate::{ScrollEthApi, ScrollEthApiError}; +use alloy_rpc_types_eth::{Log, TransactionReceipt}; +use reth_node_api::{FullNodeComponents, NodeTypes}; +use reth_primitives::TransactionMeta; +use reth_provider::{ReceiptProvider, TransactionsProvider}; +use reth_rpc_eth_api::{helpers::LoadReceipt, FromEthApiError, RpcReceipt}; +use reth_rpc_eth_types::{receipt::build_receipt, EthApiError}; + +use reth_scroll_chainspec::ScrollChainSpec; +use reth_scroll_primitives::{ScrollReceipt, ScrollTransactionSigned}; +use scroll_alloy_consensus::ScrollReceiptEnvelope; +use scroll_alloy_rpc_types::{ScrollTransactionReceipt, ScrollTransactionReceiptFields}; + +impl<N> LoadReceipt for ScrollEthApi<N> +where + Self: Send + Sync, + N: FullNodeComponents<Types: NodeTypes<ChainSpec = ScrollChainSpec>>, + Self::Provider: TransactionsProvider<Transaction = ScrollTransactionSigned> + + ReceiptProvider<Receipt = ScrollReceipt>, +{ + async fn build_transaction_receipt( + &self, + tx: ScrollTransactionSigned, + meta: TransactionMeta, + receipt: ScrollReceipt, + ) -> Result<RpcReceipt<Self::NetworkTypes>, Self::Error> { + let all_receipts = self + .inner + .eth_api + .cache() + .get_receipts(meta.block_hash) + .await + .map_err(Self::Error::from_eth_err)? + .ok_or(Self::Error::from_eth_err(EthApiError::HeaderNotFound( + meta.block_hash.into(), + )))?; + + Ok(ScrollReceiptBuilder::new(&tx, meta, &receipt, &all_receipts)?.build()) + } +} + +/// Builds an [`ScrollTransactionReceipt`]. +#[derive(Debug)] +pub struct ScrollReceiptBuilder { + /// Core receipt, has all the fields of an L1 receipt and is the basis for the Scroll receipt. + pub core_receipt: TransactionReceipt<ScrollReceiptEnvelope<Log>>, + /// Additional Scroll receipt fields. + pub scroll_receipt_fields: ScrollTransactionReceiptFields, +} + +impl ScrollReceiptBuilder { + /// Returns a new builder. + pub fn new( + transaction: &ScrollTransactionSigned, + meta: TransactionMeta, + receipt: &ScrollReceipt, + all_receipts: &[ScrollReceipt], + ) -> Result<Self, ScrollEthApiError> { + let core_receipt = + build_receipt(transaction, meta, receipt, all_receipts, None, |receipt_with_bloom| { + match receipt { + ScrollReceipt::Legacy(_) => { + ScrollReceiptEnvelope::<Log>::Legacy(receipt_with_bloom) + } + ScrollReceipt::Eip2930(_) => { + ScrollReceiptEnvelope::<Log>::Eip2930(receipt_with_bloom) + } + ScrollReceipt::Eip1559(_) => { + ScrollReceiptEnvelope::<Log>::Eip1559(receipt_with_bloom) + } + ScrollReceipt::Eip7702(_) => { + ScrollReceiptEnvelope::<Log>::Eip7702(receipt_with_bloom) + } + ScrollReceipt::L1Message(_) => { + ScrollReceiptEnvelope::<Log>::L1Message(receipt_with_bloom) + } + } + })?; + + let scroll_receipt_fields = + ScrollTransactionReceiptFields { l1_fee: Some(receipt.l1_fee().saturating_to()) }; + + Ok(Self { core_receipt, scroll_receipt_fields }) + } + + /// Builds [`ScrollTransactionReceipt`] by combing core (l1) receipt fields and additional + /// Scroll receipt fields. + pub fn build(self) -> ScrollTransactionReceipt { + let Self { core_receipt: inner, scroll_receipt_fields } = self; + + let ScrollTransactionReceiptFields { l1_fee, .. } = scroll_receipt_fields; + + ScrollTransactionReceipt { inner, l1_fee } + } +}
diff --git reth/crates/scroll/rpc/src/eth/transaction.rs scroll-reth/crates/scroll/rpc/src/eth/transaction.rs new file mode 100644 index 0000000000000000000000000000000000000000..d044b1c7d339d919b4fa56c69929c872502c3d2b --- /dev/null +++ scroll-reth/crates/scroll/rpc/src/eth/transaction.rs @@ -0,0 +1,97 @@ +//! Loads and formats Scroll transaction RPC response. + +use crate::{ + eth::{ScrollEthApiInner, ScrollNodeCore}, + ScrollEthApi, +}; +use alloy_consensus::transaction::TransactionInfo; +use alloy_primitives::{Bytes, B256}; +use reth_evm::execute::ProviderError; +use reth_node_api::FullNodeComponents; +use reth_provider::{ + BlockReader, BlockReaderIdExt, ProviderTx, ReceiptProvider, TransactionsProvider, +}; +use reth_rpc_eth_api::{ + helpers::{EthSigner, EthTransactions, LoadTransaction, SpawnBlocking}, + try_into_scroll_tx_info, FromEthApiError, FullEthApiTypes, RpcNodeCore, RpcNodeCoreExt, + TxInfoMapper, +}; +use reth_rpc_eth_types::utils::recover_raw_transaction; +use reth_scroll_primitives::ScrollReceipt; +use reth_transaction_pool::{PoolTransaction, TransactionOrigin, TransactionPool}; +use scroll_alloy_consensus::{ScrollTransactionInfo, ScrollTxEnvelope}; +use std::{ + fmt::{Debug, Formatter}, + sync::Arc, +}; + +impl<N> EthTransactions for ScrollEthApi<N> +where + Self: LoadTransaction<Provider: BlockReaderIdExt>, + N: ScrollNodeCore<Provider: BlockReader<Transaction = ProviderTx<Self::Provider>>>, +{ + fn signers(&self) -> &parking_lot::RwLock<Vec<Box<dyn EthSigner<ProviderTx<Self::Provider>>>>> { + self.inner.eth_api.signers() + } + + /// Decodes and recovers the transaction and submits it to the pool. + /// + /// Returns the hash of the transaction. + async fn send_raw_transaction(&self, tx: Bytes) -> Result<B256, Self::Error> { + let recovered = recover_raw_transaction(&tx)?; + let pool_transaction = <Self::Pool as TransactionPool>::Transaction::from_pooled(recovered); + + // submit the transaction to the pool with a `Local` origin + let hash = self + .pool() + .add_transaction(TransactionOrigin::Local, pool_transaction) + .await + .map_err(Self::Error::from_eth_err)?; + + Ok(hash) + } +} + +impl<N> LoadTransaction for ScrollEthApi<N> +where + Self: SpawnBlocking + FullEthApiTypes + RpcNodeCoreExt, + N: ScrollNodeCore<Provider: TransactionsProvider, Pool: TransactionPool>, + Self::Pool: TransactionPool, +{ +} + +/// Scroll implementation of [`TxInfoMapper`]. +/// +/// Receipt is fetched to extract the `l1_fee` for all transactions but L1 messages. +#[derive(Clone)] +pub struct ScrollTxInfoMapper<N: ScrollNodeCore>(Arc<ScrollEthApiInner<N>>); + +impl<N: ScrollNodeCore> Debug for ScrollTxInfoMapper<N> { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ScrollTxInfoMapper").finish() + } +} + +impl<N: ScrollNodeCore> ScrollTxInfoMapper<N> { + /// Creates [`ScrollTxInfoMapper`] that uses [`ReceiptProvider`] borrowed from given `eth_api`. + pub const fn new(eth_api: Arc<ScrollEthApiInner<N>>) -> Self { + Self(eth_api) + } +} + +impl<N> TxInfoMapper<&ScrollTxEnvelope> for ScrollTxInfoMapper<N> +where + N: FullNodeComponents, + N::Provider: ReceiptProvider<Receipt = ScrollReceipt>, +{ + type Out = ScrollTransactionInfo; + type Err = ProviderError; + + fn try_map( + &self, + tx: &ScrollTxEnvelope, + tx_info: TransactionInfo, + ) -> Result<Self::Out, ProviderError> { + try_into_scroll_tx_info(self.0.eth_api.provider(), tx, tx_info) + } +}
diff --git reth/crates/scroll/rpc/src/lib.rs scroll-reth/crates/scroll/rpc/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..a3058ffee023922679eeb8fa951f3294c2c60096 --- /dev/null +++ scroll-reth/crates/scroll/rpc/src/lib.rs @@ -0,0 +1,15 @@ +//! Scroll-Reth RPC support. + +#![doc( + html_logo_url = "https://raw.githubusercontent.com/paradigmxyz/reth/main/assets/reth-docs.png", + html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", + issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" +)] +#![cfg_attr(not(test), warn(unused_crate_dependencies))] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] + +pub mod error; +pub mod eth; + +pub use error::ScrollEthApiError; +pub use eth::{ScrollEthApi, ScrollReceiptBuilder};
diff --git reth/crates/scroll/trie/Cargo.toml scroll-reth/crates/scroll/trie/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..4154ad181afe452c66590e79ed870c82e7d70aa6 --- /dev/null +++ scroll-reth/crates/scroll/trie/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "reth-scroll-trie" +version.workspace = true +edition.workspace = true +rust-version.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +exclude.workspace = true + +[lints] +workspace = true + +[dependencies] +# reth +reth-trie.workspace = true + +# alloy +alloy-primitives.workspace = true +alloy-trie = { workspace = true, features = ["serde"] } + +# misc +poseidon-bn254 = { workspace = true, features = ["bn254"] } +tracing.workspace = true
diff --git reth/crates/scroll/trie/README.md scroll-reth/crates/scroll/trie/README.md new file mode 100644 index 0000000000000000000000000000000000000000..8645b5efadd58f51d8d04146614741788a95febd --- /dev/null +++ scroll-reth/crates/scroll/trie/README.md @@ -0,0 +1,5 @@ +# scroll-trie + +Fast binary Merkle-Patricia Trie (zktrie) state root calculator and proof generator for prefix-sorted bits. + +Please see the specification of zktrie [here](assets/zktrie.md). \ No newline at end of file
diff --git reth/crates/scroll/trie/assets/arch.png scroll-reth/crates/scroll/trie/assets/arch.png new file mode 100644 index 0000000000000000000000000000000000000000..aca3f8d0c5ce4eedb5d40b8dedb39d226e57efbe Binary files /dev/null and scroll-reth/crates/scroll/trie/assets/arch.png differ
diff --git reth/crates/scroll/trie/assets/deletion.png scroll-reth/crates/scroll/trie/assets/deletion.png new file mode 100644 index 0000000000000000000000000000000000000000..6c699226e0996a30199a3ff4d2441700390cb8e2 Binary files /dev/null and scroll-reth/crates/scroll/trie/assets/deletion.png differ
diff --git reth/crates/scroll/trie/assets/insertion.png scroll-reth/crates/scroll/trie/assets/insertion.png new file mode 100644 index 0000000000000000000000000000000000000000..942338a07f2ea46e33fc899fd222b6ae6671f5e4 Binary files /dev/null and scroll-reth/crates/scroll/trie/assets/insertion.png differ
diff --git reth/crates/scroll/trie/assets/zktrie.md scroll-reth/crates/scroll/trie/assets/zktrie.md new file mode 100644 index 0000000000000000000000000000000000000000..b86e1a016ab8bae8e13f7e1b9f7fe1d29bfc7711 --- /dev/null +++ scroll-reth/crates/scroll/trie/assets/zktrie.md @@ -0,0 +1,186 @@ +# zkTrie Spec + +## 1. Tree Structure + +<figure> +<img src="https://raw.githubusercontent.com/scroll-tech/reth/refs/heads/scroll/crates/scroll/trie/assets/arch.png" alt="zkTrie Structure" style="width:80%"> +<figcaption align = "center"><b>Figure 1. zkTrie Structure</b></figcaption> +</figure> + +In essence, zkTrie is a sparse binary Merkle Patricia Trie, depicted in the above figure. +Before diving into the Sparse Binary Merkle Patricia Trie, let's briefly touch on Merkle Trees and Patricia Tries. +* **Merkle Tree**: A Merkle Tree is a tree where each leaf node represents a hash of a data block, and each non-leaf node represents the hash of its child nodes. +* **Patricia Trie**: A Patricia Trie is a type of radix tree or compressed trie used to store key-value pairs efficiently. It encodes the nodes with same prefix of the key to share the common path, where the path is determined by the value of the node key. + +As illustrated in the Figure 1, there are three types of nodes in the zkTrie. +- Parent Node (type: 0): Given the zkTrie is a binary tree, a parent node has two children. +- Leaf Node (type: 1): A leaf node holds the data of a key-value pair. +- Empty Node (type: 2): An empty node is a special type of node, indicating the sub-trie that shares the same prefix is empty. + +In zkTrie, we use Poseidon hash to compute the node hash because it's more friendly and efficient to prove it in the zk circuit. + +## 2. Tree Construction + +Given a key-value pair, we first compute a *secure key* for the corresponding leaf node by hashing the original key (i.e., account address and storage key) using the Poseidon hash function. This can make the key uniformly distributed over the key space. The node key hashing method is described in the [Node Hashing](#3-node-hashing) section below. + +We then encode the path of a new leaf node by traversing the secure key from Least Significant Bit (LSB) to the Most Significant Bit (MSB). At each step, if the bit is 0, we will traverse to the left child; otherwise, traverse to the right child. + +We limit the maximum depth of zkTrie to 248, meaning that the tree will only traverse the lower 248 bits of the key. This is because the secure key space is a finite field used by Poseidon hash that doesn't occupy the full range of power of 2. This leads to an ambiguous bit representation of the key in a finite field and thus causes a soundness issue in the zk circuit. But if we truncate the key to lower 248 bits, the key space can fully occupy the range of $2^{248}$ and won't have the ambiguity in the bit representation. + +We also apply an optimization to reduce the tree depth by contracting a subtree that has only one leaf node to a single leaf node. For example, in the Figure 1, the tree has three nodes in total, with keys `0100`, `0010`, and `1010`. Because there is only one node that has key with suffix `00`, the leaf node for key `0100` only traverses the suffix `00` and doesn't fully expand its key which would have resulted in depth of 4. + +## 3. Node Hashing + +In this section, we will describe how leaf secure key and node merkle hash are computed. We use Poseidon hash in both hashing computation, denoted as `h` in the doc below. + +<aside> +💡 Note: We use `init_state = 0` in the Poseidon hash function for all use cases in the zkTrie. +</aside> + +### 3.1 Empty Node + +The node hash of an empty node is 0. + +### 3.2 Parent Node + +The parent node hash is computed as follows + +```go +parentNodeHash = h(leftChildHash, rightChildHash) +``` + +### 3.3 Leaf Node + +The node hash of a leaf node is computed as follows + +```go +leafNodeHash = h(h(1, nodeKey), valueHash) +``` + +The leaf node can hold two types of values: Ethereum accounts and storage key-value pairs. Next, we will describe how the node key and value hash are computed for each leaf node type. + +#### Ethereum Account Leaf Node +For an Ethereum Account Leaf Node, it consists of an Ethereum address and a state account struct. The secure key is derived from the Ethereum address. +``` +address[0:20] (20 bytes in big-endian) +valHi = address[0:16] +valLo = address[16:20] * 2^96 (padding 12 bytes of 0 at the end) +nodeKey = h(valHi, valLo) +``` + +A state account struct in the Scroll consists of the following fields (`Fr` indicates the finite field used in Poseidon hash and is a 254-bit value) + +- `Nonce`: u64 +- `Balance`: u256, but treated as Fr +- `StorageRoot`: Fr +- `KeccakCodeHash`: u256 +- `PoseidonCodeHash`: Fr +- `CodeSize`: u64 + +Before computing the value hash, the state account is first marshaled into a list of `u256` values. The marshaling scheme is + +``` +(The following scheme assumes the big-endian encoding) +[0:32] (bytes in big-endian) + [0:16] Reserved with all 0 + [16:24] CodeSize, uint64 in big-endian + [24:32] Nonce, uint64 in big-endian +[32:64] Balance +[64:96] StorageRoot +[96:128] KeccakCodeHash +[128:160] PoseidonCodehash +(total 160 bytes) +``` + +The marshal function also returns a `flag` value along with a vector of `u256` values. The `flag` is a bitmap that indicates whether a `u256` value CANNOT be treated as a field element (Fr). The `flag` value for state account is 8, shown below. + +``` ++--------------------+---------+------+----------+----------+ +| 0 | 1 | 2 | 3 | 4 | (index) ++--------------------+---------+------+----------+----------+ +| nonce||codesize||0 | balance | root | keccak | poseidon | (u256) ++--------------------+---------+------+----------+----------+ +| 0 | 0 | 0 | 1 | 0 | (flag bits) ++--------------------+---------+------+----------+----------+ +(LSB) (MSB) +``` + +The value hash is computed in two steps: +1. Convert the value that cannot be represented as a field element of the Poseidon hash to the field element. +2. Combine field elements in a binary tree structure till the tree root is treated as the value hash. + +In the first step, when the bit in the `flag` is 1 indicating the `u256` value that cannot be treated as a field element, we split the value into a high-128bit value and a low-128bit value, and then pass them to a Poseidon hash to derive a field element value, `h(valueHi, valueLo)`. + +Based on the definition, the value hash of the state account is computed as follows. + +``` +valueHash = +h( + h( + h(nonce||codesize||0, balance), + h( + storageRoot, + h(keccakCodeHash[0:16], keccakCodeHash[16:32]), // convert Keccak codehash to a field element + ), + ), + poseidonCodeHash, +) +``` + +#### Storage Leaf Node + +For a Storage Leaf Node, it is a key-value pair, which both are a `u256` value. The secure key of this leaf node is derived from the storage key. + +``` +storageKey[0:32] (32 bytes in big-endian) +valHi = storageKey[0:16] +valLo = storageKey[16:32] +nodeKey = h(valHi, valLo) +``` + +The storage value is a `u256` value. The `flag` for the storage value is 1, showed below. + +``` ++--------------+ +| 0 | (index) ++--------------+ +| storageValue | (u256) ++--------------+ +| 1 | (flag bits) ++--------------+ +``` + +The value hash is computed as follows + +```go +valueHash = h(storageValue[0:16], storageValue[16:32]) +``` + +## 4. Tree Operations + +### 4.1 Insertion + +<figure> +<img src="https://raw.githubusercontent.com/scroll-tech/reth/refs/heads/scroll/crates/scroll/trie/assets/insertion.png" alt="zkTrie Structure" style="width:80%"> +<figcaption align = "center"><b>Figure 2. Insert a new leaf node to zkTrie</b></figcaption> +</figure> + +When we insert a new leaf node to the existing zkTrie, there could be two cases illustrated in the Figure 2. + +1. When traversing the path of the node key, it reaches an empty node (Figure 2(b)). In this case, we just need to replace this empty node by this leaf node and backtrace the path to update the merkle hash of parent nodes till the root. +2. When traversing the path of the node key, it reaches another leaf node `b` (Figure 2(c)). In this case, we need to push down the existing leaf node `b` until the next bit in the node keys of two leaf nodes differs. At each push-down step, we need to insert an empty sibling node when necessary. When we reach the level where the bits differ, we then place two leaf nodes `b` and `c` as the left child and the right child depending on their bits. At last, we backtrace the path and update the merkle hash of all parent nodes. + +### 4.2 Deletion + +<figure> +<img src="https://raw.githubusercontent.com/scroll-tech/reth/refs/heads/scroll/crates/scroll/trie/assets/deletion.png" alt="zkTrie Structure" style="width:80%"> +<figcaption align = "center"><b>Figure 3. Delete a leaf node from the zkTrie</b></figcaption> +</figure> + + +The deletion of a leaf node is similar to the insertion. There are two cases illustrated in the Figure 3. + +1. The sibling node of to-be-deleted leaf node is a parent node (Figure 3(b)). In this case, we can just replace the node `a` by an empty node and update the node hash of its ancestors till the root node. +2. The node of to-be-deleted leaf node is a leaf node (Figure 3(c)). Similarly, we first replace the leaf node by an empty node and start to contract its sibling node upwards until its sibling node is not an empty node. For example, in Figure 3(c), we first replace the leaf node `b` by an empty node. During the contraction, since the sibling of node `c` now becomes an empty node, we move node `c` one level upward to replace its parent node. The new sibling of node `c`, node `e`, is still an empty node. So again we move node `c` upward. Now that the sibling of node `c` is node `a`, the deletion process is finished. + +Note that the sibling of a leaf node in a valid zkTrie cannot be an empty node. Otherwise, we should always prune the subtree and move the leaf node upwards.
diff --git reth/crates/scroll/trie/src/branch.rs scroll-reth/crates/scroll/trie/src/branch.rs new file mode 100644 index 0000000000000000000000000000000000000000..e6b433913d53e71f2db801ae4c89b3a6f975041c --- /dev/null +++ scroll-reth/crates/scroll/trie/src/branch.rs @@ -0,0 +1,155 @@ +use super::{ + BRANCH_NODE_LBRB_DOMAIN, BRANCH_NODE_LBRT_DOMAIN, BRANCH_NODE_LTRB_DOMAIN, + BRANCH_NODE_LTRT_DOMAIN, +}; +use alloy_primitives::{hex, B256}; +use alloy_trie::TrieMask; +use core::{fmt, ops::Range, slice::Iter}; +use poseidon_bn254::{hash_with_domain, Fr, PrimeField}; + +#[allow(unused_imports)] +use alloc::vec::Vec; + +/// The range of valid child indexes. +pub(crate) const CHILD_INDEX_RANGE: Range<u8> = 0..2; + +/// A trie mask to extract the two child indexes from a branch node. +pub(crate) const CHILD_INDEX_MASK: TrieMask = TrieMask::new(0b11); + +/// A reference to branch node and its state mask. +/// NOTE: The stack may contain more items that specified in the state mask. +#[derive(Clone)] +pub(crate) struct BranchNodeRef<'a> { + /// Reference to the collection of hash nodes. + /// NOTE: The referenced stack might have more items than the number of children + /// for this node. We should only ever access items starting from + /// [`BranchNodeRef::first_child_index`]. + pub stack: &'a [B256], + /// Reference to bitmask indicating the presence of children at + /// the respective nibble positions. + pub state_mask: TrieMask, +} + +impl fmt::Debug for BranchNodeRef<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("BranchNodeRef") + .field("stack", &self.stack.iter().map(hex::encode).collect::<Vec<_>>()) + .field("state_mask", &self.state_mask) + .field("first_child_index", &self.first_child_index()) + .finish() + } +} + +impl<'a> BranchNodeRef<'a> { + /// Create a new branch node from the stack of nodes. + #[inline] + pub(crate) const fn new(stack: &'a [B256], state_mask: TrieMask) -> Self { + Self { stack, state_mask } + } + + /// Returns the stack index of the first child for this node. + /// + /// # Panics + /// + /// If the stack length is less than number of children specified in state mask. + /// Means that the node is in inconsistent state. + #[inline] + pub(crate) fn first_child_index(&self) -> usize { + self.stack + .len() + .checked_sub((self.state_mask & CHILD_INDEX_MASK).count_ones() as usize) + .expect("branch node stack is in inconsistent state") + } + + #[inline] + fn children(&self) -> impl Iterator<Item = (u8, Option<&B256>)> + '_ { + BranchChildrenIter::new(self) + } + + /// Given the hash mask of children, return an iterator over stack items + /// that match the mask. + #[inline] + pub(crate) fn child_hashes(&self, hash_mask: TrieMask) -> impl Iterator<Item = B256> + '_ { + self.children() + .filter_map(|(i, c)| c.map(|c| (i, c))) + .filter(move |(index, _)| hash_mask.is_bit_set(*index)) + .map(|(_, child)| B256::from_slice(&child[..])) + } + + pub(crate) fn hash(&self) -> B256 { + let mut children_iter = self.children(); + + let left_child = children_iter + .next() + .map(|(_, c)| *c.unwrap_or_default()) + .expect("branch node has two children"); + let left_child = + Fr::from_repr_vartime(left_child.0).expect("left child is a valid field element"); + let right_child = children_iter + .next() + .map(|(_, c)| *c.unwrap_or_default()) + .expect("branch node has two children"); + let right_child = + Fr::from_repr_vartime(right_child.0).expect("right child is a valid field element"); + + hash_with_domain(&[left_child, right_child], self.hashing_domain()).to_repr().into() + } + + fn hashing_domain(&self) -> Fr { + match *self.state_mask { + 0b1011 => BRANCH_NODE_LBRT_DOMAIN, + 0b1111 => BRANCH_NODE_LTRT_DOMAIN, + 0b0111 => BRANCH_NODE_LTRB_DOMAIN, + 0b0011 => BRANCH_NODE_LBRB_DOMAIN, + _ => unreachable!("invalid branch node state mask"), + } + } +} + +/// Iterator over branch node children. +#[derive(Debug)] +struct BranchChildrenIter<'a> { + range: Range<u8>, + state_mask: TrieMask, + stack_iter: Iter<'a, B256>, +} + +impl<'a> BranchChildrenIter<'a> { + /// Create new iterator over branch node children. + fn new(node: &BranchNodeRef<'a>) -> Self { + Self { + range: CHILD_INDEX_RANGE, + state_mask: node.state_mask, + stack_iter: node.stack[node.first_child_index()..].iter(), + } + } +} + +impl<'a> Iterator for BranchChildrenIter<'a> { + type Item = (u8, Option<&'a B256>); + + #[inline] + fn next(&mut self) -> Option<Self::Item> { + let i = self.range.next()?; + let value = self + .state_mask + .is_bit_set(i) + .then(|| unsafe { self.stack_iter.next().unwrap_unchecked() }); + Some((i, value)) + } + + #[inline] + fn size_hint(&self) -> (usize, Option<usize>) { + let len = self.len(); + (len, Some(len)) + } +} + +impl core::iter::FusedIterator for BranchChildrenIter<'_> {} + +impl ExactSizeIterator for BranchChildrenIter<'_> { + #[inline] + fn len(&self) -> usize { + self.range.len() + } +}
diff --git reth/crates/scroll/trie/src/constants.rs scroll-reth/crates/scroll/trie/src/constants.rs new file mode 100644 index 0000000000000000000000000000000000000000..67bd414f835834ecca93f29497a396384c87f84e --- /dev/null +++ scroll-reth/crates/scroll/trie/src/constants.rs @@ -0,0 +1,4 @@ +use alloy_primitives::B256; + +/// The root hash of an empty binary Merle Patricia trie. +pub const EMPTY_ROOT_HASH: B256 = B256::ZERO;
diff --git reth/crates/scroll/trie/src/hash_builder.rs scroll-reth/crates/scroll/trie/src/hash_builder.rs new file mode 100644 index 0000000000000000000000000000000000000000..60bdf14087f523e9857e82d884fb034599ceb6d8 --- /dev/null +++ scroll-reth/crates/scroll/trie/src/hash_builder.rs @@ -0,0 +1,568 @@ +use crate::{ + branch::{BranchNodeRef, CHILD_INDEX_MASK}, + constants::EMPTY_ROOT_HASH, + leaf::HashLeaf, + sub_tree::SubTreeRef, +}; +use alloy_primitives::{map::HashMap, B256}; +use alloy_trie::{ + hash_builder::{HashBuilderValue, HashBuilderValueRef}, + nodes::LeafNodeRef, + proof::{ProofNodes, ProofRetainer}, + BranchNodeCompact, Nibbles, TrieMask, +}; +use core::cmp; +use tracing::trace; + +#[derive(Debug, Default)] +#[allow(missing_docs)] +pub struct HashBuilder { + pub key: Nibbles, + pub value: HashBuilderValue, + pub stack: Vec<B256>, + + // TODO(scroll): Introduce terminator / leaf masks + pub state_masks: Vec<TrieMask>, + pub tree_masks: Vec<TrieMask>, + pub hash_masks: Vec<TrieMask>, + + pub stored_in_database: bool, + + pub updated_branch_nodes: Option<HashMap<Nibbles, BranchNodeCompact>>, + pub proof_retainer: Option<ProofRetainer>, +} + +impl HashBuilder { + /// Enables the Hash Builder to store updated branch nodes. + /// + /// Call [`HashBuilder::split`] to get the updates to branch nodes. + pub fn with_updates(mut self, retain_updates: bool) -> Self { + self.set_updates(retain_updates); + self + } + + /// Enable specified proof retainer. + pub fn with_proof_retainer(mut self, retainer: ProofRetainer) -> Self { + self.proof_retainer = Some(retainer); + self + } + + /// Enables the Hash Builder to store updated branch nodes. + /// + /// Call [`HashBuilder::split`] to get the updates to branch nodes. + pub fn set_updates(&mut self, retain_updates: bool) { + if retain_updates { + self.updated_branch_nodes = Some(HashMap::default()); + } + } + + /// Splits the [`HashBuilder`] into a [`HashBuilder`] and hash builder updates. + pub fn split(mut self) -> (Self, HashMap<Nibbles, BranchNodeCompact>) { + let updates = self.updated_branch_nodes.take(); + (self, updates.unwrap_or_default()) + } + + /// Take and return retained proof nodes. + pub fn take_proof_nodes(&mut self) -> ProofNodes { + self.proof_retainer.take().map(ProofRetainer::into_proof_nodes).unwrap_or_default() + } + + /// The number of total updates accrued. + /// Returns `0` if [`Self::with_updates`] was not called. + pub fn updates_len(&self) -> usize { + self.updated_branch_nodes.as_ref().map(|u| u.len()).unwrap_or(0) + } + + /// Print the current stack of the Hash Builder. + pub fn print_stack(&self) { + println!("============ STACK ==============="); + for item in &self.stack { + println!("{}", alloy_primitives::hex::encode(item)); + } + println!("============ END STACK ==============="); + } + + /// Adds a new leaf element and its value to the trie hash builder. + pub fn add_leaf(&mut self, key: Nibbles, value: &[u8]) { + assert!(key > self.key, "add_leaf key {:?} self.key {:?}", key, self.key); + if !self.key.is_empty() { + self.update(&key); + } + self.set_key_value(key, HashBuilderValueRef::Bytes(value)); + } + + /// Adds a new branch element and its hash to the trie hash builder. + pub fn add_branch(&mut self, key: Nibbles, value: B256, stored_in_database: bool) { + assert!( + key > self.key || (self.key.is_empty() && key.is_empty()), + "add_branch key {:?} self.key {:?}", + key, + self.key + ); + if !self.key.is_empty() { + self.update(&key); + } else if key.is_empty() { + self.stack.push(value); + } + self.set_key_value(key, HashBuilderValueRef::Hash(&value)); + self.stored_in_database = stored_in_database; + } + + /// Returns the current root hash of the trie builder. + pub fn root(&mut self) -> B256 { + // Clears the internal state + if !self.key.is_empty() { + self.update(&Nibbles::default()); + self.key.clear(); + self.value.clear(); + } + let root = self.current_root(); + if root == EMPTY_ROOT_HASH { + if let Some(proof_retainer) = self.proof_retainer.as_mut() { + proof_retainer.retain(&Nibbles::default(), &[]) + } + } + root + } + + #[inline] + fn set_key_value(&mut self, key: Nibbles, value: HashBuilderValueRef<'_>) { + self.log_key_value("old value"); + self.key = key; + self.value.set_from_ref(value); + self.log_key_value("new value"); + } + + fn log_key_value(&self, msg: &str) { + trace!(target: "trie::hash_builder", + key = ?self.key, + value = ?self.value, + "{msg}", + ); + } + + fn current_root(&self) -> B256 { + if let Some(node_ref) = self.stack.last() { + let mut root = *node_ref; + root.reverse(); + root + } else { + EMPTY_ROOT_HASH + } + } + + /// Given a new element, it appends it to the stack and proceeds to loop through the stack state + /// and convert the nodes it can into branch / extension nodes and hash them. This ensures + /// that the top of the stack always contains the merkle root corresponding to the trie + /// built so far. + fn update(&mut self, succeeding: &Nibbles) { + let mut build_extensions = false; + // current / self.key is always the latest added element in the trie + let mut current = self.key.clone(); + debug_assert!(!current.is_empty()); + + trace!(target: "trie::hash_builder", ?current, ?succeeding, "updating merkle tree"); + + let mut i = 0usize; + loop { + let _span = tracing::trace_span!(target: "trie::hash_builder", "loop", i, ?current, build_extensions).entered(); + + let preceding_exists = !self.state_masks.is_empty(); + let preceding_len = self.state_masks.len().saturating_sub(1); + + let common_prefix_len = succeeding.common_prefix_length(current.as_slice()); + let len = cmp::max(preceding_len, common_prefix_len); + assert!(len < current.len(), "len {} current.len {}", len, current.len()); + + trace!( + target: "trie::hash_builder", + ?len, + ?common_prefix_len, + ?preceding_len, + preceding_exists, + "prefix lengths after comparing keys" + ); + + // Adjust the state masks for branch calculation + let extra_digit = current[len]; + if self.state_masks.len() <= len { + let new_len = len + 1; + trace!(target: "trie::hash_builder", new_len, old_len = self.state_masks.len(), "scaling state masks to fit"); + self.state_masks.resize(new_len, TrieMask::default()); + } + self.state_masks[len] |= TrieMask::from_nibble(extra_digit); + trace!( + target: "trie::hash_builder", + ?extra_digit, + groups = ?self.state_masks, + ); + + // Adjust the tree masks for exporting to the DB + if self.tree_masks.len() < current.len() { + self.resize_masks(current.len()); + } + + let mut len_from = len; + if !succeeding.is_empty() || preceding_exists { + len_from += 1; + } + trace!(target: "trie::hash_builder", "skipping {len_from} nibbles"); + + // The key without the common prefix + let short_node_key = current.slice(len_from..); + trace!(target: "trie::hash_builder", ?short_node_key); + + // Concatenate the 2 nodes together + if !build_extensions { + match self.value.as_ref() { + HashBuilderValueRef::Bytes(leaf_value) => { + // TODO(scroll): Replace with terminator masks + // Set the terminator mask for the leaf node + self.state_masks[len] |= TrieMask::new(0b100 << extra_digit); + let leaf_node = LeafNodeRef::new(&current, leaf_value); + let leaf_hash = leaf_node.hash_leaf(); + trace!( + target: "trie::hash_builder", + ?leaf_node, + ?leaf_hash, + "pushing leaf node", + ); + self.stack.push(leaf_hash); + // self.retain_proof_from_stack(&current.slice(..len_from)); + } + HashBuilderValueRef::Hash(hash) => { + trace!(target: "trie::hash_builder", ?hash, "pushing branch node hash"); + self.stack.push(*hash); + + if self.stored_in_database { + self.tree_masks[current.len() - 1] |= TrieMask::from_nibble( + current + .last() + .expect("must have at least a single bit in the current key"), + ); + } + self.hash_masks[current.len() - 1] |= TrieMask::from_nibble( + current + .last() + .expect("must have at least a single bit in the current key"), + ); + + build_extensions = true; + } + } + } + + if build_extensions && !short_node_key.is_empty() { + self.update_masks(&current, len_from); + let stack_last = self.stack.pop().expect("there should be at least one stack item"); + let sub_tree = SubTreeRef::new(&short_node_key, &stack_last); + let sub_tree_root = sub_tree.root(); + + trace!( + target: "trie::hash_builder", + ?short_node_key, + ?sub_tree_root, + "pushing subtree root", + ); + self.stack.push(sub_tree_root); + // self.retain_proof_from_stack(&current.slice(..len_from)); + self.resize_masks(len_from); + } + + if preceding_len <= common_prefix_len && !succeeding.is_empty() { + trace!(target: "trie::hash_builder", "no common prefix to create branch nodes from, returning"); + return; + } + + // Insert branch nodes in the stack + if !succeeding.is_empty() || preceding_exists { + // Pushes the corresponding branch node to the stack + let children = self.push_branch_node(&current, len); + // Need to store the branch node in an efficient format outside of the hash builder + self.store_branch_node(&current, len, children); + } + + self.state_masks.resize(len, TrieMask::default()); + self.resize_masks(len); + + if preceding_len == 0 { + trace!(target: "trie::hash_builder", "0 or 1 state masks means we have no more elements to process"); + return; + } + + current.truncate(preceding_len); + trace!(target: "trie::hash_builder", ?current, "truncated nibbles to {} bytes", preceding_len); + + trace!(target: "trie::hash_builder", groups = ?self.state_masks, "popping empty state masks"); + while self.state_masks.last() == Some(&TrieMask::default()) { + self.state_masks.pop(); + } + + build_extensions = true; + + i += 1; + } + } + + /// Given the size of the longest common prefix, it proceeds to create a branch node + /// from the state mask and existing stack state, and store its RLP to the top of the stack, + /// after popping all the relevant elements from the stack. + /// + /// Returns the hashes of the children of the branch node, only if `updated_branch_nodes` is + /// enabled. + fn push_branch_node(&mut self, _current: &Nibbles, len: usize) -> Vec<B256> { + let state_mask = self.state_masks[len]; + let hash_mask = self.hash_masks[len]; + let branch_node = BranchNodeRef::new(&self.stack, state_mask); + // Avoid calculating this value if it's not needed. + let children = if self.updated_branch_nodes.is_some() { + branch_node.child_hashes(hash_mask).collect() + } else { + vec![] + }; + + let branch_hash = branch_node.hash(); + + // TODO: enable proof retention + // self.retain_proof_from_stack(&current.slice(..len)); + + // Clears the stack from the branch node elements + let first_child_idx = branch_node.first_child_index(); + trace!( + target: "trie::hash_builder", + new_len = first_child_idx, + old_len = self.stack.len(), + "resizing stack to prepare branch node" + ); + self.stack.resize_with(first_child_idx, Default::default); + + trace!(target: "trie::hash_builder", ?branch_hash, "pushing branch node with {state_mask:?} mask + from stack"); + + self.stack.push(branch_hash); + children + } + + /// Given the current nibble prefix and the highest common prefix length, proceeds + /// to update the masks for the next level and store the branch node and the + /// masks in the database. We will use that when consuming the intermediate nodes + /// from the database to efficiently build the trie. + fn store_branch_node(&mut self, current: &Nibbles, len: usize, children: Vec<B256>) { + trace!(target: "trie::hash_builder", ?current, ?len, ?children, "store branch node"); + if len > 0 { + let parent_index = len - 1; + self.hash_masks[parent_index] |= TrieMask::from_nibble(current[parent_index]); + } + + let store_in_db_trie = !self.tree_masks[len].is_empty() || !self.hash_masks[len].is_empty(); + if store_in_db_trie { + if len > 0 { + let parent_index = len - 1; + self.tree_masks[parent_index] |= TrieMask::from_nibble(current[parent_index]); + } + + if self.updated_branch_nodes.is_some() { + let common_prefix = current.slice(..len); + let node = BranchNodeCompact::new( + self.state_masks[len] & CHILD_INDEX_MASK, + self.tree_masks[len], + self.hash_masks[len], + children, + (len == 0).then(|| self.current_root()), + ); + trace!(target: "trie::hash_builder", ?node, "storing updated intermediate node"); + self.updated_branch_nodes + .as_mut() + .expect("updates_branch_nodes is some") + .insert(common_prefix, node); + } + } + } + + // TODO(scroll): Enable proof retention + // fn retain_proof_from_stack(&mut self, prefix: &Nibbles) { + // if let Some(proof_retainer) = self.proof_retainer.as_mut() { + // proof_retainer.retain( + // prefix, + // self.stack.last().expect("there should be at least one stack item").as_ref(), + // ); + // } + // } + + fn update_masks(&mut self, current: &Nibbles, len_from: usize) { + if len_from > 0 { + let flag = TrieMask::from_nibble(current[len_from - 1]); + + self.hash_masks[len_from - 1] &= !flag; + + if !self.tree_masks[current.len() - 1].is_empty() { + self.tree_masks[len_from - 1] |= flag; + } + } + } + + fn resize_masks(&mut self, new_len: usize) { + trace!( + target: "trie::hash_builder", + new_len, + old_tree_mask_len = self.tree_masks.len(), + old_hash_mask_len = self.hash_masks.len(), + "resizing tree/hash masks" + ); + self.tree_masks.resize(new_len, TrieMask::default()); + self.hash_masks.resize(new_len, TrieMask::default()); + } +} + +// TODO(scroll): Introduce generic for the HashBuilder. +impl From<reth_trie::HashBuilder> for HashBuilder { + fn from(hash_builder: reth_trie::HashBuilder) -> Self { + Self { + key: hash_builder.key, + value: hash_builder.value, + stack: hash_builder + .stack + .into_iter() + .map(|x| x.as_slice().try_into().expect("RlpNode contains 32 byte hashes")) + .collect(), + state_masks: hash_builder.groups, + tree_masks: hash_builder.tree_masks, + hash_masks: hash_builder.hash_masks, + stored_in_database: hash_builder.stored_in_database, + updated_branch_nodes: hash_builder.updated_branch_nodes, + proof_retainer: hash_builder.proof_retainer, + } + } +} + +impl From<HashBuilder> for reth_trie::HashBuilder { + fn from(value: HashBuilder) -> Self { + Self { + key: value.key, + value: value.value, + stack: value + .stack + .into_iter() + .map(|x| { + reth_trie::RlpNode::from_raw(&x.0).expect("32 byte hash can be cast to RlpNode") + }) + .collect(), + groups: value.state_masks, + tree_masks: value.tree_masks, + hash_masks: value.hash_masks, + stored_in_database: value.stored_in_database, + updated_branch_nodes: value.updated_branch_nodes, + proof_retainer: value.proof_retainer, + rlp_buf: Default::default(), + } + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::key::AsBytes; + use alloc::collections::BTreeMap; + use poseidon_bn254::{hash_with_domain, Fr, PrimeField}; + + #[test] + fn test_basic_trie() { + // Test a basic trie consisting of three key value pairs: + // (0, 0, 0, 0, ... , 0) + // (0, 0, 0, 1, ... , 0) + // (0, 0, 1, 0, ... , 0) + // (1, 1, 1, 0, ... , 0) + // (1, 1, 1, 1, ... , 0) + // The branch associated with key 0xF will be collapsed into a single leaf. + + let leaf_1_key = Nibbles::from_nibbles_unchecked([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ]); + let leaf_2_key = Nibbles::from_nibbles_unchecked([ + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ]); + let leaf_3_key = Nibbles::from_nibbles_unchecked([ + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ]); + let leaf_4_key = Nibbles::from_nibbles_unchecked([ + 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ]); + let leaf_5_key = Nibbles::from_nibbles_unchecked([ + 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ]); + let leaf_keys = [ + leaf_1_key.clone(), + leaf_2_key.clone(), + leaf_3_key.clone(), + leaf_4_key.clone(), + leaf_5_key.clone(), + ]; + + let leaf_values = leaf_keys + .into_iter() + .enumerate() + .map(|(i, key)| { + let mut leaf_value = [0u8; 32]; + leaf_value[0] = i as u8 + 1; + (key, leaf_value) + }) + .collect::<BTreeMap<_, _>>(); + + let leaf_hashes: BTreeMap<_, _> = leaf_values + .iter() + .map(|(key, value)| { + let key_fr = + Fr::from_repr_vartime(key.as_bytes()).expect("key is valid field element"); + let value = Fr::from_repr_vartime(*value).expect("value is a valid field element"); + let hash = hash_with_domain(&[key_fr, value], crate::LEAF_NODE_DOMAIN); + (key.clone(), hash) + }) + .collect(); + + let mut hb = HashBuilder::default().with_updates(true); + + for (key, val) in &leaf_values { + hb.add_leaf(key.clone(), val); + } + + let root = hb.root(); + + // node_000 -> hash(leaf_1, leaf_2) LTRT + // node_00 -> hash(node_000, leaf_3) LBRT + // node_0 -> hash(node_00, EMPTY) LBRT + // node_111 -> hash(leaf_4, leaf_5) LTRT + // node_11 -> hash(EMPTY, node_111) LTRB + // node_1 -> hash(EMPTY, node_11) LTRB + // root -> hash(node_0, node_1) LBRB + + let expected: B256 = { + let node_000 = hash_with_domain( + &[*leaf_hashes.get(&leaf_1_key).unwrap(), *leaf_hashes.get(&leaf_2_key).unwrap()], + crate::BRANCH_NODE_LTRT_DOMAIN, + ); + let node_00 = hash_with_domain( + &[node_000, *leaf_hashes.get(&leaf_3_key).unwrap()], + crate::BRANCH_NODE_LBRT_DOMAIN, + ); + let node_0 = hash_with_domain(&[node_00, Fr::zero()], crate::BRANCH_NODE_LBRT_DOMAIN); + let node_111 = hash_with_domain( + &[*leaf_hashes.get(&leaf_4_key).unwrap(), *leaf_hashes.get(&leaf_5_key).unwrap()], + crate::BRANCH_NODE_LTRT_DOMAIN, + ); + let node_11 = hash_with_domain(&[Fr::zero(), node_111], crate::BRANCH_NODE_LTRB_DOMAIN); + let node_1 = hash_with_domain(&[Fr::zero(), node_11], crate::BRANCH_NODE_LTRB_DOMAIN); + + let mut root = + hash_with_domain(&[node_0, node_1], crate::BRANCH_NODE_LBRB_DOMAIN).to_repr(); + root.reverse(); + root.into() + }; + + assert_eq!(expected, root); + } +}
diff --git reth/crates/scroll/trie/src/key.rs scroll-reth/crates/scroll/trie/src/key.rs new file mode 100644 index 0000000000000000000000000000000000000000..0a58bd9f9ce06ceb31532ec32ace2c10471b079f --- /dev/null +++ scroll-reth/crates/scroll/trie/src/key.rs @@ -0,0 +1,21 @@ +use reth_trie::Nibbles; + +/// A type that can return its bytes representation encoded as a little-endian on 32 bytes. +pub(crate) trait AsBytes { + /// Returns the type as its canonical little-endian representation on 32 bytes. + fn as_bytes(&self) -> [u8; 32]; +} + +impl AsBytes for Nibbles { + fn as_bytes(&self) -> [u8; 32] { + // This is strange we are now representing the leaf key using big endian?? + let mut result = [0u8; 32]; + for (byte_index, bytes) in self.as_slice().chunks(8).enumerate() { + for (bit_index, byte) in bytes.iter().enumerate() { + result[byte_index] |= byte << bit_index; + } + } + + result + } +}
diff --git reth/crates/scroll/trie/src/leaf.rs scroll-reth/crates/scroll/trie/src/leaf.rs new file mode 100644 index 0000000000000000000000000000000000000000..79216d8f562dfdf8e2bbe231e0454858e0459ae2 --- /dev/null +++ scroll-reth/crates/scroll/trie/src/leaf.rs @@ -0,0 +1,23 @@ +use super::LEAF_NODE_DOMAIN; +use crate::key::AsBytes; +use alloy_primitives::B256; +use alloy_trie::nodes::LeafNodeRef; +use poseidon_bn254::{hash_with_domain, Fr, PrimeField}; + +/// A trait used to hash the leaf node. +pub(crate) trait HashLeaf { + /// Hash the leaf node. + fn hash_leaf(&self) -> B256; +} + +impl HashLeaf for LeafNodeRef<'_> { + fn hash_leaf(&self) -> B256 { + let leaf_key = + Fr::from_repr_vartime(self.key.as_bytes()).expect("leaf key is a valid field element"); + let leaf_value = Fr::from_repr_vartime( + <[u8; 32]>::try_from(self.value).expect("leaf value is 32 bytes"), + ) + .expect("leaf value is a valid field element"); + hash_with_domain(&[leaf_key, leaf_value], LEAF_NODE_DOMAIN).to_repr().into() + } +}
diff --git reth/crates/scroll/trie/src/lib.rs scroll-reth/crates/scroll/trie/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..5b1a5fd5aa93d6ad8bc735ff4a6d4bb1fe17679e --- /dev/null +++ scroll-reth/crates/scroll/trie/src/lib.rs @@ -0,0 +1,37 @@ +//! Fast binary Merkle-Patricia Trie (zktrie) state root calculator and proof generator for +//! prefix-sorted bits. + +#![cfg_attr(not(doctest), doc = include_str!("../assets/zktrie.md"))] + +#[macro_use] +#[allow(unused_imports)] +extern crate alloc; + +mod branch; + +mod constants; +pub use constants::EMPTY_ROOT_HASH; + +mod key; +mod leaf; +mod sub_tree; + +mod hash_builder; +pub use hash_builder::HashBuilder; + +use poseidon_bn254::Fr; + +/// The hashing domain for leaf nodes. +pub const LEAF_NODE_DOMAIN: Fr = Fr::from_raw([4, 0, 0, 0]); + +/// The hashing domain for a branch node with two terminal children. +pub const BRANCH_NODE_LTRT_DOMAIN: Fr = Fr::from_raw([6, 0, 0, 0]); + +/// The hashing domain for a branch node with a left terminal child and a right branch child. +pub const BRANCH_NODE_LTRB_DOMAIN: Fr = Fr::from_raw([7, 0, 0, 0]); + +/// The hashing domain for a branch node with a left branch child and a right terminal child. +pub const BRANCH_NODE_LBRT_DOMAIN: Fr = Fr::from_raw([8, 0, 0, 0]); + +/// The hashing domain for a branch node with two branch children. +pub const BRANCH_NODE_LBRB_DOMAIN: Fr = Fr::from_raw([9, 0, 0, 0]);
diff --git reth/crates/scroll/trie/src/sub_tree.rs scroll-reth/crates/scroll/trie/src/sub_tree.rs new file mode 100644 index 0000000000000000000000000000000000000000..31edad05c4166dcab958f28450206e81b61ab13f --- /dev/null +++ scroll-reth/crates/scroll/trie/src/sub_tree.rs @@ -0,0 +1,44 @@ +use super::{BRANCH_NODE_LBRT_DOMAIN, BRANCH_NODE_LTRB_DOMAIN}; +use alloy_primitives::{hex, B256}; +use alloy_trie::Nibbles; +use core::fmt; +use poseidon_bn254::{hash_with_domain, Fr, PrimeField}; + +/// [`SubTreeRef`] is a structure that allows for calculation of the root of a sparse binary Merkle +/// tree consisting of a single leaf node. +pub(crate) struct SubTreeRef<'a> { + /// The key to the child node. + pub key: &'a Nibbles, + /// A pointer to the child node. + pub child: &'a B256, +} + +impl<'a> SubTreeRef<'a> { + /// Creates a new subtree with the given key and a pointer to the child. + #[inline] + pub(crate) const fn new(key: &'a Nibbles, child: &'a B256) -> Self { + Self { key, child } + } + + pub(crate) fn root(&self) -> B256 { + let mut tree_root = + Fr::from_repr_vartime(self.child.0).expect("child is a valid field element"); + for bit in self.key.as_slice().iter().rev() { + tree_root = if *bit == 0 { + hash_with_domain(&[tree_root, Fr::zero()], BRANCH_NODE_LBRT_DOMAIN) + } else { + hash_with_domain(&[Fr::zero(), tree_root], BRANCH_NODE_LTRB_DOMAIN) + }; + } + tree_root.to_repr().into() + } +} + +impl fmt::Debug for SubTreeRef<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("SubTreeRef") + .field("key", &self.key) + .field("node", &hex::encode(self.child)) + .finish() + } +}
diff --git reth/crates/scroll/txpool/Cargo.toml scroll-reth/crates/scroll/txpool/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..844dffe7a5fe7fd3c78acb8a7f4948fee5a34036 --- /dev/null +++ scroll-reth/crates/scroll/txpool/Cargo.toml @@ -0,0 +1,52 @@ +[package] +name = "reth-scroll-txpool" +version.workspace = true +edition.workspace = true +rust-version.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +exclude.workspace = true + +[lints] +workspace = true + +[dependencies] +# ethereum +alloy-consensus.workspace = true +alloy-eips.workspace = true +alloy-primitives.workspace = true +alloy-rpc-types-eth.workspace = true + +# reth +reth-chain-state.workspace = true +reth-chainspec.workspace = true +reth-primitives-traits.workspace = true +reth-revm.workspace = true +reth-storage-api.workspace = true +reth-transaction-pool.workspace = true + +# revm-scroll +revm-scroll.workspace = true + +# reth-scroll +reth-scroll-evm.workspace = true +reth-scroll-forks.workspace = true +reth-scroll-primitives.workspace = true + +# scroll-alloy +scroll-alloy-consensus.workspace = true + +# metrics +reth-metrics.workspace = true +metrics.workspace = true + +# misc +c-kzg.workspace = true +derive_more.workspace = true +futures-util.workspace = true +parking_lot.workspace = true + +[dev-dependencies] +reth-scroll-chainspec.workspace = true +reth-provider = { workspace = true, features = ["test-utils"] }
diff --git reth/crates/scroll/txpool/src/lib.rs scroll-reth/crates/scroll/txpool/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..a1885e05acb3cd8a06c49e0daa63bdcba9ff2181 --- /dev/null +++ scroll-reth/crates/scroll/txpool/src/lib.rs @@ -0,0 +1,16 @@ +//! Transaction pool for Scroll node. + +mod transaction; +pub use transaction::ScrollPooledTransaction; + +mod validator; +pub use validator::{ScrollL1BlockInfo, ScrollTransactionValidator}; + +use reth_transaction_pool::{CoinbaseTipOrdering, Pool, TransactionValidationTaskExecutor}; + +/// Type alias for default scroll transaction pool +pub type ScrollTransactionPool<Client, S, T = ScrollPooledTransaction> = Pool< + TransactionValidationTaskExecutor<ScrollTransactionValidator<Client, T>>, + CoinbaseTipOrdering<T>, + S, +>;
diff --git reth/crates/scroll/txpool/src/transaction.rs scroll-reth/crates/scroll/txpool/src/transaction.rs new file mode 100644 index 0000000000000000000000000000000000000000..bee309619490223bcd4bfc79503d8240bbd91685 --- /dev/null +++ scroll-reth/crates/scroll/txpool/src/transaction.rs @@ -0,0 +1,252 @@ +use alloy_consensus::{transaction::Recovered, BlobTransactionValidationError, Typed2718}; +use alloy_eips::{ + eip2930::AccessList, eip7594::BlobTransactionSidecarVariant, eip7702::SignedAuthorization, +}; +use alloy_primitives::{Address, Bytes, TxHash, TxKind, B256, U256}; +use c_kzg::KzgSettings; +use core::fmt::Debug; +use reth_primitives_traits::{InMemorySize, SignedTransaction}; +use reth_scroll_primitives::ScrollTransactionSigned; +use reth_transaction_pool::{ + EthBlobTransactionSidecar, EthPoolTransaction, EthPooledTransaction, PoolTransaction, +}; +use std::sync::{Arc, OnceLock}; + +/// Pool transaction for Scroll. +/// +/// This type wraps the actual transaction and caches values that are frequently used by the pool. +/// For payload building this lazily tracks values that are required during payload building: +/// - Estimated compressed size of this transaction +#[derive(Debug, Clone, derive_more::Deref)] +pub struct ScrollPooledTransaction< + Cons = ScrollTransactionSigned, + Pooled = scroll_alloy_consensus::ScrollPooledTransaction, +> { + #[deref] + inner: EthPooledTransaction<Cons>, + /// The pooled transaction type. + _pd: core::marker::PhantomData<Pooled>, + + /// Cached EIP-2718 encoded bytes of the transaction, lazily computed. + encoded_2718: OnceLock<Bytes>, +} + +impl<Cons: SignedTransaction, Pooled> ScrollPooledTransaction<Cons, Pooled> { + /// Create new instance of [Self]. + pub fn new(transaction: Recovered<Cons>, encoded_length: usize) -> Self { + Self { + inner: EthPooledTransaction::new(transaction, encoded_length), + _pd: core::marker::PhantomData, + encoded_2718: Default::default(), + } + } + + /// Returns lazily computed EIP-2718 encoded bytes of the transaction. + pub fn encoded_2718(&self) -> &Bytes { + self.encoded_2718.get_or_init(|| self.inner.transaction().encoded_2718().into()) + } +} + +impl<Cons, Pooled> PoolTransaction for ScrollPooledTransaction<Cons, Pooled> +where + Cons: SignedTransaction + From<Pooled>, + Pooled: SignedTransaction + TryFrom<Cons, Error: core::error::Error>, +{ + type TryFromConsensusError = <Pooled as TryFrom<Cons>>::Error; + type Consensus = Cons; + type Pooled = Pooled; + + fn clone_into_consensus(&self) -> Recovered<Self::Consensus> { + self.inner.transaction().clone() + } + + fn into_consensus(self) -> Recovered<Self::Consensus> { + self.inner.transaction + } + + fn from_pooled(tx: Recovered<Self::Pooled>) -> Self { + let encoded_len = tx.encode_2718_len(); + Self::new(tx.convert(), encoded_len) + } + + fn hash(&self) -> &TxHash { + self.inner.transaction.tx_hash() + } + + fn sender(&self) -> Address { + self.inner.transaction.signer() + } + + fn sender_ref(&self) -> &Address { + self.inner.transaction.signer_ref() + } + + fn cost(&self) -> &U256 { + &self.inner.cost + } + + fn encoded_length(&self) -> usize { + self.inner.encoded_length + } +} + +impl<Cons: Typed2718, Pooled> Typed2718 for ScrollPooledTransaction<Cons, Pooled> { + fn ty(&self) -> u8 { + self.inner.ty() + } +} + +impl<Cons: InMemorySize, Pooled> InMemorySize for ScrollPooledTransaction<Cons, Pooled> { + fn size(&self) -> usize { + self.inner.size() + } +} + +impl<Cons, Pooled> alloy_consensus::Transaction for ScrollPooledTransaction<Cons, Pooled> +where + Cons: alloy_consensus::Transaction, + Pooled: Debug + Send + Sync + 'static, +{ + fn chain_id(&self) -> Option<u64> { + self.inner.chain_id() + } + + fn nonce(&self) -> u64 { + self.inner.nonce() + } + + fn gas_limit(&self) -> u64 { + self.inner.gas_limit() + } + + fn gas_price(&self) -> Option<u128> { + self.inner.gas_price() + } + + fn max_fee_per_gas(&self) -> u128 { + self.inner.max_fee_per_gas() + } + + fn max_priority_fee_per_gas(&self) -> Option<u128> { + self.inner.max_priority_fee_per_gas() + } + + fn max_fee_per_blob_gas(&self) -> Option<u128> { + self.inner.max_fee_per_blob_gas() + } + + fn priority_fee_or_price(&self) -> u128 { + self.inner.priority_fee_or_price() + } + + fn effective_gas_price(&self, base_fee: Option<u64>) -> u128 { + self.inner.effective_gas_price(base_fee) + } + + fn is_dynamic_fee(&self) -> bool { + self.inner.is_dynamic_fee() + } + + fn kind(&self) -> TxKind { + self.inner.kind() + } + + fn is_create(&self) -> bool { + self.inner.is_create() + } + + fn value(&self) -> U256 { + self.inner.value() + } + + fn input(&self) -> &Bytes { + self.inner.input() + } + + fn access_list(&self) -> Option<&AccessList> { + self.inner.access_list() + } + + fn blob_versioned_hashes(&self) -> Option<&[B256]> { + self.inner.blob_versioned_hashes() + } + + fn authorization_list(&self) -> Option<&[SignedAuthorization]> { + self.inner.authorization_list() + } +} + +impl<Cons, Pooled> EthPoolTransaction for ScrollPooledTransaction<Cons, Pooled> +where + Cons: SignedTransaction + From<Pooled>, + Pooled: SignedTransaction + TryFrom<Cons>, + <Pooled as TryFrom<Cons>>::Error: core::error::Error, +{ + fn take_blob(&mut self) -> EthBlobTransactionSidecar { + EthBlobTransactionSidecar::None + } + + fn try_into_pooled_eip4844( + self, + _sidecar: Arc<BlobTransactionSidecarVariant>, + ) -> Option<Recovered<Self::Pooled>> { + None + } + + fn try_from_eip4844( + _tx: Recovered<Self::Consensus>, + _sidecar: BlobTransactionSidecarVariant, + ) -> Option<Self> { + None + } + + fn validate_blob( + &self, + _sidecar: &BlobTransactionSidecarVariant, + _settings: &KzgSettings, + ) -> Result<(), BlobTransactionValidationError> { + Err(BlobTransactionValidationError::NotBlobTransaction(self.ty())) + } +} + +#[cfg(test)] +mod tests { + use crate::{ScrollPooledTransaction, ScrollTransactionValidator}; + use alloy_consensus::{transaction::Recovered, Signed}; + use alloy_eips::eip2718::Encodable2718; + use alloy_primitives::Signature; + use reth_provider::test_utils::MockEthProvider; + use reth_scroll_chainspec::SCROLL_MAINNET; + use reth_scroll_primitives::ScrollTransactionSigned; + use reth_transaction_pool::{ + blobstore::InMemoryBlobStore, validate::EthTransactionValidatorBuilder, TransactionOrigin, + TransactionValidationOutcome, + }; + use scroll_alloy_consensus::{ScrollTypedTransaction, TxL1Message}; + #[test] + fn validate_scroll_transaction() { + let client = MockEthProvider::default().with_chain_spec(SCROLL_MAINNET.clone()); + let validator = EthTransactionValidatorBuilder::new(client) + .no_shanghai() + .no_cancun() + .build(InMemoryBlobStore::default()); + let validator = ScrollTransactionValidator::new(validator); + + let origin = TransactionOrigin::External; + let signer = Default::default(); + let deposit_tx = ScrollTypedTransaction::L1Message(TxL1Message::default()); + let signature = Signature::test_signature(); + let signed_tx: ScrollTransactionSigned = Signed::new_unhashed(deposit_tx, signature).into(); + let signed_recovered = Recovered::new_unchecked(signed_tx, signer); + let len = signed_recovered.encode_2718_len(); + let pooled_tx: ScrollPooledTransaction = + ScrollPooledTransaction::new(signed_recovered, len); + let outcome = validator.validate_one(origin, pooled_tx); + + let err = match outcome { + TransactionValidationOutcome::Invalid(_, err) => err, + _ => panic!("Expected invalid transaction"), + }; + assert_eq!(err.to_string(), "transaction type not supported"); + } +}
diff --git reth/crates/scroll/txpool/src/validator.rs scroll-reth/crates/scroll/txpool/src/validator.rs new file mode 100644 index 0000000000000000000000000000000000000000..6d5dc4474581b74cc79c294c84c0dfa892e8207f --- /dev/null +++ scroll-reth/crates/scroll/txpool/src/validator.rs @@ -0,0 +1,252 @@ +use alloy_consensus::BlockHeader; +use alloy_eips::Encodable2718; +use parking_lot::RwLock; +use reth_chainspec::ChainSpecProvider; +use reth_primitives_traits::{ + transaction::error::InvalidTransactionError, Block, GotExpected, SealedBlock, +}; +use reth_revm::database::StateProviderDatabase; +use reth_scroll_evm::{ + compute_compression_ratio, spec_id_at_timestamp_and_number, RethL1BlockInfo, +}; +use reth_scroll_forks::ScrollHardforks; +use reth_storage_api::{BlockReaderIdExt, StateProviderFactory}; +use reth_transaction_pool::{ + EthPoolTransaction, EthTransactionValidator, TransactionOrigin, TransactionValidationOutcome, + TransactionValidator, +}; +use revm_scroll::l1block::L1BlockInfo; +use std::sync::{ + atomic::{AtomicU64, Ordering}, + Arc, +}; + +/// Tracks additional infos for the current block. +#[derive(Debug, Default)] +pub struct ScrollL1BlockInfo { + /// The current L1 block info. + l1_block_info: RwLock<L1BlockInfo>, + /// Current block timestamp. + timestamp: AtomicU64, + /// Current block number. + number: AtomicU64, +} + +/// Validator for Scroll transactions. +#[derive(Debug, Clone)] +pub struct ScrollTransactionValidator<Client, Tx> { + /// The type that performs the actual validation. + inner: EthTransactionValidator<Client, Tx>, + /// Additional block info required for validation. + block_info: Arc<ScrollL1BlockInfo>, + /// If true, ensure that the transaction's sender has enough balance to cover the L1 gas fee + /// derived from the tracked L1 block info. + require_l1_data_gas_fee: bool, +} + +impl<Client, Tx> ScrollTransactionValidator<Client, Tx> { + /// Returns the configured chain spec + pub fn chain_spec(&self) -> Arc<Client::ChainSpec> + where + Client: ChainSpecProvider, + { + self.inner.chain_spec() + } + + /// Returns the configured client + pub fn client(&self) -> &Client { + self.inner.client() + } + + /// Returns the current block timestamp. + fn block_timestamp(&self) -> u64 { + self.block_info.timestamp.load(Ordering::Relaxed) + } + + /// Returns the current block number. + fn block_number(&self) -> u64 { + self.block_info.number.load(Ordering::Relaxed) + } + + /// Whether to ensure that the transaction's sender has enough balance to also cover the L1 gas + /// fee. + pub fn require_l1_data_gas_fee(self, require_l1_data_gas_fee: bool) -> Self { + Self { require_l1_data_gas_fee, ..self } + } + + /// Returns whether this validator also requires the transaction's sender to have enough balance + /// to cover the L1 gas fee. + pub const fn requires_l1_data_gas_fee(&self) -> bool { + self.require_l1_data_gas_fee + } +} + +impl<Client, Tx> ScrollTransactionValidator<Client, Tx> +where + Client: ChainSpecProvider<ChainSpec: ScrollHardforks> + StateProviderFactory + BlockReaderIdExt, + Tx: EthPoolTransaction, +{ + /// Create a new [`ScrollTransactionValidator`]. + pub fn new(inner: EthTransactionValidator<Client, Tx>) -> Self { + let this = Self::with_block_info(inner, ScrollL1BlockInfo::default()); + if let Ok(Some(block)) = + this.inner.client().block_by_number_or_tag(alloy_eips::BlockNumberOrTag::Latest) + { + this.block_info.timestamp.store(block.header().timestamp(), Ordering::Relaxed); + this.block_info.number.store(block.header().number(), Ordering::Relaxed); + this.update_l1_block_info(block.header()); + } + + this + } + + /// Create a new [`ScrollTransactionValidator`] with the given [`ScrollL1BlockInfo`]. + pub fn with_block_info( + inner: EthTransactionValidator<Client, Tx>, + block_info: ScrollL1BlockInfo, + ) -> Self { + Self { inner, block_info: Arc::new(block_info), require_l1_data_gas_fee: true } + } + + /// Update the L1 block info for the given header and system transaction, if any. + pub fn update_l1_block_info<H>(&self, header: &H) + where + H: BlockHeader, + { + self.block_info.timestamp.store(header.timestamp(), Ordering::Relaxed); + self.block_info.number.store(header.number(), Ordering::Relaxed); + + let provider = + self.client().state_by_block_number_or_tag(header.number().into()).expect("msg"); + let mut db = StateProviderDatabase::new(provider); + let spec_id = + spec_id_at_timestamp_and_number(header.timestamp(), header.number(), self.chain_spec()); + if let Ok(l1_block_info) = L1BlockInfo::try_fetch(&mut db, spec_id) { + *self.block_info.l1_block_info.write() = l1_block_info; + } + } + + /// Validates a single transaction. + /// + /// See also [`TransactionValidator::validate_transaction`] + /// + /// This behaves the same as [`EthTransactionValidator::validate_one`], but in addition, ensures + /// that the account has enough balance to cover the L1 gas cost. + pub fn validate_one( + &self, + origin: TransactionOrigin, + transaction: Tx, + ) -> TransactionValidationOutcome<Tx> { + if transaction.is_eip4844() { + return TransactionValidationOutcome::Invalid( + transaction, + InvalidTransactionError::TxTypeNotSupported.into(), + ) + } + + let outcome = self.inner.validate_one(origin, transaction); + + if !self.requires_l1_data_gas_fee() { + // no need to check L1 gas fee + return outcome + } + + // ensure that the account has enough balance to cover the L1 gas cost + if let TransactionValidationOutcome::Valid { + balance, + state_nonce, + transaction: valid_tx, + propagate, + bytecode_hash, + authorities, + } = outcome + { + let mut l1_block_info = self.block_info.l1_block_info.read().clone(); + + let mut encoded = Vec::with_capacity(valid_tx.transaction().encoded_length()); + let tx = valid_tx.transaction().clone_into_consensus(); + tx.encode_2718(&mut encoded); + let compression_ratio = compute_compression_ratio(&encoded); + + let cost_addition = match l1_block_info.l1_tx_data_fee( + self.chain_spec(), + self.block_timestamp(), + self.block_number(), + &encoded, + Some(compression_ratio), + false, + ) { + Ok(cost) => cost, + Err(err) => { + return TransactionValidationOutcome::Error(*valid_tx.hash(), Box::new(err)) + } + }; + let cost = valid_tx.transaction().cost().saturating_add(cost_addition); + + // Checks for max cost + if cost > balance { + return TransactionValidationOutcome::Invalid( + valid_tx.into_transaction(), + InvalidTransactionError::InsufficientFunds( + GotExpected { got: balance, expected: cost }.into(), + ) + .into(), + ) + } + + return TransactionValidationOutcome::Valid { + balance, + state_nonce, + bytecode_hash, + transaction: valid_tx, + propagate, + authorities, + } + } + + outcome + } + + /// Validates all given transactions. + /// + /// Returns all outcomes for the given transactions in the same order. + /// + /// See also [`Self::validate_one`] + pub fn validate_all( + &self, + transactions: Vec<(TransactionOrigin, Tx)>, + ) -> Vec<TransactionValidationOutcome<Tx>> { + transactions.into_iter().map(|(origin, tx)| self.validate_one(origin, tx)).collect() + } +} + +impl<Client, Tx> TransactionValidator for ScrollTransactionValidator<Client, Tx> +where + Client: ChainSpecProvider<ChainSpec: ScrollHardforks> + StateProviderFactory + BlockReaderIdExt, + Tx: EthPoolTransaction, +{ + type Transaction = Tx; + + async fn validate_transaction( + &self, + origin: TransactionOrigin, + transaction: Self::Transaction, + ) -> TransactionValidationOutcome<Self::Transaction> { + self.validate_one(origin, transaction) + } + + async fn validate_transactions( + &self, + transactions: Vec<(TransactionOrigin, Self::Transaction)>, + ) -> Vec<TransactionValidationOutcome<Self::Transaction>> { + self.validate_all(transactions) + } + + fn on_new_head_block<B>(&self, new_tip_block: &SealedBlock<B>) + where + B: Block, + { + self.inner.on_new_head_block(new_tip_block); + self.update_l1_block_info(new_tip_block.header()); + } +}
diff --git reth/crates/ethereum/cli/src/debug_cmd/build_block.rs scroll-reth/crates/ethereum/cli/src/debug_cmd/build_block.rs index 098de0ce3236162c45df348d11da5a38e99a6b84..22260f7e3377042095e870b7a6b5f304474190ab 100644 --- reth/crates/ethereum/cli/src/debug_cmd/build_block.rs +++ scroll-reth/crates/ethereum/cli/src/debug_cmd/build_block.rs @@ -35,8 +35,6 @@ use reth_transaction_pool::{ blobstore::InMemoryBlobStore, BlobStore, EthPooledTransaction, PoolConfig, TransactionOrigin, TransactionPool, TransactionValidationTaskExecutor, }; -use reth_trie::StateRoot; -use reth_trie_db::DatabaseStateRoot; use std::{path::PathBuf, str::FromStr, sync::Arc}; use tracing::*;   @@ -236,10 +234,8 @@ ExecutionOutcome::from((block_execution_output, block.number)); debug!(target: "reth::cli", ?execution_outcome, "Executed block");   let hashed_post_state = state_provider.hashed_post_state(execution_outcome.state()); - let (state_root, trie_updates) = StateRoot::overlay_root_with_updates( - provider_factory.provider()?.tx_ref(), - hashed_post_state.clone(), - )?; + let (state_root, trie_updates) = + state_provider.state_root_with_updates(hashed_post_state.clone())?;   if state_root != block_with_senders.state_root() { eyre::bail!(
diff --git reth/crates/ethereum/cli/src/debug_cmd/merkle.rs scroll-reth/crates/ethereum/cli/src/debug_cmd/merkle.rs index 63c18f9d2dc95819272c2967a9d656d07b6a6f00..09c435b6f367841ca8e494024f69f35a42dff041 100644 --- reth/crates/ethereum/cli/src/debug_cmd/merkle.rs +++ scroll-reth/crates/ethereum/cli/src/debug_cmd/merkle.rs @@ -130,7 +130,7 @@ // build the full block client let consensus: Arc<dyn Consensus<BlockTy<N>, Error = ConsensusError>> = Arc::new(EthBeaconConsensus::new(provider_factory.chain_spec())); - let block_range_client = FullBlockClient::new(fetch_client, consensus); + let block_range_client = FullBlockClient::new(fetch_client, consensus.clone());   // get best block number let best_block_number = provider_rw.best_block_number()?; @@ -145,7 +145,8 @@ .await;   let mut account_hashing_stage = AccountHashingStage::default(); let mut storage_hashing_stage = StorageHashingStage::default(); - let mut merkle_stage = MerkleStage::default_execution(); + let mut merkle_stage = + MerkleStage::<N::Primitives>::default_execution_with_consensus(consensus);   for block in blocks.into_iter().rev() { let block_number = block.number;
diff --git reth/crates/ethereum/cli/src/interface.rs scroll-reth/crates/ethereum/cli/src/interface.rs index 6c96c6d2993666a790ef4aa226f393aa02f8647e..46fb872023887f1adea796c9915549f68c5a7efb 100644 --- reth/crates/ethereum/cli/src/interface.rs +++ scroll-reth/crates/ethereum/cli/src/interface.rs @@ -287,7 +287,15 @@ use reth_node_core::args::ColorMode;   #[test] fn parse_color_mode() { - let reth = Cli::try_parse_args_from(["reth", "node", "--color", "always"]).unwrap(); + let reth = Cli::try_parse_args_from([ + "reth", + "node", + "--color", + "always", + "--builder.gaslimit", + "10000000", + ]) + .unwrap(); assert_eq!(reth.logs.color, ColorMode::Always); }   @@ -314,7 +322,8 @@ /// Tests that the log directory is parsed correctly when using the node command. It's /// always tied to the specific chain's name. #[test] fn parse_logs_path_node() { - let mut reth = Cli::try_parse_args_from(["reth", "node"]).unwrap(); + let mut reth = + Cli::try_parse_args_from(["reth", "node", "--builder.gaslimit", "10000000"]).unwrap(); if let Some(chain_spec) = reth.command.chain_spec() { reth.logs.log_file_directory = reth.logs.log_file_directory.join(chain_spec.chain.to_string()); @@ -326,7 +335,15 @@ let mut iter = SUPPORTED_CHAINS.iter(); iter.next(); for chain in iter { - let mut reth = Cli::try_parse_args_from(["reth", "node", "--chain", chain]).unwrap(); + let mut reth = Cli::try_parse_args_from([ + "reth", + "node", + "--chain", + chain, + "--builder.gaslimit", + "10000000", + ]) + .unwrap(); let chain = reth.command.chain_spec().map(|c| c.chain.to_string()).unwrap_or(String::new()); reth.logs.log_file_directory = reth.logs.log_file_directory.join(chain.clone());
diff --git reth/crates/ethereum/consensus/src/lib.rs scroll-reth/crates/ethereum/consensus/src/lib.rs index 89ce9f72e0dc7c0ff1d3d6be7a5dd17ef81704a9..70eb799311e7651f97e120a595ed06f13a5eb06d 100644 --- reth/crates/ethereum/consensus/src/lib.rs +++ scroll-reth/crates/ethereum/consensus/src/lib.rs @@ -29,7 +29,7 @@ Block, BlockHeader, NodePrimitives, RecoveredBlock, SealedBlock, SealedHeader, };   mod validation; -pub use validation::validate_block_post_execution; +pub use validation::{validate_block_post_execution, verify_receipts};   /// Ethereum beacon consensus ///
diff --git reth/crates/ethereum/consensus/src/validation.rs scroll-reth/crates/ethereum/consensus/src/validation.rs index 5b243f92680675dd947f9751ed6777cc6ce341cb..186403d865bddc5d238066bc18c04ea30d057bc4 100644 --- reth/crates/ethereum/consensus/src/validation.rs +++ scroll-reth/crates/ethereum/consensus/src/validation.rs @@ -64,7 +64,7 @@ }   /// Calculate the receipts root, and compare it against the expected receipts root and logs /// bloom. -fn verify_receipts<R: Receipt>( +pub fn verify_receipts<R: Receipt>( expected_receipts_root: B256, expected_logs_bloom: Bloom, receipts: &[R],
diff --git reth/crates/ethereum/hardforks/src/display.rs scroll-reth/crates/ethereum/hardforks/src/display.rs index e40a117d26a3bb990c9625c3adaf56a2f4c344c0..7eda386a3cc26e3efa55a60d5fc7f2aaadc31224 100644 --- reth/crates/ethereum/hardforks/src/display.rs +++ scroll-reth/crates/ethereum/hardforks/src/display.rs @@ -119,7 +119,7 @@ format( "Pre-merge hard forks (block based)", &self.pre_merge, - self.with_merge.is_empty(), + self.with_merge.is_empty() && self.post_merge.is_empty(), f, )?;
diff --git reth/crates/ethereum/node/tests/e2e/dev.rs scroll-reth/crates/ethereum/node/tests/e2e/dev.rs index d4a24191dbd4d77ad70fbeb8324bb4c43e23e85a..e54e84f6ac68831328377b20d314834148152cc8 100644 --- reth/crates/ethereum/node/tests/e2e/dev.rs +++ scroll-reth/crates/ethereum/node/tests/e2e/dev.rs @@ -3,9 +3,10 @@ use alloy_genesis::Genesis; use alloy_primitives::{b256, hex}; use futures::StreamExt; use reth_chainspec::ChainSpec; -use reth_node_api::{BlockBody, FullNodeComponents, FullNodePrimitives, NodeTypes}; +use reth_node_api::{BlockBody, FullNodeComponents, FullNodePrimitives, NodeAddOns, NodeTypes}; use reth_node_builder::{ - rpc::RethRpcAddOns, EngineNodeLauncher, FullNode, NodeBuilder, NodeConfig, NodeHandle, + rpc::{RethRpcAddOns, RpcHandleProvider}, + EngineNodeLauncher, FullNode, NodeBuilder, NodeConfig, NodeHandle, }; use reth_node_core::args::DevArgs; use reth_node_ethereum::{node::EthereumAddOns, EthereumNode}; @@ -48,6 +49,7 @@ where N: FullNodeComponents<Provider: CanonStateSubscriptions>, AddOns: RethRpcAddOns<N, EthApi: EthTransactions>, N::Types: NodeTypes<Primitives: FullNodePrimitives>, + <AddOns as NodeAddOns<N>>::Handle: RpcHandleProvider<N, <AddOns as RethRpcAddOns<N>>::EthApi>, { let mut notifications = node.provider.canonical_state_stream();   @@ -56,7 +58,7 @@ let raw_tx = hex!( "02f876820a28808477359400847735940082520894ab0840c0e43688012c1adb0f5e3fc665188f83d28a029d394a5d630544000080c080a0a044076b7e67b5deecc63f61a8d7913fab86ca365b344b5759d1fe3563b4c39ea019eab979dd000da04dfc72bb0377c092d30fd9e1cab5ae487de49586cc8b0090" );   - let eth_api = node.rpc_registry.eth_api(); + let eth_api = node.rpc_handle().rpc_registry.eth_api();   let hash = eth_api.send_raw_transaction(raw_tx.into()).await.unwrap();
diff --git reth/crates/optimism/chainspec/src/lib.rs scroll-reth/crates/optimism/chainspec/src/lib.rs index e0d94bcd367639862727051467cbca0ae8ca038a..061cede2632d0c1a93fbde56071541a6cee035d2 100644 --- reth/crates/optimism/chainspec/src/lib.rs +++ scroll-reth/crates/optimism/chainspec/src/lib.rs @@ -61,7 +61,8 @@ pub use base_sepolia::BASE_SEPOLIA; use derive_more::{Constructor, Deref, From, Into}; use reth_chainspec::{ BaseFeeParams, BaseFeeParamsKind, ChainSpec, ChainSpecBuilder, DepositContract, - DisplayHardforks, EthChainSpec, EthereumHardforks, ForkFilter, ForkId, Hardforks, Head, + DisplayHardforks, EthChainSpec, EthereumCapabilities, EthereumHardforks, ForkFilter, ForkId, + Hardforks, Head, }; use reth_ethereum_forks::{ChainHardforks, EthereumHardfork, ForkCondition}; use reth_network_peers::NodeRecord; @@ -285,6 +286,8 @@ fn final_paris_total_difficulty(&self) -> Option<U256> { self.inner.final_paris_total_difficulty() } } + +impl EthereumCapabilities for OpChainSpec {}   impl Hardforks for OpChainSpec { fn fork<H: Hardfork>(&self, fork: H) -> ForkCondition {
diff --git reth/crates/optimism/cli/src/lib.rs scroll-reth/crates/optimism/cli/src/lib.rs index 68e58cef81edfd0bd73dead1d86af3baea755d1a..963e409b1dc09dee963598e4617f74ddc84e919c 100644 --- reth/crates/optimism/cli/src/lib.rs +++ scroll-reth/crates/optimism/cli/src/lib.rs @@ -136,7 +136,12 @@ use reth_optimism_node::args::RollupArgs;   #[test] fn parse_dev() { - let cmd = NodeCommand::<OpChainSpecParser, NoArgs>::parse_from(["op-reth", "--dev"]); + let cmd = NodeCommand::<OpChainSpecParser, NoArgs>::parse_from([ + "op-reth", + "--dev", + "--builder.gaslimit", + "30000000", + ]); let chain = OP_DEV.clone(); assert_eq!(cmd.chain.chain, chain.chain); assert_eq!(cmd.chain.genesis_hash(), chain.genesis_hash()); @@ -187,6 +192,8 @@ "--metrics", "9003", "--log.file.max-size", "100", + "--builder.gaslimit", + "10000000", ]);   match cmd.command {
diff --git reth/crates/optimism/rpc/src/sequencer.rs scroll-reth/crates/optimism/rpc/src/sequencer.rs index c3b543638bb631afbe389f67876f487031ecdc01..2e66a30275f83abfe70310fc5307e5f06074b4d7 100644 --- reth/crates/optimism/rpc/src/sequencer.rs +++ scroll-reth/crates/optimism/rpc/src/sequencer.rs @@ -121,6 +121,7 @@ Ok(Self { inner: Arc::new(inner) }) }   /// Returns the network of the client + #[allow(clippy::missing_const_for_fn)] pub fn endpoint(&self) -> &str { &self.inner.sequencer_endpoint }
diff --git reth/crates/primitives-traits/Cargo.toml scroll-reth/crates/primitives-traits/Cargo.toml index f4dcb4dcf3b1e0f8f420704a17d3751ef7be0630..e03c9c04827ca3958c510a1ffe2b59b3ce637595 100644 --- reth/crates/primitives-traits/Cargo.toml +++ scroll-reth/crates/primitives-traits/Cargo.toml @@ -22,9 +22,14 @@ alloy-genesis.workspace = true alloy-primitives = { workspace = true, features = ["k256"] } alloy-rlp.workspace = true alloy-trie.workspace = true -revm-primitives.workspace = true + +# revm +revm-primitives = { workspace = true, default-features = false } revm-bytecode.workspace = true revm-state.workspace = true + +# scroll +scroll-alloy-consensus = { workspace = true, optional = true, features = ["k256"] }   # op op-alloy-consensus = { workspace = true, optional = true, features = ["k256"] } @@ -93,6 +98,7 @@ "serde_json/std", "reth-chainspec/std", "revm-bytecode/std", "revm-state/std", + "scroll-alloy-consensus?/std", ] secp256k1 = ["alloy-consensus/secp256k1"] test-utils = [ @@ -115,6 +121,7 @@ "secp256k1?/rand", "op-alloy-consensus?/arbitrary", "alloy-trie/arbitrary", "reth-chainspec/arbitrary", + "scroll-alloy-consensus?/arbitrary", ] serde-bincode-compat = [ "serde", @@ -123,6 +130,7 @@ "alloy-consensus/serde-bincode-compat", "alloy-eips/serde-bincode-compat", "op-alloy-consensus?/serde", "op-alloy-consensus?/serde-bincode-compat", + "scroll-alloy-consensus?/serde-bincode-compat", ] serde = [ "dep:serde", @@ -140,16 +148,19 @@ "alloy-trie/serde", "revm-bytecode/serde", "revm-state/serde", "rand_08/serde", + "scroll-alloy-consensus?/serde", ] reth-codec = [ "dep:reth-codecs", "dep:modular-bitfield", "dep:byteorder", + "scroll-alloy-consensus?/reth-codec", ] op = [ "dep:op-alloy-consensus", "reth-codecs?/op", ] +scroll-alloy-traits = ["scroll-alloy-consensus"] rayon = [ "dep:rayon", ]
diff --git reth/crates/primitives-traits/src/block/header.rs scroll-reth/crates/primitives-traits/src/block/header.rs index c4df8ecf542ffecce04b1ff19bcdd5df0af0b94c..38a218d11d1cd4ebe1d208591ab65248e6b58039 100644 --- reth/crates/primitives-traits/src/block/header.rs +++ scroll-reth/crates/primitives-traits/src/block/header.rs @@ -1,7 +1,7 @@ //! Block header data primitive.   use crate::{InMemorySize, MaybeCompact, MaybeSerde, MaybeSerdeBincodeCompat}; -use alloy_primitives::Sealable; +use alloy_primitives::{Bytes, Sealable}; use core::{fmt, hash::Hash};   /// Re-exported alias @@ -33,7 +33,20 @@ + MaybeSerde + MaybeSerdeBincodeCompat + AsRef<Self> + 'static + + BlockHeaderMut { }   impl BlockHeader for alloy_consensus::Header {} + +/// Returns a mutable reference to fields of the header. +pub trait BlockHeaderMut { + /// Mutable reference to the extra data. + fn extra_data_mut(&mut self) -> &mut Bytes; +} + +impl BlockHeaderMut for alloy_consensus::Header { + fn extra_data_mut(&mut self) -> &mut Bytes { + &mut self.extra_data + } +}
diff --git reth/crates/primitives-traits/src/block/recovered.rs scroll-reth/crates/primitives-traits/src/block/recovered.rs index 44631cd38ece56f0391863a803cae1ac4fbac122..d2cc011d1b5c5f162580136f8d4063a16aa8a12a 100644 --- reth/crates/primitives-traits/src/block/recovered.rs +++ scroll-reth/crates/primitives-traits/src/block/recovered.rs @@ -58,6 +58,7 @@ Self { block: SealedBlock::new_unhashed(block), senders } }   /// Returns the recovered senders. + #[allow(clippy::missing_const_for_fn)] pub fn senders(&self) -> &[Address] { &self.senders }
diff --git reth/crates/primitives-traits/src/serde_bincode_compat.rs scroll-reth/crates/primitives-traits/src/serde_bincode_compat.rs index 8b3ca7a594b41749379a9ecc101945e6c5d9135e..d08a05ecdfdc7d2f721a96a2990b2d2b334b1f32 100644 --- reth/crates/primitives-traits/src/serde_bincode_compat.rs +++ scroll-reth/crates/primitives-traits/src/serde_bincode_compat.rs @@ -242,4 +242,17 @@ fn from_repr(repr: Self::BincodeRepr<'_>) -> Self { repr.into() } } + + #[cfg(feature = "scroll-alloy-traits")] + impl SerdeBincodeCompat for scroll_alloy_consensus::ScrollTxEnvelope { + type BincodeRepr<'a> = scroll_alloy_consensus::serde_bincode_compat::ScrollTxEnvelope<'a>; + + fn as_repr(&self) -> Self::BincodeRepr<'_> { + self.into() + } + + fn from_repr(repr: Self::BincodeRepr<'_>) -> Self { + repr.into() + } + } }
diff --git reth/crates/primitives-traits/src/size.rs scroll-reth/crates/primitives-traits/src/size.rs index 734ae78b191985612ad506acaefd99ef97154060..21b192b6bc7328ad5202968847fb9606e9095481 100644 --- reth/crates/primitives-traits/src/size.rs +++ scroll-reth/crates/primitives-traits/src/size.rs @@ -173,6 +173,49 @@ } } } } + +/// Implementations for scroll types. +#[cfg(feature = "scroll-alloy-traits")] +mod scroll { + use super::*; + use scroll_alloy_consensus::ScrollTxEnvelope; + + impl InMemorySize for scroll_alloy_consensus::ScrollTypedTransaction { + fn size(&self) -> usize { + match self { + Self::Legacy(tx) => tx.size(), + Self::Eip2930(tx) => tx.size(), + Self::Eip1559(tx) => tx.size(), + Self::Eip7702(tx) => tx.size(), + Self::L1Message(tx) => tx.size(), + } + } + } + + impl InMemorySize for scroll_alloy_consensus::ScrollPooledTransaction { + fn size(&self) -> usize { + match self { + Self::Legacy(tx) => tx.size(), + Self::Eip2930(tx) => tx.size(), + Self::Eip1559(tx) => tx.size(), + Self::Eip7702(tx) => tx.size(), + } + } + } + + impl InMemorySize for ScrollTxEnvelope { + fn size(&self) -> usize { + match self { + Self::Legacy(tx) => tx.size(), + Self::Eip2930(tx) => tx.size(), + Self::Eip1559(tx) => tx.size(), + Self::Eip7702(tx) => tx.size(), + Self::L1Message(tx) => tx.size(), + } + } + } +} + #[cfg(test)] mod tests { use super::*;
diff --git reth/crates/primitives-traits/src/transaction/signed.rs scroll-reth/crates/primitives-traits/src/transaction/signed.rs index 1142664851b72b793bbc7a3378dbf47aed33f0cf..1bbca03d74c3d6ef4ae90a402c98bd2bf58c9a45 100644 --- reth/crates/primitives-traits/src/transaction/signed.rs +++ scroll-reth/crates/primitives-traits/src/transaction/signed.rs @@ -222,3 +222,62 @@ recover_signer_unchecked(signature, signature_hash) } } } + +#[cfg(feature = "scroll-alloy-traits")] +mod scroll { + use super::*; + use scroll_alloy_consensus::{ScrollPooledTransaction, ScrollTxEnvelope}; + + impl SignedTransaction for ScrollPooledTransaction { + fn tx_hash(&self) -> &TxHash { + match self { + Self::Legacy(tx) => tx.hash(), + Self::Eip2930(tx) => tx.hash(), + Self::Eip1559(tx) => tx.hash(), + Self::Eip7702(tx) => tx.hash(), + } + } + + fn recover_signer_unchecked_with_buf( + &self, + buf: &mut Vec<u8>, + ) -> Result<Address, RecoveryError> { + match self { + Self::Legacy(tx) => tx.tx().encode_for_signing(buf), + Self::Eip2930(tx) => tx.tx().encode_for_signing(buf), + Self::Eip1559(tx) => tx.tx().encode_for_signing(buf), + Self::Eip7702(tx) => tx.tx().encode_for_signing(buf), + } + let signature_hash = keccak256(buf); + recover_signer_unchecked(self.signature(), signature_hash) + } + } + + impl SignedTransaction for ScrollTxEnvelope { + fn tx_hash(&self) -> &TxHash { + match self { + Self::Legacy(tx) => tx.hash(), + Self::Eip2930(tx) => tx.hash(), + Self::Eip1559(tx) => tx.hash(), + Self::Eip7702(tx) => tx.hash(), + Self::L1Message(tx) => tx.hash_ref(), + } + } + + fn recover_signer_unchecked_with_buf( + &self, + buf: &mut Vec<u8>, + ) -> Result<Address, RecoveryError> { + match self { + Self::Legacy(tx) => tx.tx().encode_for_signing(buf), + Self::Eip2930(tx) => tx.tx().encode_for_signing(buf), + Self::Eip1559(tx) => tx.tx().encode_for_signing(buf), + Self::Eip7702(tx) => tx.tx().encode_for_signing(buf), + Self::L1Message(tx) => return Ok(tx.sender), + } + let signature_hash = keccak256(buf); + let signature = self.signature().expect("handled L1 message in previous match"); + recover_signer_unchecked(&signature, signature_hash) + } + } +}
diff --git reth/crates/stages/stages/Cargo.toml scroll-reth/crates/stages/stages/Cargo.toml index 68e1f99d7e71756e2e103697ecedfeb0b02c1eeb..ef8d8f72f28f25b7b29e462c98ca513a06714558 100644 --- reth/crates/stages/stages/Cargo.toml +++ scroll-reth/crates/stages/stages/Cargo.toml @@ -121,6 +121,7 @@ "dep:reth-ethereum-primitives", "reth-ethereum-primitives?/test-utils", "reth-evm-ethereum/test-utils", ] +skip-state-root-validation = []   [[bench]] name = "criterion"
diff --git reth/crates/stages/stages/benches/criterion.rs scroll-reth/crates/stages/stages/benches/criterion.rs index 08f700789ab4b91278ebc94b5068736b331cd2c8..c3905df2cc087ed5a00ef8be56a9d962eaa1b682 100644 --- reth/crates/stages/stages/benches/criterion.rs +++ scroll-reth/crates/stages/stages/benches/criterion.rs @@ -5,6 +5,7 @@ use alloy_primitives::BlockNumber; use criterion::{criterion_main, measurement::WallTime, BenchmarkGroup, Criterion}; use reth_config::config::{EtlConfig, TransactionLookupConfig}; use reth_db::{test_utils::TempDatabase, Database, DatabaseEnv}; +use reth_ethereum_primitives::EthPrimitives; use reth_provider::{test_utils::MockNodeTypesWithDB, DatabaseProvider, DatabaseProviderFactory}; use reth_stages::{ stages::{MerkleStage, SenderRecoveryStage, TransactionLookupStage}, @@ -113,7 +114,7 @@ group.sample_size(10);   let db = setup::txs_testdata(DEFAULT_NUM_BLOCKS);   - let stage = MerkleStage::Both { clean_threshold: u64::MAX }; + let stage = MerkleStage::<EthPrimitives>::Both { clean_threshold: u64::MAX }; measure_stage( runtime, &mut group, @@ -124,7 +125,7 @@ 1..=DEFAULT_NUM_BLOCKS, "Merkle-incremental".to_string(), );   - let stage = MerkleStage::Both { clean_threshold: 0 }; + let stage = MerkleStage::<EthPrimitives>::Both { clean_threshold: 0 }; measure_stage( runtime, &mut group,
diff --git reth/crates/stages/stages/src/sets.rs scroll-reth/crates/stages/stages/src/sets.rs index 6f0f055a2cab577469f99178b185bee64eae6c2a..51b99f626ada5f69f7c232cd21e90e10b469ae71 100644 --- reth/crates/stages/stages/src/sets.rs +++ scroll-reth/crates/stages/stages/src/sets.rs @@ -320,18 +320,21 @@ where E: ConfigureEvm, ExecutionStages<E>: StageSet<Provider>, PruneSenderRecoveryStage: Stage<Provider>, - HashingStages: StageSet<Provider>, + HashingStages<E::Primitives>: StageSet<Provider>, HistoryIndexingStages: StageSet<Provider>, PruneStage: Stage<Provider>, { fn builder(self) -> StageSetBuilder<Provider> { - ExecutionStages::new(self.evm_config, self.consensus, self.stages_config.clone()) + ExecutionStages::new(self.evm_config, self.consensus.clone(), self.stages_config.clone()) .builder() // If sender recovery prune mode is set, add the prune sender recovery stage. .add_stage_opt(self.prune_modes.sender_recovery.map(|prune_mode| { PruneSenderRecoveryStage::new(prune_mode, self.stages_config.prune.commit_threshold) })) - .add_set(HashingStages { stages_config: self.stages_config.clone() }) + .add_set(HashingStages { + stages_config: self.stages_config.clone(), + consensus: self.consensus, + }) .add_set(HistoryIndexingStages { stages_config: self.stages_config.clone(), prune_modes: self.prune_modes.clone(), @@ -387,22 +390,25 @@ } }   /// A set containing all stages that hash account state. -#[derive(Debug, Default)] +#[derive(Debug)] #[non_exhaustive] -pub struct HashingStages { +pub struct HashingStages<P: NodePrimitives> { /// Configuration for each stage in the pipeline stages_config: StageConfig, + /// Consensus instance for validating blocks. + consensus: Arc<dyn FullConsensus<P, Error = ConsensusError>>, }   -impl<Provider> StageSet<Provider> for HashingStages +impl<Provider, P> StageSet<Provider> for HashingStages<P> where - MerkleStage: Stage<Provider>, + P: NodePrimitives, + MerkleStage<P>: Stage<Provider>, AccountHashingStage: Stage<Provider>, StorageHashingStage: Stage<Provider>, { fn builder(self) -> StageSetBuilder<Provider> { StageSetBuilder::default() - .add_stage(MerkleStage::default_unwind()) + .add_stage(MerkleStage::new_unwind(self.consensus.clone())) .add_stage(AccountHashingStage::new( self.stages_config.account_hashing, self.stages_config.etl.clone(), @@ -411,7 +417,10 @@ .add_stage(StorageHashingStage::new( self.stages_config.storage_hashing, self.stages_config.etl.clone(), )) - .add_stage(MerkleStage::new_execution(self.stages_config.merkle.clean_threshold)) + .add_stage(MerkleStage::new_execution( + self.stages_config.merkle.clean_threshold, + self.consensus, + )) } }
diff --git reth/crates/stages/stages/src/stages/merkle.rs scroll-reth/crates/stages/stages/src/stages/merkle.rs index 55173876e9643a473d586be484af4dcbe47ae247..ce31091763054066ea1cdeb30ccc25a49ac93851 100644 --- reth/crates/stages/stages/src/stages/merkle.rs +++ scroll-reth/crates/stages/stages/src/stages/merkle.rs @@ -1,25 +1,28 @@ -use alloy_consensus::BlockHeader; -use alloy_primitives::{BlockNumber, Sealable, B256}; +use alloy_consensus::BlockHeader as _; use reth_codecs::Compact; -use reth_consensus::ConsensusError; use reth_db_api::{ tables, transaction::{DbTx, DbTxMut}, }; -use reth_primitives_traits::{GotExpected, SealedHeader}; +use reth_primitives_traits::BlockHeader; use reth_provider::{ DBProvider, HeaderProvider, ProviderError, StageCheckpointReader, StageCheckpointWriter, StatsReader, TrieWriter, }; use reth_stages_api::{ - BlockErrorKind, EntitiesCheckpoint, ExecInput, ExecOutput, MerkleCheckpoint, Stage, - StageCheckpoint, StageError, StageId, UnwindInput, UnwindOutput, + EntitiesCheckpoint, ExecInput, ExecOutput, MerkleCheckpoint, Stage, StageCheckpoint, + StageError, StageId, UnwindInput, UnwindOutput, }; use reth_trie::{IntermediateStateRootState, StateRoot, StateRootProgress, StoredSubNode}; use reth_trie_db::DatabaseStateRoot; -use std::fmt::Debug; +use std::{fmt::Debug, sync::Arc}; use tracing::*;   +use alloy_primitives::{BlockNumber, Sealable, B256}; +use reth_consensus::{Consensus, ConsensusError, HeaderValidator}; +use reth_primitives_traits::{NodePrimitives, SealedHeader}; +use reth_stages_api::BlockErrorKind; + // TODO: automate the process outlined below so the user can just send in a debugging package /// The error message that we include in invalid state root errors to tell users what information /// they should include in a bug report, since true state root errors can be impossible to debug @@ -64,15 +67,23 @@ /// - [`AccountHashingStage`][crate::stages::AccountHashingStage] /// - [`StorageHashingStage`][crate::stages::StorageHashingStage] /// - [`MerkleStage::Execution`] #[derive(Debug, Clone)] -pub enum MerkleStage { +pub enum MerkleStage<P> +where + P: NodePrimitives, +{ /// The execution portion of the merkle stage. Execution { /// The threshold (in number of blocks) for switching from incremental trie building /// of changes to whole rebuild. clean_threshold: u64, + /// Consensus. + consensus: Arc<dyn Consensus<P::Block, Error = ConsensusError>>, }, /// The unwind portion of the merkle stage. - Unwind, + Unwind { + /// Consensus. + consensus: Arc<dyn Consensus<P::Block, Error = ConsensusError>>, + }, /// Able to execute and unwind. Used for tests #[cfg(any(test, feature = "test-utils"))] Both { @@ -82,20 +93,39 @@ clean_threshold: u64, }, }   -impl MerkleStage { - /// Stage default for the [`MerkleStage::Execution`]. - pub const fn default_execution() -> Self { - Self::Execution { clean_threshold: MERKLE_STAGE_DEFAULT_CLEAN_THRESHOLD } +impl<P> MerkleStage<P> +where + P: NodePrimitives, +{ + /// Stage default for the [`MerkleStage::Execution`] with the provided consensus. + pub const fn default_execution_with_consensus( + consensus: Arc<dyn Consensus<P::Block, Error = ConsensusError>>, + ) -> Self { + Self::Execution { clean_threshold: MERKLE_STAGE_DEFAULT_CLEAN_THRESHOLD, consensus } }   - /// Stage default for the [`MerkleStage::Unwind`]. - pub const fn default_unwind() -> Self { - Self::Unwind + /// Create new instance of [`MerkleStage::Execution`]. + pub const fn new_execution( + clean_threshold: u64, + consensus: Arc<dyn Consensus<P::Block, Error = ConsensusError>>, + ) -> Self { + Self::Execution { clean_threshold, consensus } }   - /// Create new instance of [`MerkleStage::Execution`]. - pub const fn new_execution(clean_threshold: u64) -> Self { - Self::Execution { clean_threshold } + /// Create new instance of [`MerkleStage::Unwind`]. + pub const fn new_unwind( + consensus: Arc<dyn Consensus<P::Block, Error = ConsensusError>>, + ) -> Self { + Self::Unwind { consensus } + } + + /// Returns the consensus for the stage. + pub fn consensus(&self) -> Arc<dyn Consensus<P::Block, Error = ConsensusError>> { + match self { + Self::Execution { consensus, .. } | Self::Unwind { consensus } => consensus.clone(), + #[cfg(any(test, feature = "test-utils"))] + Self::Both { .. } => reth_consensus::noop::NoopConsensus::arc(), + } }   /// Gets the hashing progress @@ -133,7 +163,7 @@ Ok(provider.save_stage_checkpoint_progress(StageId::MerkleExecute, buf)?) } }   -impl<Provider> Stage<Provider> for MerkleStage +impl<Provider, P> Stage<Provider> for MerkleStage<P> where Provider: DBProvider<Tx: DbTxMut> + TrieWriter @@ -141,12 +171,13 @@ + StatsReader + HeaderProvider + StageCheckpointReader + StageCheckpointWriter, + P: NodePrimitives<BlockHeader = <Provider as HeaderProvider>::Header>, { /// Return the id of the stage fn id(&self) -> StageId { match self { Self::Execution { .. } => StageId::MerkleExecute, - Self::Unwind => StageId::MerkleUnwind, + Self::Unwind { .. } => StageId::MerkleUnwind, #[cfg(any(test, feature = "test-utils"))] Self::Both { .. } => StageId::Other("MerkleBoth"), } @@ -155,11 +186,11 @@ /// Execute the stage. fn execute(&mut self, provider: &Provider, input: ExecInput) -> Result<ExecOutput, StageError> { let threshold = match self { - Self::Unwind => { + Self::Unwind { .. } => { info!(target: "sync::stages::merkle::unwind", "Stage is always skipped"); - return Ok(ExecOutput::done(StageCheckpoint::new(input.target()))) + return Ok(ExecOutput::done(StageCheckpoint::new(input.target()))); } - Self::Execution { clean_threshold } => *clean_threshold, + Self::Execution { clean_threshold, .. } => *clean_threshold, #[cfg(any(test, feature = "test-utils"))] Self::Both { clean_threshold } => *clean_threshold, }; @@ -279,7 +310,13 @@ // Reset the checkpoint self.save_execution_checkpoint(provider, None)?;   - validate_state_root(trie_root, SealedHeader::seal_slow(target_block), to_block)?; + // Ensure state root matches + validate_state_root( + self.consensus(), + trie_root, + SealedHeader::seal_slow(target_block), + to_block, + )?;   Ok(ExecOutput { checkpoint: StageCheckpoint::new(to_block) @@ -332,7 +369,13 @@ let target = provider .header_by_number(input.unwind_to)? .ok_or_else(|| ProviderError::HeaderNotFound(input.unwind_to.into()))?;   - validate_state_root(block_root, SealedHeader::seal_slow(target), input.unwind_to)?; + let consensus = self.consensus(); + validate_state_root( + consensus, + block_root, + SealedHeader::seal_slow(target), + input.unwind_to, + )?;   // Validation passed, apply unwind changes to the database. provider.write_trie_updates(&updates)?; @@ -347,21 +390,19 @@ /// Check that the computed state root matches the root in the expected header. #[inline] fn validate_state_root<H: BlockHeader + Sealable + Debug>( + consensus: Arc<dyn HeaderValidator<H>>, got: B256, expected: SealedHeader<H>, target_block: BlockNumber, ) -> Result<(), StageError> { - if got == expected.state_root() { - Ok(()) - } else { + consensus.validate_state_root(&*expected, got).inspect_err(|_|{ error!(target: "sync::stages::merkle", ?target_block, ?got, ?expected, "Failed to verify block state root! {INVALID_STATE_ROOT_ERROR_MESSAGE}"); - Err(StageError::Block { - error: BlockErrorKind::Validation(ConsensusError::BodyStateRootDiff( - GotExpected { got, expected: expected.state_root() }.into(), - )), + }).map_err(|err|{ + StageError::Block { + error: BlockErrorKind::Validation(err), block: Box::new(expected.block_with_parent()), - }) - } + } + }) }   #[cfg(test)] @@ -371,10 +412,11 @@ use crate::test_utils::{ stage_test_suite_ext, ExecuteStageTestRunner, StageTestRunner, StorageKind, TestRunnerError, TestStageDB, UnwindStageTestRunner, }; - use alloy_primitives::{keccak256, U256}; + use alloy_primitives::{keccak256, B256, U256}; use assert_matches::assert_matches; use reth_db_api::cursor::{DbCursorRO, DbCursorRW, DbDupCursorRO}; - use reth_primitives_traits::{SealedBlock, StorageEntry}; + use reth_ethereum_primitives::EthPrimitives; + use reth_primitives_traits::{SealedBlock, SealedHeader, StorageEntry}; use reth_provider::{providers::StaticFileWriter, StaticFileProviderFactory}; use reth_stages_api::StageUnitCheckpoint; use reth_static_file_types::StaticFileSegment; @@ -480,7 +522,7 @@ } }   impl StageTestRunner for MerkleTestRunner { - type S = MerkleStage; + type S = MerkleStage<EthPrimitives>;   fn db(&self) -> &TestStageDB { &self.db
diff --git reth/crates/storage/codecs/derive/src/compact/mod.rs scroll-reth/crates/storage/codecs/derive/src/compact/mod.rs index ae349cd06e590f9a630a8005000111fe3a5506a0..0e795e7a94d828553f5541998c1fd27ef389ea19 100644 --- reth/crates/storage/codecs/derive/src/compact/mod.rs +++ scroll-reth/crates/storage/codecs/derive/src/compact/mod.rs @@ -202,7 +202,7 @@ /// length. pub fn get_bit_size(ftype: &str) -> u8 { match ftype { "TransactionKind" | "TxKind" | "bool" | "Option" | "Signature" => 1, - "TxType" | "OpTxType" => 2, + "TxType" | "OpTxType" | "ScrollTxType" => 2, "u64" | "BlockNumber" | "TxNumber" | "ChainId" | "NumTransactions" => 4, "u128" => 5, "U256" => 6,
diff --git reth/crates/storage/codecs/src/alloy/transaction/ethereum.rs scroll-reth/crates/storage/codecs/src/alloy/transaction/ethereum.rs index 14d51b866fb2b49469f5c9687237ccb2b5c460a5..799fcf7861e6d16411834fbf5894a48965815d2b 100644 --- reth/crates/storage/codecs/src/alloy/transaction/ethereum.rs +++ scroll-reth/crates/storage/codecs/src/alloy/transaction/ethereum.rs @@ -112,7 +112,8 @@ Self::tx_type(self) } }   -pub(super) trait CompactEnvelope: Sized { +/// Wraps the [`Compact`] trait. +pub trait CompactEnvelope: Sized { /// Takes a buffer which can be written to. *Ideally*, it returns the length written to. fn to_compact<B>(&self, buf: &mut B) -> usize where
diff --git reth/crates/storage/codecs/src/alloy/transaction/mod.rs scroll-reth/crates/storage/codecs/src/alloy/transaction/mod.rs index 47881b6f87a9cc6c363d71bfc6947a82f3f200a9..f841ff24f1734f71171d845ecf3954d069ccbeb3 100644 --- reth/crates/storage/codecs/src/alloy/transaction/mod.rs +++ scroll-reth/crates/storage/codecs/src/alloy/transaction/mod.rs @@ -56,7 +56,7 @@ cond_mod!(eip1559, eip2930, eip4844, eip7702, legacy, txtype);   mod ethereum; -pub use ethereum::{Envelope, FromTxCompact, ToTxCompact}; +pub use ethereum::{CompactEnvelope, Envelope, FromTxCompact, ToTxCompact};   #[cfg(all(feature = "test-utils", feature = "op"))] pub mod optimism;
diff --git reth/crates/storage/db-api/Cargo.toml scroll-reth/crates/storage/db-api/Cargo.toml index 3f7e5c7b1a7ea6ceffb789723529b8b11ad374c9..900dd525f4a17407d6eced7fa91de8cb5f895246 100644 --- reth/crates/storage/db-api/Cargo.toml +++ scroll-reth/crates/storage/db-api/Cargo.toml @@ -27,6 +27,9 @@ alloy-primitives.workspace = true alloy-genesis.workspace = true alloy-consensus.workspace = true   +# scroll +reth-scroll-primitives = { workspace = true, optional = true } + # optimism reth-optimism-primitives = { workspace = true, optional = true }   @@ -84,6 +87,7 @@ "reth-stages-types/arbitrary", "alloy-consensus/arbitrary", "reth-optimism-primitives?/arbitrary", "reth-ethereum-primitives/arbitrary", + "reth-scroll-primitives?/arbitrary", ] op = [ "dep:reth-optimism-primitives", @@ -91,3 +95,4 @@ "reth-codecs/op", "reth-primitives-traits/op", ] bench = [] +scroll-alloy-traits = ["dep:reth-scroll-primitives", "reth-primitives-traits/scroll-alloy-traits"]
diff --git reth/crates/storage/db-api/src/models/mod.rs scroll-reth/crates/storage/db-api/src/models/mod.rs index cfaa39af9f1bf4bb07757279578a59171006af51..70c7324c29c5ea322a9e6da0ac7ba6999a92366c 100644 --- reth/crates/storage/db-api/src/models/mod.rs +++ scroll-reth/crates/storage/db-api/src/models/mod.rs @@ -245,6 +245,14 @@ impl_compression_for_compact!(OpTransactionSigned, OpReceipt); }   +#[cfg(feature = "scroll-alloy-traits")] +mod scroll { + use super::*; + use reth_scroll_primitives::{ScrollReceipt, ScrollTransactionSigned}; + + impl_compression_for_compact!(ScrollTransactionSigned, ScrollReceipt); +} + macro_rules! impl_compression_fixed_compact { ($($name:tt),+) => { $(
diff --git reth/crates/storage/db-api/src/tables/raw.rs scroll-reth/crates/storage/db-api/src/tables/raw.rs index 96208a25d56ba3e68d5e122ab72d544af91246ba..bbe33101e2392e13d351ed574e2b8bea3c7dd630 100644 --- reth/crates/storage/db-api/src/tables/raw.rs +++ scroll-reth/crates/storage/db-api/src/tables/raw.rs @@ -136,6 +136,7 @@ V::decompress(&self.value) }   /// Returns the raw value as seen on the database. + #[allow(clippy::missing_const_for_fn)] pub fn raw_value(&self) -> &[u8] { &self.value }
diff --git reth/crates/storage/db/Cargo.toml scroll-reth/crates/storage/db/Cargo.toml index 719c7b785c14a4db3b3f2af2a9873101cfd9d86d..df48b0b86eccc2148fa27a54138cc19beb496b82 100644 --- reth/crates/storage/db/Cargo.toml +++ scroll-reth/crates/storage/db/Cargo.toml @@ -93,6 +93,7 @@ op = [ "reth-db-api/op", "reth-primitives-traits/op", ] +scroll-alloy-traits = ["reth-db-api/scroll-alloy-traits", "reth-primitives-traits/scroll-alloy-traits"] disable-lock = []   [[bench]]
diff --git reth/crates/storage/db/src/lib.rs scroll-reth/crates/storage/db/src/lib.rs index df2510c85db318667c16dc8b5524cb87f97b60f2..b646c407e97cddb61e2baefd0a2ef40b92c73cdb 100644 --- reth/crates/storage/db/src/lib.rs +++ scroll-reth/crates/storage/db/src/lib.rs @@ -108,6 +108,7 @@ self.db.as_ref().unwrap() }   /// Returns the path to the database. + #[allow(clippy::missing_const_for_fn)] pub fn path(&self) -> &Path { &self.path }
diff --git reth/crates/storage/libmdbx-rs/src/environment.rs scroll-reth/crates/storage/libmdbx-rs/src/environment.rs index fac3cd5084ce0f0419c591b0e57c5e12a8ff61ac..902208f1c73fb775fe110c090b47cfa7fe5de296 100644 --- reth/crates/storage/libmdbx-rs/src/environment.rs +++ scroll-reth/crates/storage/libmdbx-rs/src/environment.rs @@ -60,12 +60,14 @@ }   /// Returns true if the environment was opened as WRITEMAP. #[inline] + #[allow(clippy::missing_const_for_fn)] pub fn is_write_map(&self) -> bool { self.inner.env_kind.is_write_map() }   /// Returns the kind of the environment. #[inline] + #[allow(clippy::missing_const_for_fn)] pub fn env_kind(&self) -> EnvironmentKind { self.inner.env_kind } @@ -84,6 +86,7 @@ }   /// Returns the transaction manager. #[inline] + #[allow(clippy::missing_const_for_fn)] pub(crate) fn txn_manager(&self) -> &TxnManager { &self.inner.txn_manager } @@ -131,6 +134,7 @@ /// /// The caller **must** ensure that the pointer is never dereferenced after the environment has /// been dropped. #[inline] + #[allow(clippy::missing_const_for_fn)] pub(crate) fn env_ptr(&self) -> *mut ffi::MDBX_env { self.inner.env }
diff --git reth/crates/storage/libmdbx-rs/src/transaction.rs scroll-reth/crates/storage/libmdbx-rs/src/transaction.rs index a19e70956604ce2884009e49f1872ef0221324cb..677a33474b020bba24f011cc3b042b40ea0a4cf3 100644 --- reth/crates/storage/libmdbx-rs/src/transaction.rs +++ scroll-reth/crates/storage/libmdbx-rs/src/transaction.rs @@ -130,11 +130,13 @@ /// Returns a copy of the raw pointer to the underlying MDBX transaction. #[doc(hidden)] #[cfg(test)] + #[allow(clippy::missing_const_for_fn)] pub fn txn(&self) -> *mut ffi::MDBX_txn { self.inner.txn.txn }   /// Returns a raw pointer to the MDBX environment. + #[allow(clippy::missing_const_for_fn)] pub fn env(&self) -> &Environment { &self.inner.env }
diff --git reth/crates/storage/nippy-jar/src/lib.rs scroll-reth/crates/storage/nippy-jar/src/lib.rs index b4e39d709d8cb46b553addbfd3c57daa5ae901b3..bc7ef4587c87ae7c3797fd6a8948e9e37e962df9 100644 --- reth/crates/storage/nippy-jar/src/lib.rs +++ scroll-reth/crates/storage/nippy-jar/src/lib.rs @@ -412,6 +412,7 @@ &self.data_mmap[range] }   /// Returns total size of data + #[allow(clippy::missing_const_for_fn)] pub fn size(&self) -> usize { self.data_mmap.len() }
diff --git reth/crates/storage/nippy-jar/src/writer.rs scroll-reth/crates/storage/nippy-jar/src/writer.rs index d32d9b514088bc495523072f268dc8576a8d7cfc..b20d9d65070aa334343df921c49d9ef4513d9cda 100644 --- reth/crates/storage/nippy-jar/src/writer.rs +++ scroll-reth/crates/storage/nippy-jar/src/writer.rs @@ -430,6 +430,7 @@ }   /// Returns a reference to the offsets vector. #[cfg(test)] + #[allow(clippy::missing_const_for_fn)] pub fn offsets(&self) -> &[u64] { &self.offsets }
diff --git reth/crates/storage/provider/Cargo.toml scroll-reth/crates/storage/provider/Cargo.toml index c45fde7729cf82705a8bbb35b6ca82a490dee987..2a62eaf103f3080d4775002daf6904a2881d189d 100644 --- reth/crates/storage/provider/Cargo.toml +++ scroll-reth/crates/storage/provider/Cargo.toml @@ -102,3 +102,4 @@ "reth-stages-types/test-utils", "revm-state", "tokio", ] +skip-state-root-validation = []
diff --git reth/crates/storage/provider/src/providers/database/provider.rs scroll-reth/crates/storage/provider/src/providers/database/provider.rs index 27dc5266893c1656d36f93e5e89d640711c06b30..0d61db4f27c524d8d776081541d88c2a57dd3cf2 100644 --- reth/crates/storage/provider/src/providers/database/provider.rs +++ scroll-reth/crates/storage/provider/src/providers/database/provider.rs @@ -279,7 +279,7 @@ }   // Unwind account history indices. self.unwind_account_history_indices(changed_accounts.iter())?; - let storage_range = BlockNumberAddress::range(range.clone()); + let storage_range = BlockNumberAddress::range(range);   let changed_storages = self .tx @@ -311,29 +311,11 @@ account_prefix_set: account_prefix_set.freeze(), storage_prefix_sets, destroyed_accounts, }; - let (new_state_root, trie_updates) = StateRoot::from_tx(&self.tx) + let (_, trie_updates) = StateRoot::from_tx(&self.tx) .with_prefix_sets(prefix_sets) .root_with_updates() .map_err(reth_db_api::DatabaseError::from)?;   - let parent_number = range.start().saturating_sub(1); - let parent_state_root = self - .header_by_number(parent_number)? - .ok_or_else(|| ProviderError::HeaderNotFound(parent_number.into()))? - .state_root(); - - // state root should be always correct as we are reverting state. - // but for sake of double verification we will check it again. - if new_state_root != parent_state_root { - let parent_hash = self - .block_hash(parent_number)? - .ok_or_else(|| ProviderError::HeaderNotFound(parent_number.into()))?; - return Err(ProviderError::UnwindStateRootMismatch(Box::new(RootMismatch { - root: GotExpected { got: new_state_root, expected: parent_state_root }, - block_number: parent_number, - block_hash: parent_hash, - }))) - } self.write_trie_updates(&trie_updates)?;   Ok(()) @@ -527,6 +509,7 @@ &self.tx }   /// Returns a reference to the chain specification. + #[allow(clippy::missing_const_for_fn)] pub fn chain_spec(&self) -> &N::ChainSpec { &self.chain_spec }
diff --git reth/crates/storage/provider/src/providers/mod.rs scroll-reth/crates/storage/provider/src/providers/mod.rs index 36843a22fba930ee031daf743e01ec18cd619e4b..5575d9b1ffa0cb84df28dde52c8da634f767b137 100644 --- reth/crates/storage/provider/src/providers/mod.rs +++ scroll-reth/crates/storage/provider/src/providers/mod.rs @@ -1,6 +1,6 @@ //! Contains the main provider types and traits for interacting with the blockchain's storage.   -use reth_chainspec::EthereumHardforks; +use reth_chainspec::EthereumCapabilities; use reth_db_api::table::Value; use reth_node_types::{FullNodePrimitives, NodeTypes, NodeTypesWithDB};   @@ -33,7 +33,7 @@ /// [`ProviderNodeTypes`]. pub trait NodeTypesForProvider where Self: NodeTypes< - ChainSpec: EthereumHardforks, + ChainSpec: EthereumCapabilities, Storage: ChainStorage<Self::Primitives>, Primitives: FullNodePrimitives<SignedTx: Value, Receipt: Value, BlockHeader: Value>, >, @@ -42,7 +42,7 @@ }   impl<T> NodeTypesForProvider for T where T: NodeTypes< - ChainSpec: EthereumHardforks, + ChainSpec: EthereumCapabilities, Storage: ChainStorage<T::Primitives>, Primitives: FullNodePrimitives<SignedTx: Value, Receipt: Value, BlockHeader: Value>, >
diff --git reth/crates/storage/provider/src/providers/static_file/manager.rs scroll-reth/crates/storage/provider/src/providers/static_file/manager.rs index 8c4f76bccba0c68634a12c78250dafc604c130e6..a3ab41325f867641fd76300326577a75ae466741 100644 --- reth/crates/storage/provider/src/providers/static_file/manager.rs +++ scroll-reth/crates/storage/provider/src/providers/static_file/manager.rs @@ -1105,6 +1105,7 @@ })) }   /// Returns directory where `static_files` are located. + #[allow(clippy::missing_const_for_fn)] pub fn directory(&self) -> &Path { &self.path } @@ -1196,12 +1197,14 @@ }   /// Returns `static_files` directory #[cfg(any(test, feature = "test-utils"))] + #[allow(clippy::missing_const_for_fn)] pub fn path(&self) -> &Path { &self.path }   /// Returns `static_files` transaction index #[cfg(any(test, feature = "test-utils"))] + #[allow(clippy::missing_const_for_fn)] pub fn tx_index(&self) -> &RwLock<SegmentRanges> { &self.static_files_tx_index }
diff --git reth/crates/storage/storage-api/src/chain.rs scroll-reth/crates/storage/storage-api/src/chain.rs index 5c66d055e1808ccca8e9534eb6316a4803f396d5..ffc0c8cb3c93ebd035460b9aa8139d583f6f6fdd 100644 --- reth/crates/storage/storage-api/src/chain.rs +++ scroll-reth/crates/storage/storage-api/src/chain.rs @@ -3,7 +3,7 @@ use alloc::vec::Vec; use alloy_consensus::Header; use alloy_primitives::BlockNumber; use core::marker::PhantomData; -use reth_chainspec::{ChainSpecProvider, EthereumHardforks}; +use reth_chainspec::{ChainSpecProvider, EthereumCapabilities, EthereumHardforks}; use reth_db_api::{ cursor::{DbCursorRO, DbCursorRW}, models::StoredBlockOmmers, @@ -146,7 +146,7 @@ }   impl<Provider, T, H> BlockBodyReader<Provider> for EthStorage<T, H> where - Provider: DBProvider + ChainSpecProvider<ChainSpec: EthereumHardforks>, + Provider: DBProvider + ChainSpecProvider<ChainSpec: EthereumCapabilities>, T: SignedTransaction, H: FullBlockHeader, { @@ -167,7 +167,7 @@ for (header, transactions) in inputs { // If we are past shanghai, then all blocks should have a withdrawal list, // even if empty - let withdrawals = if chain_spec.is_shanghai_active_at_timestamp(header.timestamp()) { + let withdrawals = if chain_spec.withdrawals_active(header.timestamp()) { withdrawals_cursor .seek_exact(header.number())? .map(|(_, w)| w.withdrawals)
diff --git reth/crates/trie/common/src/updates.rs scroll-reth/crates/trie/common/src/updates.rs index d4362542f005adb5dde7487ca94e25a7fbee95b8..e3146f50f908e4c48121113b12aa3d0a81bfb66d 100644 --- reth/crates/trie/common/src/updates.rs +++ scroll-reth/crates/trie/common/src/updates.rs @@ -363,6 +363,7 @@ }   impl TrieUpdatesSorted { /// Returns reference to updated account nodes. + #[allow(clippy::missing_const_for_fn)] pub fn account_nodes_ref(&self) -> &[(Nibbles, BranchNodeCompact)] { &self.account_nodes } @@ -396,6 +397,7 @@ self.is_deleted }   /// Returns reference to updated storage nodes. + #[allow(clippy::missing_const_for_fn)] pub fn storage_nodes_ref(&self) -> &[(Nibbles, BranchNodeCompact)] { &self.storage_nodes }
diff --git reth/crates/trie/trie/Cargo.toml scroll-reth/crates/trie/trie/Cargo.toml index adee3291b805b1c355d714df96f9c234ea3b9732..68f6e9640bc381f5c55eea32e9b8dcca9d627c1b 100644 --- reth/crates/trie/trie/Cargo.toml +++ scroll-reth/crates/trie/trie/Cargo.toml @@ -18,7 +18,7 @@ reth-primitives-traits.workspace = true reth-stages-types.workspace = true reth-storage-errors.workspace = true reth-trie-sparse.workspace = true -reth-trie-common = { workspace = true, features = ["rayon"] } +reth-trie-common.workspace = true   revm-database.workspace = true   @@ -64,6 +64,8 @@ proptest-arbitrary-interop.workspace = true proptest.workspace = true   [features] +default = ["rayon"] +rayon = ["reth-trie-common/rayon"] metrics = ["reth-metrics", "dep:metrics"] serde = [ "alloy-primitives/serde", @@ -90,6 +92,7 @@ "reth-trie-common/test-utils", "reth-ethereum-primitives/test-utils", "reth-trie-sparse/test-utils", "reth-stages-types/test-utils", + "reth-primitives-traits/test-utils", ]   [[bench]]
diff --git reth/.config/zepter.yaml scroll-reth/.config/zepter.yaml index b754d06a062c0a45dcbd02ea103ac05ccaa43f55..3fb82bec823f2ef4cb5a1c6f37478212de9560ed 100644 --- reth/.config/zepter.yaml +++ scroll-reth/.config/zepter.yaml @@ -8,14 +8,14 @@ # The examples in the following comments assume crate `A` to have a dependency on crate `B`. workflows: check: - [ - "lint", - # Check that `A` activates the features of `B`. - "propagate-feature", - # These are the features to check: - "--features=std,op,dev,asm-keccak,jemalloc,jemalloc-prof,tracy-allocator,serde-bincode-compat,serde,test-utils,arbitrary,bench,alloy-compat", - # Do not try to add a new section to `[features]` of `A` only because `B` exposes that feature. There are edge-cases where this is still needed, but we can add them manually. - "--left-side-feature-missing=ignore", - # Ignore the case that `A` it outside of the workspace. Otherwise it will report errors in external dependencies that we have no influence on. + "lint", + # Check that `A` activates the features of `B`. + "propagate-feature", + # These are the features to check: + "--features=std,op,scroll-alloy-traits,dev,asm-keccak,jemalloc,jemalloc-prof,tracy-allocator,serde-bincode-compat,serde,test-utils,arbitrary,bench,alloy-compat", + # Do not try to add a new section to `[features]` of `A` only because `B` exposes that feature. There are edge-cases where this is still needed, but we can add them manually. + "--left-side-feature-missing=ignore", + # Ignore the case that `A` it outside of the workspace. Otherwise it will report errors in external dependencies that we have no influence on.   "--left-side-outside-workspace=ignore", # Auxiliary flags:
diff --git reth/.gitignore scroll-reth/.gitignore index 1072d75dfaa5d18d2b93c942169a01a7a534f536..7ec2e506cf349dd928485971d3c56560e22bab16 100644 --- reth/.gitignore +++ scroll-reth/.gitignore @@ -2,6 +2,7 @@ # Generated by Cargo # will have compiled files and executables ./debug/ target/ +datadir/   # These are backup files generated by rustfmt **/*.rs.bk
diff --git reth/Cargo.lock scroll-reth/Cargo.lock index 7ec5d723d6b8b31c0099ab21598e88d696a43db3..cf4bed5a888ac8216f8e05feae145cad86a7ff36 100644 --- reth/Cargo.lock +++ scroll-reth/Cargo.lock @@ -97,9 +97,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"   [[package]] name = "alloy-chains" -version = "0.2.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "517e5acbd38b6d4c59da380e8bbadc6d365bf001903ce46cf5521c53c647e07b" +checksum = "5848366a4f08dca1caca0a6151294a4799fe2e59ba25df100491d92e0b921b1c" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -137,9 +137,9 @@ ]   [[package]] name = "alloy-consensus-any" -version = "1.0.9" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "142daffb15d5be1a2b20d2cd540edbcef03037b55d4ff69dc06beb4d06286dba" +checksum = "f31b286aeef04a32720c10defd21c3aa6c626154ac442b55f6d472caeb1c6741" dependencies = [ "alloy-consensus", "alloy-eips", @@ -289,9 +289,9 @@ ]   [[package]] name = "alloy-hardforks" -version = "0.2.6" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbff8445282ec080c2673692062bd4930d7a0d6bda257caf138cfc650c503000" +checksum = "1d6b8067561eb8f884b215ace4c962313c5467e47bde6b457c8c51e268fb5d99" dependencies = [ "alloy-chains", "alloy-eip2124", @@ -385,9 +385,9 @@ ]   [[package]] name = "alloy-op-hardforks" -version = "0.2.6" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ddfbb5cc9f614efa5d56e0d7226214bb67b29271d44b6ddfcbbe25eb0ff898b" +checksum = "08043c9284e597f9b5cf741cc6d906fdb26c195a01d88423c84c00ffda835713" dependencies = [ "alloy-hardforks", "auto_impl", @@ -578,9 +578,9 @@ ]   [[package]] name = "alloy-rpc-types-any" -version = "1.0.9" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "508b2fbe66d952089aa694e53802327798806498cd29ff88c75135770ecaabfc" +checksum = "67971a228100ac65bd86e90439028853435f21796330ef08f00a70a918a84126" dependencies = [ "alloy-consensus-any", "alloy-rpc-types-eth", @@ -843,7 +843,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ea5a76d7f2572174a382aedf36875bedf60bcc41116c9f031cf08040703a2dc" dependencies = [ "alloy-json-rpc", + "alloy-rpc-types-engine", "alloy-transport", + "http-body-util", + "hyper", + "hyper-tls", + "hyper-util", + "jsonwebtoken", "reqwest", "serde_json", "tower", @@ -1446,9 +1452,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"   [[package]] name = "backon" -version = "1.5.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "302eaff5357a264a2c42f127ecb8bac761cf99749fc3dc95677e2743991f99e7" +checksum = "fd0b50b1b78dbadd44ab18b3c794e496f3a139abb9fbc27d9c94c4eebbb96496" dependencies = [ "fastrand 2.3.0", "tokio", @@ -2104,9 +2110,9 @@ ]   [[package]] name = "clap" -version = "4.5.39" +version = "4.5.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd60e63e9be68e5fb56422e397cf9baddded06dae1d2e523401542383bc72a9f" +checksum = "ed93b9805f8ba930df42c2590f05453d5ec36cbb85d018868a5b24d31f6ac000" dependencies = [ "clap_builder", "clap_derive", @@ -2114,9 +2120,9 @@ ]   [[package]] name = "clap_builder" -version = "4.5.39" +version = "4.5.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89cc6392a1f72bbeb820d71f32108f61fdaf18bc526e1d23954168a67759ef51" +checksum = "379026ff283facf611b0ea629334361c4211d1b12ee01024eec1591133b04120" dependencies = [ "anstream", "anstyle", @@ -2270,7 +2276,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" dependencies = [ "lazy_static", - "windows-sys 0.59.0", + "windows-sys 0.48.0", ]   [[package]] @@ -2394,9 +2400,19 @@ ]   [[package]] name = "core-foundation" -version = "0.10.1" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" dependencies = [ "core-foundation-sys", "libc", @@ -3128,6 +3144,17 @@ "syn 2.0.101", ]   [[package]] +name = "enumn" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] name = "equivalent" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3734,6 +3761,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"   [[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] name = "form_urlencoded" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4128,6 +4170,12 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"   [[package]] name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hermit-abi" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f154ce46856750ed433c8649605bf7ed2de3bc35fd9d2a9f30cddd873c80cb08" @@ -4351,22 +4399,34 @@ "webpki-roots 1.0.0", ]   [[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] name = "hyper-util" -version = "0.1.13" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c293b6b3d21eca78250dc7dbebd6b9210ec5530e038cbfe0661b5c47ab06e8" +checksum = "cf9f1e950e0d9d1d3c47184416723cf29c0d1f93bd8cccf37e4beb6b44f31710" dependencies = [ - "base64 0.22.1", "bytes", "futures-channel", - "futures-core", "futures-util", "http", "http-body", "hyper", - "ipnet", "libc", - "percent-encoding", "pin-project-lite", "socket2", "tokio", @@ -4386,7 +4446,7 @@ "iana-time-zone-haiku", "js-sys", "log", "wasm-bindgen", - "windows-core 0.61.2", + "windows-core 0.58.0", ]   [[package]] @@ -4829,7 +4889,7 @@ version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" dependencies = [ - "hermit-abi", + "hermit-abi 0.5.1", "libc", "windows-sys 0.59.0", ] @@ -5182,9 +5242,9 @@ ]   [[package]] name = "libloading" -version = "0.8.8" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" +checksum = "6a793df0d7afeac54f95b471d3af7f0d4fb975699f972341a4b76988d49cdf0c" dependencies = [ "cfg-if", "windows-targets 0.53.0", @@ -5337,9 +5397,9 @@ checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956"   [[package]] name = "lock_api" -version = "0.4.13" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -5703,6 +5763,23 @@ "unsigned-varint", ]   [[package]] +name = "native-tls" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework 2.11.1", + "security-framework-sys", + "tempfile", +] + +[[package]] name = "nom" version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -5839,11 +5916,11 @@ ]   [[package]] name = "num_cpus" -version = "1.17.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.9", "libc", ]   @@ -6034,9 +6111,8 @@ ]   [[package]] name = "op-revm" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0e8a3830a2be82166fbe9ead34361149ff4320743ed7ee5502ab779de221361" +version = "5.0.0" +source = "git+https://github.com/scroll-tech/revm?branch=feat%2Freth-v74#28f83c2759ac9d2d23969a931cf308b375bb6bd4" dependencies = [ "auto_impl", "once_cell", @@ -6051,10 +6127,48 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"   [[package]] +name = "openssl" +version = "0.10.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fedfea7d58a1f73118430a55da6a286e7b044961736ce96a16a17068ea25e5da" +dependencies = [ + "bitflags 2.9.1", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] name = "openssl-probe" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + +[[package]] +name = "openssl-sys" +version = "0.9.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e145e1651e858e820e4860f7b9c5e169bc1d8ce1c86043be79fa7b7634821847" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +]   [[package]] name = "opentelemetry" @@ -6219,9 +6333,9 @@ checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba"   [[package]] name = "parking_lot" -version = "0.12.4" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", @@ -6229,9 +6343,9 @@ ]   [[package]] name = "parking_lot_core" -version = "0.9.11" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", @@ -6481,9 +6595,9 @@ ]   [[package]] name = "prettyplease" -version = "0.2.33" +version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dee91521343f4c5c6a63edd65e54f31f5c92fe8978c40a4282f8372194c6a7d" +checksum = "664ec5419c51e34154eec046ebcba56312d5a2fc3b09a06da188e1ad21afadf6" dependencies = [ "proc-macro2", "syn 2.0.101", @@ -7025,9 +7139,9 @@ checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2"   [[package]] name = "reqwest" -version = "0.12.18" +version = "0.12.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e98ff6b0dbbe4d5a37318f433d4fc82babd21631f194d370409ceb2e40b2f0b5" +checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb" dependencies = [ "base64 0.22.1", "bytes", @@ -7050,6 +7164,7 @@ "pin-project-lite", "quinn", "rustls", "rustls-native-certs", + "rustls-pemfile", "rustls-pki-types", "serde", "serde_json", @@ -7059,14 +7174,14 @@ "tokio", "tokio-rustls", "tokio-util", "tower", - "tower-http", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "wasm-streams", "web-sys", - "webpki-roots 1.0.0", + "webpki-roots 0.26.11", + "windows-registry", ]   [[package]] @@ -7511,6 +7626,7 @@ "reth-ethereum-primitives", "reth-optimism-primitives", "reth-primitives-traits", "reth-prune-types", + "reth-scroll-primitives", "reth-stages-types", "reth-storage-errors", "reth-trie-common", @@ -7779,6 +7895,7 @@ "reth-payload-builder", "reth-payload-primitives", "reth-provider", "reth-transaction-pool", + "scroll-alloy-rpc-types-engine", "tokio", "tokio-stream", "tracing", @@ -8296,6 +8413,7 @@ "reth-storage-api", "reth-storage-errors", "reth-trie-common", "revm", + "scroll-alloy-evm", ]   [[package]] @@ -8629,6 +8747,7 @@ version = "1.4.8" dependencies = [ "alloy-primitives", "alloy-rpc-types-admin", + "async-trait", "auto_impl", "derive_more", "enr", @@ -8638,6 +8757,7 @@ "reth-ethereum-forks", "reth-network-p2p", "reth-network-peers", "reth-network-types", + "reth-primitives-traits", "reth-tokio-util", "serde", "thiserror 2.0.12", @@ -9427,6 +9547,7 @@ "reth-chain-state", "reth-chainspec", "reth-errors", "reth-primitives-traits", + "scroll-alloy-rpc-types-engine", "serde", "thiserror 2.0.12", "tokio", @@ -9501,6 +9622,7 @@ "reth-codecs", "revm-bytecode", "revm-primitives", "revm-state", + "scroll-alloy-consensus", "secp256k1", "serde", "serde_json", @@ -10019,12 +10141,319 @@ "op-alloy-consensus", "op-alloy-rpc-types", "reth-optimism-primitives", "reth-primitives-traits", + "reth-scroll-primitives", "reth-storage-api", + "scroll-alloy-consensus", + "scroll-alloy-rpc-types", "serde", "thiserror 2.0.12", ]   [[package]] +name = "reth-scroll-chainspec" +version = "1.4.8" +dependencies = [ + "alloy-chains", + "alloy-consensus", + "alloy-eips", + "alloy-genesis", + "alloy-primitives", + "alloy-serde", + "derive_more", + "once_cell", + "reth-chainspec", + "reth-ethereum-forks", + "reth-network-peers", + "reth-primitives-traits", + "reth-scroll-forks", + "reth-trie-common", + "scroll-alloy-hardforks", + "serde", + "serde_json", +] + +[[package]] +name = "reth-scroll-cli" +version = "1.4.8" +dependencies = [ + "clap", + "eyre", + "proptest", + "reth-cli", + "reth-cli-commands", + "reth-cli-runner", + "reth-consensus", + "reth-db", + "reth-node-builder", + "reth-node-core", + "reth-node-metrics", + "reth-scroll-chainspec", + "reth-scroll-evm", + "reth-scroll-node", + "reth-scroll-primitives", + "reth-tracing", + "scroll-alloy-consensus", + "tracing", +] + +[[package]] +name = "reth-scroll-consensus" +version = "1.4.8" +dependencies = [ + "alloy-consensus", + "alloy-primitives", + "reth-chainspec", + "reth-consensus", + "reth-consensus-common", + "reth-ethereum-consensus", + "reth-execution-types", + "reth-primitives-traits", + "reth-scroll-primitives", + "scroll-alloy-hardforks", + "thiserror 2.0.12", + "tracing", +] + +[[package]] +name = "reth-scroll-engine-primitives" +version = "1.4.8" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "alloy-rpc-types-engine", + "arbitrary", + "eyre", + "rand 0.9.1", + "reth-chain-state", + "reth-chainspec", + "reth-engine-primitives", + "reth-payload-builder", + "reth-payload-primitives", + "reth-primitives", + "reth-primitives-traits", + "reth-scroll-chainspec", + "reth-scroll-primitives", + "scroll-alloy-hardforks", + "scroll-alloy-rpc-types-engine", + "serde", + "sha2 0.10.9", +] + +[[package]] +name = "reth-scroll-evm" +version = "1.4.8" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-evm", + "alloy-primitives", + "derive_more", + "eyre", + "reth-chainspec", + "reth-evm", + "reth-execution-types", + "reth-primitives", + "reth-primitives-traits", + "reth-scroll-chainspec", + "reth-scroll-forks", + "reth-scroll-primitives", + "revm", + "revm-primitives", + "revm-scroll", + "scroll-alloy-consensus", + "scroll-alloy-evm", + "scroll-alloy-hardforks", + "thiserror 2.0.12", + "tracing", +] + +[[package]] +name = "reth-scroll-forks" +version = "1.4.8" +dependencies = [ + "alloy-chains", + "alloy-primitives", + "auto_impl", + "once_cell", + "reth-ethereum-forks", + "scroll-alloy-hardforks", + "serde", +] + +[[package]] +name = "reth-scroll-node" +version = "1.4.8" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-genesis", + "alloy-network", + "alloy-primitives", + "alloy-rpc-types-engine", + "eyre", + "futures", + "reth-basic-payload-builder", + "reth-chainspec", + "reth-db", + "reth-e2e-test-utils", + "reth-engine-local", + "reth-eth-wire-types", + "reth-evm", + "reth-network", + "reth-node-api", + "reth-node-builder", + "reth-node-core", + "reth-node-types", + "reth-payload-builder", + "reth-primitives", + "reth-primitives-traits", + "reth-provider", + "reth-revm", + "reth-rpc-eth-types", + "reth-rpc-server-types", + "reth-scroll-chainspec", + "reth-scroll-consensus", + "reth-scroll-engine-primitives", + "reth-scroll-evm", + "reth-scroll-node", + "reth-scroll-payload", + "reth-scroll-primitives", + "reth-scroll-rpc", + "reth-scroll-txpool", + "reth-tasks", + "reth-tracing", + "reth-transaction-pool", + "reth-trie-db", + "revm", + "scroll-alloy-consensus", + "scroll-alloy-evm", + "scroll-alloy-hardforks", + "scroll-alloy-rpc-types-engine", + "serde_json", + "thiserror 2.0.12", + "tokio", + "tracing", +] + +[[package]] +name = "reth-scroll-payload" +version = "1.4.8" +dependencies = [ + "alloy-consensus", + "alloy-primitives", + "alloy-rlp", + "futures-util", + "reth-basic-payload-builder", + "reth-chain-state", + "reth-chainspec", + "reth-evm", + "reth-execution-types", + "reth-payload-builder", + "reth-payload-primitives", + "reth-payload-util", + "reth-primitives-traits", + "reth-revm", + "reth-scroll-engine-primitives", + "reth-scroll-evm", + "reth-scroll-primitives", + "reth-storage-api", + "reth-transaction-pool", + "revm", + "scroll-alloy-evm", + "scroll-alloy-hardforks", + "thiserror 2.0.12", + "tracing", +] + +[[package]] +name = "reth-scroll-primitives" +version = "1.4.8" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "arbitrary", + "bytes", + "modular-bitfield", + "once_cell", + "rand 0.9.1", + "reth-codecs", + "reth-primitives-traits", + "reth-zstd-compressors", + "rstest", + "scroll-alloy-consensus", + "serde", +] + +[[package]] +name = "reth-scroll-rpc" +version = "1.4.8" +dependencies = [ + "alloy-consensus", + "alloy-primitives", + "alloy-rpc-types-eth", + "eyre", + "jsonrpsee-types", + "parking_lot", + "reth-chainspec", + "reth-evm", + "reth-network-api", + "reth-node-api", + "reth-node-builder", + "reth-primitives", + "reth-primitives-traits", + "reth-provider", + "reth-rpc", + "reth-rpc-eth-api", + "reth-rpc-eth-types", + "reth-scroll-chainspec", + "reth-scroll-evm", + "reth-scroll-primitives", + "reth-tasks", + "reth-transaction-pool", + "revm", + "scroll-alloy-consensus", + "scroll-alloy-evm", + "scroll-alloy-hardforks", + "scroll-alloy-network", + "scroll-alloy-rpc-types", + "thiserror 2.0.12", + "tokio", +] + +[[package]] +name = "reth-scroll-txpool" +version = "1.4.8" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rpc-types-eth", + "c-kzg", + "derive_more", + "futures-util", + "metrics", + "parking_lot", + "reth-chain-state", + "reth-chainspec", + "reth-metrics", + "reth-primitives-traits", + "reth-provider", + "reth-revm", + "reth-scroll-chainspec", + "reth-scroll-evm", + "reth-scroll-forks", + "reth-scroll-primitives", + "reth-storage-api", + "reth-transaction-pool", + "revm-scroll", + "scroll-alloy-consensus", +] + +[[package]] name = "reth-stages" version = "1.4.8" dependencies = [ @@ -10504,9 +10933,8 @@ ]   [[package]] name = "revm" -version = "24.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d277408ff8d6f747665ad9e52150ab4caf8d5eaf0d787614cf84633c8337b4" +version = "24.0.0" +source = "git+https://github.com/scroll-tech/revm?branch=feat%2Freth-v74#774616019e9562b12cbe1c3f1cdd110793f8084c" dependencies = [ "revm-bytecode", "revm-context", @@ -10524,8 +10952,7 @@ [[package]] name = "revm-bytecode" version = "4.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d91f9b90b3bab18942252de2d970ee8559794c49ca7452b2cc1774456040f8fb" +source = "git+https://github.com/scroll-tech/revm?branch=feat%2Freth-v74#774616019e9562b12cbe1c3f1cdd110793f8084c" dependencies = [ "bitvec", "once_cell", @@ -10536,9 +10963,8 @@ ]   [[package]] name = "revm-context" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b01aad49e1233f94cebda48a4e5cef022f7c7ed29b4edf0d202b081af23435ef" +version = "5.0.0" +source = "git+https://github.com/scroll-tech/revm?branch=feat%2Freth-v74#774616019e9562b12cbe1c3f1cdd110793f8084c" dependencies = [ "cfg-if", "derive-where", @@ -10553,8 +10979,7 @@ [[package]] name = "revm-context-interface" version = "5.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b844f48a411e62c7dde0f757bf5cce49c85b86d6fc1d3b2722c07f2bec4c3ce" +source = "git+https://github.com/scroll-tech/revm?branch=feat%2Freth-v74#774616019e9562b12cbe1c3f1cdd110793f8084c" dependencies = [ "alloy-eip2930", "alloy-eip7702", @@ -10569,8 +10994,7 @@ [[package]] name = "revm-database" version = "4.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad3fbe34f6bb00a9c3155723b3718b9cb9f17066ba38f9eb101b678cd3626775" +source = "git+https://github.com/scroll-tech/revm?branch=feat%2Freth-v74#774616019e9562b12cbe1c3f1cdd110793f8084c" dependencies = [ "alloy-eips", "revm-bytecode", @@ -10583,8 +11007,7 @@ [[package]] name = "revm-database-interface" version = "4.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b8acd36784a6d95d5b9e1b7be3ce014f1e759abb59df1fa08396b30f71adc2a" +source = "git+https://github.com/scroll-tech/revm?branch=feat%2Freth-v74#774616019e9562b12cbe1c3f1cdd110793f8084c" dependencies = [ "auto_impl", "revm-primitives", @@ -10594,9 +11017,8 @@ ]   [[package]] name = "revm-handler" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "481e8c3290ff4fa1c066592fdfeb2b172edfd14d12e6cade6f6f5588cad9359a" +version = "5.0.0" +source = "git+https://github.com/scroll-tech/revm?branch=feat%2Freth-v74#774616019e9562b12cbe1c3f1cdd110793f8084c" dependencies = [ "auto_impl", "revm-bytecode", @@ -10612,9 +11034,8 @@ ]   [[package]] name = "revm-inspector" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc1167ef8937d8867888e63581d8ece729a72073d322119ef4627d813d99ecb" +version = "5.0.0" +source = "git+https://github.com/scroll-tech/revm?branch=feat%2Freth-v74#774616019e9562b12cbe1c3f1cdd110793f8084c" dependencies = [ "auto_impl", "revm-context", @@ -10650,8 +11071,7 @@ [[package]] name = "revm-interpreter" version = "20.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5ee65e57375c6639b0f50555e92a4f1b2434349dd32f52e2176f5c711171697" +source = "git+https://github.com/scroll-tech/revm?branch=feat%2Freth-v74#774616019e9562b12cbe1c3f1cdd110793f8084c" dependencies = [ "revm-bytecode", "revm-context-interface", @@ -10662,8 +11082,7 @@ [[package]] name = "revm-precompile" version = "21.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f9311e735123d8d53a02af2aa81877bba185be7c141be7f931bb3d2f3af449c" +source = "git+https://github.com/scroll-tech/revm?branch=feat%2Freth-v74#774616019e9562b12cbe1c3f1cdd110793f8084c" dependencies = [ "ark-bls12-381", "ark-bn254", @@ -10687,8 +11106,7 @@ [[package]] name = "revm-primitives" version = "19.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18ea2ea0134568ee1e14281ce52f60e2710d42be316888d464c53e37ff184fd8" +source = "git+https://github.com/scroll-tech/revm?branch=feat%2Freth-v74#774616019e9562b12cbe1c3f1cdd110793f8084c" dependencies = [ "alloy-primitives", "num_enum", @@ -10696,10 +11114,23 @@ "serde", ]   [[package]] +name = "revm-scroll" +version = "0.1.0" +source = "git+https://github.com/scroll-tech/scroll-revm?rev=6ccb897197d7ed319463df487f428fce0a77b47f#6ccb897197d7ed319463df487f428fce0a77b47f" +dependencies = [ + "auto_impl", + "enumn", + "once_cell", + "revm", + "revm-inspector", + "revm-primitives", + "serde", +] + +[[package]] name = "revm-state" version = "4.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0040c61c30319254b34507383ba33d85f92949933adf6525a2cede05d165e1fa" +source = "git+https://github.com/scroll-tech/revm?branch=feat%2Freth-v74#774616019e9562b12cbe1c3f1cdd110793f8084c" dependencies = [ "bitflags 2.9.1", "revm-bytecode", @@ -10971,7 +11402,16 @@ dependencies = [ "openssl-probe", "rustls-pki-types", "schannel", - "security-framework", + "security-framework 3.2.0", +] + +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", ]   [[package]] @@ -10990,7 +11430,7 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19787cda76408ec5404443dc8b31795c87cd8fec49762dc75fa727740d34acc1" dependencies = [ - "core-foundation", + "core-foundation 0.10.0", "core-foundation-sys", "jni", "log", @@ -10999,7 +11439,7 @@ "rustls", "rustls-native-certs", "rustls-platform-verifier-android", "rustls-webpki", - "security-framework", + "security-framework 3.2.0", "security-framework-sys", "webpki-root-certs 0.26.11", "windows-sys 0.59.0", @@ -11094,6 +11534,157 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"   [[package]] +name = "scroll-alloy-consensus" +version = "1.4.8" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "arbitrary", + "bincode 1.3.3", + "derive_more", + "modular-bitfield", + "proptest", + "proptest-arbitrary-interop", + "rand 0.9.1", + "reth-codecs", + "reth-codecs-derive", + "serde", + "serde_json", + "serde_with", + "test-fuzz", +] + +[[package]] +name = "scroll-alloy-evm" +version = "1.4.8" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-evm", + "alloy-hardforks", + "alloy-primitives", + "auto_impl", + "eyre", + "reth-evm", + "reth-scroll-chainspec", + "reth-scroll-evm", + "revm", + "revm-scroll", + "scroll-alloy-consensus", + "scroll-alloy-hardforks", + "serde", + "zstd", +] + +[[package]] +name = "scroll-alloy-hardforks" +version = "1.4.8" +dependencies = [ + "alloy-hardforks", + "auto_impl", + "serde", +] + +[[package]] +name = "scroll-alloy-network" +version = "1.4.8" +dependencies = [ + "alloy-consensus", + "alloy-network", + "alloy-primitives", + "alloy-provider", + "alloy-rpc-types-eth", + "alloy-signer", + "scroll-alloy-consensus", + "scroll-alloy-rpc-types", +] + +[[package]] +name = "scroll-alloy-provider" +version = "1.4.8" +dependencies = [ + "alloy-primitives", + "alloy-provider", + "alloy-rpc-client", + "alloy-rpc-types-engine", + "alloy-transport", + "alloy-transport-http", + "async-trait", + "derive_more", + "eyre", + "futures-util", + "http-body-util", + "jsonrpsee", + "reqwest", + "reth-engine-primitives", + "reth-payload-builder", + "reth-payload-primitives", + "reth-primitives", + "reth-primitives-traits", + "reth-provider", + "reth-rpc-api", + "reth-rpc-builder", + "reth-rpc-engine-api", + "reth-scroll-chainspec", + "reth-scroll-engine-primitives", + "reth-scroll-node", + "reth-scroll-payload", + "reth-tasks", + "reth-tracing", + "reth-transaction-pool", + "scroll-alloy-network", + "scroll-alloy-rpc-types-engine", + "thiserror 2.0.12", + "tokio", + "tower", +] + +[[package]] +name = "scroll-alloy-rpc-types" +version = "1.4.8" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-network-primitives", + "alloy-primitives", + "alloy-rpc-types-eth", + "alloy-serde", + "arbitrary", + "derive_more", + "scroll-alloy-consensus", + "serde", + "serde_json", + "similar-asserts", +] + +[[package]] +name = "scroll-alloy-rpc-types-engine" +version = "1.4.8" +dependencies = [ + "alloy-primitives", + "alloy-rpc-types-engine", + "arbitrary", + "serde", + "serde_json", +] + +[[package]] +name = "scroll-reth" +version = "1.4.8" +dependencies = [ + "clap", + "reth-cli-util", + "reth-engine-local", + "reth-provider", + "reth-scroll-cli", + "reth-scroll-node", + "tracing", +] + +[[package]] name = "sec1" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -11131,12 +11722,25 @@ ]   [[package]] name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags 2.9.1", + "core-foundation 0.9.4", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework" version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" dependencies = [ "bitflags 2.9.1", - "core-foundation", + "core-foundation 0.10.0", "core-foundation-sys", "libc", "security-framework-sys", @@ -11530,9 +12134,9 @@ ]   [[package]] name = "socket2" -version = "0.5.10" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" dependencies = [ "libc", "windows-sys 0.52.0", @@ -12052,6 +12656,16 @@ "syn 2.0.101", ]   [[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] name = "tokio-rustls" version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -12380,9 +12994,9 @@ ]   [[package]] name = "tracy-client" -version = "0.18.1" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3927832d93178f979a970d26deed7b03510586e328f31b0f9ad7a73985b8332a" +checksum = "d90a2c01305b02b76fdd89ac8608bae27e173c829a35f7d76a345ab5d33836db" dependencies = [ "loom", "once_cell", @@ -12392,12 +13006,12 @@ ]   [[package]] name = "tracy-client-sys" -version = "0.25.0" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c032d68a49d25d9012a864fef1c64ac17aee43c87e0477bf7301d8ae8bfea7b7" +checksum = "69fff37da548239c3bf9e64a12193d261e8b22b660991c6fd2df057c168f435f" dependencies = [ "cc", - "windows-targets 0.52.6", + "windows-targets 0.48.5", ]   [[package]] @@ -12943,7 +13557,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.48.0", ]   [[package]] @@ -13126,6 +13740,17 @@ "windows-link", ]   [[package]] +name = "windows-registry" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" +dependencies = [ + "windows-result 0.3.4", + "windows-strings 0.3.1", + "windows-targets 0.53.0", +] + +[[package]] name = "windows-result" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -13160,6 +13785,15 @@ checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" dependencies = [ "windows-result 0.2.0", "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" +dependencies = [ + "windows-link", ]   [[package]]
diff --git reth/Cargo.toml scroll-reth/Cargo.toml index 1f733f392be2ee38a3ec74d44a5b799cf3563238..d9bea7dcd781b78910d7645848f4bf0d75dd58c8 100644 --- reth/Cargo.toml +++ scroll-reth/Cargo.toml @@ -106,6 +106,25 @@ "crates/rpc/rpc-server-types/", "crates/rpc/rpc-testing-util/", "crates/rpc/rpc-types-compat/", "crates/rpc/rpc/", + "crates/scroll/alloy/consensus", + "crates/scroll/alloy/evm", + "crates/scroll/alloy/hardforks", + "crates/scroll/alloy/network", + "crates/scroll/alloy/provider", + "crates/scroll/alloy/rpc-types", + "crates/scroll/alloy/rpc-types-engine", + "crates/scroll/bin/scroll-reth", + "crates/scroll/chainspec", + "crates/scroll/cli", + "crates/scroll/consensus", + "crates/scroll/engine-primitives", + "crates/scroll/evm", + "crates/scroll/hardforks", + "crates/scroll/node", + "crates/scroll/payload", + "crates/scroll/primitives", + "crates/scroll/txpool", + "crates/scroll/rpc", "crates/stages/api/", "crates/stages/stages/", "crates/stages/types/", @@ -436,7 +455,7 @@ reth-testing-utils = { path = "testing/testing-utils" } reth-tokio-util = { path = "crates/tokio-util" } reth-tracing = { path = "crates/tracing" } reth-transaction-pool = { path = "crates/transaction-pool" } -reth-trie = { path = "crates/trie/trie" } +reth-trie = { path = "crates/trie/trie", default-features = false } reth-trie-common = { path = "crates/trie/common", default-features = false } reth-trie-db = { path = "crates/trie/db" } reth-trie-parallel = { path = "crates/trie/parallel" } @@ -446,17 +465,18 @@ reth-ress-protocol = { path = "crates/ress/protocol" } reth-ress-provider = { path = "crates/ress/provider" }   # revm -revm = { version = "24.0.1", default-features = false } -revm-bytecode = { version = "4.0.0", default-features = false } -revm-database = { version = "4.0.0", default-features = false } -revm-state = { version = "4.0.0", default-features = false } -revm-primitives = { version = "19.0.0", default-features = false } -revm-interpreter = { version = "20.0.0", default-features = false } -revm-inspector = { version = "5.0.0", default-features = false } -revm-context = { version = "5.0.0", default-features = false } -revm-context-interface = { version = "5.0.0", default-features = false } -revm-database-interface = { version = "4.0.0", default-features = false } -op-revm = { version = "5.0.0", default-features = false } +revm = { git = "https://github.com/scroll-tech/revm", branch = "feat/reth-v74", default-features = false, features = ["secp256r1", "enable_eip7702"] } +revm-bytecode = { git = "https://github.com/scroll-tech/revm", branch = "feat/reth-v74", default-features = false } +revm-database = { git = "https://github.com/scroll-tech/revm", branch = "feat/reth-v74", default-features = false } +revm-state = { git = "https://github.com/scroll-tech/revm", branch = "feat/reth-v74", default-features = false } +revm-primitives = { git = "https://github.com/scroll-tech/revm", branch = "feat/reth-v74", default-features = false } +revm-interpreter = { git = "https://github.com/scroll-tech/revm", branch = "feat/reth-v74", default-features = false } +revm-inspector = { git = "https://github.com/scroll-tech/revm", branch = "feat/reth-v74", default-features = false } +revm-context = { git = "https://github.com/scroll-tech/revm", branch = "feat/reth-v74", default-features = false } +revm-context-interface = { git = "https://github.com/scroll-tech/revm", branch = "feat/reth-v74", default-features = false } +revm-database-interface = { git = "https://github.com/scroll-tech/revm", branch = "feat/reth-v74", default-features = false } +op-revm = { git = "https://github.com/scroll-tech/revm", branch = "feat/reth-v74", default-features = false } +revm-scroll = { git = "https://github.com/scroll-tech/scroll-revm", rev = "6ccb897197d7ed319463df487f428fce0a77b47f", default-features = false } revm-inspectors = "0.23.0"   # eth @@ -475,7 +495,7 @@ alloy-consensus = { version = "1.0.9", default-features = false } alloy-contract = { version = "1.0.9", default-features = false } alloy-eips = { version = "1.0.9", default-features = false } -alloy-genesis = { version = "1.0.9", default-features = false } +alloy-genesis = { version = "1.0.9, <1.0.13", default-features = false } alloy-json-rpc = { version = "1.0.9", default-features = false } alloy-network = { version = "1.0.9", default-features = false } alloy-network-primitives = { version = "1.0.9", default-features = false } @@ -500,6 +520,29 @@ alloy-transport-http = { version = "1.0.9", features = ["reqwest-rustls-tls"], default-features = false } alloy-transport-ipc = { version = "1.0.9", default-features = false } alloy-transport-ws = { version = "1.0.9", default-features = false }   +# scroll +scroll-alloy-consensus = { path = "crates/scroll/alloy/consensus", default-features = false } +scroll-alloy-evm = { path = "crates/scroll/alloy/evm" } +scroll-alloy-hardforks = { path = "crates/scroll/alloy/hardforks", default-features = false } +scroll-alloy-network = { path = "crates/scroll/alloy/network", default-features = false } +scroll-alloy-rpc-types = { path = "crates/scroll/alloy/rpc-types", default-features = false } +scroll-alloy-rpc-types-engine = { path = "crates/scroll/alloy/rpc-types-engine", default-features = false } +scroll-alloy-provider = { path = "crates/scroll/alloy/provider" } +reth-scroll-chainspec = { path = "crates/scroll/chainspec", default-features = false } +reth-scroll-cli = { path = "crates/scroll/cli" } +reth-scroll-consensus = { path = "crates/scroll/consensus" } +reth-scroll-evm = { path = "crates/scroll/evm", default-features = false } +reth-scroll-engine-primitives = { path = "crates/scroll/engine-primitives" } +reth-scroll-forks = { path = "crates/scroll/hardforks", default-features = false } +reth-scroll-node = { path = "crates/scroll/node" } +reth-scroll-payload = { path = "crates/scroll/payload" } +reth-scroll-primitives = { path = "crates/scroll/primitives", default-features = false } +reth-scroll-rpc = { path = "crates/scroll/rpc" } +reth-scroll-trie = { path = "crates/scroll/trie" } +reth-scroll-txpool = { path = "crates/scroll/txpool" } +# TODO (scroll): point to crates.io/tag once the crate is published/a tag is created. +poseidon-bn254 = { git = "https://github.com/scroll-tech/poseidon-bn254", rev = "526a64a", features = ["bn254"] } + # op alloy-op-evm = { version = "0.10.0", default-features = false } alloy-op-hardforks = "0.2.2" @@ -512,7 +555,7 @@ op-alloy-flz = { version = "0.13.1", default-features = false }   # misc aquamarine = "0.6" -auto_impl = "1" +auto_impl = { version = "1", default-features = false } backon = { version = "1.2", default-features = false, features = ["std-blocking-sleep", "tokio-sleep"] } bincode = "1.3" bitflags = "2.4" @@ -702,36 +745,35 @@ walkdir = "2.3.3" vergen-git2 = "1.0.5"   [patch.crates-io] -# alloy-consensus = { git = "https://github.com/alloy-rs/alloy", branch = "main" } -# alloy-contract = { git = "https://github.com/alloy-rs/alloy", branch = "main" } -# alloy-eips = { git = "https://github.com/alloy-rs/alloy", branch = "main" } -# alloy-genesis = { git = "https://github.com/alloy-rs/alloy", branch = "main" } -# alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy", branch = "main" } -# alloy-network = { git = "https://github.com/alloy-rs/alloy", branch = "main" } -# alloy-network-primitives = { git = "https://github.com/alloy-rs/alloy", branch = "main" } -# alloy-provider = { git = "https://github.com/alloy-rs/alloy", branch = "main" } -# alloy-pubsub = { git = "https://github.com/alloy-rs/alloy", branch = "main" } -# alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy", branch = "main" } -# alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", branch = "main" } -# alloy-rpc-types-admin = { git = "https://github.com/alloy-rs/alloy", branch = "main" } -# alloy-rpc-types-anvil = { git = "https://github.com/alloy-rs/alloy", branch = "main" } -# alloy-rpc-types-beacon = { git = "https://github.com/alloy-rs/alloy", branch = "main" } -# alloy-rpc-types-debug = { git = "https://github.com/alloy-rs/alloy", branch = "main" } -# alloy-rpc-types-engine = { git = "https://github.com/alloy-rs/alloy", branch = "main" } -# alloy-rpc-types-eth = { git = "https://github.com/alloy-rs/alloy", branch = "main" } -# alloy-rpc-types-mev = { git = "https://github.com/alloy-rs/alloy", branch = "main" } -# alloy-rpc-types-trace = { git = "https://github.com/alloy-rs/alloy", branch = "main" } -# alloy-rpc-types-txpool = { git = "https://github.com/alloy-rs/alloy", branch = "main" } -# alloy-serde = { git = "https://github.com/alloy-rs/alloy", branch = "main" } -# alloy-signer = { git = "https://github.com/alloy-rs/alloy", branch = "main" } -# alloy-signer-local = { git = "https://github.com/alloy-rs/alloy", branch = "main" } -# alloy-transport = { git = "https://github.com/alloy-rs/alloy", branch = "main" } -# alloy-transport-http = { git = "https://github.com/alloy-rs/alloy", branch = "main" } -# alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy", branch = "main" } -# alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy", branch = "main" } - -# alloy-evm = { git = "https://github.com/alloy-rs/evm", branch = "main" } -# alloy-op-evm = { git = "https://github.com/alloy-rs/evm", branch = "main" } +revm = { git = "https://github.com/scroll-tech/revm", branch = "feat/reth-v74" } +op-revm = { git = "https://github.com/scroll-tech/revm", branch = "feat/reth-v74" } +# alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" } +# alloy-contract = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" } +# alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" } +# alloy-genesis = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" } +# alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" } +# alloy-network = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" } +# alloy-network-primitives = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" } +# alloy-provider = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" } +# alloy-pubsub = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" } +# alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" } +# alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" } +# alloy-rpc-types-admin = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" } +# alloy-rpc-types-anvil = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" } +# alloy-rpc-types-beacon = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" } +# alloy-rpc-types-debug = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" } +# alloy-rpc-types-engine = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" } +# alloy-rpc-types-eth = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" } +# alloy-rpc-types-mev = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" } +# alloy-rpc-types-trace = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" } +# alloy-rpc-types-txpool = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" } +# alloy-serde = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" } +# alloy-signer = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" } +# alloy-signer-local = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" } +# alloy-transport = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" } +# alloy-transport-http = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" } +# alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" } +# alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" } # # op-alloy-consensus = { git = "https://github.com/alloy-rs/op-alloy", rev = "ad607c1" } # op-alloy-network = { git = "https://github.com/alloy-rs/op-alloy", rev = "ad607c1" }
diff --git reth/Cross.toml scroll-reth/Cross.toml index 9b4fd44f75248c31f2591afc76289148786111ef..eff8da32fdef48acc4e92fd81e4c78460f0285b4 100644 --- reth/Cross.toml +++ scroll-reth/Cross.toml @@ -34,5 +34,14 @@ env.passthrough = [ "CARGO_TARGET_RISCV64GC_UNKNOWN_LINUX_GNU_LINKER=riscv64-linux-gnu-gcc", ]   +[target.x86_64-pc-windows-gnu] +# Why do we need a custom Dockerfile on Windows: +# 1. `reth-libmdbx` stopped working with MinGW 9.3 that cross image comes with. +# 2. To be able to update the version of MinGW, we need to also update the Ubuntu that the image is based on. +# +# Also see https://github.com/cross-rs/cross/issues/1667 +# Inspired by https://github.com/cross-rs/cross/blob/9e2298e17170655342d3248a9c8ac37ef92ba38f/docker/Dockerfile.x86_64-pc-windows-gnu#L51 +dockerfile = "./Dockerfile.x86_64-pc-windows-gnu" + [build.env] passthrough = ["JEMALLOC_SYS_WITH_LG_PAGE"]
diff --git reth/Makefile scroll-reth/Makefile index c9c09a6171f591f8c6cb4d6cd506cd8d13cf1319..de37ed49fc574cc166c7447693b34d38ad492ad7 100644 --- reth/Makefile +++ scroll-reth/Makefile @@ -55,6 +55,13 @@ --features "$(FEATURES)" \ --profile "$(PROFILE)" \ $(CARGO_INSTALL_EXTRA_FLAGS)   +.PHONY: install-scroll +install-scroll: ## Build and install the scroll-reth binary under `~/.cargo/bin`. + cargo install --path crates/scroll/bin/scroll-reth --bin scroll-reth --force --locked \ + --features "skip-state-root-validation" \ + --profile "$(PROFILE)" \ + $(CARGO_INSTALL_EXTRA_FLAGS) + .PHONY: build build: ## Build the reth binary into `target` directory. cargo build --bin reth --features "$(FEATURES)" --profile "$(PROFILE)" @@ -414,6 +421,37 @@ --tests \ --benches \ --locked \ --all-features + +lint-scroll-reth: + cargo +nightly clippy \ + --workspace \ + --bin "scroll-reth" \ + --lib \ + --examples \ + --tests \ + --benches \ + --features "$(BIN_OTHER_FEATURES) skip-state-root-validation" \ + -- -D warnings + +lint-all: + cargo +nightly clippy \ + --workspace \ + --lib \ + --examples \ + --tests \ + --benches \ + --all-features \ + --locked + +lint-udeps: + cargo +nightly udeps --workspace --lib --examples --tests --benches --all-features --locked \ + --exclude reth-optimism-cli --exclude reth-optimism-consensus --exclude reth-optimism-payload-builder \ + --exclude reth-optimism-node --exclude reth-optimism-evm --exclude reth-optimism-node --exclude reth-optimism-rpc \ + --exclude op-reth --exclude "example-*" --exclude reth --exclude reth-payload-primitives \ + --exclude reth-e2e-test-utils --exclude reth-ethereum-payload-builder --exclude reth-exex-test-utils \ + --exclude reth-node-ethereum --exclude reth-scroll-cli --exclude reth-scroll-evm \ + --exclude reth-scroll-node --exclude "scroll-reth" --exclude reth-scroll-rpc \ + --exclude reth-scroll-trie   lint-codespell: ensure-codespell codespell --skip "*.json" --skip "./testing/ef-tests/ethereum-tests"
diff --git reth/book/cli/reth/node.md scroll-reth/book/cli/reth/node.md index 6dcd98c684b3e1fa655f21f8f45c6688e730b002..f97980b34c6a39aca215be16517432f50b65b1ad 100644 --- reth/book/cli/reth/node.md +++ scroll-reth/book/cli/reth/node.md @@ -565,10 +565,10 @@ Interval is specified in seconds or in milliseconds if the value ends with `ms`: * `50ms` -> 50 milliseconds * `1` -> 1 second   [default: 1]   - --builder.deadline <SECONDS> + --builder.deadline <DEADLINE> The deadline for when the payload builder job should resolve   - [default: 12] + [default: 12s]   --builder.max-tasks <MAX_PAYLOAD_TASKS> Maximum number of tasks to spawn for building a payload
diff --git reth/book/sources/Cargo.toml scroll-reth/book/sources/Cargo.toml index b374ad798b5d7d3c186d511ec535c655f4f1f980..c98694dc5c3b9fa79931d08ecee0f4592b3d830f 100644 --- reth/book/sources/Cargo.toml +++ scroll-reth/book/sources/Cargo.toml @@ -11,3 +11,6 @@ reth-exex = { path = "../../crates/exex/exex" } reth-node-ethereum = { path = "../../crates/ethereum/node" } reth-tracing = { path = "../../crates/tracing" } reth-node-api = { path = "../../crates/node/api" } + +[patch.crates-io] +revm = { git = "https://github.com/scroll-tech/revm", branch = "feat/reth-v74" }
diff --git reth/crates/chainspec/src/api.rs scroll-reth/crates/chainspec/src/api.rs index 79cf223358236a818b2c0d4b2471cb6eab7eaee9..d1c7b3e5a3a70d8185861babe4fd8935be99d033 100644 --- reth/crates/chainspec/src/api.rs +++ scroll-reth/crates/chainspec/src/api.rs @@ -132,3 +132,13 @@ fn final_paris_total_difficulty(&self) -> Option<U256> { self.paris_block_and_final_difficulty.map(|(_, final_difficulty)| final_difficulty) } } + +/// Trait representing the current capacities of the fork. +pub trait EthereumCapabilities: EthereumHardforks { + /// Returns true if the withdrawals are active. + fn withdrawals_active(&self, ts: u64) -> bool { + self.is_shanghai_active_at_timestamp(ts) + } +} + +impl EthereumCapabilities for ChainSpec {}
diff --git reth/crates/chainspec/src/lib.rs scroll-reth/crates/chainspec/src/lib.rs index d140bf88bee0fc6248090ee10ecd73e84ff7bf28..3c59008496c3c550583219e03db3705cf82a0fcf 100644 --- reth/crates/chainspec/src/lib.rs +++ scroll-reth/crates/chainspec/src/lib.rs @@ -25,7 +25,7 @@ pub use alloy_chains::{Chain, ChainKind, NamedChain}; /// Re-export for convenience pub use reth_ethereum_forks::*;   -pub use api::EthChainSpec; +pub use api::{EthChainSpec, EthereumCapabilities}; pub use info::ChainInfo; #[cfg(any(test, feature = "test-utils"))] pub use spec::test_fork_ids;
diff --git reth/crates/chainspec/src/spec.rs scroll-reth/crates/chainspec/src/spec.rs index b3161700b6e911103a83c288a661c8414112e92a..151eb08446340d81d66c715be719def77d45a77d 100644 --- reth/crates/chainspec/src/spec.rs +++ scroll-reth/crates/chainspec/src/spec.rs @@ -27,8 +27,8 @@ ChainHardforks, DisplayHardforks, EthereumHardfork, EthereumHardforks, ForkCondition, ForkFilter, ForkFilterKey, ForkHash, ForkId, Hardfork, Hardforks, Head, DEV_HARDFORKS, }; use reth_network_peers::{ - holesky_nodes, hoodi_nodes, mainnet_nodes, op_nodes, op_testnet_nodes, sepolia_nodes, - NodeRecord, + holesky_nodes, hoodi_nodes, mainnet_nodes, op_nodes, op_testnet_nodes, scroll_nodes, + scroll_sepolia_nodes, sepolia_nodes, NodeRecord, }; use reth_primitives_traits::{sync::LazyLock, SealedHeader};   @@ -623,9 +623,11 @@ C::Holesky => Some(holesky_nodes()), C::Hoodi => Some(hoodi_nodes()), // opstack uses the same bootnodes for all chains: <https://github.com/paradigmxyz/reth/issues/14603> C::Base | C::Optimism | C::Unichain | C::World => Some(op_nodes()), + C::Scroll => Some(scroll_nodes()), C::OptimismSepolia | C::BaseSepolia | C::UnichainSepolia | C::WorldSepolia => { Some(op_testnet_nodes()) } + C::ScrollSepolia => Some(scroll_sepolia_nodes()),   // fallback for optimism chains chain if chain.is_optimism() && chain.is_testnet() => Some(op_testnet_nodes()),
diff --git reth/crates/cli/commands/src/node.rs scroll-reth/crates/cli/commands/src/node.rs index 3c7b98cbe1cebcd803aedee749a8606042b623dc..7a3ac269da68a4e44b7ef530e15098b7777e70b9 100644 --- reth/crates/cli/commands/src/node.rs +++ scroll-reth/crates/cli/commands/src/node.rs @@ -236,18 +236,29 @@ assert_eq!(err.kind(), clap::error::ErrorKind::DisplayHelp); }   #[test] - fn parse_common_node_command_chain_args() { + fn parse_rommon_node_command_chain_args() { for chain in SUPPORTED_CHAINS { - let args: NodeCommand<EthereumChainSpecParser> = - NodeCommand::parse_from(["reth", "--chain", chain]); + let args: NodeCommand<EthereumChainSpecParser> = NodeCommand::parse_from([ + "reth", + "--chain", + chain, + "--builder.gaslimit", + "10000000", + ]); assert_eq!(args.chain.chain, chain.parse::<reth_chainspec::Chain>().unwrap()); } }   #[test] fn parse_discovery_addr() { - let cmd: NodeCommand<EthereumChainSpecParser> = - NodeCommand::try_parse_args_from(["reth", "--discovery.addr", "127.0.0.1"]).unwrap(); + let cmd: NodeCommand<EthereumChainSpecParser> = NodeCommand::try_parse_args_from([ + "reth", + "--discovery.addr", + "127.0.0.1", + "--builder.gaslimit", + "10000000", + ]) + .unwrap(); assert_eq!(cmd.network.discovery.addr, IpAddr::V4(Ipv4Addr::LOCALHOST)); }   @@ -259,6 +270,8 @@ "--discovery.addr", "127.0.0.1", "--addr", "127.0.0.1", + "--builder.gaslimit", + "10000000", ]) .unwrap(); assert_eq!(cmd.network.discovery.addr, IpAddr::V4(Ipv4Addr::LOCALHOST)); @@ -267,46 +280,83 @@ }   #[test] fn parse_discovery_port() { - let cmd: NodeCommand<EthereumChainSpecParser> = - NodeCommand::try_parse_args_from(["reth", "--discovery.port", "300"]).unwrap(); + let cmd: NodeCommand<EthereumChainSpecParser> = NodeCommand::try_parse_args_from([ + "reth", + "--discovery.port", + "300", + "--builder.gaslimit", + "10000000", + ]) + .unwrap(); assert_eq!(cmd.network.discovery.port, 300); }   #[test] fn parse_port() { - let cmd: NodeCommand<EthereumChainSpecParser> = - NodeCommand::try_parse_args_from(["reth", "--discovery.port", "300", "--port", "99"]) - .unwrap(); + let cmd: NodeCommand<EthereumChainSpecParser> = NodeCommand::try_parse_args_from([ + "reth", + "--discovery.port", + "300", + "--port", + "99", + "--builder.gaslimit", + "10000000", + ]) + .unwrap(); assert_eq!(cmd.network.discovery.port, 300); assert_eq!(cmd.network.port, 99); }   #[test] fn parse_metrics_port() { - let cmd: NodeCommand<EthereumChainSpecParser> = - NodeCommand::try_parse_args_from(["reth", "--metrics", "9001"]).unwrap(); + let cmd: NodeCommand<EthereumChainSpecParser> = NodeCommand::try_parse_args_from([ + "reth", + "--metrics", + "9001", + "--builder.gaslimit", + "10000000", + ]) + .unwrap(); assert_eq!(cmd.metrics, Some(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 9001)));   - let cmd: NodeCommand<EthereumChainSpecParser> = - NodeCommand::try_parse_args_from(["reth", "--metrics", ":9001"]).unwrap(); + let cmd: NodeCommand<EthereumChainSpecParser> = NodeCommand::try_parse_args_from([ + "reth", + "--metrics", + ":9001", + "--builder.gaslimit", + "10000000", + ]) + .unwrap(); assert_eq!(cmd.metrics, Some(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 9001)));   - let cmd: NodeCommand<EthereumChainSpecParser> = - NodeCommand::try_parse_args_from(["reth", "--metrics", "localhost:9001"]).unwrap(); + let cmd: NodeCommand<EthereumChainSpecParser> = NodeCommand::try_parse_args_from([ + "reth", + "--metrics", + "localhost:9001", + "--builder.gaslimit", + "10000000", + ]) + .unwrap(); assert_eq!(cmd.metrics, Some(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 9001))); }   #[test] fn parse_config_path() { - let cmd: NodeCommand<EthereumChainSpecParser> = - NodeCommand::try_parse_args_from(["reth", "--config", "my/path/to/reth.toml"]).unwrap(); + let cmd: NodeCommand<EthereumChainSpecParser> = NodeCommand::try_parse_args_from([ + "reth", + "--config", + "my/path/to/reth.toml", + "--builder.gaslimit", + "10000000", + ]) + .unwrap(); // always store reth.toml in the data dir, not the chain specific data dir let data_dir = cmd.datadir.resolve_datadir(cmd.chain.chain); let config_path = cmd.config.unwrap_or_else(|| data_dir.config()); assert_eq!(config_path, Path::new("my/path/to/reth.toml"));   let cmd: NodeCommand<EthereumChainSpecParser> = - NodeCommand::try_parse_args_from(["reth"]).unwrap(); + NodeCommand::try_parse_args_from(["reth", "--builder.gaslimit", "10000000"]).unwrap();   // always store reth.toml in the data dir, not the chain specific data dir let data_dir = cmd.datadir.resolve_datadir(cmd.chain.chain); @@ -318,15 +368,21 @@ #[test] fn parse_db_path() { let cmd: NodeCommand<EthereumChainSpecParser> = - NodeCommand::try_parse_args_from(["reth"]).unwrap(); + NodeCommand::try_parse_args_from(["reth", "--builder.gaslimit", "10000000"]).unwrap(); let data_dir = cmd.datadir.resolve_datadir(cmd.chain.chain);   let db_path = data_dir.db(); let end = format!("reth/{}/db", SUPPORTED_CHAINS[0]); assert!(db_path.ends_with(end), "{:?}", cmd.config);   - let cmd: NodeCommand<EthereumChainSpecParser> = - NodeCommand::try_parse_args_from(["reth", "--datadir", "my/custom/path"]).unwrap(); + let cmd: NodeCommand<EthereumChainSpecParser> = NodeCommand::try_parse_args_from([ + "reth", + "--datadir", + "my/custom/path", + "--builder.gaslimit", + "10000000", + ]) + .unwrap(); let data_dir = cmd.datadir.resolve_datadir(cmd.chain.chain);   let db_path = data_dir.db(); @@ -335,7 +391,8 @@ }   #[test] fn parse_instance() { - let mut cmd: NodeCommand<EthereumChainSpecParser> = NodeCommand::parse_from(["reth"]); + let mut cmd: NodeCommand<EthereumChainSpecParser> = + NodeCommand::parse_from(["reth", "--builder.gaslimit", "10000000"]); cmd.rpc.adjust_instance_ports(cmd.instance); cmd.network.port = DEFAULT_DISCOVERY_PORT; // check rpc port numbers @@ -346,7 +403,7 @@ // check network listening port number assert_eq!(cmd.network.port, 30303);   let mut cmd: NodeCommand<EthereumChainSpecParser> = - NodeCommand::parse_from(["reth", "--instance", "2"]); + NodeCommand::parse_from(["reth", "--instance", "2", "--builder.gaslimit", "10000000"]); cmd.rpc.adjust_instance_ports(cmd.instance); cmd.network.port = DEFAULT_DISCOVERY_PORT + 2 - 1; // check rpc port numbers @@ -357,7 +414,7 @@ // check network listening port number assert_eq!(cmd.network.port, 30304);   let mut cmd: NodeCommand<EthereumChainSpecParser> = - NodeCommand::parse_from(["reth", "--instance", "3"]); + NodeCommand::parse_from(["reth", "--instance", "3", "--builder.gaslimit", "10000000"]); cmd.rpc.adjust_instance_ports(cmd.instance); cmd.network.port = DEFAULT_DISCOVERY_PORT + 3 - 1; // check rpc port numbers @@ -370,8 +427,12 @@ }   #[test] fn parse_with_unused_ports() { - let cmd: NodeCommand<EthereumChainSpecParser> = - NodeCommand::parse_from(["reth", "--with-unused-ports"]); + let cmd: NodeCommand<EthereumChainSpecParser> = NodeCommand::parse_from([ + "reth", + "--with-unused-ports", + "--builder.gaslimit", + "10000000", + ]); assert!(cmd.with_unused_ports); }   @@ -382,6 +443,8 @@ "reth", "--with-unused-ports", "--instance", "2", + "--builder.gaslimit", + "10000000", ]) .unwrap_err(); assert_eq!(err.kind(), clap::error::ErrorKind::ArgumentConflict); @@ -389,7 +452,8 @@ }   #[test] fn with_unused_ports_check_zero() { - let mut cmd: NodeCommand<EthereumChainSpecParser> = NodeCommand::parse_from(["reth"]); + let mut cmd: NodeCommand<EthereumChainSpecParser> = + NodeCommand::parse_from(["reth", "--builder.gaslimit", "10000000"]); cmd.rpc = cmd.rpc.with_unused_ports(); cmd.network = cmd.network.with_unused_ports();
diff --git reth/crates/cli/commands/src/stage/dump/merkle.rs scroll-reth/crates/cli/commands/src/stage/dump/merkle.rs index 904d43dbade0cde0f03b97680db46bfc78f84f1a..8e82c3e1403b66acd465bfadfca130d081fc042d 100644 --- reth/crates/cli/commands/src/stage/dump/merkle.rs +++ scroll-reth/crates/cli/commands/src/stage/dump/merkle.rs @@ -4,7 +4,7 @@ use super::setup; use alloy_primitives::BlockNumber; use eyre::Result; use reth_config::config::EtlConfig; -use reth_consensus::{ConsensusError, FullConsensus}; +use reth_consensus::{noop::NoopConsensus, ConsensusError, FullConsensus}; use reth_db::DatabaseEnv; use reth_db_api::{database::Database, table::TableImporter, tables}; use reth_db_common::DbTool; @@ -96,7 +96,7 @@ StorageHashingStage::default().unwind(&provider, unwind).unwrap(); AccountHashingStage::default().unwind(&provider, unwind).unwrap();   - MerkleStage::default_unwind().unwind(&provider, unwind)?; + MerkleStage::<N::Primitives>::new_unwind(NoopConsensus::arc()).unwind(&provider, unwind)?;   // Bring Plainstate to TO (hashing stage execution requires it) let mut exec_stage = ExecutionStage::new( @@ -159,9 +159,10 @@ { info!(target: "reth::cli", "Executing stage."); let provider = output_provider_factory.database_provider_rw()?;   - let mut stage = MerkleStage::Execution { + let mut stage = MerkleStage::<N::Primitives>::Execution { // Forces updating the root instead of calculating from scratch clean_threshold: u64::MAX, + consensus: NoopConsensus::arc(), };   loop {
diff --git reth/crates/cli/commands/src/stage/run.rs scroll-reth/crates/cli/commands/src/stage/run.rs index e21f3996edc8afc4673e916a23990d84dfd2c02b..f30d452b5838a2969686cc48a21058a567036553 100644 --- reth/crates/cli/commands/src/stage/run.rs +++ scroll-reth/crates/cli/commands/src/stage/run.rs @@ -298,10 +298,16 @@ etl_config, )), None, ), - StageEnum::Merkle => ( - Box::new(MerkleStage::new_execution(config.stages.merkle.clean_threshold)), - Some(Box::new(MerkleStage::default_unwind())), - ), + StageEnum::Merkle => { + let consensus = Arc::new(components.consensus().clone()); + ( + Box::new(MerkleStage::<N::Primitives>::new_execution( + config.stages.merkle.clean_threshold, + consensus.clone(), + )), + Some(Box::new(MerkleStage::<N::Primitives>::new_unwind(consensus))), + ) + } StageEnum::AccountHistory => ( Box::new(IndexAccountHistoryStage::new( config.stages.index_account_history,
diff --git reth/crates/consensus/consensus/src/lib.rs scroll-reth/crates/consensus/consensus/src/lib.rs index 93babfe3a14635698fd877fb080052f52d91a88c..951c2c0ef5566c0047cf0b0740447888229a0b4b 100644 --- reth/crates/consensus/consensus/src/lib.rs +++ scroll-reth/crates/consensus/consensus/src/lib.rs @@ -18,7 +18,7 @@ use reth_execution_types::BlockExecutionResult; use reth_primitives_traits::{ constants::{MAXIMUM_GAS_LIMIT_BLOCK, MINIMUM_GAS_LIMIT}, transaction::error::InvalidTransactionError, - Block, GotExpected, GotExpectedBoxed, NodePrimitives, RecoveredBlock, SealedBlock, + Block, BlockHeader, GotExpected, GotExpectedBoxed, NodePrimitives, RecoveredBlock, SealedBlock, SealedHeader, };   @@ -73,7 +73,7 @@ }   /// `HeaderValidator` is a protocol that validates headers and their relationships. #[auto_impl::auto_impl(&, Arc)] -pub trait HeaderValidator<H = Header>: Debug + Send + Sync { +pub trait HeaderValidator<H: BlockHeader = Header>: Debug + Send + Sync { /// Validate if header is correct and follows consensus specification. /// /// This is called on standalone header to check if all hashes are correct. @@ -121,6 +121,22 @@ } } Ok(()) } + + /// Validate the block header against a provided expected state root. + fn validate_state_root(&self, header: &H, root: B256) -> Result<(), ConsensusError> { + validate_state_root(header, root) + } +} + +/// Validate the provided state root against the block's state root. +pub fn validate_state_root<H: BlockHeader>(header: &H, root: B256) -> Result<(), ConsensusError> { + if header.state_root() != root { + return Err(ConsensusError::BodyStateRootDiff( + GotExpected { got: root, expected: header.state_root() }.into(), + )) + } + + Ok(()) }   /// Consensus Errors
diff --git reth/crates/consensus/consensus/src/noop.rs scroll-reth/crates/consensus/consensus/src/noop.rs index 259fae27d677eb5d57a1da8030a88b5b0206ff9e..dd0f9ae1e30eccfb1287bd1e5883a91bd9a6c6e0 100644 --- reth/crates/consensus/consensus/src/noop.rs +++ scroll-reth/crates/consensus/consensus/src/noop.rs @@ -1,7 +1,10 @@ use crate::{Consensus, ConsensusError, FullConsensus, HeaderValidator}; use alloc::sync::Arc; +use alloy_primitives::B256; use reth_execution_types::BlockExecutionResult; -use reth_primitives_traits::{Block, NodePrimitives, RecoveredBlock, SealedBlock, SealedHeader}; +use reth_primitives_traits::{ + Block, BlockHeader, NodePrimitives, RecoveredBlock, SealedBlock, SealedHeader, +};   /// A Consensus implementation that does nothing. #[derive(Debug, Copy, Clone, Default)] @@ -15,7 +18,7 @@ Arc::new(Self::default()) } }   -impl<H> HeaderValidator<H> for NoopConsensus { +impl<H: BlockHeader> HeaderValidator<H> for NoopConsensus { fn validate_header(&self, _header: &SealedHeader<H>) -> Result<(), ConsensusError> { Ok(()) } @@ -25,6 +28,10 @@ &self, _header: &SealedHeader<H>, _parent: &SealedHeader<H>, ) -> Result<(), ConsensusError> { + Ok(()) + } + + fn validate_state_root(&self, _header: &H, _root: B256) -> Result<(), ConsensusError> { Ok(()) } }
diff --git reth/crates/consensus/consensus/src/test_utils.rs scroll-reth/crates/consensus/consensus/src/test_utils.rs index ad881cc9a7fb0c6b32c9543ec2e72f068b9b034a..e36ab0141761a4ef5e6a335c2710830580bea5eb 100644 --- reth/crates/consensus/consensus/src/test_utils.rs +++ scroll-reth/crates/consensus/consensus/src/test_utils.rs @@ -1,7 +1,10 @@ use crate::{Consensus, ConsensusError, FullConsensus, HeaderValidator}; +use alloy_primitives::B256; use core::sync::atomic::{AtomicBool, Ordering}; use reth_execution_types::BlockExecutionResult; -use reth_primitives_traits::{Block, NodePrimitives, RecoveredBlock, SealedBlock, SealedHeader}; +use reth_primitives_traits::{ + Block, BlockHeader, NodePrimitives, RecoveredBlock, SealedBlock, SealedHeader, +};   /// Consensus engine implementation for testing #[derive(Debug)] @@ -84,7 +87,7 @@ } } }   -impl<H> HeaderValidator<H> for TestConsensus { +impl<H: BlockHeader> HeaderValidator<H> for TestConsensus { fn validate_header(&self, _header: &SealedHeader<H>) -> Result<(), ConsensusError> { if self.fail_validation() { Err(ConsensusError::BaseFeeMissing) @@ -103,5 +106,9 @@ Err(ConsensusError::BaseFeeMissing) } else { Ok(()) } + } + + fn validate_state_root(&self, _header: &H, _root: B256) -> Result<(), ConsensusError> { + Ok(()) } }
diff --git reth/crates/e2e-test-utils/src/lib.rs scroll-reth/crates/e2e-test-utils/src/lib.rs index 99d48c937398b9f6eef8bdf7f06f7e7d6f2a2f4d..08c5895e02a864258a61f2d3be2851e89b5a466d 100644 --- reth/crates/e2e-test-utils/src/lib.rs +++ scroll-reth/crates/e2e-test-utils/src/lib.rs @@ -5,9 +5,10 @@ use reth_chainspec::{ChainSpec, EthChainSpec}; use reth_db::{test_utils::TempDatabase, DatabaseEnv}; use reth_engine_local::LocalPayloadAttributesBuilder; use reth_network_api::test_utils::PeersHandleProvider; +use reth_node_api::NodeAddOns; use reth_node_builder::{ components::NodeComponentsBuilder, - rpc::{EngineValidatorAddOn, RethRpcAddOns}, + rpc::{EngineValidatorAddOn, RethRpcAddOns, RpcHandleProvider}, EngineNodeLauncher, FullNodeTypesAdapter, Node, NodeAdapter, NodeBuilder, NodeComponents, NodeConfig, NodeHandle, NodePrimitives, NodeTypes, NodeTypesWithDBAdapter, PayloadAttributesBuilder, PayloadTypes, @@ -55,6 +56,7 @@ >, N::AddOns: RethRpcAddOns<Adapter<N>> + EngineValidatorAddOn<Adapter<N>>, LocalPayloadAttributesBuilder<N::ChainSpec>: PayloadAttributesBuilder<<<N as NodeTypes>::Payload as PayloadTypes>::PayloadAttributes>, + TmpNodeAddOnsHandle<N>: RpcHandleProvider<Adapter<N>, TmpNodeEthApi<N>>, { let tasks = TaskManager::current(); let exec = tasks.executor(); @@ -118,6 +120,7 @@ where N: NodeBuilderHelper, LocalPayloadAttributesBuilder<N::ChainSpec>: PayloadAttributesBuilder<<N::Payload as PayloadTypes>::PayloadAttributes>, + TmpNodeAddOnsHandle<N>: RpcHandleProvider<Adapter<N>, TmpNodeEthApi<N>>, { let tasks = TaskManager::current(); let exec = tasks.executor(); @@ -198,6 +201,14 @@ TmpNodeAdapter<N, Provider>, >>::Components, >;   +/// Type alias for a `NodeHandle` for a `TmpNodeAdapter`. +pub type TmpNodeAddOnsHandle<N> = + <<N as Node<TmpNodeAdapter<N>>>::AddOns as NodeAddOns<Adapter<N>>>::Handle; + +/// Type alias for the `EthApi` for a `TmpNodeAdapter`. +pub type TmpNodeEthApi<N> = + <<N as Node<TmpNodeAdapter<N>>>::AddOns as RethRpcAddOns<Adapter<N>>>::EthApi; + /// Type alias for a type of `NodeHelper` pub type NodeHelperType<N, Provider = BlockchainProvider<NodeTypesWithDBAdapter<N, TmpDB>>> = NodeTestContext<Adapter<N, Provider>, <N as Node<TmpNodeAdapter<N, Provider>>>::AddOns>; @@ -234,6 +245,7 @@ ChainSpec: From<ChainSpec> + Clone, >, LocalPayloadAttributesBuilder<Self::ChainSpec>: PayloadAttributesBuilder<<Self::Payload as PayloadTypes>::PayloadAttributes>, + TmpNodeAddOnsHandle<Self>: RpcHandleProvider<Adapter<Self>, TmpNodeEthApi<Self>>, { }   @@ -268,5 +280,6 @@ ChainSpec: From<ChainSpec> + Clone, >, LocalPayloadAttributesBuilder<Self::ChainSpec>: PayloadAttributesBuilder<<Self::Payload as PayloadTypes>::PayloadAttributes>, + TmpNodeAddOnsHandle<Self>: RpcHandleProvider<Adapter<Self>, TmpNodeEthApi<Self>>, { }
diff --git reth/crates/e2e-test-utils/src/node.rs scroll-reth/crates/e2e-test-utils/src/node.rs index 4a096ac5a7f76f1e0599822fa9cdf1a7b8d59d88..0261978a6696f05dd241cb596aa6a9a9843cd002 100644 --- reth/crates/e2e-test-utils/src/node.rs +++ scroll-reth/crates/e2e-test-utils/src/node.rs @@ -13,7 +13,10 @@ use reth_node_api::{ Block, BlockBody, BlockTy, EngineApiMessageVersion, FullNodeComponents, PayloadTypes, PrimitivesTy, }; -use reth_node_builder::{rpc::RethRpcAddOns, FullNode, NodeTypes}; +use reth_node_builder::{ + rpc::{RethRpcAddOns, RpcHandleProvider}, + FullNode, NodeTypes, +}; use reth_node_core::primitives::SignedTransaction; use reth_payload_primitives::{BuiltPayload, PayloadBuilderAttributes}; use reth_provider::{ @@ -33,6 +36,7 @@ pub struct NodeTestContext<Node, AddOns> where Node: FullNodeComponents, AddOns: RethRpcAddOns<Node>, + AddOns::Handle: RpcHandleProvider<Node, <AddOns as RethRpcAddOns<Node>>::EthApi>, { /// The core structure representing the full node. pub inner: FullNode<Node, AddOns>, @@ -53,6 +57,7 @@ Node: FullNodeComponents, Node::Types: NodeTypes<ChainSpec: EthereumHardforks, Payload = Payload>, Node::Network: PeersHandleProvider, AddOns: RethRpcAddOns<Node>, + AddOns::Handle: RpcHandleProvider<Node, <AddOns as RethRpcAddOns<Node>>::EthApi>, { /// Creates a new test node pub async fn new( @@ -67,7 +72,7 @@ attributes_generator, ) .await?, network: NetworkTestContext::new(node.network.clone()), - rpc: RpcTestContext { inner: node.add_ons_handle.rpc_registry }, + rpc: RpcTestContext { inner: node.add_ons_handle.rpc_handle().rpc_registry.clone() }, canonical_stream: node.provider.canonical_state_stream(), }) } @@ -258,6 +263,7 @@ /// Sends a forkchoice update message to the engine. pub async fn update_forkchoice(&self, current_head: B256, new_head: B256) -> eyre::Result<()> { self.inner .add_ons_handle + .rpc_handle() .beacon_engine_handle .fork_choice_updated( ForkchoiceState { @@ -283,6 +289,7 @@ pub async fn submit_payload(&self, payload: Payload::BuiltPayload) -> eyre::Result<B256> { let block_hash = payload.block().hash(); self.inner .add_ons_handle + .rpc_handle() .beacon_engine_handle .new_payload(Payload::block_to_payload(payload.block().clone())) .await?;
diff --git reth/crates/e2e-test-utils/src/testsuite/mod.rs scroll-reth/crates/e2e-test-utils/src/testsuite/mod.rs index 851053d8ebe65ef3487e773423c9cbd4ccf6ef0f..811d76a68db1f19adb1d9ac7f84da0338ff39d16 100644 --- reth/crates/e2e-test-utils/src/testsuite/mod.rs +++ scroll-reth/crates/e2e-test-utils/src/testsuite/mod.rs @@ -2,7 +2,8 @@ //! Utilities for running e2e tests against a node or a network of nodes.   use crate::{ testsuite::actions::{Action, ActionBox}, - NodeBuilderHelper, PayloadAttributesBuilder, + Adapter, NodeBuilderHelper, PayloadAttributesBuilder, RpcHandleProvider, TmpNodeAddOnsHandle, + TmpNodeEthApi, }; use alloy_primitives::B256; use eyre::Result; @@ -286,6 +287,7 @@ N: NodeBuilderHelper, LocalPayloadAttributesBuilder<N::ChainSpec>: PayloadAttributesBuilder< <<N as NodeTypes>::Payload as PayloadTypes>::PayloadAttributes, >, + TmpNodeAddOnsHandle<N>: RpcHandleProvider<Adapter<N>, TmpNodeEthApi<N>>, { let mut setup = self.setup.take();
diff --git reth/crates/e2e-test-utils/src/testsuite/setup.rs scroll-reth/crates/e2e-test-utils/src/testsuite/setup.rs index 9198851af52fe7f43b671905240148c81f1a9f7b..0894c2082032ed281355a848e0e09262abb68801 100644 --- reth/crates/e2e-test-utils/src/testsuite/setup.rs +++ scroll-reth/crates/e2e-test-utils/src/testsuite/setup.rs @@ -1,6 +1,9 @@ //! Test setup utilities for configuring the initial state.   -use crate::{setup_engine, testsuite::Environment, NodeBuilderHelper, PayloadAttributesBuilder}; +use crate::{ + setup_engine, testsuite::Environment, Adapter, NodeBuilderHelper, PayloadAttributesBuilder, + RpcHandleProvider, TmpNodeAddOnsHandle, TmpNodeEthApi, +}; use alloy_eips::BlockNumberOrTag; use alloy_primitives::B256; use alloy_rpc_types_engine::{ForkchoiceState, PayloadAttributes}; @@ -133,6 +136,7 @@ N: NodeBuilderHelper, LocalPayloadAttributesBuilder<N::ChainSpec>: PayloadAttributesBuilder< <<N as NodeTypes>::Payload as PayloadTypes>::PayloadAttributes, >, + TmpNodeAddOnsHandle<N>: RpcHandleProvider<Adapter<N>, TmpNodeEthApi<N>>, { let chain_spec = self.chain_spec.clone().ok_or_else(|| eyre!("Chain specification is required"))?;
diff --git reth/crates/e2e-test-utils/src/transaction.rs scroll-reth/crates/e2e-test-utils/src/transaction.rs index 54f984692423783cec0903ec2c26af5918e3da9b..3ee437ce376e58befef9bf7150ca9f14252fd62d 100644 --- reth/crates/e2e-test-utils/src/transaction.rs +++ scroll-reth/crates/e2e-test-utils/src/transaction.rs @@ -36,6 +36,17 @@ let signed = Self::transfer_tx(chain_id, wallet).await; signed.encoded_2718().into() }   + /// Creates a transfer with a nonce and signs it, returning bytes. + pub async fn transfer_tx_nonce_bytes( + chain_id: u64, + wallet: PrivateKeySigner, + nonce: u64, + ) -> Bytes { + let tx = tx(chain_id, 21000, None, None, nonce, Some(20e9 as u128)); + let signed = Self::sign_tx(wallet, tx).await; + signed.encoded_2718().into() + } + /// Creates a deployment transaction and signs it, returning an envelope. pub async fn deploy_tx( chain_id: u64,
diff --git reth/crates/engine/local/Cargo.toml scroll-reth/crates/engine/local/Cargo.toml index 98793a24b21d53d32f3545147b881ad5a9f4e21d..2de5ec3c8826b0c41324a0f2ae497ab05fd76cd4 100644 --- reth/crates/engine/local/Cargo.toml +++ scroll-reth/crates/engine/local/Cargo.toml @@ -23,6 +23,9 @@ alloy-consensus.workspace = true alloy-primitives = { workspace = true, features = ["getrandom"] } alloy-rpc-types-engine.workspace = true   +# scroll +scroll-alloy-rpc-types-engine = { workspace = true, optional = true } + # async tokio.workspace = true tokio-stream.workspace = true @@ -44,3 +47,7 @@ "dep:op-alloy-rpc-types-engine", "dep:reth-optimism-chainspec", "reth-payload-primitives/op", ] +scroll-alloy-traits = [ + "dep:scroll-alloy-rpc-types-engine", + "reth-payload-primitives/scroll-alloy-traits", +]
diff --git reth/crates/engine/local/src/payload.rs scroll-reth/crates/engine/local/src/payload.rs index 0c34279d60bdcb6bada70d0b53de89dec3c5b3a0..7d126f9406dbe018e1bd03dbb791d8b396f9fabd 100644 --- reth/crates/engine/local/src/payload.rs +++ scroll-reth/crates/engine/local/src/payload.rs @@ -64,6 +64,22 @@ } } }   +#[cfg(feature = "scroll-alloy-traits")] +impl<ChainSpec> PayloadAttributesBuilder<scroll_alloy_rpc_types_engine::ScrollPayloadAttributes> + for LocalPayloadAttributesBuilder<ChainSpec> +where + ChainSpec: Send + Sync + EthereumHardforks + 'static, +{ + fn build(&self, timestamp: u64) -> scroll_alloy_rpc_types_engine::ScrollPayloadAttributes { + scroll_alloy_rpc_types_engine::ScrollPayloadAttributes { + payload_attributes: self.build(timestamp), + transactions: None, + no_tx_pool: false, + block_data_hint: None, + } + } +} + /// A temporary workaround to support local payload engine launcher for arbitrary payload /// attributes. // TODO(mattsse): This should be reworked so that LocalPayloadAttributesBuilder can be implemented
diff --git reth/crates/engine/tree/Cargo.toml scroll-reth/crates/engine/tree/Cargo.toml index 8b17a4a8a759c3068dbf57f783594aa9bb3a7924..df164b461d5476c55b03353081937c262013e3aa 100644 --- reth/crates/engine/tree/Cargo.toml +++ scroll-reth/crates/engine/tree/Cargo.toml @@ -139,3 +139,4 @@ "reth-ethereum-primitives/test-utils", "reth-node-ethereum/test-utils", "reth-evm-ethereum/test-utils", ] +skip-state-root-validation = ["reth-stages/skip-state-root-validation"]
diff --git reth/crates/engine/tree/benches/channel_perf.rs scroll-reth/crates/engine/tree/benches/channel_perf.rs index 74067d4de70e75ff4f7192d4ce481d465f2992da..448a25a05f1cadae4b7daccb71fc219c0a3e6a9b 100644 --- reth/crates/engine/tree/benches/channel_perf.rs +++ scroll-reth/crates/engine/tree/benches/channel_perf.rs @@ -25,7 +25,7 @@ info: AccountInfo { balance: U256::from(100), nonce: 10, code_hash: B256::from_slice(&rng.r#gen::<[u8; 32]>()), - code: Default::default(), + ..Default::default() }, storage, status: AccountStatus::Loaded,
diff --git reth/crates/engine/tree/benches/state_root_task.rs scroll-reth/crates/engine/tree/benches/state_root_task.rs index c049763e8d115d6d3fef8c616da6c05b41edd0e4..75ddad00bcf1ac6600ec98d4d3f533360cc39c98 100644 --- reth/crates/engine/tree/benches/state_root_task.rs +++ scroll-reth/crates/engine/tree/benches/state_root_task.rs @@ -69,11 +69,13 @@ status: AccountStatus::SelfDestructed, } } else { RevmAccount { + #[allow(clippy::needless_update)] info: AccountInfo { balance: U256::from(rng.r#gen::<u64>()), nonce: rng.r#gen::<u64>(), code_hash: KECCAK_EMPTY, code: Some(Default::default()), + ..Default::default() }, storage: (0..rng.gen_range(0..=params.storage_slots_per_account)) .map(|_| {
diff --git reth/crates/evm/evm/Cargo.toml scroll-reth/crates/evm/evm/Cargo.toml index b29bcc6be8a4727c0bf2f8b9e42da74ed1002663..00c29072c42aa1f332ab144019ff4c57cafc35be 100644 --- reth/crates/evm/evm/Cargo.toml +++ scroll-reth/crates/evm/evm/Cargo.toml @@ -28,6 +28,9 @@ alloy-eips.workspace = true alloy-evm.workspace = true alloy-consensus.workspace = true   +# scroll +scroll-alloy-evm = { workspace = true, optional = true } + auto_impl.workspace = true derive_more.workspace = true futures-util.workspace = true @@ -57,6 +60,7 @@ "derive_more/std", "reth-storage-api/std", "reth-trie-common/std", "reth-ethereum-primitives/std", + "scroll-alloy-evm?/std", ] metrics = ["std", "dep:metrics", "dep:reth-metrics"] test-utils = [ @@ -65,3 +69,4 @@ "reth-trie-common/test-utils", "reth-ethereum-primitives/test-utils", ] op = ["alloy-evm/op", "reth-primitives-traits/op"] +scroll-alloy-traits = ["dep:scroll-alloy-evm", "reth-primitives-traits/scroll-alloy-traits"]
diff --git reth/crates/evm/evm/src/execute.rs scroll-reth/crates/evm/evm/src/execute.rs index 0192791676c9dc62d09318eb7bd081c146303f4b..5c9f22334d18f0a0426276a11f936541fe6c6c62 100644 --- reth/crates/evm/evm/src/execute.rs +++ scroll-reth/crates/evm/evm/src/execute.rs @@ -474,7 +474,6 @@ #[cfg(test)] mod tests { use super::*; use crate::Address; - use alloy_consensus::constants::KECCAK_EMPTY; use alloy_evm::block::state_changes::balance_increment_state; use alloy_primitives::{address, map::HashMap, U256}; use core::marker::PhantomData; @@ -546,12 +545,8 @@ ) -> State<CacheDB<EmptyDB>> { let db = CacheDB::<EmptyDB>::default(); let mut state = State::builder().with_database(db).with_bundle_update().build();   - let account_info = AccountInfo { - balance: U256::from(balance), - nonce, - code_hash: KECCAK_EMPTY, - code: None, - }; + let account_info = + AccountInfo { balance: U256::from(balance), nonce, ..Default::default() }; state.insert_account(addr, account_info); state } @@ -587,8 +582,7 @@ let addr2 = address!("0x2000000000000000000000000000000000000000");   let mut state = setup_state_with_account(addr1, 100, 1);   - let account2 = - AccountInfo { balance: U256::from(200), nonce: 1, code_hash: KECCAK_EMPTY, code: None }; + let account2 = AccountInfo { balance: U256::from(200), nonce: 1, ..Default::default() }; state.insert_account(addr2, account2);   let mut increments = HashMap::default(); @@ -609,8 +603,7 @@ let addr2 = address!("0x2000000000000000000000000000000000000000");   let mut state = setup_state_with_account(addr1, 100, 1);   - let account2 = - AccountInfo { balance: U256::from(200), nonce: 1, code_hash: KECCAK_EMPTY, code: None }; + let account2 = AccountInfo { balance: U256::from(200), nonce: 1, ..Default::default() }; state.insert_account(addr2, account2);   let mut increments = HashMap::default();
diff --git reth/crates/evm/evm/src/lib.rs scroll-reth/crates/evm/evm/src/lib.rs index 4555149df43386dfd3ca1b22e2bdd45639046a10..bafd973adb0b27fc95f5f2dd0c9baaa12c7ef4ca 100644 --- reth/crates/evm/evm/src/lib.rs +++ scroll-reth/crates/evm/evm/src/lib.rs @@ -389,3 +389,22 @@ fn set_access_list(&mut self, access_list: AccessList) { self.base.set_access_list(access_list); } } + +#[cfg(feature = "scroll-alloy-traits")] +impl<T: TransactionEnv> TransactionEnv for scroll_alloy_evm::ScrollTransactionIntoTxEnv<T> { + fn set_gas_limit(&mut self, gas_limit: u64) { + self.base.set_gas_limit(gas_limit); + } + + fn nonce(&self) -> u64 { + TransactionEnv::nonce(&self.base) + } + + fn set_nonce(&mut self, nonce: u64) { + self.base.set_nonce(nonce); + } + + fn set_access_list(&mut self, access_list: AccessList) { + self.base.set_access_list(access_list); + } +}
diff --git reth/crates/evm/evm/src/metrics.rs scroll-reth/crates/evm/evm/src/metrics.rs index 878d298defe1dce5c60acbed8ba6b9c5b87619e0..b8f6d4f34ee23ef511e6c60f7a90ae08f023591f 100644 --- reth/crates/evm/evm/src/metrics.rs +++ scroll-reth/crates/evm/evm/src/metrics.rs @@ -157,6 +157,7 @@ }   #[cfg(test)] mod tests { + #![allow(clippy::needless_update)] use super::*; use alloy_eips::eip7685::Requests; use alloy_evm::{block::CommitChanges, EthEvm}; @@ -286,6 +287,7 @@ balance: U256::from(100), nonce: 10, code_hash: B256::random(), code: Default::default(), + ..Default::default() }, storage, status: AccountStatus::Loaded,
diff --git reth/crates/exex/types/src/notification.rs scroll-reth/crates/exex/types/src/notification.rs index cf0d7580556a8ec2d9f947f4e35c0731b2eaafc3..8a62fa9230e86d0df64bf133510a6260fcbb87e3 100644 --- reth/crates/exex/types/src/notification.rs +++ scroll-reth/crates/exex/types/src/notification.rs @@ -7,6 +7,7 @@ /// Notifications sent to an `ExEx`. #[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[allow(clippy::large_enum_variant)] pub enum ExExNotification<N: NodePrimitives = reth_chain_state::EthPrimitives> { /// Chain got committed without a reorg, and only the new chain is returned. ChainCommitted {
diff --git reth/crates/net/eth-wire-types/src/capability.rs scroll-reth/crates/net/eth-wire-types/src/capability.rs index 3f39bed60647df467d8d7cccffb927d429159fb9..2c8119027d1b9bf75eaefe819a1c899792c3e77b 100644 --- reth/crates/net/eth-wire-types/src/capability.rs +++ scroll-reth/crates/net/eth-wire-types/src/capability.rs @@ -160,6 +160,7 @@ impl Capabilities { /// Returns all capabilities. #[inline] + #[allow(clippy::missing_const_for_fn)] pub fn capabilities(&self) -> &[Capability] { &self.inner }
diff --git reth/crates/net/network-api/Cargo.toml scroll-reth/crates/net/network-api/Cargo.toml index 4ecfa1f593e994f31cc5f5dd25bc96d1d5011786..7257ec6b99de0fb1ce6c2aa4daa4f4f67a71d653 100644 --- reth/crates/net/network-api/Cargo.toml +++ scroll-reth/crates/net/network-api/Cargo.toml @@ -19,6 +19,7 @@ reth-network-p2p.workspace = true reth-eth-wire-types.workspace = true reth-tokio-util.workspace = true reth-ethereum-forks.workspace = true +reth-primitives-traits.workspace = true   # ethereum alloy-primitives = { workspace = true, features = ["getrandom"] } @@ -26,6 +27,7 @@ alloy-rpc-types-admin.workspace = true enr = { workspace = true, default-features = false, features = ["rust-secp256k1"] }   # async +async-trait.workspace = true futures.workspace = true tokio-stream.workspace = true   @@ -44,4 +46,5 @@ "reth-network-types/serde", "alloy-primitives/serde", "enr/serde", "reth-ethereum-forks/serde", + "reth-primitives-traits/serde", ]
diff --git reth/crates/net/network-api/src/block.rs scroll-reth/crates/net/network-api/src/block.rs new file mode 100644 index 0000000000000000000000000000000000000000..dab7766188cc7b31d47b55b05c7a9a65b8bd8ebb --- /dev/null +++ scroll-reth/crates/net/network-api/src/block.rs @@ -0,0 +1,26 @@ +use super::*; +use reth_primitives_traits::Block; +use reth_tokio_util::EventStream; +use tokio::sync::oneshot; + +/// The message that is broadcast to subscribers of the block import channel. +#[derive(Debug, Clone)] +pub struct NewBlockWithPeer<B> { + /// The peer that sent the block. + pub peer_id: PeerId, + /// The block that was received. + pub block: B, +} + +/// Provides a listener for new blocks on the eth wire protocol. +pub trait EthWireBlockListenerProvider { + /// The network primitives. + type Block: Block; + + /// Create a new eth wire block listener. + fn eth_wire_block_listener( + &self, + ) -> impl Future< + Output = Result<EventStream<NewBlockWithPeer<Self::Block>>, oneshot::error::RecvError>, + > + Send; +}
diff --git reth/crates/net/network-api/src/lib.rs scroll-reth/crates/net/network-api/src/lib.rs index ff469f16a4753b7bb5bb3c3ba4525329fabada97..e08416968c7b1fa49f5e0fdf9ebb8fa7f16bf630 100644 --- reth/crates/net/network-api/src/lib.rs +++ scroll-reth/crates/net/network-api/src/lib.rs @@ -20,7 +20,11 @@ pub mod events; /// Implementation of network traits for that does nothing. pub mod noop;   +/// Type used for the eth wire bridge. +pub mod block; + pub mod test_utils; +use block::EthWireBlockListenerProvider; use test_utils::PeersHandleProvider;   pub use alloy_rpc_types_admin::EthProtocolInfo; @@ -53,6 +57,7 @@ + NetworkInfo + NetworkEventListenerProvider + Peers + PeersHandleProvider + + EthWireBlockListenerProvider<Block = <Self::Primitives as NetworkPrimitives>::Block> + Clone + Unpin + 'static @@ -67,6 +72,7 @@ + NetworkInfo + NetworkEventListenerProvider + Peers + PeersHandleProvider + + EthWireBlockListenerProvider<Block = <Self::Primitives as NetworkPrimitives>::Block> + Clone + Unpin + 'static
diff --git reth/crates/net/network/src/config.rs scroll-reth/crates/net/network/src/config.rs index cb04541a0200330e1d398fb79eea73a2ab6b0ad3..aee5dce37fb9138994ab26d9c532b886e3f33c0c 100644 --- reth/crates/net/network/src/config.rs +++ scroll-reth/crates/net/network/src/config.rs @@ -4,6 +4,7 @@ use crate::{ error::NetworkError, import::{BlockImport, ProofOfStakeBlockImport}, transactions::TransactionsManagerConfig, + transform::header::HeaderTransform, NetworkHandle, NetworkManager, }; use reth_chainspec::{ChainSpecProvider, EthChainSpec, Hardforks}; @@ -90,6 +91,8 @@ /// <https://github.com/ethereum/devp2p/blob/master/rlpx.md#initial-handshake>. /// This can be overridden to support custom handshake logic via the /// [`NetworkConfigBuilder`]. pub handshake: Arc<dyn EthRlpxHandshake>, + /// A transformation hook applied to the downloaded headers. + pub header_transform: Box<dyn HeaderTransform<N::BlockHeader>>, }   // === impl NetworkConfig === @@ -217,6 +220,8 @@ nat: Option<NatResolver>, /// The Ethereum P2P handshake, see also: /// <https://github.com/ethereum/devp2p/blob/master/rlpx.md#initial-handshake>. handshake: Arc<dyn EthRlpxHandshake>, + /// The header transform type. + header_transform: Option<Box<dyn HeaderTransform<N::BlockHeader>>>, }   impl NetworkConfigBuilder<EthNetworkPrimitives> { @@ -257,6 +262,7 @@ block_import: None, transactions_manager_config: Default::default(), nat: None, handshake: Arc::new(EthHandshake::default()), + header_transform: None, } }   @@ -565,6 +571,15 @@ self.handshake = handshake; self }   + /// Sets the header transform type. + pub fn header_transform( + mut self, + header_transform: Box<dyn HeaderTransform<N::BlockHeader>>, + ) -> Self { + self.header_transform = Some(header_transform); + self + } + /// Consumes the type and creates the actual [`NetworkConfig`] /// for the given client type that can interact with the chain. /// @@ -597,6 +612,7 @@ block_import, transactions_manager_config, nat, handshake, + header_transform, } = self;   let head = head.unwrap_or_else(|| Head { @@ -665,6 +681,7 @@ tx_gossip_disabled, transactions_manager_config, nat, handshake, + header_transform: header_transform.unwrap_or_else(|| Box::new(())), } } }
diff --git reth/crates/net/network/src/fetch/mod.rs scroll-reth/crates/net/network/src/fetch/mod.rs index ece4fb626a4a85e16c29e1b4522440e2a238041d..794c184e69db7b2e24a684ef2bfbc89ed4b2f45e 100644 --- reth/crates/net/network/src/fetch/mod.rs +++ scroll-reth/crates/net/network/src/fetch/mod.rs @@ -4,7 +4,7 @@ mod client;   pub use client::FetchClient;   -use crate::{message::BlockRequest, session::BlockRangeInfo}; +use crate::{message::BlockRequest, session::BlockRangeInfo, transform::header::HeaderTransform}; use alloy_primitives::B256; use futures::StreamExt; use reth_eth_wire::{EthNetworkPrimitives, GetBlockBodies, GetBlockHeaders, NetworkPrimitives}; @@ -55,12 +55,18 @@ /// Receiver for new incoming download requests download_requests_rx: UnboundedReceiverStream<DownloadRequest<N>>, /// Sender for download requests, used to detach a [`FetchClient`] download_requests_tx: UnboundedSender<DownloadRequest<N>>, + /// A transformation hook applied to the downloaded headers. + header_transform: Box<dyn HeaderTransform<N::BlockHeader>>, }   // === impl StateSyncer ===   impl<N: NetworkPrimitives> StateFetcher<N> { - pub(crate) fn new(peers_handle: PeersHandle, num_active_peers: Arc<AtomicUsize>) -> Self { + pub(crate) fn new( + peers_handle: PeersHandle, + num_active_peers: Arc<AtomicUsize>, + header_transform: Box<dyn HeaderTransform<N::BlockHeader>>, + ) -> Self { let (download_requests_tx, download_requests_rx) = mpsc::unbounded_channel(); Self { inflight_headers_requests: Default::default(), @@ -71,6 +77,7 @@ num_active_peers, queued_requests: Default::default(), download_requests_rx: UnboundedReceiverStream::new(download_requests_rx), download_requests_tx, + header_transform, } }   @@ -272,8 +279,10 @@ let is_likely_bad_response = resp.as_ref().is_some_and(|r| res.is_likely_bad_headers_response(&r.request));   if let Some(resp) = resp { - // delegate the response - let _ = resp.response.send(res.map(|h| (peer_id, h).into())); + // apply the header transform and delegate the response + let _ = resp.response.send(res.map(|h| { + (peer_id, h.into_iter().map(|h| self.header_transform.map(h)).collect()).into() + })); }   if let Some(peer) = self.peers.get_mut(&peer_id) { @@ -484,8 +493,11 @@ #[tokio::test(flavor = "multi_thread")] async fn test_poll_fetcher() { let manager = PeersManager::new(PeersConfig::default()); - let mut fetcher = - StateFetcher::<EthNetworkPrimitives>::new(manager.handle(), Default::default()); + let mut fetcher = StateFetcher::<EthNetworkPrimitives>::new( + manager.handle(), + Default::default(), + Box::new(()), + );   poll_fn(move |cx| { assert!(fetcher.poll(cx).is_pending()); @@ -506,8 +518,11 @@ #[tokio::test] async fn test_peer_rotation() { let manager = PeersManager::new(PeersConfig::default()); - let mut fetcher = - StateFetcher::<EthNetworkPrimitives>::new(manager.handle(), Default::default()); + let mut fetcher = StateFetcher::<EthNetworkPrimitives>::new( + manager.handle(), + Default::default(), + Box::new(()), + ); // Add a few random peers let peer1 = B512::random(); let peer2 = B512::random(); @@ -530,8 +545,11 @@ #[tokio::test] async fn test_peer_prioritization() { let manager = PeersManager::new(PeersConfig::default()); - let mut fetcher = - StateFetcher::<EthNetworkPrimitives>::new(manager.handle(), Default::default()); + let mut fetcher = StateFetcher::<EthNetworkPrimitives>::new( + manager.handle(), + Default::default(), + Box::new(()), + ); // Add a few random peers let peer1 = B512::random(); let peer2 = B512::random(); @@ -556,8 +574,11 @@ #[tokio::test] async fn test_on_block_headers_response() { let manager = PeersManager::new(PeersConfig::default()); - let mut fetcher = - StateFetcher::<EthNetworkPrimitives>::new(manager.handle(), Default::default()); + let mut fetcher = StateFetcher::<EthNetworkPrimitives>::new( + manager.handle(), + Default::default(), + Box::new(()), + ); let peer_id = B512::random();   assert_eq!(fetcher.on_block_headers_response(peer_id, Ok(vec![Header::default()])), None); @@ -587,8 +608,11 @@ #[tokio::test] async fn test_header_response_outcome() { let manager = PeersManager::new(PeersConfig::default()); - let mut fetcher = - StateFetcher::<EthNetworkPrimitives>::new(manager.handle(), Default::default()); + let mut fetcher = StateFetcher::<EthNetworkPrimitives>::new( + manager.handle(), + Default::default(), + Box::new(()), + ); let peer_id = B512::random();   let request_pair = || {
diff --git reth/crates/net/network/src/lib.rs scroll-reth/crates/net/network/src/lib.rs index 1072d526fbb00029e7d9edfba7f5a17e73169b36..1d8e4d15be3c37b69e2b0b829c2b11b02a99709e 100644 --- reth/crates/net/network/src/lib.rs +++ scroll-reth/crates/net/network/src/lib.rs @@ -130,6 +130,7 @@ pub mod message; pub mod peers; pub mod protocol; pub mod transactions; +pub mod transform;   mod budget; mod builder;
diff --git reth/crates/net/network/src/manager.rs scroll-reth/crates/net/network/src/manager.rs index 4bc279b335a8b5f685dabfe18b044a0abec2e78a..d2ce957614e2849edc95a26ccf6263e5a55a94df 100644 --- reth/crates/net/network/src/manager.rs +++ scroll-reth/crates/net/network/src/manager.rs @@ -21,7 +21,7 @@ config::NetworkConfig, discovery::Discovery, error::{NetworkError, ServiceKind}, eth_requests::IncomingEthRequest, - import::{BlockImport, BlockImportEvent, BlockImportOutcome, BlockValidation, NewBlockEvent}, + import::{BlockImportEvent, BlockImportOutcome, BlockValidation}, listener::ConnectionListener, message::{NewBlockMessage, PeerMessage}, metrics::{DisconnectMetrics, NetworkMetrics, NETWORK_POOL_TRANSACTIONS_SCOPE}, @@ -39,9 +39,11 @@ use futures::{Future, StreamExt}; use parking_lot::Mutex; use reth_chainspec::EnrForkIdEntry; use reth_eth_wire::{DisconnectReason, EthNetworkPrimitives, NetworkPrimitives}; +use reth_eth_wire_types::NewBlockPayload; use reth_fs_util::{self as fs, FsPathError}; use reth_metrics::common::mpsc::UnboundedMeteredSender; use reth_network_api::{ + block::NewBlockWithPeer, events::{PeerEvent, SessionInfo}, test_utils::PeersHandle, EthProtocolInfo, NetworkEvent, NetworkStatus, PeerInfo, PeerRequest, @@ -109,7 +111,7 @@ handle: NetworkHandle<N>, /// Receiver half of the command channel set up between this type and the [`NetworkHandle`] from_handle_rx: UnboundedReceiverStream<NetworkHandleMessage<N>>, /// Handles block imports according to the `eth` protocol. - block_import: Box<dyn BlockImport<N::NewBlockPayload>>, + block_import: EventSender<NewBlockWithPeer<N::Block>>, /// Sender for high level network events. event_sender: EventSender<NetworkEvent<PeerRequest<N>>>, /// Sender half to send events to the @@ -237,7 +239,7 @@ listener_addr, peers_config, sessions_config, chain_id, - block_import, + block_import: _, network_mode, boot_nodes, executor, @@ -250,6 +252,7 @@ tx_gossip_disabled, transactions_manager_config: _, nat, handshake, + header_transform, } = config;   let peers_manager = PeersManager::new(peers_config); @@ -311,6 +314,7 @@ crate::state::BlockNumReader::new(client), discovery, peers_manager, Arc::clone(&num_active_peers), + header_transform, );   let swarm = Swarm::new(incoming, sessions, state); @@ -339,7 +343,7 @@ Ok(Self { swarm, handle, from_handle_rx: UnboundedReceiverStream::new(from_handle_rx), - block_import, + block_import: EventSender::new(1000), event_sender, to_transactions_manager: None, to_eth_request_handler: None, @@ -522,6 +526,7 @@ } } }   + #[allow(dead_code)] /// Invoked after a `NewBlock` message from the peer was validated fn on_block_import_result(&mut self, event: BlockImportEvent<N::NewBlockPayload>) { match event { @@ -588,14 +593,16 @@ self.within_pow_or_disconnect(peer_id, |this| { // update peer's state, to track what blocks this peer has seen this.swarm.state_mut().on_new_block_hashes(peer_id, hashes.0.clone()); // start block import process for the hashes - this.block_import.on_new_block(peer_id, NewBlockEvent::Hashes(hashes)); + // this.block_import.on_new_block(peer_id, NewBlockEvent::Hashes(hashes)); }) } PeerMessage::NewBlock(block) => { self.within_pow_or_disconnect(peer_id, move |this| { this.swarm.state_mut().on_new_block(peer_id, block.hash); + let block = Arc::unwrap_or_clone(block.block); // start block import process - this.block_import.on_new_block(peer_id, NewBlockEvent::Block(block)); + this.block_import + .notify(NewBlockWithPeer { peer_id, block: block.block().clone() }); }); } PeerMessage::PooledTransactions(msg) => { @@ -626,6 +633,9 @@ /// Handler for received messages from a handle fn on_handle_message(&mut self, msg: NetworkHandleMessage<N>) { match msg { + NetworkHandleMessage::EthWireBlockListener(tx) => { + let _ = tx.send(self.block_import.new_listener()); + } NetworkHandleMessage::DiscoveryListener(tx) => { self.swarm.state_mut().discovery_mut().add_listener(tx); } @@ -1075,11 +1085,6 @@ let start = Instant::now(); let mut poll_durations = NetworkManagerPollDurations::default();   let this = self.get_mut(); - - // poll new block imports (expected to be a noop for POS) - while let Poll::Ready(outcome) = this.block_import.poll(cx) { - this.on_block_import_result(outcome); - }   // These loops drive the entire state of network and does a lot of work. Under heavy load // (many messages/events), data may arrive faster than it can be processed (incoming
diff --git reth/crates/net/network/src/network.rs scroll-reth/crates/net/network/src/network.rs index cfc3d56cb28dceebbd9cde934287d2dcc2a7e0e1..ffe8bf1531fc7b3e5cec090d0ff1f7a88669d396 100644 --- reth/crates/net/network/src/network.rs +++ scroll-reth/crates/net/network/src/network.rs @@ -14,6 +14,7 @@ NewPooledTransactionHashes, SharedTransactions, }; use reth_ethereum_forks::Head; use reth_network_api::{ + block::{EthWireBlockListenerProvider, NewBlockWithPeer}, events::{NetworkPeersEvents, PeerEvent, PeerEventStream}, test_utils::{PeersHandle, PeersHandleProvider}, BlockDownloaderProvider, DiscoveryEvent, NetworkError, NetworkEvent, @@ -88,15 +89,18 @@ Self { inner: Arc::new(inner) } }   /// Returns the [`PeerId`] used in the network. + #[allow(clippy::missing_const_for_fn)] pub fn peer_id(&self) -> &PeerId { &self.inner.local_peer_id }   + #[allow(clippy::missing_const_for_fn)] fn manager(&self) -> &UnboundedSender<NetworkHandleMessage<N>> { &self.inner.to_manager_tx }   /// Returns the mode of the network, either pow, or pos + #[allow(clippy::missing_const_for_fn)] pub fn mode(&self) -> &NetworkMode { &self.inner.network_mode } @@ -182,11 +186,13 @@ self.send_message(NetworkHandleMessage::SetNetworkState(network_conn)); }   /// Whether tx gossip is disabled + #[allow(clippy::missing_const_for_fn)] pub fn tx_gossip_disabled(&self) -> bool { self.inner.tx_gossip_disabled }   /// Returns the secret key used for authenticating sessions. + #[allow(clippy::missing_const_for_fn)] pub fn secret_key(&self) -> &SecretKey { &self.inner.secret_key } @@ -216,6 +222,18 @@ fn discovery_listener(&self) -> UnboundedReceiverStream<DiscoveryEvent> { let (tx, rx) = mpsc::unbounded_channel(); let _ = self.manager().send(NetworkHandleMessage::DiscoveryListener(tx)); UnboundedReceiverStream::new(rx) + } +} + +impl<N: NetworkPrimitives> EthWireBlockListenerProvider for NetworkHandle<N> { + type Block = <N as NetworkPrimitives>::Block; + + async fn eth_wire_block_listener( + &self, + ) -> Result<EventStream<NewBlockWithPeer<Self::Block>>, oneshot::error::RecvError> { + let (tx, rx) = oneshot::channel(); + self.send_message(NetworkHandleMessage::EthWireBlockListener(tx)); + rx.await } }   @@ -548,4 +566,6 @@ /// Connect to the given peer. ConnectPeer(PeerId, PeerKind, PeerAddr), /// Message to update the node's advertised block range information. InternalBlockRangeUpdate(BlockRangeUpdate), + /// Retries a eth wire new block event listener. + EthWireBlockListener(oneshot::Sender<EventStream<NewBlockWithPeer<N::Block>>>), }
diff --git reth/crates/net/network/src/state.rs scroll-reth/crates/net/network/src/state.rs index be01312bff09a14c6f2df675ce5f3a9587b61bf4..89ad4874cc208b7bca70036cc01e0352d6e3006c 100644 --- reth/crates/net/network/src/state.rs +++ scroll-reth/crates/net/network/src/state.rs @@ -7,6 +7,7 @@ fetch::{BlockResponseOutcome, FetchAction, StateFetcher}, message::{BlockRequest, NewBlockMessage, PeerResponse, PeerResponseResult}, peers::{PeerAction, PeersManager}, session::BlockRangeInfo, + transform::header::HeaderTransform, FetchClient, }; use alloy_consensus::BlockHeader; @@ -102,8 +103,10 @@ client: BlockNumReader, discovery: Discovery, peers_manager: PeersManager, num_active_peers: Arc<AtomicUsize>, + header_transform: Box<dyn HeaderTransform<N::BlockHeader>>, ) -> Self { - let state_fetcher = StateFetcher::new(peers_manager.handle(), num_active_peers); + let state_fetcher = + StateFetcher::new(peers_manager.handle(), num_active_peers, header_transform); Self { active_peers: Default::default(), peers_manager, @@ -596,7 +599,7 @@ peers_manager: Default::default(), queued_messages: Default::default(), client: BlockNumReader(Box::new(NoopProvider::default())), discovery: Discovery::noop(), - state_fetcher: StateFetcher::new(handle, Default::default()), + state_fetcher: StateFetcher::new(handle, Default::default(), Box::new(())), } }
diff --git reth/crates/net/network/src/test_utils/testnet.rs scroll-reth/crates/net/network/src/test_utils/testnet.rs index 08cbcf7f85eccbde69ef2a81d5b5fb3081b1f70c..d064ed7eda654f1e6f50b2af0fef972cbb7c4f30 100644 --- reth/crates/net/network/src/test_utils/testnet.rs +++ scroll-reth/crates/net/network/src/test_utils/testnet.rs @@ -104,6 +104,7 @@ &mut self.peers }   /// Return a slice of all peers. + #[allow(clippy::missing_const_for_fn)] pub fn peers(&self) -> &[Peer<C, Pool>] { &self.peers } @@ -354,6 +355,7 @@ rx.await.unwrap() }   /// Returns the [`PeerHandle`]s of this [`Testnet`]. + #[allow(clippy::missing_const_for_fn)] pub fn peers(&self) -> &[PeerHandle<Pool>] { &self.peers }
diff --git reth/crates/net/network/src/transform/header.rs scroll-reth/crates/net/network/src/transform/header.rs new file mode 100644 index 0000000000000000000000000000000000000000..ef5e777fea0ed536f0913f68378f39e71909e4a8 --- /dev/null +++ scroll-reth/crates/net/network/src/transform/header.rs @@ -0,0 +1,15 @@ +//! Abstraction over a transformation applied to headers. + +use reth_primitives_traits::BlockHeader; + +/// An instance of the trait applies a mapping to the input header. +pub trait HeaderTransform<H: BlockHeader>: std::fmt::Debug + Send + Sync { + /// Applies a mapping to the input header. + fn map(&self, header: H) -> H; +} + +impl<H: BlockHeader> HeaderTransform<H> for () { + fn map(&self, header: H) -> H { + header + } +}
diff --git reth/crates/net/network/src/transform/mod.rs scroll-reth/crates/net/network/src/transform/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..2081c2bd939cc50a1accdacd545427963b9fb1b7 --- /dev/null +++ scroll-reth/crates/net/network/src/transform/mod.rs @@ -0,0 +1,4 @@ +//! Provides an abstraction over transformation hooks that can be applied to data response from +//! peers. + +pub mod header;
diff --git reth/crates/net/p2p/src/full_block.rs scroll-reth/crates/net/p2p/src/full_block.rs index 3a7422c841812b6b536eb5701a1bde64c27e7d63..aa48f6c610e00253498fd81d8267857bdf18113a 100644 --- reth/crates/net/p2p/src/full_block.rs +++ scroll-reth/crates/net/p2p/src/full_block.rs @@ -10,7 +10,7 @@ }; use alloy_consensus::BlockHeader; use alloy_primitives::{Sealable, B256}; use core::marker::PhantomData; -use reth_consensus::{Consensus, ConsensusError}; +use reth_consensus::{Consensus, ConsensusError, HeaderValidator}; use reth_eth_wire_types::{EthNetworkPrimitives, HeadersDirection, NetworkPrimitives}; use reth_network_peers::{PeerId, WithPeerId}; use reth_primitives_traits::{SealedBlock, SealedHeader};
diff --git reth/crates/net/peers/src/bootnodes/mod.rs scroll-reth/crates/net/peers/src/bootnodes/mod.rs index e510e0b1cda2cc8dedc793703107497342c21891..17fa1741d8dbc646ff09c239df0bf73d0643fc4e 100644 --- reth/crates/net/peers/src/bootnodes/mod.rs +++ scroll-reth/crates/net/peers/src/bootnodes/mod.rs @@ -9,6 +9,9 @@ mod optimism; pub use optimism::*;   +mod scroll; +pub use scroll::*; + /// Returns parsed mainnet nodes pub fn mainnet_nodes() -> Vec<NodeRecord> { parse_nodes(&MAINNET_BOOTNODES[..]) @@ -47,6 +50,16 @@ /// Returns parsed op-stack base testnet nodes pub fn base_testnet_nodes() -> Vec<NodeRecord> { parse_nodes(OP_TESTNET_BOOTNODES) +} + +/// Returns parsed scroll mainnet nodes +pub fn scroll_nodes() -> Vec<NodeRecord> { + parse_nodes(SCROLL_BOOTNODES) +} + +/// Returns parsed scroll seplo nodes +pub fn scroll_sepolia_nodes() -> Vec<NodeRecord> { + parse_nodes(SCROLL_SEPOLIA_BOOTNODES) }   /// Parses all the nodes
diff --git reth/crates/net/peers/src/bootnodes/scroll.rs scroll-reth/crates/net/peers/src/bootnodes/scroll.rs new file mode 100644 index 0000000000000000000000000000000000000000..7d85fcc8d077b6ea67ea3a1c5426620fb2185500 --- /dev/null +++ scroll-reth/crates/net/peers/src/bootnodes/scroll.rs @@ -0,0 +1,17 @@ +//! Scroll bootnodes come from <https://github.com/scroll-tech/go-ethereum/blob/develop/params/bootnodes.go> + +/// Scroll mainnet boot nodes. +pub static SCROLL_BOOTNODES: &[&str] = &[ + "enode://c6ac91f43df3d63916ac1ae411cdd5ba249d55d48a7bec7f8cd5bb351a31aba437e5a69e8a1de74d73fdfeba8af1cfe9caf9846ecd3abf60d1ffdf4925b55b23@54.186.123.248:30303", + "enode://fdcc807b5d1353f3a1e98b90208ce6ef1b7d446136e51eaa8ad657b55518a2f8b37655e42375d61622e6ea18f3faf9d070c9bbdf012cf5484bcbad33b7a15fb1@44.227.91.206:30303", + "enode://6beb5a3efbb39be73d17630b6da48e94c0ce7ec665172111463cb470197b20c12faa1fa6f835b81c28571277d1017e65c4e426cc92a46141cf69118ecf28ac03@44.237.194.52:30303", + "enode://7cf893d444eb8e129dca0f6485b3df579911606e7c728be4fa55fcc5f155a37c3ce07d217ccec5447798bde465ac2bdba2cb8763d107e9f3257e787579e9f27e@52.35.203.107:30303", + "enode://c7b2d94e95da343db6e667a01cef90376a592f2d277fbcbf6e9c9186734ed8003d01389571bd10cdbab7a6e5adfa6f0c7b55644d0db24e0b9deb4ec80f842075@54.70.236.187:30303", +]; + +/// Scroll sepolia boot nodes. +pub static SCROLL_SEPOLIA_BOOTNODES: &[&str] = &[ + "enode://ceb1636bac5cbb262e5ad5b2cd22014bdb35ffe7f58b3506970d337a63099481814a338dbcd15f2d28757151e3ecd40ba38b41350b793cd0d910ff0436654f8c@35.85.84.250:30303", + "enode://29cee709c400533ae038a875b9ca975c8abef9eade956dcf3585e940acd5c0ae916968f514bd37d1278775aad1b7db30f7032a70202a87fd7365bd8de3c9f5fc@44.242.39.33:30303", + "enode://dd1ac5433c5c2b04ca3166f4cb726f8ff6d2da83dbc16d9b68b1ea83b7079b371eb16ef41c00441b6e85e32e33087f3b7753ea9e8b1e3f26d3e4df9208625e7f@54.148.111.168:30303", +];
diff --git reth/crates/node/builder/Cargo.toml scroll-reth/crates/node/builder/Cargo.toml index d08c62d38ceeaf9da4db4460c784772ea28f22e7..b8103d8b3dce3b35d3948c43ce06c2c87f40b135 100644 --- reth/crates/node/builder/Cargo.toml +++ scroll-reth/crates/node/builder/Cargo.toml @@ -111,3 +111,7 @@ "reth-db-api/op", "reth-engine-local/op", "reth-evm/op", ] +skip-state-root-validation = [ + "reth-stages/skip-state-root-validation", + "reth-engine-tree/skip-state-root-validation", +]
diff --git reth/crates/node/builder/src/builder/mod.rs scroll-reth/crates/node/builder/src/builder/mod.rs index 73baae37c62ee53c29b44848545f35b838214352..c5f7c57c441b0ee7e31bcd340c3cc258fc4cb355 100644 --- reth/crates/node/builder/src/builder/mod.rs +++ scroll-reth/crates/node/builder/src/builder/mod.rs @@ -6,7 +6,7 @@ use crate::{ common::WithConfigs, components::NodeComponentsBuilder, node::FullNode, - rpc::{RethRpcAddOns, RethRpcServerHandles, RpcContext}, + rpc::{RethRpcAddOns, RethRpcServerHandles, RpcContext, RpcHandleProvider}, BlockReaderFor, DebugNode, DebugNodeLauncher, EngineNodeLauncher, LaunchNode, Node, }; use alloy_eips::eip4844::env_settings::EnvKzgSettings; @@ -49,6 +49,14 @@ /// The adapter type for a reth node with the builtin provider type // Note: we need to hardcode this because custom components might depend on it in associated types. pub type RethFullAdapter<DB, Types> = FullNodeTypesAdapter<Types, DB, BlockchainProvider<NodeTypesWithDBAdapter<Types, DB>>>; + +/// A full node adapter for a reth node with the builtin provider type +type FullNodeAdapter<N, DB> = NodeAdapter< + RethFullAdapter<DB, N>, + <<N as Node<RethFullAdapter<DB, N>>>::ComponentsBuilder as NodeComponentsBuilder< + RethFullAdapter<DB, N>, + >>::Components, +>;   #[expect(clippy::doc_markdown)] #[cfg_attr(doc, aquamarine::aquamarine)] @@ -350,11 +358,10 @@ >>::Node, > where N: Node<RethFullAdapter<DB, N>, ChainSpec = ChainSpec> + NodeTypesForProvider, - N::AddOns: RethRpcAddOns< - NodeAdapter< - RethFullAdapter<DB, N>, - <N::ComponentsBuilder as NodeComponentsBuilder<RethFullAdapter<DB, N>>>::Components, - >, + N::AddOns: RethRpcAddOns<FullNodeAdapter<N, DB>>, + <N::AddOns as NodeAddOns<FullNodeAdapter<N, DB>>>::Handle: RpcHandleProvider< + FullNodeAdapter<N, DB>, + <N::AddOns as RethRpcAddOns<FullNodeAdapter<N, DB>>>::EthApi, >, N::Primitives: FullNodePrimitives, EngineNodeLauncher: LaunchNode< @@ -407,6 +414,12 @@ where T: FullNodeTypes, CB: NodeComponentsBuilder<T>, AO: RethRpcAddOns<NodeAdapter<T, CB::Components>>, + <AO as NodeAddOns< + NodeAdapter<T, <CB as NodeComponentsBuilder<T>>::Components>, + >>::Handle: RpcHandleProvider< + NodeAdapter<T, <CB as NodeComponentsBuilder<T>>::Components>, + <AO as RethRpcAddOns<NodeAdapter<T, <CB as NodeComponentsBuilder<T>>::Components>>>::EthApi, + >, { /// Returns a reference to the node builder's config. pub const fn config(&self) -> &NodeConfig<<T::Types as NodeTypes>::ChainSpec> {
diff --git reth/crates/node/builder/src/builder/states.rs scroll-reth/crates/node/builder/src/builder/states.rs index c4ced59d493bcbbd67499eeb9ef2d028ec32e213..8e016c264a1d52bc8e6a636704dd7cea23a098d5 100644 --- reth/crates/node/builder/src/builder/states.rs +++ scroll-reth/crates/node/builder/src/builder/states.rs @@ -9,7 +9,7 @@ use crate::{ components::{NodeComponents, NodeComponentsBuilder}, hooks::NodeHooks, launch::LaunchNode, - rpc::{RethRpcAddOns, RethRpcServerHandles, RpcContext}, + rpc::{RethRpcAddOns, RethRpcServerHandles, RpcContext, RpcHandleProvider}, AddOns, FullNode, };   @@ -249,6 +249,12 @@ where T: FullNodeTypes, CB: NodeComponentsBuilder<T>, AO: RethRpcAddOns<NodeAdapter<T, CB::Components>>, + <AO as reth_node_api::NodeAddOns< + NodeAdapter<T, <CB as NodeComponentsBuilder<T>>::Components>, + >>::Handle: RpcHandleProvider< + NodeAdapter<T, <CB as NodeComponentsBuilder<T>>::Components>, + <AO as RethRpcAddOns<NodeAdapter<T, <CB as NodeComponentsBuilder<T>>::Components>>>::EthApi, + >, { /// Launches the node with the given launcher. pub async fn launch_with<L>(self, launcher: L) -> eyre::Result<L::Node>
diff --git reth/crates/node/builder/src/handle.rs scroll-reth/crates/node/builder/src/handle.rs index 2997a8687afdb3114c52255e0ac4407fea725b0e..112c85408b1abf7a67c82164e3c747bb7f548843 100644 --- reth/crates/node/builder/src/handle.rs +++ scroll-reth/crates/node/builder/src/handle.rs @@ -3,11 +3,18 @@ use reth_node_api::FullNodeComponents; use reth_node_core::exit::NodeExitFuture;   -use crate::{node::FullNode, rpc::RethRpcAddOns}; +use crate::{ + node::FullNode, + rpc::{RethRpcAddOns, RpcHandleProvider}, +};   /// A Handle to the launched node. #[must_use = "Needs to await the node exit future"] -pub struct NodeHandle<Node: FullNodeComponents, AddOns: RethRpcAddOns<Node>> { +pub struct NodeHandle<Node: FullNodeComponents, AddOns: RethRpcAddOns<Node>> +where + <AddOns as reth_node_api::NodeAddOns<Node>>::Handle: + RpcHandleProvider<Node, <AddOns as RethRpcAddOns<Node>>::EthApi>, +{ /// All node components. pub node: FullNode<Node, AddOns>, /// The exit future of the node. @@ -18,6 +25,8 @@ impl<Node, AddOns> NodeHandle<Node, AddOns> where Node: FullNodeComponents, AddOns: RethRpcAddOns<Node>, + <AddOns as reth_node_api::NodeAddOns<Node>>::Handle: + RpcHandleProvider<Node, <AddOns as RethRpcAddOns<Node>>::EthApi>, { /// Waits for the node to exit, if it was configured to exit. pub async fn wait_for_node_exit(self) -> eyre::Result<()> { @@ -29,6 +38,8 @@ impl<Node, AddOns> fmt::Debug for NodeHandle<Node, AddOns> where Node: FullNodeComponents, AddOns: RethRpcAddOns<Node>, + <AddOns as reth_node_api::NodeAddOns<Node>>::Handle: + RpcHandleProvider<Node, <AddOns as RethRpcAddOns<Node>>::EthApi>, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("NodeHandle")
diff --git reth/crates/node/builder/src/launch/debug.rs scroll-reth/crates/node/builder/src/launch/debug.rs index 7609616b0312dc717d8c514bd212bf9f1c496a4e..e299399d40aab27f12cb0bcffd34a972ec968fa8 100644 --- reth/crates/node/builder/src/launch/debug.rs +++ scroll-reth/crates/node/builder/src/launch/debug.rs @@ -1,5 +1,8 @@ use super::LaunchNode; -use crate::{rpc::RethRpcAddOns, EngineNodeLauncher, Node, NodeHandle}; +use crate::{ + rpc::{RethRpcAddOns, RpcHandleProvider}, + EngineNodeLauncher, Node, NodeHandle, +}; use alloy_provider::network::AnyNetwork; use jsonrpsee::core::{DeserializeOwned, Serialize}; use reth_chainspec::EthChainSpec; @@ -36,10 +39,16 @@ where N: FullNodeComponents<Types: DebugNode<N>>, AddOns: RethRpcAddOns<N>, L: LaunchNode<Target, Node = NodeHandle<N, AddOns>>, + <AddOns as reth_node_api::NodeAddOns<N>>::Handle: + RpcHandleProvider<N, <AddOns as RethRpcAddOns<N>>::EthApi>, { type Node = NodeHandle<N, AddOns>;   - async fn launch_node(self, target: Target) -> eyre::Result<Self::Node> { + async fn launch_node(self, target: Target) -> eyre::Result<Self::Node> + where + <AddOns as reth_node_api::NodeAddOns<N>>::Handle: + RpcHandleProvider<N, <AddOns as RethRpcAddOns<N>>::EthApi>, + { let handle = self.inner.launch_node(target).await?;   let config = &handle.node.config; @@ -57,7 +66,7 @@ }) .await?;   let rpc_consensus_client = DebugConsensusClient::new( - handle.node.add_ons_handle.beacon_engine_handle.clone(), + handle.node.rpc_handle().beacon_engine_handle.clone(), Arc::new(block_provider), );   @@ -89,7 +98,7 @@ })?, N::Types::rpc_to_primitive_block, ); let rpc_consensus_client = DebugConsensusClient::new( - handle.node.add_ons_handle.beacon_engine_handle.clone(), + handle.node.rpc_handle().beacon_engine_handle.clone(), Arc::new(block_provider), ); handle.node.task_executor.spawn_critical("etherscan consensus client", async move {
diff --git reth/crates/node/builder/src/launch/engine.rs scroll-reth/crates/node/builder/src/launch/engine.rs index 295b4fa191661528d8981cb3f6ea5375d136d4dd..025aaacf8f83e98439af92fb4bd3f2075c460732 100644 --- reth/crates/node/builder/src/launch/engine.rs +++ scroll-reth/crates/node/builder/src/launch/engine.rs @@ -1,13 +1,5 @@ //! Engine node related functionality.   -use crate::{ - common::{Attached, LaunchContextWith, WithConfigs}, - hooks::NodeHooks, - rpc::{EngineValidatorAddOn, RethRpcAddOns, RpcHandle}, - setup::build_networked_pipeline, - AddOns, AddOnsContext, ExExLauncher, FullNode, LaunchContext, LaunchNode, NodeAdapter, - NodeBuilderWithComponents, NodeComponents, NodeComponentsBuilder, NodeHandle, NodeTypesAdapter, -}; use alloy_consensus::BlockHeader; use futures::{future::Either, stream, stream_select, StreamExt}; use reth_chainspec::{EthChainSpec, EthereumHardforks}; @@ -42,6 +34,15 @@ use std::sync::Arc; use tokio::sync::{mpsc::unbounded_channel, oneshot}; use tokio_stream::wrappers::UnboundedReceiverStream;   +use crate::{ + common::{Attached, LaunchContextWith, WithConfigs}, + hooks::NodeHooks, + rpc::{EngineValidatorAddOn, RethRpcAddOns, RpcHandleProvider}, + setup::build_networked_pipeline, + AddOns, AddOnsContext, ExExLauncher, FullNode, LaunchContext, LaunchNode, NodeAdapter, + NodeBuilderWithComponents, NodeComponents, NodeComponentsBuilder, NodeHandle, NodeTypesAdapter, +}; + /// The engine node launcher. #[derive(Debug)] pub struct EngineNodeLauncher { @@ -78,6 +79,12 @@ AO: RethRpcAddOns<NodeAdapter<T, CB::Components>> + EngineValidatorAddOn<NodeAdapter<T, CB::Components>>, LocalPayloadAttributesBuilder<Types::ChainSpec>: PayloadAttributesBuilder< <<Types as NodeTypes>::Payload as PayloadTypes>::PayloadAttributes, + >, + <AO as reth_node_api::NodeAddOns< + NodeAdapter<T, <CB as NodeComponentsBuilder<T>>::Components>, + >>::Handle: RpcHandleProvider< + NodeAdapter<T, <CB as NodeComponentsBuilder<T>>::Components>, + <AO as RethRpcAddOns<NodeAdapter<T, <CB as NodeComponentsBuilder<T>>::Components>>>::EthApi, >, { type Node = NodeHandle<NodeAdapter<T, CB::Components>, AO>; @@ -284,8 +291,7 @@ events, ), );   - let RpcHandle { rpc_server_handles, rpc_registry, engine_events, beacon_engine_handle } = - add_ons.launch_add_ons(add_ons_ctx).await?; + let add_ons_handle = add_ons.launch_add_ons(add_ons_ctx).await?;   // Run consensus engine to completion let initial_target = ctx.initial_backfill_target()?; @@ -370,12 +376,7 @@ payload_builder_handle: ctx.components().payload_builder_handle().clone(), task_executor: ctx.task_executor().clone(), config: ctx.node_config().clone(), data_dir: ctx.data_dir().clone(), - add_ons_handle: RpcHandle { - rpc_server_handles, - rpc_registry, - engine_events, - beacon_engine_handle, - }, + add_ons_handle, }; // Notify on node started on_node_started.on_event(FullNode::clone(&full_node))?;
diff --git reth/crates/node/builder/src/node.rs scroll-reth/crates/node/builder/src/node.rs index 966b1227629d2da82a3e3dc9b95271c5b3556273..b2c8c4894d2a840d1e405f8b1ac697fa6838aa6e 100644 --- reth/crates/node/builder/src/node.rs +++ scroll-reth/crates/node/builder/src/node.rs @@ -1,6 +1,7 @@ // re-export the node api types pub use reth_node_api::{FullNodeTypes, NodeTypes};   +use super::rpc::RpcHandleProvider; use crate::{components::NodeComponentsBuilder, rpc::RethRpcAddOns, NodeAdapter, NodeAddOns}; use reth_node_api::{EngineTypes, FullNodeComponents, PayloadTypes}; use reth_node_core::{ @@ -152,15 +153,17 @@ where Payload: PayloadTypes, Node: FullNodeComponents<Types: NodeTypes<Payload = Payload>>, AddOns: RethRpcAddOns<Node>, + <AddOns as reth_node_api::NodeAddOns<Node>>::Handle: + RpcHandleProvider<Node, <AddOns as RethRpcAddOns<Node>>::EthApi>, { /// Returns the [`RpcServerHandle`] to the started rpc server. - pub const fn rpc_server_handle(&self) -> &RpcServerHandle { - &self.add_ons_handle.rpc_server_handles.rpc + pub fn rpc_server_handle(&self) -> &RpcServerHandle { + &self.add_ons_handle.rpc_handle().rpc_server_handles.rpc }   /// Returns the [`AuthServerHandle`] to the started authenticated engine API server. - pub const fn auth_server_handle(&self) -> &AuthServerHandle { - &self.add_ons_handle.rpc_server_handles.auth + pub fn auth_server_handle(&self) -> &AuthServerHandle { + &self.add_ons_handle.rpc_handle().rpc_server_handles.auth } }   @@ -169,6 +172,8 @@ where Engine: EngineTypes, Node: FullNodeComponents<Types: NodeTypes<Payload = Engine>>, AddOns: RethRpcAddOns<Node>, + <AddOns as reth_node_api::NodeAddOns<Node>>::Handle: + RpcHandleProvider<Node, <AddOns as RethRpcAddOns<Node>>::EthApi>, { /// Returns the [`EngineApiClient`] interface for the authenticated engine API. ///
diff --git reth/crates/node/builder/src/rpc.rs scroll-reth/crates/node/builder/src/rpc.rs index e965dfb2fbd846c464615d49d2111c67c0399c87..90d9d7b8ac516a77f2b837b91f3971839bf31ca8 100644 --- reth/crates/node/builder/src/rpc.rs +++ scroll-reth/crates/node/builder/src/rpc.rs @@ -353,6 +353,20 @@ .finish() } }   +/// Trait to provide access to the RPC handle. +pub trait RpcHandleProvider<Node: FullNodeComponents, EthApi: EthApiTypes> { + /// Returns the rpc server handles. + fn rpc_handle(&self) -> &RpcHandle<Node, EthApi>; +} + +impl<Node: FullNodeComponents, EthApi: EthApiTypes> RpcHandleProvider<Node, EthApi> + for RpcHandle<Node, EthApi> +{ + fn rpc_handle(&self) -> &Self { + self + } +} + /// Handle returned when only the regular RPC server (HTTP/WS/IPC) is launched. /// /// This handle provides access to the RPC server endpoints and registry, but does not @@ -777,8 +791,9 @@ }   /// Helper trait implemented for add-ons producing [`RpcHandle`]. Used by common node launcher /// implementations. -pub trait RethRpcAddOns<N: FullNodeComponents>: - NodeAddOns<N, Handle = RpcHandle<N, Self::EthApi>> +pub trait RethRpcAddOns<N: FullNodeComponents>: NodeAddOns<N> +where + Self::Handle: RpcHandleProvider<N, Self::EthApi>, { /// eth API implementation. type EthApi: EthApiTypes;
diff --git reth/crates/node/core/src/args/payload_builder.rs scroll-reth/crates/node/core/src/args/payload_builder.rs index f751bcc070ccf6629d9af6fcaf5d7b3959df6da0..d658241c21c9c4c06b7d7f143c045204fc209a8b 100644 --- reth/crates/node/core/src/args/payload_builder.rs +++ scroll-reth/crates/node/core/src/args/payload_builder.rs @@ -5,7 +5,7 @@ use clap::{ builder::{RangedU64ValueParser, TypedValueParser}, Arg, Args, Command, }; -use reth_cli_util::{parse_duration_from_secs, parse_duration_from_secs_or_ms}; +use reth_cli_util::parse_duration_from_secs_or_ms; use std::{borrow::Cow, ffi::OsStr, time::Duration};   /// Parameters for configuring the Payload Builder @@ -29,7 +29,7 @@ #[arg(long = "builder.interval", value_parser = parse_duration_from_secs_or_ms, default_value = "1", value_name = "DURATION")] pub interval: Duration,   /// The deadline for when the payload builder job should resolve. - #[arg(long = "builder.deadline", value_parser = parse_duration_from_secs, default_value = "12", value_name = "SECONDS")] + #[arg(long = "builder.deadline", value_parser = parse_duration_from_secs_or_ms, default_value = "12s", value_name = "DEADLINE")] pub deadline: Duration,   /// Maximum number of tasks to spawn for building a payload.
diff --git reth/crates/payload/primitives/Cargo.toml scroll-reth/crates/payload/primitives/Cargo.toml index bb305961fe1db30af6f793835daec93a7473aaca..3953554c456b6342e6a1bf16c0bc8ea7a2229c7d 100644 --- reth/crates/payload/primitives/Cargo.toml +++ scroll-reth/crates/payload/primitives/Cargo.toml @@ -22,7 +22,8 @@ # alloy alloy-eips.workspace = true alloy-primitives.workspace = true alloy-rpc-types-engine = { workspace = true, features = ["serde"] } -op-alloy-rpc-types-engine = { workspace = true, optional = true } +op-alloy-rpc-types-engine = { workspace = true, optional = true, features = ["serde"] } +scroll-alloy-rpc-types-engine = { workspace = true, optional = true, features = ["serde"] }   # misc auto_impl.workspace = true @@ -44,8 +45,10 @@ "op-alloy-rpc-types-engine?/std", "serde/std", "thiserror/std", "reth-primitives-traits/std", + "scroll-alloy-rpc-types-engine?/std", ] op = [ "dep:op-alloy-rpc-types-engine", "reth-primitives-traits/op", ] +scroll-alloy-traits = ["dep:scroll-alloy-rpc-types-engine", "reth-primitives-traits/scroll-alloy-traits"]
diff --git reth/crates/payload/primitives/src/traits.rs scroll-reth/crates/payload/primitives/src/traits.rs index ee503c90005152f2932d04c3135df46fd6e437d6..6a1a69a05ac5710fe35f5a5dafae17f5e5744a58 100644 --- reth/crates/payload/primitives/src/traits.rs +++ scroll-reth/crates/payload/primitives/src/traits.rs @@ -121,6 +121,21 @@ self.payload_attributes.parent_beacon_block_root } }   +#[cfg(feature = "scroll-alloy-traits")] +impl PayloadAttributes for scroll_alloy_rpc_types_engine::ScrollPayloadAttributes { + fn timestamp(&self) -> u64 { + self.payload_attributes.timestamp + } + + fn withdrawals(&self) -> Option<&Vec<Withdrawal>> { + self.payload_attributes.withdrawals.as_ref() + } + + fn parent_beacon_block_root(&self) -> Option<B256> { + self.payload_attributes.parent_beacon_block_root + } +} + /// A builder that can return the current payload attribute. pub trait PayloadAttributesBuilder<Attributes>: Send + Sync + 'static { /// Return a new payload attribute from the builder.
diff --git reth/crates/rpc/rpc-eth-api/src/lib.rs scroll-reth/crates/rpc/rpc-eth-api/src/lib.rs index 3f1cb449cc577e1b7f79a349908b568afdecc86b..3916b5eb6969ade897a8ed2ca00a9d210c0f8b3d 100644 --- reth/crates/rpc/rpc-eth-api/src/lib.rs +++ scroll-reth/crates/rpc/rpc-eth-api/src/lib.rs @@ -31,8 +31,8 @@ pub use reth_rpc_eth_types::error::{ AsEthApiError, FromEthApiError, FromEvmError, IntoEthApiError, }; pub use reth_rpc_types_compat::{ - try_into_op_tx_info, IntoRpcTx, RpcConverter, TransactionCompat, TransactionConversionError, - TryIntoSimTx, TxInfoMapper, + try_into_op_tx_info, try_into_scroll_tx_info, IntoRpcTx, RpcConverter, TransactionCompat, + TransactionConversionError, TryIntoSimTx, TxInfoMapper, }; pub use types::{EthApiTypes, FullEthApiTypes, RpcBlock, RpcHeader, RpcReceipt, RpcTransaction};
diff --git reth/crates/rpc/rpc-eth-types/src/fee_history.rs scroll-reth/crates/rpc/rpc-eth-types/src/fee_history.rs index 0425a38629b75399451075f600d88dc1e233bde1..616b53b86fbe247249bf9d6fa477bbde2cbb4b0f 100644 --- reth/crates/rpc/rpc-eth-types/src/fee_history.rs +++ scroll-reth/crates/rpc/rpc-eth-types/src/fee_history.rs @@ -47,6 +47,7 @@ }   /// How the cache is configured. #[inline] + #[allow(clippy::missing_const_for_fn)] pub fn config(&self) -> &FeeHistoryCacheConfig { &self.inner.config }
diff --git reth/crates/rpc/rpc-types-compat/Cargo.toml scroll-reth/crates/rpc/rpc-types-compat/Cargo.toml index 51f2299eaa98072b51fb436604c2ef092d81bf97..0fa228c7d04f38c09b02949fa625bdd7653b2e9c 100644 --- reth/crates/rpc/rpc-types-compat/Cargo.toml +++ scroll-reth/crates/rpc/rpc-types-compat/Cargo.toml @@ -22,6 +22,11 @@ alloy-rpc-types-eth = { workspace = true, default-features = false, features = ["serde"] } alloy-consensus.workspace = true alloy-network.workspace = true   +# scroll +reth-scroll-primitives = { workspace = true, features = ["serde", "serde-bincode-compat"] } +scroll-alloy-consensus.workspace = true +scroll-alloy-rpc-types.workspace = true + # optimism op-alloy-consensus.workspace = true op-alloy-rpc-types.workspace = true
diff --git reth/crates/rpc/rpc-types-compat/src/lib.rs scroll-reth/crates/rpc/rpc-types-compat/src/lib.rs index b203691263a437c2a9cce0400f2b1865b100bd32..8b0e5a4d0eb99e989e81cd2407ed9e3631f1b2f0 100644 --- reth/crates/rpc/rpc-types-compat/src/lib.rs +++ scroll-reth/crates/rpc/rpc-types-compat/src/lib.rs @@ -13,6 +13,6 @@ pub mod block; pub mod transaction; pub use transaction::{ - try_into_op_tx_info, IntoRpcTx, RpcConverter, TransactionCompat, TransactionConversionError, - TryIntoSimTx, TxInfoMapper, + try_into_op_tx_info, try_into_scroll_tx_info, IntoRpcTx, RpcConverter, TransactionCompat, + TransactionConversionError, TryIntoSimTx, TxInfoMapper, };
diff --git reth/crates/rpc/rpc-types-compat/src/transaction.rs scroll-reth/crates/rpc/rpc-types-compat/src/transaction.rs index ca531ed9e009ff6a66932d4308ccb3675d794d12..40bac0a985d6e173e2147cceea511cfed9f9900e 100644 --- reth/crates/rpc/rpc-types-compat/src/transaction.rs +++ scroll-reth/crates/rpc/rpc-types-compat/src/transaction.rs @@ -14,7 +14,10 @@ }; use op_alloy_rpc_types::OpTransactionRequest; use reth_optimism_primitives::DepositReceipt; use reth_primitives_traits::{NodePrimitives, SignedTransaction, TxTy}; +use reth_scroll_primitives::ScrollReceipt; use reth_storage_api::{errors::ProviderError, ReceiptProvider}; +use scroll_alloy_consensus::{ScrollAdditionalInfo, ScrollTransactionInfo, ScrollTxEnvelope}; +use scroll_alloy_rpc_types::ScrollTransactionRequest; use serde::{Deserialize, Serialize}; use std::{convert::Infallible, error::Error, fmt::Debug, marker::PhantomData}; use thiserror::Error; @@ -139,6 +142,25 @@ Ok(OpTransactionInfo::new(tx_info, deposit_meta)) }   +/// Creates [`ScrollTransactionInfo`] by adding [`ScrollAdditionalInfo`] to [`TransactionInfo`] if +/// `tx` is not a L1 message. +pub fn try_into_scroll_tx_info<T: ReceiptProvider<Receipt = ScrollReceipt>>( + provider: &T, + tx: &ScrollTxEnvelope, + tx_info: TransactionInfo, +) -> Result<ScrollTransactionInfo, ProviderError> { + let additional_info = if tx.is_l1_message() { + None + } else { + provider + .receipt_by_hash(*tx.tx_hash())? + .map(|receipt| ScrollAdditionalInfo { l1_fee: receipt.l1_fee() }) + } + .unwrap_or_default(); + + Ok(ScrollTransactionInfo::new(tx_info, additional_info)) +} + impl IntoRpcTx<op_alloy_rpc_types::Transaction> for OpTxEnvelope { type TxInfo = OpTransactionInfo;   @@ -151,6 +173,18 @@ op_alloy_rpc_types::Transaction::from_transaction(self.with_signer(signer), tx_info) } }   +impl IntoRpcTx<scroll_alloy_rpc_types::Transaction> for ScrollTxEnvelope { + type TxInfo = ScrollTransactionInfo; + + fn into_rpc_tx( + self, + signer: Address, + tx_info: Self::TxInfo, + ) -> scroll_alloy_rpc_types::Transaction { + scroll_alloy_rpc_types::Transaction::from_transaction(self.with_signer(signer), tx_info) + } +} + impl TryIntoSimTx<EthereumTxEnvelope<TxEip4844>> for TransactionRequest { fn try_into_sim_tx(self) -> Result<EthereumTxEnvelope<TxEip4844>, ValueError<Self>> { Self::build_typed_simulate_transaction(self) @@ -160,6 +194,20 @@ impl TryIntoSimTx<OpTxEnvelope> for TransactionRequest { fn try_into_sim_tx(self) -> Result<OpTxEnvelope, ValueError<Self>> { let request: OpTransactionRequest = self.into(); + let tx = request.build_typed_tx().map_err(|request| { + ValueError::new(request.as_ref().clone(), "Required fields missing") + })?; + + // Create an empty signature for the transaction. + let signature = Signature::new(Default::default(), Default::default(), false); + + Ok(tx.into_signed(signature).into()) + } +} + +impl TryIntoSimTx<ScrollTxEnvelope> for TransactionRequest { + fn try_into_sim_tx(self) -> Result<ScrollTxEnvelope, ValueError<Self>> { + let request: ScrollTransactionRequest = self.into(); let tx = request.build_typed_tx().map_err(|request| { ValueError::new(request.as_ref().clone(), "Required fields missing") })?;
diff --git reth/crates/rpc/rpc/src/debug.rs scroll-reth/crates/rpc/rpc/src/debug.rs index 4ca8317f5c3ad5681b02bddd38cf347f8006d4c1..3f9518fd38c5173065226b20160395881170990e 100644 --- reth/crates/rpc/rpc/src/debug.rs +++ scroll-reth/crates/rpc/rpc/src/debug.rs @@ -62,6 +62,7 @@ Self { inner } }   /// Access the underlying `Eth` API. + #[allow(clippy::missing_const_for_fn)] pub fn eth_api(&self) -> &Eth { &self.inner.eth_api }
diff --git reth/crates/rpc/rpc/src/eth/bundle.rs scroll-reth/crates/rpc/rpc/src/eth/bundle.rs index 07ed06fdc90903c830341335afe76e0499280790..1c67139582e252102cdbdb1067d799ea5deb194f 100644 --- reth/crates/rpc/rpc/src/eth/bundle.rs +++ scroll-reth/crates/rpc/rpc/src/eth/bundle.rs @@ -34,6 +34,7 @@ Self { inner: Arc::new(EthBundleInner { eth_api, blocking_task_guard }) } }   /// Access the underlying `Eth` API. + #[allow(clippy::missing_const_for_fn)] pub fn eth_api(&self) -> &Eth { &self.inner.eth_api }
diff --git reth/crates/rpc/rpc/src/eth/filter.rs scroll-reth/crates/rpc/rpc/src/eth/filter.rs index f44d5b7c572b27987e98309340c7ad4d24237ca9..1f67bee89580afb6ac71a200c5724e031b789ba3 100644 --- reth/crates/rpc/rpc/src/eth/filter.rs +++ scroll-reth/crates/rpc/rpc/src/eth/filter.rs @@ -134,6 +134,7 @@ eth_filter }   /// Returns all currently active filters + #[allow(clippy::missing_const_for_fn)] pub fn active_filters(&self) -> &ActiveFilters<RpcTransaction<Eth::NetworkTypes>> { &self.inner.active_filters }
diff --git reth/crates/rpc/rpc/src/eth/sim_bundle.rs scroll-reth/crates/rpc/rpc/src/eth/sim_bundle.rs index 0bd3fb67076b1368776c57057e9fe59c047f9458..9bd78ad851e35e657fa25cd1499b61203f568692 100644 --- reth/crates/rpc/rpc/src/eth/sim_bundle.rs +++ scroll-reth/crates/rpc/rpc/src/eth/sim_bundle.rs @@ -74,6 +74,7 @@ Self { inner: Arc::new(EthSimBundleInner { eth_api, blocking_task_guard }) } }   /// Access the underlying `Eth` API. + #[allow(clippy::missing_const_for_fn)] pub fn eth_api(&self) -> &Eth { &self.inner.eth_api }
diff --git reth/crates/rpc/rpc/src/reth.rs scroll-reth/crates/rpc/rpc/src/reth.rs index 8f8decd7f4aac9b617dc848ab2ed4567b036787f..3aaa1ebc5e0b61269a0e29ba0364aa872704ff29 100644 --- reth/crates/rpc/rpc/src/reth.rs +++ scroll-reth/crates/rpc/rpc/src/reth.rs @@ -27,6 +27,7 @@ // === impl RethApi ===   impl<Provider> RethApi<Provider> { /// The provider that can interact with the chain. + #[allow(clippy::missing_const_for_fn)] pub fn provider(&self) -> &Provider { &self.inner.provider }
diff --git reth/crates/rpc/rpc/src/trace.rs scroll-reth/crates/rpc/rpc/src/trace.rs index a2c621045d08073427bbc7248eb0a6bc329dfa6a..4e620ec185287b8db2ed33dcae90ef0866e08743 100644 --- reth/crates/rpc/rpc/src/trace.rs +++ scroll-reth/crates/rpc/rpc/src/trace.rs @@ -64,6 +64,7 @@ self.inner.blocking_task_guard.clone().acquire_owned().await }   /// Access the underlying `Eth` API. + #[allow(clippy::missing_const_for_fn)] pub fn eth_api(&self) -> &Eth { &self.inner.eth_api }
diff --git reth/crates/transaction-pool/Cargo.toml scroll-reth/crates/transaction-pool/Cargo.toml index 7ea980542c083af6cd1d92a2c3fa91f668af161b..3e826f34707d947fce36b3dac7dd697bee0a578c 100644 --- reth/crates/transaction-pool/Cargo.toml +++ scroll-reth/crates/transaction-pool/Cargo.toml @@ -59,6 +59,7 @@ proptest = { workspace = true, optional = true } proptest-arbitrary-interop = { workspace = true, optional = true }   [dev-dependencies] +alloy-consensus = { workspace = true, features = ["k256"] } reth-provider = { workspace = true, features = ["test-utils"] } reth-tracing.workspace = true alloy-primitives = { workspace = true, features = ["rand"] }
diff --git reth/crates/transaction-pool/src/lib.rs scroll-reth/crates/transaction-pool/src/lib.rs index 8250a8613c480343834230665ad7ce17bb44439f..1a72585fc80744573f0327de4c4bf3931bd1c696 100644 --- reth/crates/transaction-pool/src/lib.rs +++ scroll-reth/crates/transaction-pool/src/lib.rs @@ -233,6 +233,7 @@ Self { pool: Arc::new(PoolInner::new(validator, ordering, blob_store, config)) } }   /// Returns the wrapped pool. + #[allow(clippy::missing_const_for_fn)] pub(crate) fn inner(&self) -> &PoolInner<V, T, S> { &self.pool }
diff --git reth/crates/transaction-pool/src/pool/mod.rs scroll-reth/crates/transaction-pool/src/pool/mod.rs index 90bd7dcb20785f2c32681061bbbb87a432476415..20b4b076e973057942e9991310f077651b6c8465 100644 --- reth/crates/transaction-pool/src/pool/mod.rs +++ scroll-reth/crates/transaction-pool/src/pool/mod.rs @@ -1027,6 +1027,7 @@ PendingTransactionIter { kind, iter } }   /// Returns if the transaction should be propagated. + #[allow(clippy::missing_const_for_fn)] pub(crate) fn is_propagate_allowed(&self) -> bool { self.transaction.propagate } @@ -1117,6 +1118,7 @@ } }   /// Returns the discarded transactions if there were any + #[allow(clippy::missing_const_for_fn)] pub(crate) fn discarded_transactions(&self) -> Option<&[Arc<ValidPoolTransaction<T>>]> { match self { Self::Pending(tx) => Some(&tx.discarded), @@ -1160,6 +1162,7 @@ }   /// Returns the [`TransactionId`] of the added transaction #[cfg(test)] + #[allow(clippy::missing_const_for_fn)] pub(crate) fn id(&self) -> &TransactionId { match self { Self::Pending(added) => added.transaction.id(),
diff --git reth/crates/transaction-pool/src/pool/pending.rs scroll-reth/crates/transaction-pool/src/pool/pending.rs index fde9c36df4594352332ce9ea73c817e42e7432ec..d3e65f711fa4c517b6c579e8ee929970cfd93731 100644 --- reth/crates/transaction-pool/src/pool/pending.rs +++ scroll-reth/crates/transaction-pool/src/pool/pending.rs @@ -579,6 +579,7 @@ }   impl<T: TransactionOrdering> PendingTransaction<T> { /// The next transaction of the sender: `nonce + 1` + #[allow(clippy::missing_const_for_fn)] pub(crate) fn unlocks(&self) -> TransactionId { self.transaction.transaction_id.descendant() }
diff --git reth/crates/transaction-pool/src/validate/eth.rs scroll-reth/crates/transaction-pool/src/validate/eth.rs index a5f85ac6edb02d0cf1c7ad35b16e1246358a1293..1fb628d57c211302de3ab3f432d4ad2c7864e768 100644 --- reth/crates/transaction-pool/src/validate/eth.rs +++ scroll-reth/crates/transaction-pool/src/validate/eth.rs @@ -58,6 +58,7 @@ self.client().chain_spec() }   /// Returns the configured client + #[allow(clippy::missing_const_for_fn)] pub fn client(&self) -> &Client { &self.inner.client } @@ -210,6 +211,7 @@ // === impl EthTransactionValidatorInner ===   impl<Client: ChainSpecProvider, Tx> EthTransactionValidatorInner<Client, Tx> { /// Returns the configured chain id + #[allow(clippy::missing_const_for_fn)] pub(crate) fn chain_id(&self) -> u64 { self.client.chain_spec().chain().id() } @@ -1091,11 +1093,17 @@ SpecId::SHANGHAI } else { SpecId::MERGE }; + // TODO(scroll): SpecId is starting to leak from revm to reth. Find a solution to avoid + // having to add `is_eip_7702_enabled` everywhere. + let is_eip7702_enabled = true; + let is_eip7623_enabled = true;   let gas = revm_interpreter::gas::calculate_initial_tx_gas( spec_id, transaction.input(), transaction.is_create(), + is_eip7702_enabled, + is_eip7623_enabled, transaction.access_list().map(|l| l.len()).unwrap_or_default() as u64, transaction .access_list()
diff --git reth/deny.toml scroll-reth/deny.toml index fd2eb5c11cdf24e638c0e0470a571b26f9d8500c..9ce15ab1b65b8b52ac978f0480f4e88669e6292a 100644 --- reth/deny.toml +++ scroll-reth/deny.toml @@ -20,7 +20,7 @@ # Lint level for when a crate version requirement is `*` wildcards = "allow" highlight = "all" # List of crates to deny -deny = [{ name = "openssl" }] +# TODO issue #201 deny = [{ name = "openssl" }] # Certain crates/versions that will be skipped when doing duplicate detection. skip = [] # Similarly to `skip` allows you to skip certain crates during duplicate @@ -65,6 +65,21 @@ { allow = ["MPL-2.0"], name = "option-ext" }, { allow = ["MPL-2.0"], name = "webpki-root-certs" }, ]   +# Skip the poseidon-bn254, bn254 and zktrie crates for license verification. We should at some point publish a license for them. +[licenses.private] +ignore = true +ignore-sources = [ + "https://github.com/scroll-tech/poseidon-bn254", + "https://github.com/scroll-tech/bn254", + "https://github.com/scroll-tech/zktrie.git", + "https://github.com/scroll-tech/scroll-revm.git", +] + +[[licenses.clarify]] +name = "ring" +expression = "LicenseRef-ring" +license-files = [{ path = "LICENSE", hash = 0xbd0eed23 }] + [[licenses.clarify]] name = "rustls-webpki" expression = "LicenseRef-rustls-webpki" @@ -89,4 +104,9 @@ "https://github.com/paradigmxyz/revm-inspectors", "https://github.com/alloy-rs/evm", "https://github.com/alloy-rs/hardforks", "https://github.com/paradigmxyz/jsonrpsee", + "https://github.com/scroll-tech/bn254", + "https://github.com/scroll-tech/sp1-intrinsics", + "https://github.com/scroll-tech/poseidon-bn254", + "https://github.com/scroll-tech/scroll-revm.git", + "https://github.com/scroll-tech/revm.git", ]
diff --git reth/examples/custom-node/src/primitives/header.rs scroll-reth/examples/custom-node/src/primitives/header.rs index 806560eaf842e2a28e0b1ace7ce421073e97419b..884c9c4cb1c45f7a8281b1d89a51d85978080799 100644 --- reth/examples/custom-node/src/primitives/header.rs +++ scroll-reth/examples/custom-node/src/primitives/header.rs @@ -5,6 +5,7 @@ }; use alloy_rlp::{Encodable, RlpDecodable, RlpEncodable}; use reth_codecs::Compact; use reth_ethereum::primitives::{BlockHeader, InMemorySize}; +use reth_op::primitives::block::header::BlockHeaderMut; use revm_primitives::keccak256; use serde::{Deserialize, Serialize};   @@ -182,6 +183,12 @@ } }   impl BlockHeader for CustomHeader {} + +impl BlockHeaderMut for CustomHeader { + fn extra_data_mut(&mut self) -> &mut Bytes { + &mut self.inner.extra_data + } +}   mod serde_bincode_compat { use alloy_consensus::serde_bincode_compat::Header;
diff --git reth/.github/assets/check_rv32imac.sh scroll-reth/.github/assets/check_rv32imac.sh index 9d9c421ca208c14d9f85466a5cb993eefc0a54b4..49bea6a900d96c540288e18633fbd6533ddbe672 100755 --- reth/.github/assets/check_rv32imac.sh +++ scroll-reth/.github/assets/check_rv32imac.sh @@ -36,6 +36,14 @@ reth-optimism-forks reth-optimism-consensus reth-optimism-primitives reth-optimism-evm + + ## scroll + reth-scroll-chainspec + scroll-alloy-consensus + scroll-alloy-evm + scroll-alloy-rpc-types + scroll-alloy-rpc-types-engine + )   # Array to hold the results
diff --git reth/.github/assets/check_wasm.sh scroll-reth/.github/assets/check_wasm.sh index 5504cb17e62f1a997bbb3926b47202e459d334ca..e759a549283b9ca3bae09d95a9308dabb8b36cb2 100755 --- reth/.github/assets/check_wasm.sh +++ scroll-reth/.github/assets/check_wasm.sh @@ -74,6 +74,18 @@ reth-optimism-txpool # reth-transaction-pool reth-era-downloader # tokio reth-era-utils # tokio reth-tracing-otlp + reth-scroll-cli # tokio + reth-scroll-node # tokio + reth-scroll # tokio + reth-scroll-state-commitment # tokio + reth-scroll-chainspec # tokio + reth-scroll-consensus # c-kzg + reth-scroll-evm # tokio + reth-scroll-rpc #tokio + reth-scroll-engine-primitives # proptest + reth-scroll-payload # c-kzg + reth-scroll-primitives # c-kzg + reth-scroll-txpool )   # Array to hold the results
diff --git reth/.github/workflows/bench.yml scroll-reth/.github/workflows/bench.yml index 43bb159dfd3ae86cfde61288bf1bc7f5fbd6c684..2ce5c45dd28b422d71adb49b4c779e0e77464681 100644 --- reth/.github/workflows/bench.yml +++ scroll-reth/.github/workflows/bench.yml @@ -5,7 +5,7 @@ pull_request: # TODO: Disabled temporarily for https://github.com/CodSpeedHQ/runner/issues/55 # merge_group: push: - branches: [main] + branches: [scroll]   env: CARGO_TERM_COLOR: always @@ -15,8 +15,7 @@ name: bench jobs: codspeed: - runs-on: - group: Reth + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: @@ -36,4 +35,3 @@ - name: Run the benchmarks uses: CodSpeedHQ/action@v3 with: run: cargo codspeed run --workspace - token: ${{ secrets.CODSPEED_TOKEN }}
diff --git reth/.github/workflows/book.yml scroll-reth/.github/workflows/book.yml index 837d47e9f84c5f3844ec6f0eb48fa16d696b7249..ac6650624c3adbde641d8ef32f326cf2676bf959 100644 --- reth/.github/workflows/book.yml +++ scroll-reth/.github/workflows/book.yml @@ -4,9 +4,9 @@ name: book   on: push: - branches: [main] + branches: [main, scroll] pull_request: - branches: [main] + branches: [main, scroll] merge_group:   jobs:
diff --git reth/.github/workflows/compact.yml scroll-reth/.github/workflows/compact.yml index 06ddec20cb7c3b730b05f37ed6c7ef7ffa26ec4a..f87f97fa25433875dbc1fb5ca62f56e12348d3d7 100644 --- reth/.github/workflows/compact.yml +++ scroll-reth/.github/workflows/compact.yml @@ -9,7 +9,7 @@ on: pull_request: merge_group: push: - branches: [main] + branches: [scroll]   env: CARGO_TERM_COLOR: always @@ -17,13 +17,13 @@ name: compact-codec jobs: compact-codec: - runs-on: - group: Reth + runs-on: ubuntu-latest strategy: matrix: bin: - cargo run --bin reth --features "dev" - cargo run --bin op-reth --features "dev" --manifest-path crates/optimism/bin/Cargo.toml + - cargo run --bin scroll-reth --features "skip-state-root-validation dev" --manifest-path crates/scroll/bin/scroll-reth/Cargo.toml steps: - uses: rui314/setup-mold@v1 - uses: dtolnay/rust-toolchain@stable @@ -33,8 +33,8 @@ cache-on-failure: true - name: Checkout base uses: actions/checkout@v4 with: - ref: ${{ github.base_ref || 'main' }} - # On `main` branch, generates test vectors and serializes them to disk using `Compact`. + ref: ${{ github.base_ref || 'scroll' }} + # On `scroll` branch, generates test vectors and serializes them to disk using `Compact`. - name: Generate compact vectors run: | ${{ matrix.bin }} -- test-vectors compact --write
diff --git reth/.github/workflows/deny.yml scroll-reth/.github/workflows/deny.yml new file mode 100644 index 0000000000000000000000000000000000000000..6908a3d5a56117cac1a581d25953fa4670ba45cd --- /dev/null +++ scroll-reth/.github/workflows/deny.yml @@ -0,0 +1,27 @@ +# Runs `cargo-deny` when modifying `Cargo.lock`. + +name: deny + +on: + push: + branches: [main, scroll] + paths: [Cargo.lock] + pull_request: + branches: [main, scroll] + paths: [Cargo.lock] + merge_group: + +env: + CARGO_TERM_COLOR: always + +concurrency: deny-${{ github.head_ref || github.run_id }} + +jobs: + deny: + name: deny + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: EmbarkStudios/cargo-deny-action@v2 + with: + command: check all
diff --git reth/.github/workflows/hive.yml scroll-reth/.github/workflows/hive.yml index afba4c126dda06f519b650c1dbcac69105f09ce0..7bab615fbc172c27ce824afb02a4aeac59e0ecb8 100644 --- reth/.github/workflows/hive.yml +++ scroll-reth/.github/workflows/hive.yml @@ -24,8 +24,7 @@ prepare-hive: if: github.repository == 'paradigmxyz/reth' timeout-minutes: 45 - runs-on: - group: Reth + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Checkout hive tests @@ -147,8 +146,7 @@ needs: - prepare-reth - prepare-hive name: run ${{ matrix.scenario.sim }}${{ matrix.scenario.limit && format(' - {0}', matrix.scenario.limit) }} - runs-on: - group: Reth + runs-on: ubuntu-latest permissions: issues: write steps: @@ -215,8 +213,7 @@ cat hivetests/workspace/logs/reth/client-*.log notify-on-error: needs: test if: failure() - runs-on: - group: Reth + runs-on: ubuntu-latest steps: - name: Slack Webhook Action uses: rtCamp/action-slack-notify@v2
diff --git reth/.github/workflows/integration.yml scroll-reth/.github/workflows/integration.yml index aad87b0fea84d875b0e9daec53cb25fcf4a93e60..3e9a8139dc7c45b8d0e4650d177c66a439bfe04c 100644 --- reth/.github/workflows/integration.yml +++ scroll-reth/.github/workflows/integration.yml @@ -6,10 +6,7 @@ on: pull_request: merge_group: push: - branches: [main] - schedule: - # Run once a day at 3:00 UTC - - cron: '0 3 * * *' + branches: [main, scroll]   env: CARGO_TERM_COLOR: always @@ -22,9 +19,7 @@ jobs: test: name: test / ${{ matrix.network }} - if: github.event_name != 'schedule' - runs-on: - group: Reth + runs-on: ubuntu-latest env: RUST_BACKTRACE: 1 strategy: @@ -57,7 +52,7 @@ integration-success: name: integration success runs-on: ubuntu-latest - if: always() && github.event_name != 'schedule' + if: always() needs: [test] timeout-minutes: 30 steps: @@ -66,7 +61,7 @@ uses: re-actors/alls-green@release/v1 with: jobs: ${{ toJSON(needs) }}   - era-files: + era-files: name: era1 file integration tests once a day if: github.event_name == 'schedule' runs-on: ubuntu-latest
diff --git reth/.github/workflows/lint.yml scroll-reth/.github/workflows/lint.yml index 5d67bee3de0bad62f634e5467b7f4c92cea06a12..639f5affb133fa70359f509a602a920e3f79c51e 100644 --- reth/.github/workflows/lint.yml +++ scroll-reth/.github/workflows/lint.yml @@ -4,7 +4,7 @@ on: pull_request: merge_group: push: - branches: [main] + branches: [main, scroll]   env: CARGO_TERM_COLOR: always @@ -20,9 +20,13 @@ include: - type: ethereum args: --workspace --lib --examples --tests --benches --locked features: "ethereum asm-keccak jemalloc jemalloc-prof min-error-logs min-warn-logs min-info-logs min-debug-logs min-trace-logs" - - type: book - args: --manifest-path book/sources/Cargo.toml --workspace --bins - features: "" + - type: scroll + args: --bin scroll-reth --workspace --lib --examples --tests --benches --locked + features: "skip-state-root-validation asm-keccak jemalloc jemalloc-prof min-error-logs min-warn-logs min-info-logs min-debug-logs min-trace-logs" + # issue <https://github.com/scroll-tech/reth/issues/250> + # - type: book + # args: --manifest-path book/sources/Cargo.toml --workspace --bins + # features: "" steps: - uses: actions/checkout@v4 - uses: rui314/setup-mold@v1 @@ -96,7 +100,7 @@ run: .github/assets/check_rv32imac.sh   crate-checks: runs-on: ubuntu-latest - timeout-minutes: 30 + timeout-minutes: 45 steps: - uses: actions/checkout@v4 - uses: rui314/setup-mold@v1 @@ -106,6 +110,7 @@ - uses: Swatinem/rust-cache@v2 with: cache-on-failure: true - run: cargo hack check --workspace + - run: cargo check -p scroll-reth   msrv: name: MSRV @@ -174,7 +179,6 @@ with: cache-on-failure: true - uses: taiki-e/install-action@cargo-udeps - run: cargo udeps --workspace --lib --examples --tests --benches --all-features --locked - book: name: book runs-on: ubuntu-latest @@ -237,7 +241,7 @@ # Checks that selected rates can compile with power set of features features: name: features runs-on: ubuntu-latest - timeout-minutes: 30 + timeout-minutes: 60 steps: - uses: actions/checkout@v4 - uses: rui314/setup-mold@v1 @@ -270,6 +274,21 @@ deny: uses: ithacaxyz/ci/.github/workflows/deny.yml@main   + openvm: + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: nightly-2025-02-14 + - uses: Swatinem/rust-cache@v2 + with: + cache-on-failure: true + - run: cargo install --locked --git http://github.com/openvm-org/openvm.git --tag v1.2.0 cargo-openvm + - name: verify openvm compatibility + run: cargo openvm build --manifest-dir crates/scroll/openvm-compat + lint-success: name: lint success runs-on: ubuntu-latest @@ -289,6 +308,7 @@ - no-test-deps - features - feature-propagation - deny + - openvm timeout-minutes: 30 steps: - name: Decide whether the needed jobs succeeded or failed
diff --git reth/.github/workflows/pages.yaml scroll-reth/.github/workflows/pages.yaml new file mode 100644 index 0000000000000000000000000000000000000000..5717d4c95543c09d59a7d51803826ead2641a5c5 --- /dev/null +++ scroll-reth/.github/workflows/pages.yaml @@ -0,0 +1,33 @@ +name: Build and publish forkdiff github-pages +permissions: + contents: write +on: + push: + branches: + - scroll +jobs: + deploy: + concurrency: ci-${{ github.ref }} + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 1000 # make sure to fetch the old commit we diff against + + - name: Build forkdiff + uses: "docker://protolambda/forkdiff:0.1.0" + with: + args: -repo=/github/workspace -fork=/github/workspace/fork.yaml -out=/github/workspace/index.html + + - name: Build pages + run: | + mkdir -p tmp/pages + mv index.html tmp/pages/index.html + touch tmp/pages/.nojekyll + + - name: Deploy + uses: JamesIves/github-pages-deploy-action@v4 + with: + folder: tmp/pages + clean: true \ No newline at end of file
diff --git reth/.github/workflows/prepare-reth.yml scroll-reth/.github/workflows/prepare-reth.yml index 422eba19d16738c729bbd73aad7a592537842af0..9b04eea2f706c107191e94d66f7ae0219dcef815 100644 --- reth/.github/workflows/prepare-reth.yml +++ scroll-reth/.github/workflows/prepare-reth.yml @@ -26,8 +26,7 @@ jobs: prepare-reth: if: github.repository == 'paradigmxyz/reth' timeout-minutes: 45 - runs-on: - group: Reth + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: mkdir artifacts
diff --git reth/.github/workflows/release.yml scroll-reth/.github/workflows/release.yml index 3fdcaf6d9c3919436cab53f9f970e1a01076efc8..87c85561478fcb6a075d0f9ebff1a5fb06c14fbc 100644 --- reth/.github/workflows/release.yml +++ scroll-reth/.github/workflows/release.yml @@ -285,4 +285,4 @@ echo "- Docker images would be pushed to registry" echo "- A draft release would be created" echo "" echo "### Next Steps" - echo "To perform a real release, push a git tag." + echo "To perform a real release, push a git tag." \ No newline at end of file
diff --git reth/.github/workflows/stage.yml scroll-reth/.github/workflows/stage.yml index 5c3262827626c52a94a993a4a7ea55acdfd60e1e..95d784257a56ab0a915090773691080530b47387 100644 --- reth/.github/workflows/stage.yml +++ scroll-reth/.github/workflows/stage.yml @@ -6,7 +6,7 @@ on: pull_request: merge_group: push: - branches: [main] + branches: [ main, scroll ]   env: CARGO_TERM_COLOR: always @@ -22,8 +22,7 @@ stage: name: stage-run-test # Only run stage commands test in merge groups if: github.event_name == 'merge_group' - runs-on: - group: Reth + runs-on: ubuntu-latest env: RUST_LOG: info,sync=error RUST_BACKTRACE: 1
diff --git reth/.github/workflows/stale.yml scroll-reth/.github/workflows/stale.yml index 38cca2fb1a9b895f2c548d9db60678c3d6bea241..0d51a0e4eae60c0a8e5a7383eb134b85966b638a 100644 --- reth/.github/workflows/stale.yml +++ scroll-reth/.github/workflows/stale.yml @@ -4,8 +4,6 @@ name: stale issues   on: workflow_dispatch: {} - schedule: - - cron: "30 1 * * *"   jobs: close-issues:
diff --git reth/.github/workflows/sync.yml scroll-reth/.github/workflows/sync.yml index 952cab361abaf40431e4ed6c754f82612cdbae8e..e7d1c93f18b544744d6ede275cecf38a4938f91d 100644 --- reth/.github/workflows/sync.yml +++ scroll-reth/.github/workflows/sync.yml @@ -4,6 +4,8 @@ name: sync test   on: merge_group: + push: + branches: [main, scroll]   env: CARGO_TERM_COLOR: always @@ -15,8 +17,7 @@ jobs: sync: name: sync (${{ matrix.chain.bin }}) - runs-on: - group: Reth + runs-on: ubuntu-latest env: RUST_LOG: info,sync=error RUST_BACKTRACE: 1 @@ -36,6 +37,12 @@ chain: base tip: "0xbb9b85352c7ebca6ba8efc63bd66cecd038c92ec8ebd02e153a3e0b197e672b7" block: 10000 unwind-target: "0x118a6e922a8c6cab221fc5adfe5056d2b72d58c6580e9c5629de55299e2cf8de" + - build: install-scroll + bin: scroll-reth + chain: scroll-mainnet + tip: "0x1f398ce1e03b9d7d7fcba8512dc43c9f84ecaffb15954ab178fab48151e84484" + block: 50000 + unwind-target: "0xc434910471ff41b3f097360b8c5d20459018023833081192dbe490a12ae2937f" steps: - uses: actions/checkout@v4 - uses: rui314/setup-mold@v1 @@ -51,6 +58,7 @@ ${{ matrix.chain.bin }} node \ --chain ${{ matrix.chain.chain }} \ --debug.tip ${{ matrix.chain.tip }} \ --debug.max-block ${{ matrix.chain.block }} \ + --builder.gaslimit 20000000 \ --debug.terminate - name: Verify the target block hash run: |
diff --git reth/.github/workflows/unit.yml scroll-reth/.github/workflows/unit.yml index 767a3e5c0adf4b722ee6cac7620b8f4213dc3978..1cfacc4dbaeed1f0ae6ec690379ce423735e9595 100644 --- reth/.github/workflows/unit.yml +++ scroll-reth/.github/workflows/unit.yml @@ -6,7 +6,7 @@ on: pull_request: merge_group: push: - branches: [main] + branches: [main, scroll]   env: CARGO_TERM_COLOR: always @@ -20,7 +20,7 @@ jobs: test: name: test / ${{ matrix.type }} (${{ matrix.partition }}/${{ matrix.total_partitions }}) runs-on: - group: Reth + group: scroll-reth-runner-group env: RUST_BACKTRACE: 1 strategy: @@ -42,12 +42,19 @@ - type: optimism args: --features "asm-keccak" --locked --exclude reth --exclude reth-bench --exclude "example-*" --exclude "reth-ethereum-*" --exclude "*-ethereum" partition: 2 total_partitions: 2 - - type: book - args: --manifest-path book/sources/Cargo.toml + - type: scroll + args: -p "reth-scroll-*" -p "scroll-alloy-*" --locked partition: 1 total_partitions: 1 + # issue <https://github.com/scroll-tech/reth/issues/250> + # - type: book + # args: --manifest-path book/sources/Cargo.toml + # partition: 1 + # total_partitions: 1 timeout-minutes: 30 steps: + - name: Free up disk space + run: rm -rf /opt/hostedtoolcache - uses: actions/checkout@v4 - uses: rui314/setup-mold@v1 - uses: dtolnay/rust-toolchain@stable @@ -64,18 +71,19 @@ run: | cargo nextest run \ ${{ matrix.args }} --workspace \ --exclude ef-tests --no-tests=warn \ - --partition hash:${{ matrix.partition }}/2 \ + --partition hash:${{ matrix.partition }}/${{ matrix.total_partitions }} \ -E "!kind(test)"   state: name: Ethereum state tests - runs-on: - group: Reth + runs-on: ubuntu-latest env: RUST_LOG: info,sync=error RUST_BACKTRACE: 1 timeout-minutes: 30 steps: + - name: Free up disk space + run: rm -rf /opt/hostedtoolcache - uses: actions/checkout@v4 - name: Checkout ethereum/tests uses: actions/checkout@v4 @@ -95,8 +103,7 @@ - run: cargo nextest run --release -p ef-tests --features "asm-keccak ef-tests"   doc: name: doc tests - runs-on: - group: Reth + runs-on: ubuntu-latest env: RUST_BACKTRACE: 1 timeout-minutes: 30
diff --git reth/fork.yaml scroll-reth/fork.yaml new file mode 100644 index 0000000000000000000000000000000000000000..c3db7adb2f5c6e53e0470ae2f4da1c4dbe71bf9b --- /dev/null +++ scroll-reth/fork.yaml @@ -0,0 +1,42 @@ +title: "scroll-reth - reth fork diff overview" +footer: | + Fork-diff overview of [`scroll-reth`](https://github.com/scroll-tech/reth), a fork of [`reth`](https://github.com/paradigmxyz/reth). +base: + name: reth + url: https://github.com/paradigmxyz/reth + hash: 41ed7e0b7961ae5d9cb29de66eed2992ca5528d4 +fork: + name: scroll-reth + url: https://github.com/scroll-tech/reth + ref: refs/heads/scroll +def: + title: "scroll-reth" + description: | + This is an overview of the changes in [`scroll-reth`](https://github.com/scroll-tech/reth), + a fork of [`reth`](https://github.com/paradigmxyz/reth). + sub: + - title: "crates/scroll" + globs: + - "crates/scroll/**/*" + - title: "crates/ethereum" + globs: + - "crates/ethereum/**/*" + - title: "crates/optimism" + globs: + - "crates/optimism/**/*" + - title: "crates/primitives" + globs: + - "crates/primitives*/**/*" + - title: "crates/stages" + globs: + - "crates/stages/**/*" + - title: "crates/storage" + globs: + - "crates/storage/**/*" + - title: "crates/trie" + globs: + - "crates/trie/**/*" + +ignore: + - "fork.yaml" + - ".github/**"