diff --git a/Cargo.lock b/Cargo.lock index 1cfaa9a2..f70a11c1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1253,6 +1253,29 @@ version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" +[[package]] +name = "bytecheck" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50c8f430744b23b54ad15161fcbc22d82a29b73eacbe425fea23ec822600bc6f" +dependencies = [ + "bytecheck_derive", + "ptr_meta", + "rancor", + "simdutf8", +] + +[[package]] +name = "bytecheck_derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523363cbe1df49b68215efdf500b103ac3b0fb4836aed6d15689a076eadb8fff" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "bytemuck" version = "1.20.0" @@ -2967,6 +2990,26 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "munge" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64142d38c84badf60abf06ff9bd80ad2174306a5b11bd4706535090a30a419df" +dependencies = [ + "munge_macro", +] + +[[package]] +name = "munge_macro" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bb5c1d8184f13f7d0ccbeeca0def2f9a181bce2624302793005f5ca8aa62e5e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "native-tls" version = "0.2.12" @@ -3613,6 +3656,26 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "ptr_meta" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe9e76f66d3f9606f44e45598d155cb13ecf09f4a28199e48daf8c8fc937ea90" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca414edb151b4c8d125c12566ab0d74dc9cdba36fb80eb7b848c15f495fd32d1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "puffin" version = "0.19.1" @@ -3696,6 +3759,15 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" +[[package]] +name = "rancor" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caf5f7161924b9d1cea0e4cabc97c372cea92b5f927fc13c6bca67157a0ad947" +dependencies = [ + "ptr_meta", +] + [[package]] name = "rand" version = "0.8.5" @@ -3817,6 +3889,15 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +[[package]] +name = "rend" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a35e8a6bf28cd121053a66aa2e6a2e3eaffad4a60012179f0e864aa5ffeff215" +dependencies = [ + "bytecheck", +] + [[package]] name = "reqwest" version = "0.12.8" @@ -4756,6 +4837,36 @@ dependencies = [ "stability", ] +[[package]] +name = "rkyv" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b11a153aec4a6ab60795f8ebe2923c597b16b05bb1504377451e705ef1a45323" +dependencies = [ + "bytecheck", + "bytes", + "hashbrown 0.15.0", + "indexmap 2.6.0", + "munge", + "ptr_meta", + "rancor", + "rend", + "rkyv_derive", + "tinyvec", + "uuid", +] + +[[package]] +name = "rkyv_derive" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "beb382a4d9f53bd5c0be86b10d8179c3f8a14c30bf774ff77096ed6581e35981" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "rlp" version = "0.5.2" @@ -5206,6 +5317,12 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + [[package]] name = "slab" version = "0.4.9" @@ -5888,6 +6005,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "uuid" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" + [[package]] name = "valuable" version = "0.1.0" @@ -6389,6 +6512,7 @@ dependencies = [ "reth-primitives", "reth-revm", "reth-storage-errors", + "rkyv", "serde", "thiserror", "tiny-keccak", @@ -6480,6 +6604,8 @@ dependencies = [ "reth-primitives", "reth-revm", "reth-storage-errors", + "risc0-zkvm", + "rkyv", "serde", "serde_json", "tokio", diff --git a/Cargo.toml b/Cargo.toml index 4a3fd355..5fb2f010 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -82,6 +82,7 @@ log = "0.4.22" flate2 = "1.0.34" once_cell = "1.20.2" pot = "3.0.1" +rkyv = "0.8.9" serde = { version = "1.0.210", features = ["derive"] } serde_json = { version = "1.0.128", features = ["alloc"] } thiserror = "1.0.64" diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index 3d9d2d5a..73f78caa 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -10,6 +10,7 @@ alloy-primitives.workspace = true alloy-rlp.workspace = true k256.workspace = true pot.workspace = true +rkyv.workspace = true serde.workspace = true thiserror.workspace = true tiny-keccak.workspace = true diff --git a/crates/core/src/driver.rs b/crates/core/src/driver.rs index cfa30fe2..0e9f2b3d 100644 --- a/crates/core/src/driver.rs +++ b/crates/core/src/driver.rs @@ -20,8 +20,8 @@ use std::sync::Arc; pub trait CoreDriver: Default { type ChainSpec: 'static; - type Block: Serialize + DeserializeOwned + 'static; - type Header: Serialize + DeserializeOwned + 'static; + type Block: Clone + Serialize + DeserializeOwned + 'static; + type Header: Clone + Serialize + DeserializeOwned + 'static; type Receipt: Serialize + DeserializeOwned + 'static; type Transaction: Serialize + DeserializeOwned + 'static; diff --git a/crates/core/src/mpt.rs b/crates/core/src/mpt.rs index 98e95e8e..144024ba 100644 --- a/crates/core/src/mpt.rs +++ b/crates/core/src/mpt.rs @@ -43,13 +43,41 @@ pub const EMPTY_ROOT: B256 = /// optimizing storage. However, operations targeting a truncated part will fail and /// return an error. Another distinction of this implementation is that branches cannot /// store values, aligning with the construction of MPTs in Ethereum. -#[derive(Clone, Debug, Default, PartialEq, Eq, Ord, PartialOrd, Serialize, Deserialize)] +#[derive( + Clone, + Debug, + Default, + PartialEq, + Eq, + Ord, + PartialOrd, + Serialize, + Deserialize, + rkyv::Archive, + rkyv::Serialize, + rkyv::Deserialize, +)] +#[rkyv(bytecheck( + bounds( + __C: rkyv::validation::ArchiveContext, + __C::Error: rkyv::rancor::Source, + ) +))] +#[rkyv(serialize_bounds( + __S: rkyv::ser::Writer + rkyv::ser::Allocator, + __S::Error: rkyv::rancor::Source, +))] +#[rkyv(deserialize_bounds( + __D::Error: rkyv::rancor::Source +))] pub struct MptNode { /// The type and data of the node. - data: MptNodeData, + #[rkyv(omit_bounds)] + pub data: MptNodeData, /// Cache for a previously computed reference of this node. This is skipped during /// serialization. #[serde(skip)] + #[rkyv(with = rkyv::with::Skip)] cached_reference: RefCell>, } @@ -88,21 +116,64 @@ impl From for MptNode { /// Each node in the trie can be of one of several types, each with its own specific data /// structure. This enum provides a clear and type-safe way to represent the data /// associated with each node type. -#[derive(Clone, Debug, Default, PartialEq, Eq, Ord, PartialOrd, Serialize, Deserialize)] +#[derive( + Clone, + Debug, + Default, + PartialEq, + Eq, + Ord, + PartialOrd, + Serialize, + Deserialize, + rkyv::Archive, + rkyv::Serialize, + rkyv::Deserialize, +)] +#[rkyv(bytecheck( + bounds( + __C: rkyv::validation::ArchiveContext, + ) +))] +#[rkyv(serialize_bounds( + __S: rkyv::ser::Writer + rkyv::ser::Allocator, + __S::Error: rkyv::rancor::Source, +))] +#[rkyv(deserialize_bounds( + __D::Error: rkyv::rancor::Source +))] pub enum MptNodeData { /// Represents an empty trie node. #[default] Null, /// A node that can have up to 16 children. Each child is an optional boxed [MptNode]. - Branch([Option>; 16]), + Branch( + // #[rkyv(with = rkyv::with::Map>>)] + #[rkyv(omit_bounds)] [Option>; 16], + ), /// A leaf node that contains a key and a value, both represented as byte vectors. Leaf(Vec, Vec), /// A node that has exactly one child and is used to represent a shared prefix of /// several keys. - Extension(Vec, Box), + Extension( + Vec, + // #[rkyv(with = Box)] + #[rkyv(omit_bounds)] Box, + ), /// Represents a sub-trie by its hash, allowing for efficient storage of large /// sub-tries without storing their entire content. - Digest(B256), + Digest(#[rkyv(with = B256Def)] B256), +} + +#[derive(Clone, Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)] +#[rkyv(remote = B256)] +#[rkyv(archived = ArchivedB256)] +pub struct B256Def(pub [u8; 32]); + +impl From for B256 { + fn from(value: B256Def) -> Self { + B256::new(value.0) + } } /// Represents the ways in which one node can reference another node inside the sparse diff --git a/crates/core/src/stateless/client.rs b/crates/core/src/stateless/client.rs index 5aab6dbd..04096d1c 100644 --- a/crates/core/src/stateless/client.rs +++ b/crates/core/src/stateless/client.rs @@ -14,13 +14,14 @@ use crate::driver::CoreDriver; use crate::rescue::{Recoverable, Wrapper}; -use crate::stateless::data::StatelessClientData; +use crate::stateless::data::{ + RkyvStatelessClientData, StatelessClientChainData, StatelessClientData, +}; use crate::stateless::engine::StatelessClientEngine; use crate::stateless::execute::ExecutionStrategy; use crate::stateless::finalize::FinalizationStrategy; use crate::stateless::initialize::InitializationStrategy; use crate::stateless::validate::ValidationStrategy; -use std::io::Read; pub trait StatelessClient where @@ -32,18 +33,23 @@ where type Execution: ExecutionStrategy>; type Finalization: FinalizationStrategy; - fn data_from_reader( - reader: I, - ) -> anyhow::Result> { - Ok(pot::from_reader(reader)?) - } - fn data_from_slice( slice: &[u8], ) -> anyhow::Result> { Ok(pot::from_slice(slice)?) } + fn data_from_parts( + rkyv_slice: &[u8], + pot_slice: &[u8], + ) -> anyhow::Result> { + let rkyv_data = + rkyv::from_bytes::(rkyv_slice)?; + let chain_data = + pot::from_slice::>(pot_slice)?; + Ok(StatelessClientData::::from_parts(rkyv_data, chain_data)) + } + fn validate( data: StatelessClientData, ) -> anyhow::Result> { diff --git a/crates/core/src/stateless/data.rs b/crates/core/src/stateless/data.rs index 03b4a80c..02893b53 100644 --- a/crates/core/src/stateless/data.rs +++ b/crates/core/src/stateless/data.rs @@ -16,33 +16,361 @@ use crate::mpt::MptNode; use alloy_primitives::map::HashMap; use alloy_primitives::{Address, Bytes, U256}; use k256::ecdsa::VerifyingKey; +use k256::elliptic_curve::sec1::EncodedPoint; +use k256::Secp256k1; use reth_chainspec::NamedChain; +use rkyv::vec::{ArchivedVec, VecResolver}; +use rkyv::with::{ArchiveWith, DeserializeWith, SerializeWith}; +use rkyv::{Archive, Place}; use serde::{Deserialize, Serialize}; /// Represents the state of an account's storage. /// The storage trie together with the used storage slots allow us to reconstruct all the /// required values. -pub type StorageEntry = (MptNode, Vec); +#[derive( + Debug, + Clone, + Default, + Eq, + PartialEq, + Deserialize, + Serialize, + rkyv::Archive, + rkyv::Serialize, + rkyv::Deserialize, +)] +pub struct StorageEntry { + pub storage_trie: MptNode, + #[rkyv(with = rkyv::with::Map)] + pub slots: Vec, +} + +impl ArchiveWith for StorageEntry { + type Archived = ArchivedStorageEntry; + type Resolver = StorageEntryResolver; + + fn resolve_with(field: &StorageEntry, resolver: Self::Resolver, out: Place) { + field.resolve(resolver, out); + } +} + +impl SerializeWith for StorageEntry +where + S: rkyv::rancor::Fallible + rkyv::ser::Allocator + rkyv::ser::Writer + ?Sized, + ::Error: rkyv::rancor::Source, +{ + fn serialize_with( + field: &StorageEntry, + serializer: &mut S, + ) -> Result { + rkyv::Serialize::serialize(field, serializer) + } +} + +impl DeserializeWith for StorageEntry +where + D: rkyv::rancor::Fallible + ?Sized, + ::Error: rkyv::rancor::Source, +{ + fn deserialize_with( + field: &ArchivedStorageEntry, + deserializer: &mut D, + ) -> Result { + rkyv::Deserialize::deserialize(field, deserializer) + } +} /// External block input. #[derive(Debug, Clone, Default, Eq, PartialEq, Deserialize, Serialize)] +pub struct _StatelessClientData { + /// The chain for this data + pub chain: NamedChain, + /// Block and transaction data to execute + pub blocks: Vec, + /// List of public keys for transaction signatures + pub signers: Vec>, + /// State trie of the parent block. + pub state_trie: MptNode, + /// Maps each address with its storage trie and the used storage slots. + pub storage_tries: HashMap, + /// The code for each account + pub contracts: HashMap, + /// Immediate parent header + pub parent_header: Header, + /// List of at most 256 previous block headers + pub ancestor_headers: Vec
, + /// Total difficulty before executing block + pub total_difficulty: U256, +} + +/// External block input. +#[derive( + Debug, + Clone, + Default, + Eq, + PartialEq, + Deserialize, + Serialize, + rkyv::Archive, + rkyv::Serialize, + rkyv::Deserialize, +)] pub struct StatelessClientData { /// The chain for this data + #[rkyv(with = EncodeNamedChain)] pub chain: NamedChain, /// Block and transaction data to execute pub blocks: Vec, /// List of public keys for transaction signatures + #[rkyv(with = rkyv::with::Map>)] pub signers: Vec>, /// State trie of the parent block. pub state_trie: MptNode, /// Maps each address with its storage trie and the used storage slots. + #[rkyv(with = rkyv::with::MapKV)] pub storage_tries: HashMap, /// The code for each account + #[rkyv(with = rkyv::with::MapKV)] pub contracts: HashMap, /// Immediate parent header pub parent_header: Header, /// List of at most 256 previous block headers pub ancestor_headers: Vec
, /// Total difficulty before executing block + #[rkyv(with = U256Def)] pub total_difficulty: U256, } + +/// External block input. +#[derive( + Debug, + Clone, + Default, + Eq, + PartialEq, + Deserialize, + Serialize, + rkyv::Archive, + rkyv::Serialize, + rkyv::Deserialize, +)] +pub struct RkyvStatelessClientData { + /// The chain for this data + #[rkyv(with = EncodeNamedChain)] + pub chain: NamedChain, + /// List of public keys for transaction signatures + #[rkyv(with = rkyv::with::Map>)] + pub signers: Vec>, + /// State trie of the parent block. + pub state_trie: MptNode, + /// Maps each address with its storage trie and the used storage slots. + #[rkyv(with = rkyv::with::MapKV)] + pub storage_tries: HashMap, + /// The code for each account + #[rkyv(with = rkyv::with::MapKV)] + pub contracts: HashMap, + /// Total difficulty before executing block + #[rkyv(with = U256Def)] + pub total_difficulty: U256, +} + +impl From> for RkyvStatelessClientData { + fn from(value: StatelessClientData) -> Self { + Self { + chain: value.chain, + signers: value.signers, + state_trie: value.state_trie, + storage_tries: value.storage_tries, + contracts: value.contracts, + total_difficulty: value.total_difficulty, + } + } +} + +/// External block input. +#[derive( + Debug, + Clone, + Default, + Eq, + PartialEq, + Deserialize, + Serialize, + rkyv::Archive, + rkyv::Serialize, + rkyv::Deserialize, +)] +pub struct StatelessClientChainData { + /// Block and transaction data to execute + pub blocks: Vec, + /// Immediate parent header + pub parent_header: Header, + /// List of at most 256 previous block headers + pub ancestor_headers: Vec
, +} + +impl From> for StatelessClientChainData { + fn from(value: StatelessClientData) -> Self { + Self { + blocks: value.blocks, + parent_header: value.parent_header, + ancestor_headers: value.ancestor_headers, + } + } +} + +impl StatelessClientData { + pub fn from_parts( + rkyved: RkyvStatelessClientData, + chain: StatelessClientChainData, + ) -> Self { + Self { + chain: rkyved.chain, + blocks: chain.blocks, + signers: rkyved.signers, + state_trie: rkyved.state_trie, + storage_tries: rkyved.storage_tries, + contracts: rkyved.contracts, + parent_header: chain.parent_header, + ancestor_headers: chain.ancestor_headers, + total_difficulty: rkyved.total_difficulty, + } + } +} + +pub struct EncodeNamedChain; + +impl ArchiveWith for EncodeNamedChain { + type Archived = rkyv::Archived; + type Resolver = rkyv::Resolver; + + fn resolve_with(field: &NamedChain, resolver: Self::Resolver, out: Place) { + let val: u64 = (*field).into(); + val.resolve(resolver, out); + } +} + +impl SerializeWith for EncodeNamedChain +where + S: rkyv::rancor::Fallible + rkyv::ser::Allocator + rkyv::ser::Writer + ?Sized, +{ + fn serialize_with(field: &NamedChain, serializer: &mut S) -> Result { + let val: u64 = (*field).into(); + rkyv::Serialize::serialize(&val, serializer) + } +} + +impl DeserializeWith, NamedChain, D> for EncodeNamedChain +where + D: rkyv::rancor::Fallible + ?Sized, +{ + fn deserialize_with(field: &rkyv::Archived, _: &mut D) -> Result { + Ok(NamedChain::try_from(field.to_native()).unwrap()) + } +} + +pub struct EncodeVerifyingKey; + +impl ArchiveWith for EncodeVerifyingKey { + type Archived = ArchivedVec; + type Resolver = VecResolver; + + fn resolve_with(field: &VerifyingKey, resolver: Self::Resolver, out: Place) { + let encoded = field.to_encoded_point(false).to_bytes().to_vec(); + encoded.resolve(resolver, out); + } +} + +impl SerializeWith for EncodeVerifyingKey +where + S: rkyv::rancor::Fallible + rkyv::ser::Allocator + rkyv::ser::Writer + ?Sized, +{ + fn serialize_with( + field: &VerifyingKey, + serializer: &mut S, + ) -> Result { + let encoded = field.to_encoded_point(false).to_bytes().to_vec(); + rkyv::Serialize::serialize(&encoded, serializer) + } +} + +impl DeserializeWith, VerifyingKey, D> for EncodeVerifyingKey +where + D: rkyv::rancor::Fallible + ?Sized, +{ + fn deserialize_with(field: &ArchivedVec, _: &mut D) -> Result { + let encoded_point = EncodedPoint::::from_bytes(field.as_slice()).unwrap(); + Ok(VerifyingKey::from_encoded_point(&encoded_point).unwrap()) + } +} + +#[derive(Clone, Debug, Hash, Eq, PartialEq, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)] +#[rkyv(remote = Address)] +#[rkyv(archived = ArchivedAddress)] +#[rkyv(derive(Hash, Eq, PartialEq))] +pub struct AddressDef(pub [u8; 20]); + +impl From for Address { + fn from(value: AddressDef) -> Self { + Address::new(value.0) + } +} + +#[derive(Clone, Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)] +#[rkyv(remote = U256)] +#[rkyv(archived = ArchivedU256)] +pub struct U256Def { + #[rkyv(getter = U256::as_limbs)] + limbs: [u64; 4], +} + +impl From for U256 { + fn from(value: U256Def) -> Self { + U256::from_limbs(value.limbs) + } +} + +pub struct EncodeBytes; + +impl ArchiveWith for EncodeBytes { + type Archived = ArchivedVec; + type Resolver = VecResolver; + + fn resolve_with(field: &Bytes, resolver: Self::Resolver, out: Place) { + ArchivedVec::::resolve_from_slice(field.0.as_ref(), resolver, out); + } +} + +impl SerializeWith for EncodeBytes +where + S: rkyv::rancor::Fallible + rkyv::ser::Allocator + rkyv::ser::Writer + ?Sized, +{ + fn serialize_with(field: &Bytes, serializer: &mut S) -> Result { + rkyv::Serialize::serialize(&field.0.to_vec(), serializer) + } +} + +impl DeserializeWith>, Bytes, D> for EncodeBytes +where + D: rkyv::rancor::Fallible + ?Sized, +{ + fn deserialize_with(field: &rkyv::Archived>, _: &mut D) -> Result { + let res = Bytes::copy_from_slice(field.as_slice()); + Ok(res) + } +} + +// impl SerializeWith for EncodeBytes +// where +// S: rkyv::rancor::Fallible + rkyv::ser::Allocator + rkyv::ser::Writer + ?Sized, +// { +// fn serialize_with(field: &Bytes, serializer: &mut S) -> Result { +// ArchivedVec::::serialize_from_slice(&field.0, serializer) +// } +// } + +// impl DeserializeWith>, Bytes, D> for EncodeBytes +// where +// D: rkyv::rancor::Fallible + ?Sized, +// {} diff --git a/crates/core/src/stateless/finalize.rs b/crates/core/src/stateless/finalize.rs index c5a3ccc0..7d222931 100644 --- a/crates/core/src/stateless/finalize.rs +++ b/crates/core/src/stateless/finalize.rs @@ -59,7 +59,8 @@ impl FinalizationStrategy for RethFinaliza for storage_change in storage { // getting a mutable reference is more efficient than calling remove // every account must have an entry, even newly created accounts - let (storage_trie, _) = storage_tries.get_mut(&storage_change.address).unwrap(); + let StorageEntry { storage_trie, .. } = + storage_tries.get_mut(&storage_change.address).unwrap(); // for cleared accounts always start from the empty trie if storage_change.wipe_storage { storage_trie.clear(); @@ -92,7 +93,7 @@ impl FinalizationStrategy for RethFinaliza continue; } let storage_root = { - let (storage_trie, _) = storage_tries.get(address).unwrap(); + let StorageEntry { storage_trie, .. } = storage_tries.get(address).unwrap(); storage_trie.hash() }; @@ -114,7 +115,7 @@ impl FinalizationStrategy for RethFinaliza .context("state_trie.delete")?; } // Apply account storage only changes - for (address, (storage_trie, _)) in storage_tries { + for (address, StorageEntry { storage_trie, .. }) in storage_tries { if storage_trie.is_reference_cached() { continue; } diff --git a/crates/core/src/stateless/initialize.rs b/crates/core/src/stateless/initialize.rs index 6d11baf3..16483516 100644 --- a/crates/core/src/stateless/initialize.rs +++ b/crates/core/src/stateless/initialize.rs @@ -64,7 +64,14 @@ impl InitializationStrategy for MemoryDbSt // Load account data into db let mut accounts = HashMap::with_capacity(storage_tries.len()); - for (address, (storage_trie, slots)) in storage_tries { + for ( + address, + StorageEntry { + storage_trie, + slots, + }, + ) in storage_tries + { // consume the slots, as they are no longer needed afterward let slots = take(slots); diff --git a/crates/preflight/Cargo.toml b/crates/preflight/Cargo.toml index 35140605..fd1b2794 100644 --- a/crates/preflight/Cargo.toml +++ b/crates/preflight/Cargo.toml @@ -10,16 +10,18 @@ workspace = true alloy.workspace = true anyhow.workspace = true async-trait.workspace = true +flate2.workspace = true k256.workspace = true log.workspace = true +pot.workspace = true +rkyv.workspace = true serde.workspace = true -flate2.workspace = true +serde_json.workspace = true +tokio.workspace = true reth-chainspec.workspace = true reth-revm.workspace = true reth-primitives.workspace = true reth-storage-errors.workspace = true -pot.workspace = true -serde_json.workspace = true -tokio.workspace = true +risc0-zkvm.workspace = true \ No newline at end of file diff --git a/crates/preflight/src/client.rs b/crates/preflight/src/client.rs index b4e0a79c..3cec7cb6 100644 --- a/crates/preflight/src/client.rs +++ b/crates/preflight/src/client.rs @@ -30,7 +30,7 @@ use zeth_core::mpt::{ parse_proof, resolve_nodes_in_place, shorten_node_path, MptNode, MptNodeReference, }; use zeth_core::rescue::Wrapper; -use zeth_core::stateless::data::StatelessClientData; +use zeth_core::stateless::data::{StatelessClientData, StorageEntry}; use zeth_core::stateless::engine::StatelessClientEngine; use zeth_core::stateless::execute::ExecutionStrategy; use zeth_core::stateless::validate::ValidationStrategy; @@ -253,7 +253,9 @@ where .collect(); resolve_nodes_in_place(&mut state_trie, &node_store); // resolve storage orphans - if let Some((trie, _)) = storage_tries.get_mut(&account_proof.address) { + if let Some(StorageEntry { storage_trie, .. }) = + storage_tries.get_mut(&account_proof.address) + { for storage_proof in account_proof.storage_proof { let node_store: HashMap = parse_proof(&storage_proof.proof)? @@ -277,7 +279,7 @@ where ); } } - resolve_nodes_in_place(trie, &node_store); + resolve_nodes_in_place(storage_trie, &node_store); } } } @@ -300,8 +302,8 @@ where // Report stats info!("State trie: {} nodes", state_trie.size()); let storage_nodes: u64 = storage_tries - .iter() - .map(|(_, (n, _))| n.size() as u64) + .values() + .map(|e| e.storage_trie.size() as u64) .sum(); info!( "Storage tries: {storage_nodes} total nodes over {} accounts", diff --git a/crates/preflight/src/lib.rs b/crates/preflight/src/lib.rs index 5e4d5a3e..915f2c48 100644 --- a/crates/preflight/src/lib.rs +++ b/crates/preflight/src/lib.rs @@ -26,7 +26,9 @@ use tokio::task::spawn_blocking; use zeth_core::driver::CoreDriver; use zeth_core::rescue::Recoverable; use zeth_core::stateless::client::StatelessClient; -use zeth_core::stateless::data::StatelessClientData; +use zeth_core::stateless::data::{ + RkyvStatelessClientData, StatelessClientChainData, StatelessClientData, +}; pub mod client; pub mod db; @@ -36,7 +38,8 @@ pub mod trie; #[derive(Debug, Default, Clone)] pub struct Witness { - pub encoded_input: Vec, + pub encoded_rkyv_input: Vec, + pub encoded_chain_input: Vec, pub validated_tip_hash: B256, pub validated_tip_number: u64, pub validated_tail_hash: B256, @@ -46,11 +49,18 @@ pub struct Witness { impl Witness { pub fn driver_from(data: &StatelessClientData) -> Self { - let encoded_input = pot::to_vec(&data).expect("serialization failed"); + let rkyv_data = RkyvStatelessClientData::from(data.clone()); + let encoded_rkyv_input = rkyv::to_bytes::(&rkyv_data) + .expect("rkyv serialization failed") + .to_vec(); + let chain_data = StatelessClientChainData::::from(data.clone()); + let encoded_chain_input = pot::to_vec(&chain_data).expect("pot serialization failed"); + // let encoded_input = pot::to_vec(&data).expect("serialization failed"); let tip = R::block_header(data.blocks.last().unwrap()); let tail = &data.parent_header; Self { - encoded_input, + encoded_rkyv_input, + encoded_chain_input, validated_tip_hash: R::header_hash(tip), validated_tip_number: R::block_number(tip), validated_tail_hash: R::header_hash(tail), @@ -96,11 +106,15 @@ where // Verify that the transactions run correctly info!( "Running from memory (Input size: {} bytes) ...", - build_result.encoded_input.len() + // build_result.encoded_input.len() + build_result.encoded_rkyv_input.len() + build_result.encoded_chain_input.len() ); let deserialized_preflight_data: StatelessClientData = - Self::StatelessClient::data_from_reader(build_result.encoded_input.as_slice()) - .context("input deserialization failed")?; + Self::StatelessClient::data_from_parts( + build_result.encoded_rkyv_input.as_slice(), + build_result.encoded_chain_input.as_slice(), + ) + .context("input deserialization failed")?; ::validate(deserialized_preflight_data) .expect("Block validation failed"); info!("Memory run successful ..."); diff --git a/crates/preflight/src/trie.rs b/crates/preflight/src/trie.rs index fde634e2..ceb747b4 100644 --- a/crates/preflight/src/trie.rs +++ b/crates/preflight/src/trie.rs @@ -51,7 +51,10 @@ pub fn extend_proof_tries( }); // insert inaccessible storage trie if let alloy::primitives::map::Entry::Vacant(e) = storage_tries.entry(address) { - e.insert((initialization_proof.storage_hash.into(), vec![])); + e.insert(StorageEntry { + storage_trie: initialization_proof.storage_hash.into(), + slots: vec![], + }); } // storage for encountered storage trie data let mut storage_nodes = HashMap::new(); @@ -65,8 +68,8 @@ pub fn extend_proof_tries( let storage_entry = storage_tries.get_mut(&address).unwrap(); let storage_key = U256::from_be_bytes(storage_proof.key.0 .0); // Push the storage key if new - if !storage_entry.1.contains(&storage_key) { - storage_entry.1.push(storage_key); + if !storage_entry.slots.contains(&storage_key) { + storage_entry.slots.push(storage_key); } // Load storage trie nodes into store proof_nodes.into_iter().for_each(|node| { @@ -97,7 +100,7 @@ pub fn extend_proof_tries( let storage_entry = storage_tries.get_mut(&address).unwrap(); // Load up newly found storage nodes - resolve_nodes_in_place(&mut storage_entry.0, &storage_nodes); + resolve_nodes_in_place(&mut storage_entry.storage_trie, &storage_nodes); // validate storage orphans for (prefix, digest) in potential_storage_orphans { if let Some(node) = storage_nodes.get(&MptNodeReference::Digest(digest)) { @@ -163,7 +166,13 @@ pub fn proofs_to_tries( // if no slots are provided, return the trie only consisting of the storage root if initialization_proof.storage_proof.is_empty() { - storage.insert(address, (initialization_proof.storage_hash.into(), vec![])); + storage.insert( + address, + StorageEntry { + storage_trie: initialization_proof.storage_hash.into(), + slots: vec![], + }, + ); continue; } @@ -201,7 +210,13 @@ pub fn proofs_to_tries( .iter() .map(|p| U256::from_be_bytes(p.key.0 .0)) .collect(); - storage.insert(address, (storage_trie, slots)); + storage.insert( + address, + StorageEntry { + storage_trie, + slots, + }, + ); } let state_trie = resolve_nodes(&state_root_node, &state_nodes); assert_eq!(state_trie.hash(), state_root); diff --git a/crates/zeth/src/executor.rs b/crates/zeth/src/executor.rs index 80d75005..0167e13a 100644 --- a/crates/zeth/src/executor.rs +++ b/crates/zeth/src/executor.rs @@ -25,7 +25,8 @@ pub fn build_executor_env<'b>( ) -> anyhow::Result> { let run_args = cli.run_args(); let mut builder = ExecutorEnv::builder(); - builder.write_frame(&witness.encoded_input); + builder.write_frame(&witness.encoded_rkyv_input); + builder.write_frame(&witness.encoded_chain_input); builder.segment_limit_po2(run_args.execution_po2); if run_args.profile { if std::env::var("RISC0_PPROF_OUT").is_ok() { diff --git a/guests/reth-ethereum/Cargo.lock b/guests/reth-ethereum/Cargo.lock index 24083890..d8b019c3 100644 --- a/guests/reth-ethereum/Cargo.lock +++ b/guests/reth-ethereum/Cargo.lock @@ -734,6 +734,29 @@ version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" +[[package]] +name = "bytecheck" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50c8f430744b23b54ad15161fcbc22d82a29b73eacbe425fea23ec822600bc6f" +dependencies = [ + "bytecheck_derive", + "ptr_meta", + "rancor", + "simdutf8", +] + +[[package]] +name = "bytecheck_derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523363cbe1df49b68215efdf500b103ac3b0fb4836aed6d15689a076eadb8fff" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.85", +] + [[package]] name = "bytemuck" version = "1.20.0" @@ -1690,6 +1713,26 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "munge" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64142d38c84badf60abf06ff9bd80ad2174306a5b11bd4706535090a30a419df" +dependencies = [ + "munge_macro", +] + +[[package]] +name = "munge_macro" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bb5c1d8184f13f7d0ccbeeca0def2f9a181bce2624302793005f5ca8aa62e5e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.85", +] + [[package]] name = "num" version = "0.4.3" @@ -2074,6 +2117,26 @@ dependencies = [ "unarray", ] +[[package]] +name = "ptr_meta" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe9e76f66d3f9606f44e45598d155cb13ecf09f4a28199e48daf8c8fc937ea90" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca414edb151b4c8d125c12566ab0d74dc9cdba36fb80eb7b848c15f495fd32d1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.85", +] + [[package]] name = "quick-error" version = "1.2.3" @@ -2095,6 +2158,15 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" +[[package]] +name = "rancor" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caf5f7161924b9d1cea0e4cabc97c372cea92b5f927fc13c6bca67157a0ad947" +dependencies = [ + "ptr_meta", +] + [[package]] name = "rand" version = "0.8.5" @@ -2184,6 +2256,15 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +[[package]] +name = "rend" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a35e8a6bf28cd121053a66aa2e6a2e3eaffad4a60012179f0e864aa5ffeff215" +dependencies = [ + "bytecheck", +] + [[package]] name = "reth-chainspec" version = "1.1.0" @@ -2845,6 +2926,36 @@ dependencies = [ "stability", ] +[[package]] +name = "rkyv" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b11a153aec4a6ab60795f8ebe2923c597b16b05bb1504377451e705ef1a45323" +dependencies = [ + "bytecheck", + "bytes", + "hashbrown 0.15.0", + "indexmap 2.6.0", + "munge", + "ptr_meta", + "rancor", + "rend", + "rkyv_derive", + "tinyvec", + "uuid", +] + +[[package]] +name = "rkyv_derive" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "beb382a4d9f53bd5c0be86b10d8179c3f8a14c30bf774ff77096ed6581e35981" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.85", +] + [[package]] name = "rlp" version = "0.5.2" @@ -3158,6 +3269,12 @@ dependencies = [ "rand_core", ] +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + [[package]] name = "slab" version = "0.4.9" @@ -3560,6 +3677,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "uuid" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" + [[package]] name = "valuable" version = "0.1.0" @@ -3806,6 +3929,7 @@ dependencies = [ "reth-primitives", "reth-revm", "reth-storage-errors", + "rkyv", "serde", "thiserror", "tiny-keccak", diff --git a/guests/reth-ethereum/src/main.rs b/guests/reth-ethereum/src/main.rs index 9958808f..86621a7b 100644 --- a/guests/reth-ethereum/src/main.rs +++ b/guests/reth-ethereum/src/main.rs @@ -33,9 +33,10 @@ pub extern "C" fn __ctzsi2(x: u32) -> usize { fn main() { // todo: load up revm with hashbrown feat + let stateless_client_data_rkyv = env::read_frame(); let stateless_client_data_pot = env::read_frame(); env::log("Deserializing input data"); - let stateless_client_data = RethStatelessClient::data_from_slice(&stateless_client_data_pot) + let stateless_client_data = RethStatelessClient::data_from_parts(&stateless_client_data_rkyv, &stateless_client_data_pot) .expect("Failed to load client data from stdin"); let validation_depth = stateless_client_data.blocks.len() as u64; assert!(stateless_client_data.chain.is_ethereum(), "This program only supports Ethereum chains"); diff --git a/guests/reth-optimism/Cargo.lock b/guests/reth-optimism/Cargo.lock index 0829f1aa..5ad84757 100644 --- a/guests/reth-optimism/Cargo.lock +++ b/guests/reth-optimism/Cargo.lock @@ -710,6 +710,29 @@ version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" +[[package]] +name = "bytecheck" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50c8f430744b23b54ad15161fcbc22d82a29b73eacbe425fea23ec822600bc6f" +dependencies = [ + "bytecheck_derive", + "ptr_meta", + "rancor", + "simdutf8", +] + +[[package]] +name = "bytecheck_derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523363cbe1df49b68215efdf500b103ac3b0fb4836aed6d15689a076eadb8fff" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.85", +] + [[package]] name = "bytemuck" version = "1.20.0" @@ -1666,6 +1689,26 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "munge" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64142d38c84badf60abf06ff9bd80ad2174306a5b11bd4706535090a30a419df" +dependencies = [ + "munge_macro", +] + +[[package]] +name = "munge_macro" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bb5c1d8184f13f7d0ccbeeca0def2f9a181bce2624302793005f5ca8aa62e5e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.85", +] + [[package]] name = "num" version = "0.4.3" @@ -2071,6 +2114,26 @@ dependencies = [ "unarray", ] +[[package]] +name = "ptr_meta" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe9e76f66d3f9606f44e45598d155cb13ecf09f4a28199e48daf8c8fc937ea90" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca414edb151b4c8d125c12566ab0d74dc9cdba36fb80eb7b848c15f495fd32d1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.85", +] + [[package]] name = "quick-error" version = "1.2.3" @@ -2092,6 +2155,15 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" +[[package]] +name = "rancor" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caf5f7161924b9d1cea0e4cabc97c372cea92b5f927fc13c6bca67157a0ad947" +dependencies = [ + "ptr_meta", +] + [[package]] name = "rand" version = "0.8.5" @@ -2181,6 +2253,15 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +[[package]] +name = "rend" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a35e8a6bf28cd121053a66aa2e6a2e3eaffad4a60012179f0e864aa5ffeff215" +dependencies = [ + "bytecheck", +] + [[package]] name = "reth-chainspec" version = "1.1.0" @@ -2885,6 +2966,36 @@ dependencies = [ "stability", ] +[[package]] +name = "rkyv" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b11a153aec4a6ab60795f8ebe2923c597b16b05bb1504377451e705ef1a45323" +dependencies = [ + "bytecheck", + "bytes", + "hashbrown 0.15.0", + "indexmap 2.6.0", + "munge", + "ptr_meta", + "rancor", + "rend", + "rkyv_derive", + "tinyvec", + "uuid", +] + +[[package]] +name = "rkyv_derive" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "beb382a4d9f53bd5c0be86b10d8179c3f8a14c30bf774ff77096ed6581e35981" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.85", +] + [[package]] name = "rlp" version = "0.5.2" @@ -3198,6 +3309,12 @@ dependencies = [ "rand_core", ] +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + [[package]] name = "slab" version = "0.4.9" @@ -3600,6 +3717,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "uuid" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" + [[package]] name = "valuable" version = "0.1.0" @@ -3846,6 +3969,7 @@ dependencies = [ "reth-primitives", "reth-revm", "reth-storage-errors", + "rkyv", "serde", "thiserror", "tiny-keccak", diff --git a/guests/reth-optimism/src/main.rs b/guests/reth-optimism/src/main.rs index 9a5f85cd..3dee433c 100644 --- a/guests/reth-optimism/src/main.rs +++ b/guests/reth-optimism/src/main.rs @@ -33,9 +33,10 @@ pub extern "C" fn __ctzsi2(x: u32) -> usize { fn main() { // todo: load up revm with hashbrown feat + let stateless_client_data_rkyv = env::read_frame(); let stateless_client_data_pot = env::read_frame(); env::log("Deserializing input data"); - let stateless_client_data = OpRethStatelessClient::data_from_slice(&stateless_client_data_pot) + let stateless_client_data = OpRethStatelessClient::data_from_parts(&stateless_client_data_rkyv, &stateless_client_data_pot) .expect("Failed to load client data from stdin"); let validation_depth = stateless_client_data.blocks.len() as u64; assert!(stateless_client_data.chain.is_optimism(), "This program only supports Optimism chains");