diff --git a/Cargo.lock b/Cargo.lock index 6a86d0e5..1e556a7d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1810,12 +1810,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "critical-section" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" - [[package]] name = "crossbeam" version = "0.8.4" @@ -4040,10 +4034,6 @@ name = "once_cell" version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" -dependencies = [ - "critical-section", - "portable-atomic", -] [[package]] name = "open-fastrlp" @@ -4872,7 +4862,6 @@ dependencies = [ "clap 4.5.4", "ethers-core", "raiko-lib", - "raiko-primitives", "reqwest 0.11.27", "reqwest 0.12.4", "revm", @@ -4923,7 +4912,6 @@ dependencies = [ "proptest", "raiko-core", "raiko-lib", - "raiko-primitives", "reqwest 0.11.27", "reqwest 0.12.4", "revm", @@ -4954,6 +4942,7 @@ name = "raiko-lib" version = "0.1.0" dependencies = [ "alloy-consensus 0.1.0 (git+https://github.com/brechtpd/alloy?branch=175_4e22b9e)", + "alloy-eips 0.1.0 (git+https://github.com/brechtpd/alloy?branch=175_4e22b9e)", "alloy-network 0.1.0 (git+https://github.com/brechtpd/alloy?branch=175_4e22b9e)", "alloy-primitives", "alloy-rlp", @@ -4967,16 +4956,20 @@ dependencies = [ "chrono", "flate2", "hex", + "hex-literal", "lazy_static", "libflate", "log", "once_cell", - "raiko-primitives", "revm", + "revm-primitives", + "rlp", "serde", "serde_json", "serde_with", "sha2", + "sha3", + "tempfile", "thiserror", "thiserror-no-std", "tokio", @@ -4998,32 +4991,6 @@ dependencies = [ "risc0-binfmt", ] -[[package]] -name = "raiko-primitives" -version = "0.1.0" -dependencies = [ - "alloy-dyn-abi", - "alloy-eips 0.1.0 (git+https://github.com/brechtpd/alloy?branch=175_4e22b9e)", - "alloy-primitives", - "alloy-rlp", - "alloy-rlp-derive", - "alloy-rpc-types 0.1.0 (git+https://github.com/brechtpd/alloy?branch=175_4e22b9e)", - "alloy-sol-types", - "anyhow", - "bincode", - "c-kzg-taiko", - "hex-literal", - "once_cell", - "revm-primitives", - "rlp", - "serde", - "serde_json", - "sha2", - "sha3", - "tempfile", - "thiserror", -] - [[package]] name = "raiko-setup" version = "0.1.0" @@ -5056,7 +5023,6 @@ dependencies = [ "prometheus", "proptest", "raiko-lib", - "raiko-primitives", "reqwest 0.11.27", "reqwest 0.12.4", "revm", @@ -5563,7 +5529,6 @@ dependencies = [ "log", "once_cell", "raiko-lib", - "raiko-primitives", "risc0-zkvm", "serde", "serde_json", @@ -6307,7 +6272,6 @@ dependencies = [ "dirs", "hex", "raiko-lib", - "raiko-primitives", "rand", "rand_core", "secp256k1 0.27.0", @@ -6334,7 +6298,6 @@ dependencies = [ "once_cell", "pem 3.0.4", "raiko-lib", - "raiko-primitives", "serde", "serde_json", "serde_with", diff --git a/Cargo.toml b/Cargo.toml index 43678683..8d98ad0f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,6 @@ resolver = "2" members = [ "lib", "host", - "primitives", "provers/sp1/driver", "provers/sp1/builder", "provers/risc0/driver", @@ -33,7 +32,6 @@ opt-level = 3 # raiko raiko-lib = { path = "./lib", features = ["std"] } -raiko-primitives = { path = "./primitives" } raiko-core = { path = "./core" } # revm diff --git a/core/Cargo.toml b/core/Cargo.toml index 4b652b1f..9a359519 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -11,8 +11,7 @@ risc0-driver = { path = "../provers/risc0/driver", optional = true } sgx-prover = { path = "../provers/sgx/prover", optional = true } # raiko -raiko-lib = { workspace = true } -raiko-primitives = { workspace = true, features = ["c-kzg"] } +raiko-lib = { workspace = true, features = ["c-kzg"] } # alloy alloy-rlp = { workspace = true } diff --git a/core/src/lib.rs b/core/src/lib.rs index e58d39a1..fdb56ac1 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -179,8 +179,10 @@ mod tests { }; use alloy_primitives::Address; use clap::ValueEnum; - use raiko_lib::consts::{Network, SupportedChainSpecs}; - use raiko_primitives::B256; + use raiko_lib::{ + consts::{Network, SupportedChainSpecs}, + primitives::B256, + }; use serde_json::{json, Value}; use std::{collections::HashMap, env}; diff --git a/core/src/preflight.rs b/core/src/preflight.rs index 7ccf5bcb..7ca9413e 100644 --- a/core/src/preflight.rs +++ b/core/src/preflight.rs @@ -16,13 +16,13 @@ use raiko_lib::{ decode_anchor, proposeBlockCall, BlockProposed, GuestInput, TaikoGuestInput, TaikoProverData, }, + primitives::{ + eip4844::{kzg_to_versioned_hash, MAINNET_KZG_TRUSTED_SETUP}, + mpt::proofs_to_tries, + }, utils::{generate_transactions, to_header, zlib_compress_data}, Measurement, }; -use raiko_primitives::{ - eip4844::{kzg_to_versioned_hash, MAINNET_KZG_TRUSTED_SETUP}, - mpt::proofs_to_tries, -}; use serde::{Deserialize, Serialize}; use std::{collections::HashSet, sync::Arc}; use tracing::{info, warn}; @@ -488,7 +488,7 @@ async fn get_block_proposed_event( }; let event = BlockProposed::decode_log(&log_struct, false) .map_err(|_| RaikoError::Anyhow(anyhow!("Could not decode log")))?; - if event.blockId == raiko_primitives::U256::from(l2_block_number) { + if event.blockId == raiko_lib::primitives::U256::from(l2_block_number) { let Some(log_tx_hash) = log.transaction_hash else { bail!("No transaction hash in the log") }; @@ -615,9 +615,9 @@ mod test { use ethers_core::types::Transaction; use raiko_lib::{ consts::{Network, SupportedChainSpecs}, + primitives::{eip4844::parse_kzg_trusted_setup, kzg::KzgSettings}, utils::decode_transactions, }; - use raiko_primitives::{eip4844::parse_kzg_trusted_setup, kzg::KzgSettings}; use super::*; diff --git a/core/src/provider/db.rs b/core/src/provider/db.rs index 8897fe74..0d6646c1 100644 --- a/core/src/provider/db.rs +++ b/core/src/provider/db.rs @@ -15,8 +15,13 @@ use std::{collections::HashSet, mem::take}; use alloy_consensus::Header as AlloyConsensusHeader; use alloy_primitives::Bytes; -use raiko_lib::{builder::OptimisticDatabase, consts::ChainSpec, mem_db::MemDb, utils::to_header}; -use raiko_primitives::{Address, B256, U256}; +use raiko_lib::{ + builder::OptimisticDatabase, + consts::ChainSpec, + mem_db::MemDb, + primitives::{Address, B256, U256}, + utils::to_header, +}; use revm::{ primitives::{Account, AccountInfo, Bytecode, HashMap}, Database, DatabaseCommit, diff --git a/host/Cargo.toml b/host/Cargo.toml index ec2c20a6..668c17a6 100644 --- a/host/Cargo.toml +++ b/host/Cargo.toml @@ -12,8 +12,7 @@ risc0-driver = { path = "../provers/risc0/driver", optional = true } sgx-prover = { path = "../provers/sgx/prover", optional = true } # raiko -raiko-lib = { workspace = true } -raiko-primitives = { workspace = true, features = ["c-kzg"] } +raiko-lib = { workspace = true, features = ["c-kzg"] } raiko-core = { workspace = true } # alloy diff --git a/lib/Cargo.toml b/lib/Cargo.toml index 6d2ac371..0a34f66f 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -4,9 +4,8 @@ version = "0.1.0" edition = "2021" [dependencies] -raiko-primitives = { workspace = true } - alloy-rlp = { workspace = true } +alloy-eips = { workspace = true } alloy-rlp-derive = { workspace = true } alloy-sol-types = { workspace = true } alloy-primitives = { workspace = true } @@ -14,22 +13,31 @@ alloy-rpc-types = { workspace = true } alloy-consensus = { workspace = true } alloy-network = { workspace = true } +# errors anyhow = { workspace = true } -libflate = { workspace = true } -once_cell = { workspace = true } -revm = { workspace = true } +thiserror-no-std = { workspace = true } +thiserror = { workspace = true, optional = true } + +# serde serde = { workspace = true } serde_json = { workspace = true, optional = true } serde_with = { workspace = true, optional = true } -thiserror-no-std = { workspace = true } + +# revm +revm = { workspace = true } +revm-primitives = { workspace = true } + +libflate = { workspace = true } +once_cell = { workspace = true } url = { workspace = true } hex = { workspace = true } c-kzg = { workspace = true } sha2 = { workspace = true } +sha3 = { workspace = true } +rlp = { workspace = true, features = ["std"] } cfg-if = { workspace = true } # [target.'cfg(feature = "std")'.dependencies] -thiserror = { workspace = true, optional = true } flate2 = { workspace = true, optional = true } log = { workspace = true, optional = true } @@ -38,8 +46,11 @@ chrono = { workspace = true, optional = true } lazy_static = { workspace = true } +tempfile = { workspace = true, optional = true } + [dev-dependencies] bincode = "1.3" +hex-literal = { workspace = true } [features] default = ["std"] @@ -55,9 +66,8 @@ std = [ "dep:serde_with", # "dep:tokio", ] -tracer = [ - "revm/serde-json", -] +tracer = ["revm/serde-json"] sgx = [] sp1 = [] risc0 = [] +c-kzg = ["revm-primitives/c-kzg", "dep:tempfile"] diff --git a/lib/src/builder/execute.rs b/lib/src/builder/execute.rs index 67fbf0ef..3bf50580 100644 --- a/lib/src/builder/execute.rs +++ b/lib/src/builder/execute.rs @@ -20,10 +20,6 @@ use alloy_primitives::{TxKind, U256}; use anyhow::{anyhow, bail, ensure, Context, Error, Result}; #[cfg(feature = "std")] use log::debug; -use raiko_primitives::{ - alloy_eips::eip4788::SYSTEM_ADDRESS, mpt::MptNode, receipt::Receipt, Bloom, Rlp2718Bytes, - RlpBytes, -}; use revm::{ interpreter::Host, primitives::{ @@ -44,7 +40,12 @@ use crate::{ builder::BlockBuilder, clear_line, consts::GWEI_TO_WEI, - guest_mem_forget, inplace_print, print_duration, + guest_mem_forget, inplace_print, + primitives::{ + alloy_eips::eip4788::SYSTEM_ADDRESS, mpt::MptNode, receipt::Receipt, Bloom, Rlp2718Bytes, + RlpBytes, + }, + print_duration, time::{AddAssign, Duration, Instant}, utils::{check_anchor_tx, generate_transactions}, Measurement, diff --git a/lib/src/builder/finalize.rs b/lib/src/builder/finalize.rs index a499125e..a13442f5 100644 --- a/lib/src/builder/finalize.rs +++ b/lib/src/builder/finalize.rs @@ -16,16 +16,16 @@ use core::mem; use alloy_consensus::Header as AlloyConsensusHeader; use anyhow::Result; -use raiko_primitives::{ - keccak::keccak, - mpt::{MptNode, StateAccount}, -}; use revm::{Database, DatabaseCommit}; use crate::{ builder::BlockBuilder, guest_mem_forget, mem_db::{AccountState, MemDb}, + primitives::{ + keccak::keccak, + mpt::{MptNode, StateAccount}, + }, }; pub trait BlockFinalizeStrategy diff --git a/lib/src/builder/initialize.rs b/lib/src/builder/initialize.rs index 5db6fb7c..394f19d8 100644 --- a/lib/src/builder/initialize.rs +++ b/lib/src/builder/initialize.rs @@ -15,11 +15,6 @@ use core::mem; use anyhow::{bail, Result}; -use raiko_primitives::{ - keccak::{keccak, KECCAK_EMPTY}, - mpt::StateAccount, - Bytes, -}; use revm::{ primitives::{AccountInfo, Bytecode, HashMap, B256}, Database, DatabaseCommit, @@ -30,6 +25,11 @@ use crate::{ consts::MAX_BLOCK_HASH_AGE, guest_mem_forget, mem_db::{AccountState, DbAccount, MemDb}, + primitives::{ + keccak::{keccak, KECCAK_EMPTY}, + mpt::StateAccount, + Bytes, + }, utils::HeaderHasher, }; @@ -98,7 +98,7 @@ impl DbInitStrategy for MemDbInitStrategy { // load storage reads let mut storage = HashMap::with_capacity(slots.len()); for slot in slots { - let value: raiko_primitives::U256 = storage_trie + let value: crate::primitives::U256 = storage_trie .get_rlp(&keccak(slot.to_be_bytes::<32>()))? .unwrap_or_default(); storage.insert(slot, value); diff --git a/lib/src/builder/mod.rs b/lib/src/builder/mod.rs index 100ca16d..6afeb72a 100644 --- a/lib/src/builder/mod.rs +++ b/lib/src/builder/mod.rs @@ -14,7 +14,6 @@ use alloy_consensus::Header as AlloyConsensusHeader; use anyhow::Result; -use raiko_primitives::mpt::MptNode; use revm::{Database, DatabaseCommit}; pub use self::execute::TkoTxExecStrategy; @@ -27,6 +26,7 @@ use crate::{ consts::ChainSpec, input::GuestInput, mem_db::MemDb, + primitives::mpt::MptNode, }; pub mod execute; diff --git a/lib/src/consts.rs b/lib/src/consts.rs index afcc14ee..2e17ca8f 100644 --- a/lib/src/consts.rs +++ b/lib/src/consts.rs @@ -19,13 +19,13 @@ use alloc::collections::BTreeMap; use alloy_primitives::Address; use anyhow::{anyhow, bail, Result}; -use raiko_primitives::{uint, BlockNumber, ChainId, U256}; use revm::primitives::SpecId; use serde::{Deserialize, Serialize}; use serde_json::Value; #[cfg(not(feature = "std"))] use crate::no_std::*; +use crate::primitives::{uint, BlockNumber, ChainId, U256}; use std::collections::HashMap; use std::path::PathBuf; diff --git a/lib/src/input.rs b/lib/src/input.rs index cb183713..afddeb54 100644 --- a/lib/src/input.rs +++ b/lib/src/input.rs @@ -19,14 +19,17 @@ use alloy_consensus::Header as AlloyConsensusHeader; use alloy_rpc_types::Withdrawal as AlloyWithdrawal; use alloy_sol_types::{sol, SolCall}; use anyhow::{anyhow, Result}; -use raiko_primitives::{mpt::MptNode, Address, Bytes, B256, U256}; use revm::primitives::HashMap; use serde::{Deserialize, Serialize}; use serde_with::serde_as; #[cfg(not(feature = "std"))] use crate::no_std::*; -use crate::{consts::ChainSpec, serde_with::RlpBytes, serde_with::RlpHexBytes}; +use crate::{ + consts::ChainSpec, + primitives::{mpt::MptNode, Address, Bytes, B256, U256}, + serde_with::{RlpBytes, RlpHexBytes}, +}; /// Represents the state of an account's storage. /// The storage trie together with the used storage slots allow us to reconstruct all the diff --git a/lib/src/lib.rs b/lib/src/lib.rs index 905f89db..8190f204 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -32,6 +32,7 @@ pub mod builder; pub mod consts; pub mod input; pub mod mem_db; +pub mod primitives; pub mod protocol_instance; pub mod prover; pub mod utils; diff --git a/lib/src/mem_db.rs b/lib/src/mem_db.rs index bc3e96ed..50930945 100644 --- a/lib/src/mem_db.rs +++ b/lib/src/mem_db.rs @@ -12,7 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. use anyhow::anyhow; -use raiko_primitives::{Address, B256, U256}; use revm::{ primitives::{Account, AccountInfo, Bytecode}, Database, DatabaseCommit, @@ -21,9 +20,12 @@ use serde::{Deserialize, Serialize}; use std::collections::{hash_map::Entry, HashMap}; use thiserror_no_std::Error as ThisError; -use crate::builder::OptimisticDatabase; #[cfg(not(feature = "std"))] use crate::no_std::*; +use crate::{ + builder::OptimisticDatabase, + primitives::{Address, B256, U256}, +}; /// Error returned by the [MemDb]. #[derive(Debug, ThisError)] diff --git a/lib/src/primitives/eip4844.rs b/lib/src/primitives/eip4844.rs new file mode 100644 index 00000000..0dfca5a2 --- /dev/null +++ b/lib/src/primitives/eip4844.rs @@ -0,0 +1,87 @@ +//! Helpers for working with EIP-4844 blob fee. + +// re-exports from revm for calculating blob fee +pub use revm_primitives::{calc_blob_gasprice, calc_excess_blob_gas as calculate_excess_blob_gas}; +#[cfg(feature = "c-kzg")] +use sha2::{Digest, Sha256}; + +#[cfg(feature = "c-kzg")] +use crate::primitives::B256; + +/// Calculates the versioned hash for a KzgCommitment +/// +/// Specified in [EIP-4844](https://eips.ethereum.org/EIPS/eip-4844#header-extension) +#[cfg(feature = "c-kzg")] +pub fn kzg_to_versioned_hash(commitment: &c_kzg::KzgCommitment) -> B256 { + let mut res = Sha256::digest(commitment.as_slice()); + res[0] = VERSIONED_HASH_VERSION_KZG; + B256::new(res.into()) +} + +/// Constants for EIP-4844 +/// from https://github.com/paradigmxyz/reth/blob/79452eadaf4963f1e8d78a18b1f490d7c560aa54/crates/primitives/src/constants/eip4844.rs#L2 +pub use alloy_eips::eip4844::{ + BLOB_GASPRICE_UPDATE_FRACTION, BLOB_TX_MIN_BLOB_GASPRICE, DATA_GAS_PER_BLOB, + FIELD_ELEMENTS_PER_BLOB, FIELD_ELEMENT_BYTES, MAX_BLOBS_PER_BLOCK, MAX_DATA_GAS_PER_BLOCK, + TARGET_BLOBS_PER_BLOCK, TARGET_DATA_GAS_PER_BLOCK, VERSIONED_HASH_VERSION_KZG, +}; +/// [EIP-4844](https://eips.ethereum.org/EIPS/eip-4844#parameters) protocol constants and utils for shard Blob Transactions. +#[cfg(feature = "c-kzg")] +pub use trusted_setup::*; + +#[cfg(feature = "c-kzg")] +mod trusted_setup { + use std::{io::Write, sync::Arc}; + + use once_cell::sync::Lazy; + pub use revm_primitives::kzg::parse_kzg_trusted_setup; + + use crate::primitives::kzg::KzgSettings; + + /// KZG trusted setup + pub static MAINNET_KZG_TRUSTED_SETUP: Lazy> = Lazy::new(|| { + Arc::new( + c_kzg::KzgSettings::load_trusted_setup( + &revm_primitives::kzg::G1_POINTS.0, + &revm_primitives::kzg::G2_POINTS.0, + ) + .expect("failed to load trusted setup"), + ) + }); + + /// Loads the trusted setup parameters from the given bytes and returns the + /// [KzgSettings]. + /// + /// This creates a temp file to store the bytes and then loads the [KzgSettings] from + /// the file via [KzgSettings::load_trusted_setup_file]. + pub fn load_trusted_setup_from_bytes( + bytes: &[u8], + ) -> Result { + let mut file = tempfile::NamedTempFile::new().map_err(LoadKzgSettingsError::TempFileErr)?; + file.write_all(bytes) + .map_err(LoadKzgSettingsError::TempFileErr)?; + KzgSettings::load_trusted_setup_file(file.path()).map_err(LoadKzgSettingsError::KzgError) + } + + /// Error type for loading the trusted setup. + #[derive(Debug, thiserror::Error)] + pub enum LoadKzgSettingsError { + /// Failed to create temp file to store bytes for loading [KzgSettings] via + /// [KzgSettings::load_trusted_setup_file]. + #[error("failed to setup temp file: {0}")] + TempFileErr(#[from] std::io::Error), + /// Kzg error + #[error("KZG error: {0:?}")] + KzgError(#[from] c_kzg::Error), + } + + #[cfg(test)] + mod tests { + use super::*; + + #[test] + fn ensure_load_kzg_settings() { + let _settings = Arc::clone(&MAINNET_KZG_TRUSTED_SETUP); + } + } +} diff --git a/lib/src/primitives/keccak.rs b/lib/src/primitives/keccak.rs new file mode 100644 index 00000000..a7ae6f28 --- /dev/null +++ b/lib/src/primitives/keccak.rs @@ -0,0 +1,38 @@ +// Copyright 2023 RISC Zero, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use alloy_primitives::{b256, B256}; +use sha3::{Digest, Keccak256}; + +/// Represents the Keccak-256 hash of an empty byte slice. +/// +/// This is a constant value and can be used as a default or placeholder +/// in various cryptographic operations. +pub const KECCAK_EMPTY: B256 = + b256!("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"); + +/// Computes the Keccak-256 hash of the provided data. +/// +/// This function is a thin wrapper around the Keccak256 hashing algorithm +/// and is optimized for performance. +/// +/// # TODO +/// - Consider switching the return type to `B256` for consistency with other parts of the +/// codebase. +#[inline] +pub fn keccak(data: impl AsRef<[u8]>) -> [u8; 32] { + // TODO: Remove this benchmarking code once performance testing is complete. + // std::hint::black_box(sha2::Sha256::digest(&data)); + Keccak256::digest(data).into() +} diff --git a/lib/src/primitives/mod.rs b/lib/src/primitives/mod.rs new file mode 100644 index 00000000..1ddf8115 --- /dev/null +++ b/lib/src/primitives/mod.rs @@ -0,0 +1,69 @@ +// Copyright 2023 RISC Zero, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//#![cfg_attr(not(feature = "std"), no_std)] + +extern crate alloc; +extern crate core; + +pub use alloc::{vec, vec::Vec}; + +pub mod eip4844; +pub mod keccak; +pub mod mpt; +pub mod receipt; +pub mod signature; + +#[cfg(feature = "c-kzg")] +pub use c_kzg as kzg; + +pub mod revm; +pub use alloy_eips; +pub use alloy_primitives::*; +pub use alloy_rlp as rlp; + +pub trait RlpBytes { + /// Returns the RLP-encoding. + fn to_rlp(&self) -> Vec; +} + +impl RlpBytes for T +where + T: rlp::Encodable, +{ + #[inline] + fn to_rlp(&self) -> Vec { + let rlp_length = self.length(); + let mut out = Vec::with_capacity(rlp_length); + self.encode(&mut out); + debug_assert_eq!(out.len(), rlp_length); + out + } +} + +pub trait Rlp2718Bytes { + /// Returns the RLP-encoding. + fn to_rlp_2718(&self) -> Vec; +} + +impl Rlp2718Bytes for T +where + T: alloy_eips::eip2718::Encodable2718, +{ + #[inline] + fn to_rlp_2718(&self) -> Vec { + let mut out = Vec::new(); + self.encode_2718(&mut out); + out + } +} diff --git a/lib/src/primitives/mpt.rs b/lib/src/primitives/mpt.rs new file mode 100644 index 00000000..9680f7a4 --- /dev/null +++ b/lib/src/primitives/mpt.rs @@ -0,0 +1,1407 @@ +// Copyright 2023 RISC Zero, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use core::{ + cell::RefCell, + cmp, + fmt::{Debug, Write}, + iter, mem, +}; + +use alloy_primitives::{b256, TxNumber, B256, U256}; +use alloy_rlp::Encodable; +use alloy_rlp_derive::{RlpDecodable, RlpEncodable, RlpMaxEncodedLen}; +use alloy_rpc_types::EIP1186AccountProofResponse; +use anyhow::{Context, Result}; +use revm_primitives::{Address, HashMap}; +use rlp::{Decodable, DecoderError, Prototype, Rlp}; +use serde::{Deserialize, Serialize}; +use thiserror::Error as ThisError; + +pub type StorageEntry = (MptNode, Vec); + +/// Represents an Ethereum account within the state trie. +/// +/// The `StateAccount` struct encapsulates key details of an Ethereum account, including +/// its nonce, balance, storage root, and the hash of its associated bytecode. This +/// representation is used when interacting with or querying the Ethereum state trie. +#[derive( + Debug, + Clone, + PartialEq, + Eq, + Serialize, + Deserialize, + RlpEncodable, + RlpDecodable, + RlpMaxEncodedLen, +)] +pub struct StateAccount { + /// The number of transactions sent from this account's address. + pub nonce: TxNumber, + /// The current balance of the account in Wei. + pub balance: U256, + /// The root of the account's storage trie, representing all stored contract data. + pub storage_root: B256, + /// The Keccak-256 hash of the account's associated bytecode (if it's a contract). + pub code_hash: B256, +} + +impl Default for StateAccount { + /// Provides default values for a [StateAccount]. + /// + /// The default account has a nonce of 0, a balance of 0 Wei, an empty storage root, + /// and an empty bytecode hash. + fn default() -> Self { + Self { + nonce: 0, + balance: U256::ZERO, + storage_root: EMPTY_ROOT, + code_hash: KECCAK_EMPTY, + } + } +} + +pub trait RlpBytes { + /// Returns the RLP-encoding. + fn to_rlp(&self) -> Vec; +} + +impl RlpBytes for T +where + T: alloy_rlp::Encodable, +{ + #[inline] + fn to_rlp(&self) -> Vec { + let rlp_length = self.length(); + let mut out = Vec::with_capacity(rlp_length); + self.encode(&mut out); + debug_assert_eq!(out.len(), rlp_length); + out + } +} + +/// Root hash of an empty trie. +pub const EMPTY_ROOT: B256 = + b256!("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"); + +extern crate alloc; + +/// Represents the Keccak-256 hash of an empty byte slice. +/// +/// This is a constant value and can be used as a default or placeholder +/// in various cryptographic operations. +pub const KECCAK_EMPTY: B256 = + b256!("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"); + +/// Computes the Keccak-256 hash of the provided data. +/// +/// This function is a thin wrapper around the Keccak256 hashing algorithm +/// and is optimized for performance. +/// +/// # TODO +/// - Consider switching the return type to `B256` for consistency with other parts of the +/// codebase. +#[inline] +pub fn keccak(data: impl AsRef<[u8]>) -> [u8; 32] { + // TODO: Remove this benchmarking code once performance testing is complete. + // std::hint::black_box(sha2::Sha256::digest(&data)); + *alloy_primitives::utils::keccak256(data) +} + +/// Represents the root node of a sparse Merkle Patricia Trie. +/// +/// The "sparse" nature of this trie allows for truncation of certain unneeded parts, +/// representing them by their node hash. This design choice is particularly useful for +/// 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)] +pub struct MptNode { + /// The type and data of the node. + data: MptNodeData, + /// Cache for a previously computed reference of this node. This is skipped during + /// serialization. + #[serde(skip)] + cached_reference: RefCell>, +} + +/// Represents custom error types for the sparse Merkle Patricia Trie (MPT). +/// +/// These errors cover various scenarios that can occur during trie operations, such as +/// encountering unresolved nodes, finding values in branches where they shouldn't be, and +/// issues related to RLP (Recursive Length Prefix) encoding and decoding. +#[derive(Debug, ThisError)] +pub enum Error { + /// Triggered when an operation reaches an unresolved node. The associated `B256` + /// value provides details about the unresolved node. + #[error("reached an unresolved node: {0:#}")] + NodeNotResolved(B256), + /// Occurs when a value is unexpectedly found in a branch node. + #[error("branch node with value")] + ValueInBranch, + /// Represents errors related to the RLP encoding and decoding using the `alloy_rlp` + /// library. + #[error("RLP error")] + Rlp(#[from] alloy_rlp::Error), + /// Represents errors related to the RLP encoding and decoding, specifically legacy + /// errors. + #[error("RLP error")] + LegacyRlp(#[from] DecoderError), +} + +/// Represents the various types of data that can be stored within a node in the sparse +/// Merkle Patricia Trie (MPT). +/// +/// 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)] +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]), + /// 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), + /// Represents a sub-trie by its hash, allowing for efficient storage of large + /// sub-tries without storing their entire content. + Digest(B256), +} + +/// Represents the ways in which one node can reference another node inside the sparse +/// Merkle Patricia Trie (MPT). +/// +/// Nodes in the MPT can reference other nodes either directly through their byte +/// representation or indirectly through a hash of their encoding. This enum provides a +/// clear and type-safe way to represent these references. +#[derive(Clone, Debug, PartialEq, Eq, Hash, Ord, PartialOrd, Serialize, Deserialize)] +pub enum MptNodeReference { + /// Represents a direct reference to another node using its byte encoding. Typically + /// used for short encodings that are less than 32 bytes in length. + Bytes(Vec), + /// Represents an indirect reference to another node using the Keccak hash of its long + /// encoding. Used for encodings that are not less than 32 bytes in length. + Digest(B256), +} + +/// Provides a conversion from [MptNodeData] to [MptNode]. +/// +/// This implementation allows for conversion from [MptNodeData] to [MptNode], +/// initializing the `data` field with the provided value and setting the +/// `cached_reference` field to `None`. +impl From for MptNode { + fn from(value: MptNodeData) -> Self { + Self { + data: value, + cached_reference: RefCell::new(None), + } + } +} + +/// Provides encoding functionalities for the `MptNode` type. +/// +/// This implementation allows for the serialization of an [MptNode] into its RLP-encoded +/// form. The encoding is done based on the type of node data ([MptNodeData]) it holds. +impl Encodable for MptNode { + /// Encodes the node into the provided `out` buffer. + /// + /// The encoding is done using the Recursive Length Prefix (RLP) encoding scheme. The + /// method handles different node data types and encodes them accordingly. + #[inline] + fn encode(&self, out: &mut dyn alloy_rlp::BufMut) { + match &self.data { + MptNodeData::Null => { + out.put_u8(alloy_rlp::EMPTY_STRING_CODE); + } + MptNodeData::Branch(nodes) => { + alloy_rlp::Header { + list: true, + payload_length: self.payload_length(), + } + .encode(out); + for child in nodes { + match child { + Some(node) => node.reference_encode(out), + None => out.put_u8(alloy_rlp::EMPTY_STRING_CODE), + } + } + // in the MPT reference, branches have values so always add empty value + out.put_u8(alloy_rlp::EMPTY_STRING_CODE); + } + MptNodeData::Leaf(prefix, value) => { + alloy_rlp::Header { + list: true, + payload_length: self.payload_length(), + } + .encode(out); + prefix.as_slice().encode(out); + value.as_slice().encode(out); + } + MptNodeData::Extension(prefix, node) => { + alloy_rlp::Header { + list: true, + payload_length: self.payload_length(), + } + .encode(out); + prefix.as_slice().encode(out); + node.reference_encode(out); + } + MptNodeData::Digest(digest) => { + digest.encode(out); + } + } + } + + /// Returns the length of the encoded node in bytes. + /// + /// This method calculates the length of the RLP-encoded node. It's useful for + /// determining the size requirements for storage or transmission. + #[inline] + fn length(&self) -> usize { + let payload_length = self.payload_length(); + payload_length + alloy_rlp::length_of_length(payload_length) + } +} + +/// Provides decoding functionalities for the [MptNode] type. +/// +/// This implementation allows for the deserialization of an RLP-encoded [MptNode] back +/// into its original form. The decoding is done based on the prototype of the RLP data, +/// ensuring that the node is reconstructed accurately. +/// +/// **Note**: This implementation is still using the older RLP library and needs to be +/// migrated to `alloy_rlp` in the future. +// TODO: migrate to alloy_rlp +impl Decodable for MptNode { + /// Decodes an RLP-encoded node from the provided `rlp` buffer. + /// + /// The method handles different RLP prototypes and reconstructs the `MptNode` based + /// on the encoded data. If the RLP data does not match any known prototype or if + /// there's an error during decoding, an error is returned. + fn decode(rlp: &Rlp) -> Result { + match rlp.prototype()? { + Prototype::Null | Prototype::Data(0) => Ok(MptNodeData::Null.into()), + Prototype::List(2) => { + let path: Vec = rlp.val_at(0)?; + let prefix = path[0]; + if (prefix & (2 << 4)) == 0 { + let node: MptNode = Decodable::decode(&rlp.at(1)?)?; + Ok(MptNodeData::Extension(path, Box::new(node)).into()) + } else { + Ok(MptNodeData::Leaf(path, rlp.val_at(1)?).into()) + } + } + Prototype::List(17) => { + let mut node_list = Vec::with_capacity(16); + for node_rlp in rlp.iter().take(16) { + match node_rlp.prototype()? { + Prototype::Null | Prototype::Data(0) => { + node_list.push(None); + } + _ => node_list.push(Some(Box::new(Decodable::decode(&node_rlp)?))), + } + } + let value: Vec = rlp.val_at(16)?; + if value.is_empty() { + Ok(MptNodeData::Branch(node_list.try_into().unwrap()).into()) + } else { + Err(DecoderError::Custom("branch node with value")) + } + } + Prototype::Data(32) => { + let bytes: Vec = rlp.as_val()?; + Ok(MptNodeData::Digest(B256::from_slice(&bytes)).into()) + } + _ => Err(DecoderError::RlpIncorrectListLen), + } + } +} + +/// Represents a node in the sparse Merkle Patricia Trie (MPT). +/// +/// The [MptNode] type encapsulates the data and functionalities associated with a node in +/// the MPT. It provides methods for manipulating the trie, such as inserting, deleting, +/// and retrieving values, as well as utility methods for encoding, decoding, and +/// debugging. +impl MptNode { + /// Clears the trie, replacing its data with an empty node, [MptNodeData::Null]. + /// + /// This method effectively removes all key-value pairs from the trie. + #[inline] + pub fn clear(&mut self) { + self.data = MptNodeData::Null; + self.invalidate_ref_cache(); + } + + /// Decodes an RLP-encoded [MptNode] from the provided byte slice. + /// + /// This method allows for the deserialization of a previously serialized [MptNode]. + #[inline] + pub fn decode(bytes: impl AsRef<[u8]>) -> Result { + rlp::decode(bytes.as_ref()).map_err(Error::from) + } + + /// Retrieves the underlying data of the node. + /// + /// This method provides a reference to the node's data, allowing for inspection and + /// manipulation. + #[inline] + pub fn as_data(&self) -> &MptNodeData { + &self.data + } + + /// Retrieves the [MptNodeReference] reference of the node when it's referenced inside + /// another node. + /// + /// This method provides a way to obtain a compact representation of the node for + /// storage or transmission purposes. + #[inline] + pub fn reference(&self) -> MptNodeReference { + self.cached_reference + .borrow_mut() + .get_or_insert_with(|| self.calc_reference()) + .clone() + } + + /// Computes and returns the 256-bit hash of the node. + /// + /// This method provides a unique identifier for the node based on its content. + #[inline] + pub fn hash(&self) -> B256 { + match self.data { + MptNodeData::Null => EMPTY_ROOT, + _ => match self.reference() { + MptNodeReference::Digest(digest) => digest, + MptNodeReference::Bytes(bytes) => keccak(bytes).into(), + }, + } + } + + /// Encodes the [MptNodeReference] of this node into the `out` buffer. + fn reference_encode(&self, out: &mut dyn alloy_rlp::BufMut) { + match self.reference() { + // if the reference is an RLP-encoded byte slice, copy it directly + MptNodeReference::Bytes(bytes) => out.put_slice(&bytes), + // if the reference is a digest, RLP-encode it with its fixed known length + MptNodeReference::Digest(digest) => { + out.put_u8(alloy_rlp::EMPTY_STRING_CODE + 32); + out.put_slice(digest.as_slice()); + } + } + } + + /// Returns the length of the encoded [MptNodeReference] of this node. + fn reference_length(&self) -> usize { + match self.reference() { + MptNodeReference::Bytes(bytes) => bytes.len(), + MptNodeReference::Digest(_) => 1 + 32, + } + } + + fn calc_reference(&self) -> MptNodeReference { + match &self.data { + MptNodeData::Null => MptNodeReference::Bytes(vec![alloy_rlp::EMPTY_STRING_CODE]), + MptNodeData::Digest(digest) => MptNodeReference::Digest(*digest), + _ => { + let encoded = alloy_rlp::encode(self); + if encoded.len() < 32 { + MptNodeReference::Bytes(encoded) + } else { + MptNodeReference::Digest(keccak(encoded).into()) + } + } + } + } + + /// Determines if the trie is empty. + /// + /// This method checks if the node represents an empty trie, i.e., it doesn't contain + /// any key-value pairs. + #[inline] + pub fn is_empty(&self) -> bool { + matches!(&self.data, MptNodeData::Null) + } + + /// Determines if the node represents a digest. + /// + /// A digest is a compact representation of a sub-trie, represented by its hash. + #[inline] + pub fn is_digest(&self) -> bool { + matches!(&self.data, MptNodeData::Digest(_)) + } + + /// Retrieves the nibbles corresponding to the node's prefix. + /// + /// Nibbles are half-bytes, and in the context of the MPT, they represent parts of + /// keys. + #[inline] + pub fn nibs(&self) -> Vec { + match &self.data { + MptNodeData::Null | MptNodeData::Branch(_) | MptNodeData::Digest(_) => vec![], + MptNodeData::Leaf(prefix, _) | MptNodeData::Extension(prefix, _) => prefix_nibs(prefix), + } + } + + /// Retrieves the value associated with a given key in the trie. + /// + /// If the key is not present in the trie, this method returns `None`. Otherwise, it + /// returns a reference to the associated value. If [None] is returned, the key is + /// provably not in the trie. + #[inline] + pub fn get(&self, key: &[u8]) -> Result, Error> { + self.get_internal(&to_nibs(key)) + } + + /// Retrieves the RLP-decoded value corresponding to the key. + /// + /// If the key is not present in the trie, this method returns `None`. Otherwise, it + /// returns the RLP-decoded value. + #[inline] + pub fn get_rlp(&self, key: &[u8]) -> Result, Error> { + match self.get(key)? { + Some(mut bytes) => Ok(Some(T::decode(&mut bytes)?)), + None => Ok(None), + } + } + + fn get_internal(&self, key_nibs: &[u8]) -> Result, Error> { + match &self.data { + MptNodeData::Null => Ok(None), + MptNodeData::Branch(nodes) => { + if let Some((i, tail)) = key_nibs.split_first() { + match nodes[*i as usize] { + Some(ref node) => node.get_internal(tail), + None => Ok(None), + } + } else { + Ok(None) + } + } + MptNodeData::Leaf(prefix, value) => { + if prefix_nibs(prefix) == key_nibs { + Ok(Some(value)) + } else { + Ok(None) + } + } + MptNodeData::Extension(prefix, node) => { + if let Some(tail) = key_nibs.strip_prefix(prefix_nibs(prefix).as_slice()) { + node.get_internal(tail) + } else { + Ok(None) + } + } + MptNodeData::Digest(digest) => Err(Error::NodeNotResolved(*digest)), + } + } + + /// Removes a key from the trie. + /// + /// This method attempts to remove a key-value pair from the trie. If the key is + /// present, it returns `true`. Otherwise, it returns `false`. + #[inline] + pub fn delete(&mut self, key: &[u8]) -> Result { + self.delete_internal(&to_nibs(key)) + } + + fn delete_internal(&mut self, key_nibs: &[u8]) -> Result { + match &mut self.data { + MptNodeData::Null => return Ok(false), + MptNodeData::Branch(children) => { + if let Some((i, tail)) = key_nibs.split_first() { + let child = &mut children[*i as usize]; + match child { + Some(node) => { + if !node.delete_internal(tail)? { + return Ok(false); + } + // if the node is now empty, remove it + if node.is_empty() { + *child = None; + } + } + None => return Ok(false), + } + } else { + return Err(Error::ValueInBranch); + } + + let mut remaining = children.iter_mut().enumerate().filter(|(_, n)| n.is_some()); + // there will always be at least one remaining node + let (index, node) = remaining.next().unwrap(); + // if there is only exactly one node left, we need to convert the branch + if remaining.next().is_none() { + let mut orphan = node.take().unwrap(); + match &mut orphan.data { + // if the orphan is a leaf, prepend the corresponding nib to it + MptNodeData::Leaf(prefix, orphan_value) => { + let new_nibs: Vec<_> = + iter::once(index as u8).chain(prefix_nibs(prefix)).collect(); + self.data = MptNodeData::Leaf( + to_encoded_path(&new_nibs, true), + mem::take(orphan_value), + ); + } + // if the orphan is an extension, prepend the corresponding nib to it + MptNodeData::Extension(prefix, orphan_child) => { + let new_nibs: Vec<_> = + iter::once(index as u8).chain(prefix_nibs(prefix)).collect(); + self.data = MptNodeData::Extension( + to_encoded_path(&new_nibs, false), + mem::take(orphan_child), + ); + } + // if the orphan is a branch or digest, convert to an extension + MptNodeData::Branch(_) | MptNodeData::Digest(_) => { + self.data = MptNodeData::Extension( + to_encoded_path(&[index as u8], false), + orphan, + ); + } + MptNodeData::Null => unreachable!(), + } + } + } + MptNodeData::Leaf(prefix, _) => { + if prefix_nibs(prefix) != key_nibs { + return Ok(false); + } + self.data = MptNodeData::Null; + } + MptNodeData::Extension(prefix, child) => { + let mut self_nibs = prefix_nibs(prefix); + if let Some(tail) = key_nibs.strip_prefix(self_nibs.as_slice()) { + if !child.delete_internal(tail)? { + return Ok(false); + } + } else { + return Ok(false); + } + + // an extension can only point to a branch or a digest; since it's sub trie was + // modified, we need to make sure that this property still holds + match &mut child.data { + // if the child is empty, remove the extension + MptNodeData::Null => { + self.data = MptNodeData::Null; + } + // for a leaf, replace the extension with the extended leaf + MptNodeData::Leaf(prefix, value) => { + self_nibs.extend(prefix_nibs(prefix)); + self.data = + MptNodeData::Leaf(to_encoded_path(&self_nibs, true), mem::take(value)); + } + // for an extension, replace the extension with the extended extension + MptNodeData::Extension(prefix, node) => { + self_nibs.extend(prefix_nibs(prefix)); + self.data = MptNodeData::Extension( + to_encoded_path(&self_nibs, false), + mem::take(node), + ); + } + // for a branch or digest, the extension is still correct + MptNodeData::Branch(_) | MptNodeData::Digest(_) => {} + } + } + MptNodeData::Digest(digest) => return Err(Error::NodeNotResolved(*digest)), + }; + + self.invalidate_ref_cache(); + Ok(true) + } + + /// Inserts a key-value pair into the trie. + /// + /// This method attempts to insert a new key-value pair into the trie. If the + /// insertion is successful, it returns `true`. If the key already exists, it updates + /// the value and returns `false`. + #[inline] + pub fn insert(&mut self, key: &[u8], value: Vec) -> Result { + assert!(!value.is_empty(), "value must not be empty"); + self.insert_internal(&to_nibs(key), value) + } + + /// Inserts an RLP-encoded value into the trie. + /// + /// This method inserts a value that's been encoded using RLP into the trie. + #[inline] + pub fn insert_rlp(&mut self, key: &[u8], value: impl Encodable) -> Result { + self.insert_internal(&to_nibs(key), value.to_rlp()) + } + + #[inline] + pub fn insert_rlp_encoded(&mut self, key: &[u8], value: Vec) -> Result { + self.insert_internal(&to_nibs(key), value) + } + + fn insert_internal(&mut self, key_nibs: &[u8], value: Vec) -> Result { + match &mut self.data { + MptNodeData::Null => { + self.data = MptNodeData::Leaf(to_encoded_path(key_nibs, true), value); + } + MptNodeData::Branch(children) => { + if let Some((i, tail)) = key_nibs.split_first() { + let child = &mut children[*i as usize]; + match child { + Some(node) => { + if !node.insert_internal(tail, value)? { + return Ok(false); + } + } + // if the corresponding child is empty, insert a new leaf + None => { + *child = Some(Box::new( + MptNodeData::Leaf(to_encoded_path(tail, true), value).into(), + )); + } + } + } else { + return Err(Error::ValueInBranch); + } + } + MptNodeData::Leaf(prefix, old_value) => { + let self_nibs = prefix_nibs(prefix); + let common_len = lcp(&self_nibs, key_nibs); + if common_len == self_nibs.len() && common_len == key_nibs.len() { + // if self_nibs == key_nibs, update the value if it is different + if old_value == &value { + return Ok(false); + } + *old_value = value; + } else if common_len == self_nibs.len() || common_len == key_nibs.len() { + return Err(Error::ValueInBranch); + } else { + let split_point = common_len + 1; + // otherwise, create a branch with two children + let mut children: [Option>; 16] = Default::default(); + + children[self_nibs[common_len] as usize] = Some(Box::new( + MptNodeData::Leaf( + to_encoded_path(&self_nibs[split_point..], true), + mem::take(old_value), + ) + .into(), + )); + children[key_nibs[common_len] as usize] = Some(Box::new( + MptNodeData::Leaf(to_encoded_path(&key_nibs[split_point..], true), value) + .into(), + )); + + let branch = MptNodeData::Branch(children); + if common_len > 0 { + // create parent extension for new branch + self.data = MptNodeData::Extension( + to_encoded_path(&self_nibs[..common_len], false), + Box::new(branch.into()), + ); + } else { + self.data = branch; + } + } + } + MptNodeData::Extension(prefix, existing_child) => { + let self_nibs = prefix_nibs(prefix); + let common_len = lcp(&self_nibs, key_nibs); + if common_len == self_nibs.len() { + // traverse down for update + if !existing_child.insert_internal(&key_nibs[common_len..], value)? { + return Ok(false); + } + } else if common_len == key_nibs.len() { + return Err(Error::ValueInBranch); + } else { + let split_point = common_len + 1; + // otherwise, create a branch with two children + let mut children: [Option>; 16] = Default::default(); + + children[self_nibs[common_len] as usize] = if split_point < self_nibs.len() { + Some(Box::new( + MptNodeData::Extension( + to_encoded_path(&self_nibs[split_point..], false), + mem::take(existing_child), + ) + .into(), + )) + } else { + Some(mem::take(existing_child)) + }; + children[key_nibs[common_len] as usize] = Some(Box::new( + MptNodeData::Leaf(to_encoded_path(&key_nibs[split_point..], true), value) + .into(), + )); + + let branch = MptNodeData::Branch(children); + if common_len > 0 { + // Create parent extension for new branch + self.data = MptNodeData::Extension( + to_encoded_path(&self_nibs[..common_len], false), + Box::new(branch.into()), + ); + } else { + self.data = branch; + } + } + } + MptNodeData::Digest(digest) => return Err(Error::NodeNotResolved(*digest)), + }; + + self.invalidate_ref_cache(); + Ok(true) + } + + fn invalidate_ref_cache(&mut self) { + self.cached_reference.borrow_mut().take(); + } + + /// Returns the number of traversable nodes in the trie. + /// + /// This method provides a count of all the nodes that can be traversed within the + /// trie. + pub fn size(&self) -> usize { + match self.as_data() { + MptNodeData::Null | MptNodeData::Digest(_) => 0, + MptNodeData::Branch(children) => { + children.iter().flatten().map(|n| n.size()).sum::() + 1 + } + MptNodeData::Leaf(_, _) => 1, + MptNodeData::Extension(_, child) => child.size() + 1, + } + } + + /// Formats the trie as a string list, where each line corresponds to a trie leaf. + /// + /// This method is primarily used for debugging purposes, providing a visual + /// representation of the trie's structure. + pub fn debug_rlp(&self) -> Vec { + // convert the nibs to hex + let nibs: String = self.nibs().iter().fold(String::new(), |mut output, n| { + let _ = write!(output, "{n:x}"); + output + }); + + match self.as_data() { + MptNodeData::Null => vec![format!("{:?}", MptNodeData::Null)], + MptNodeData::Branch(children) => children + .iter() + .enumerate() + .flat_map(|(i, child)| { + match child { + Some(node) => node.debug_rlp::(), + None => vec!["None".to_string()], + } + .into_iter() + .map(move |s| format!("{i:x} {s}")) + }) + .collect(), + MptNodeData::Leaf(_, data) => { + vec![format!( + "{nibs} -> {:?}", + T::decode(&mut &data[..]).unwrap() + )] + } + MptNodeData::Extension(_, node) => node + .debug_rlp::() + .into_iter() + .map(|s| format!("{nibs} {s}")) + .collect(), + MptNodeData::Digest(digest) => vec![format!("#{digest:#}")], + } + } + + /// Returns the length of the RLP payload of the node. + fn payload_length(&self) -> usize { + match &self.data { + MptNodeData::Null => 0, + MptNodeData::Branch(nodes) => { + 1 + nodes + .iter() + .map(|child| child.as_ref().map_or(1, |node| node.reference_length())) + .sum::() + } + MptNodeData::Leaf(prefix, value) => { + prefix.as_slice().length() + value.as_slice().length() + } + MptNodeData::Extension(prefix, node) => { + prefix.as_slice().length() + node.reference_length() + } + MptNodeData::Digest(_) => 32, + } + } +} + +/// Converts a byte slice into a vector of nibbles. +/// +/// A nibble is 4 bits or half of an 8-bit byte. This function takes each byte from the +/// input slice, splits it into two nibbles, and appends them to the resulting vector. +pub fn to_nibs(slice: &[u8]) -> Vec { + let mut result = Vec::with_capacity(2 * slice.len()); + for byte in slice { + result.push(byte >> 4); + result.push(byte & 0xf); + } + result +} + +/// Encodes a slice of nibbles into a vector of bytes, with an additional prefix to +/// indicate the type of node (leaf or extension). +/// +/// The function starts by determining the type of node based on the `is_leaf` parameter. +/// If the node is a leaf, the prefix is set to `0x20`. If the length of the nibbles is +/// odd, the prefix is adjusted and the first nibble is incorporated into it. +/// +/// The remaining nibbles are then combined into bytes, with each pair of nibbles forming +/// a single byte. The resulting vector starts with the prefix, followed by the encoded +/// bytes. +pub fn to_encoded_path(mut nibs: &[u8], is_leaf: bool) -> Vec { + let mut prefix = u8::from(is_leaf) * 0x20; + if nibs.len() % 2 != 0 { + prefix += 0x10 + nibs[0]; + nibs = &nibs[1..]; + } + iter::once(prefix) + .chain(nibs.chunks_exact(2).map(|byte| (byte[0] << 4) + byte[1])) + .collect() +} + +/// Returns the length of the common prefix. +fn lcp(a: &[u8], b: &[u8]) -> usize { + for (i, (a, b)) in iter::zip(a, b).enumerate() { + if a != b { + return i; + } + } + cmp::min(a.len(), b.len()) +} + +fn prefix_nibs(prefix: &[u8]) -> Vec { + let (extension, tail) = prefix.split_first().unwrap(); + // the first bit of the first nibble denotes the parity + let is_odd = extension & (1 << 4) != 0; + + let mut result = Vec::with_capacity(2 * tail.len() + usize::from(is_odd)); + // for odd lengths, the second nibble contains the first element + if is_odd { + result.push(extension & 0xf); + } + for nib in tail { + result.push(nib >> 4); + result.push(nib & 0xf); + } + result +} + +/// Parses proof bytes into a vector of MPT nodes. +pub fn parse_proof(proof: &[impl AsRef<[u8]>]) -> Result> { + Ok(proof + .iter() + .map(MptNode::decode) + .collect::, _>>()?) +} + +/// Creates a Merkle Patricia trie from an EIP-1186 proof. +/// For inclusion proofs the returned trie contains exactly one leaf with the value. +pub fn mpt_from_proof(proof_nodes: &[MptNode]) -> Result { + let mut next: Option = None; + for (i, node) in proof_nodes.iter().enumerate().rev() { + // there is nothing to replace for the last node + let Some(replacement) = next else { + next = Some(node.clone()); + continue; + }; + + // the next node must have a digest reference + let MptNodeReference::Digest(ref child_ref) = replacement.reference() else { + panic!("node {} in proof is not referenced by hash", i + 1); + }; + // find the child that references the next node + let resolved: MptNode = match node.as_data().clone() { + MptNodeData::Branch(mut children) => { + if let Some(child) = children.iter_mut().flatten().find( + |child| matches!(child.as_data(), MptNodeData::Digest(d) if d == child_ref), + ) { + *child = Box::new(replacement); + } else { + panic!("node {i} does not reference the successor"); + } + MptNodeData::Branch(children).into() + } + MptNodeData::Extension(prefix, child) => { + assert!( + matches!(child.as_data(), MptNodeData::Digest(d) if d == child_ref), + "node {i} does not reference the successor" + ); + MptNodeData::Extension(prefix, Box::new(replacement)).into() + } + MptNodeData::Null | MptNodeData::Leaf(_, _) | MptNodeData::Digest(_) => { + panic!("node {i} has no children to replace"); + } + }; + + next = Some(resolved); + } + + // the last node in the proof should be the root + Ok(next.unwrap_or_default()) +} + +/// Verifies that the given proof is a valid proof of exclusion for the given key. +pub fn is_not_included(key: &[u8], proof_nodes: &[MptNode]) -> Result { + let proof_trie = mpt_from_proof(proof_nodes).unwrap(); + // for valid proofs, the get must not fail + let value = proof_trie.get(key).unwrap(); + + Ok(value.is_none()) +} + +/// Creates a new MPT trie where all the digests contained in `node_store` are resolved. +pub fn resolve_nodes(root: &MptNode, node_store: &HashMap) -> MptNode { + let trie = match root.as_data() { + MptNodeData::Null | MptNodeData::Leaf(_, _) => root.clone(), + MptNodeData::Branch(children) => { + let children: Vec<_> = children + .iter() + .map(|child| { + child + .as_ref() + .map(|node| Box::new(resolve_nodes(node, node_store))) + }) + .collect(); + MptNodeData::Branch(children.try_into().unwrap()).into() + } + MptNodeData::Extension(prefix, target) => { + MptNodeData::Extension(prefix.clone(), Box::new(resolve_nodes(target, node_store))) + .into() + } + MptNodeData::Digest(digest) => { + if let Some(node) = node_store.get(&MptNodeReference::Digest(*digest)) { + resolve_nodes(node, node_store) + } else { + root.clone() + } + } + }; + // the root hash must not change + debug_assert_eq!(root.hash(), trie.hash()); + + trie +} + +/// Returns a list of all possible nodes that can be created by shortening the path of the +/// given node. +/// When nodes in an MPT are deleted, leaves or extensions may be extended. To still be +/// able to identify the original nodes, we create all shortened versions of the node. +pub fn shorten_node_path(node: &MptNode) -> Vec { + let mut res = Vec::new(); + let nibs = node.nibs(); + match node.as_data() { + MptNodeData::Null | MptNodeData::Branch(_) | MptNodeData::Digest(_) => {} + MptNodeData::Leaf(_, value) => { + for i in 0..=nibs.len() { + res.push( + MptNodeData::Leaf(to_encoded_path(&nibs[i..], true), value.clone()).into(), + ); + } + } + MptNodeData::Extension(_, child) => { + for i in 0..=nibs.len() { + res.push( + MptNodeData::Extension(to_encoded_path(&nibs[i..], false), child.clone()) + .into(), + ); + } + } + }; + res +} + +pub fn proofs_to_tries( + state_root: B256, + parent_proofs: HashMap, + proofs: HashMap, +) -> Result<(MptNode, HashMap)> { + // if no addresses are provided, return the trie only consisting of the state root + if parent_proofs.is_empty() { + return Ok((node_from_digest(state_root), HashMap::new())); + } + + let mut storage: HashMap = HashMap::with_capacity(parent_proofs.len()); + + let mut state_nodes = HashMap::new(); + let mut state_root_node = MptNode::default(); + for (address, proof) in parent_proofs { + let proof_nodes = parse_proof(&proof.account_proof).unwrap(); + mpt_from_proof(&proof_nodes).unwrap(); + + // the first node in the proof is the root + if let Some(node) = proof_nodes.first() { + state_root_node = node.clone(); + } + + for node in proof_nodes { + state_nodes.insert(node.reference(), node); + } + + let fini_proofs = proofs.get(&address).unwrap(); + + // assure that addresses can be deleted from the state trie + add_orphaned_leafs(address, &fini_proofs.account_proof, &mut state_nodes)?; + + // if no slots are provided, return the trie only consisting of the storage root + let storage_root = proof.storage_hash; + if proof.storage_proof.is_empty() { + let storage_root_node = node_from_digest(storage_root); + storage.insert(address, (storage_root_node, vec![])); + continue; + } + + let mut storage_nodes = HashMap::new(); + let mut storage_root_node = MptNode::default(); + for storage_proof in &proof.storage_proof { + let proof_nodes = parse_proof(&storage_proof.proof).unwrap(); + mpt_from_proof(&proof_nodes).unwrap(); + + // the first node in the proof is the root + if let Some(node) = proof_nodes.first() { + storage_root_node = node.clone(); + } + + for node in proof_nodes { + storage_nodes.insert(node.reference(), node); + } + } + + // assure that slots can be deleted from the storage trie + for storage_proof in &fini_proofs.storage_proof { + add_orphaned_leafs( + storage_proof.key.0 .0, + &storage_proof.proof, + &mut storage_nodes, + )?; + } + // create the storage trie, from all the relevant nodes + let storage_trie = resolve_nodes(&storage_root_node, &storage_nodes); + assert_eq!(storage_trie.hash(), storage_root); + + // convert the slots to a vector of U256 + let slots = proof + .storage_proof + .iter() + .map(|p| U256::from_be_bytes(p.key.0 .0)) + .collect(); + storage.insert(address, (storage_trie, slots)); + } + let state_trie = resolve_nodes(&state_root_node, &state_nodes); + assert_eq!(state_trie.hash(), state_root); + + Ok((state_trie, storage)) +} + +/// Adds all the leaf nodes of non-inclusion proofs to the nodes. +fn add_orphaned_leafs( + key: impl AsRef<[u8]>, + proof: &[impl AsRef<[u8]>], + nodes_by_reference: &mut HashMap, +) -> Result<()> { + if !proof.is_empty() { + let proof_nodes = parse_proof(proof).context("invalid proof encoding")?; + if is_not_included(&keccak(key), &proof_nodes)? { + // add the leaf node to the nodes + let leaf = proof_nodes.last().unwrap(); + for node in shorten_node_path(leaf) { + nodes_by_reference.insert(node.reference(), node); + } + } + } + + Ok(()) +} + +/// Creates a new MPT node from a digest. +fn node_from_digest(digest: B256) -> MptNode { + match digest { + EMPTY_ROOT | B256::ZERO => MptNode::default(), + _ => MptNodeData::Digest(digest).into(), + } +} + +#[cfg(test)] +mod tests { + use hex_literal::hex; + + use super::*; + + #[test] + pub fn test_trie_pointer_no_keccak() { + let cases = [ + ("do", "verb"), + ("dog", "puppy"), + ("doge", "coin"), + ("horse", "stallion"), + ]; + for (k, v) in cases { + let node: MptNode = + MptNodeData::Leaf(k.as_bytes().to_vec(), v.as_bytes().to_vec()).into(); + assert!( + matches!(node.reference(),MptNodeReference::Bytes(bytes) if bytes == node.to_rlp().to_vec()) + ); + } + } + + #[test] + pub fn test_to_encoded_path() { + // extension node with an even path length + let nibbles = vec![0x0a, 0x0b, 0x0c, 0x0d]; + assert_eq!(to_encoded_path(&nibbles, false), vec![0x00, 0xab, 0xcd]); + // extension node with an odd path length + let nibbles = vec![0x0a, 0x0b, 0x0c]; + assert_eq!(to_encoded_path(&nibbles, false), vec![0x1a, 0xbc]); + // leaf node with an even path length + let nibbles = vec![0x0a, 0x0b, 0x0c, 0x0d]; + assert_eq!(to_encoded_path(&nibbles, true), vec![0x20, 0xab, 0xcd]); + // leaf node with an odd path length + let nibbles = vec![0x0a, 0x0b, 0x0c]; + assert_eq!(to_encoded_path(&nibbles, true), vec![0x3a, 0xbc]); + } + + #[test] + pub fn test_lcp() { + let cases = [ + (vec![], vec![], 0), + (vec![0xa], vec![0xa], 1), + (vec![0xa, 0xb], vec![0xa, 0xc], 1), + (vec![0xa, 0xb], vec![0xa, 0xb], 2), + (vec![0xa, 0xb], vec![0xa, 0xb, 0xc], 2), + (vec![0xa, 0xb, 0xc], vec![0xa, 0xb, 0xc], 3), + (vec![0xa, 0xb, 0xc], vec![0xa, 0xb, 0xc, 0xd], 3), + (vec![0xa, 0xb, 0xc, 0xd], vec![0xa, 0xb, 0xc, 0xd], 4), + ]; + for (a, b, cpl) in cases { + assert_eq!(lcp(&a, &b), cpl) + } + } + + #[test] + pub fn test_empty() { + let trie = MptNode::default(); + + assert!(trie.is_empty()); + assert_eq!(trie.reference(), MptNodeReference::Bytes(vec![0x80])); + let expected = hex!("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"); + assert_eq!(expected, trie.hash().0); + + // test RLP encoding + let mut out = Vec::new(); + trie.encode(&mut out); + assert_eq!(out, vec![0x80]); + assert_eq!(trie.length(), out.len()); + let decoded = MptNode::decode(out).unwrap(); + assert_eq!(trie.hash(), decoded.hash()); + } + + #[test] + pub fn test_empty_key() { + let mut trie = MptNode::default(); + + trie.insert(&[], b"empty".to_vec()).unwrap(); + assert_eq!(trie.get(&[]).unwrap(), Some(b"empty".as_ref())); + assert!(trie.delete(&[]).unwrap()); + } + + #[test] + pub fn test_clear() { + let mut trie = MptNode::default(); + trie.insert(b"dog", b"puppy".to_vec()).unwrap(); + assert!(!trie.is_empty()); + assert_ne!(trie.hash(), EMPTY_ROOT); + + trie.clear(); + assert!(trie.is_empty()); + assert_eq!(trie.hash(), EMPTY_ROOT); + } + + #[test] + pub fn test_tiny() { + // trie consisting of an extension, a branch and two leafs + let mut trie = MptNode::default(); + trie.insert_rlp(b"a", 0u8).unwrap(); + trie.insert_rlp(b"b", 1u8).unwrap(); + + assert!(!trie.is_empty()); + let exp_rlp = hex!("d816d680c3208180c220018080808080808080808080808080"); + assert_eq!(trie.reference(), MptNodeReference::Bytes(exp_rlp.to_vec())); + let exp_hash = hex!("6fbf23d6ec055dd143ff50d558559770005ff44ae1d41276f1bd83affab6dd3b"); + assert_eq!(trie.hash().0, exp_hash); + + // test RLP encoding + let mut out = Vec::new(); + trie.encode(&mut out); + assert_eq!(out, exp_rlp.to_vec()); + assert_eq!(trie.length(), out.len()); + let decoded = MptNode::decode(out).unwrap(); + assert_eq!(trie.hash(), decoded.hash()); + } + + #[test] + pub fn test_partial() { + let mut trie = MptNode::default(); + trie.insert_rlp(b"aa", 0u8).unwrap(); + trie.insert_rlp(b"ab", 1u8).unwrap(); + trie.insert_rlp(b"ba", 2u8).unwrap(); + + let exp_hash = trie.hash(); + + // replace one node with its digest + let MptNodeData::Extension(_, node) = &mut trie.data else { + panic!("extension expected") + }; + **node = MptNodeData::Digest(node.hash()).into(); + assert!(node.is_digest()); + + let trie = MptNode::decode(trie.to_rlp()).unwrap(); + assert_eq!(trie.hash(), exp_hash); + + // lookups should fail + trie.get(b"aa").unwrap_err(); + trie.get(b"a0").unwrap_err(); + } + + #[test] + pub fn test_branch_value() { + let mut trie = MptNode::default(); + trie.insert(b"do", b"verb".to_vec()).unwrap(); + // leads to a branch with value which is not supported + trie.insert(b"dog", b"puppy".to_vec()).unwrap_err(); + } + + #[test] + pub fn test_insert() { + let mut trie = MptNode::default(); + let vals = vec![ + ("painting", "place"), + ("guest", "ship"), + ("mud", "leave"), + ("paper", "call"), + ("gate", "boast"), + ("tongue", "gain"), + ("baseball", "wait"), + ("tale", "lie"), + ("mood", "cope"), + ("menu", "fear"), + ]; + for (key, val) in &vals { + assert!(trie + .insert(key.as_bytes(), val.as_bytes().to_vec()) + .unwrap()); + } + + let expected = hex!("2bab6cdf91a23ebf3af683728ea02403a98346f99ed668eec572d55c70a4b08f"); + assert_eq!(expected, trie.hash().0); + + for (key, value) in &vals { + assert_eq!(trie.get(key.as_bytes()).unwrap(), Some(value.as_bytes())); + } + + // check inserting duplicate keys + assert!(trie.insert(vals[0].0.as_bytes(), b"new".to_vec()).unwrap()); + assert!(!trie.insert(vals[0].0.as_bytes(), b"new".to_vec()).unwrap()); + + // try RLP roundtrip + let decoded = MptNode::decode(trie.to_rlp()).unwrap(); + assert_eq!(trie.hash(), decoded.hash()); + } + + #[test] + pub fn test_keccak_trie() { + const N: usize = 512; + + // insert + let mut trie = MptNode::default(); + for i in 0..N { + assert!(trie.insert_rlp(&keccak(i.to_be_bytes()), i).unwrap()); + + // check hash against trie build in reverse + let mut reference = MptNode::default(); + for j in (0..=i).rev() { + reference.insert_rlp(&keccak(j.to_be_bytes()), j).unwrap(); + } + assert_eq!(trie.hash(), reference.hash()); + } + + let expected = hex!("7310027edebdd1f7c950a7fb3413d551e85dff150d45aca4198c2f6315f9b4a7"); + assert_eq!(trie.hash().0, expected); + + // get + for i in 0..N { + assert_eq!(trie.get_rlp(&keccak(i.to_be_bytes())).unwrap(), Some(i)); + assert!(trie.get(&keccak((i + N).to_be_bytes())).unwrap().is_none()); + } + + // delete + for i in 0..N { + assert!(trie.delete(&keccak(i.to_be_bytes())).unwrap()); + + let mut reference = MptNode::default(); + for j in ((i + 1)..N).rev() { + reference.insert_rlp(&keccak(j.to_be_bytes()), j).unwrap(); + } + assert_eq!(trie.hash(), reference.hash()); + } + assert!(trie.is_empty()); + } + + #[test] + pub fn test_index_trie() { + const N: usize = 512; + + // insert + let mut trie = MptNode::default(); + for i in 0..N { + assert!(trie.insert_rlp(&i.to_rlp(), i).unwrap()); + + // check hash against trie build in reverse + let mut reference = MptNode::default(); + for j in (0..=i).rev() { + reference.insert_rlp(&j.to_rlp(), j).unwrap(); + } + assert_eq!(trie.hash(), reference.hash()); + + // try RLP roundtrip + let decoded = MptNode::decode(trie.to_rlp()).unwrap(); + assert_eq!(trie.hash(), decoded.hash()); + } + + // get + for i in 0..N { + assert_eq!(trie.get_rlp(&i.to_rlp()).unwrap(), Some(i)); + assert!(trie.get(&(i + N).to_rlp()).unwrap().is_none()); + } + + // delete + for i in 0..N { + assert!(trie.delete(&i.to_rlp()).unwrap()); + + let mut reference = MptNode::default(); + for j in ((i + 1)..N).rev() { + reference.insert_rlp(&j.to_rlp(), j).unwrap(); + } + assert_eq!(trie.hash(), reference.hash()); + } + assert!(trie.is_empty()); + } +} diff --git a/lib/src/primitives/receipt.rs b/lib/src/primitives/receipt.rs new file mode 100644 index 00000000..175de22d --- /dev/null +++ b/lib/src/primitives/receipt.rs @@ -0,0 +1,229 @@ +// Copyright 2023 RISC Zero, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +extern crate alloc; +extern crate core; + +pub use alloc::{ + boxed::Box, + format, + string::{String, ToString}, + vec, + vec::Vec, +}; +pub use core::{ + convert::From, + default::Default, + option::{Option, Option::*}, + result::{Result, Result::*}, +}; + +use alloy_primitives::{Address, Bloom, BloomInput, Bytes, B256, U256}; +use alloy_rlp::Encodable; +use alloy_rlp_derive::RlpEncodable; +use serde::{Deserialize, Serialize}; + +/// Represents an Ethereum log entry. +#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize, RlpEncodable)] +pub struct Log { + /// Contract that emitted this log. + pub address: Address, + /// Topics of the log. The number of logs depend on what `LOG` opcode is used. + pub topics: Vec, + /// Arbitrary length data. + pub data: Bytes, +} + +/// Payload of a [Receipt]. +#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize, RlpEncodable)] +pub struct ReceiptPayload { + /// Indicates whether the transaction was executed successfully. + pub success: bool, + /// Total gas used by the transaction. + pub cumulative_gas_used: U256, + /// A bloom filter that contains indexed information of logs for quick searching. + pub logs_bloom: Bloom, + /// Logs generated during the execution of the transaction. + pub logs: Vec, +} + +/// Receipt containing result of transaction execution. +#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)] +pub struct Receipt { + /// Type of Receipt. + pub tx_type: u8, + /// Detailed payload of the receipt. + pub payload: ReceiptPayload, +} + +impl Encodable for Receipt { + /// Encodes the receipt into the `out` buffer. + #[inline] + fn encode(&self, out: &mut dyn alloy_rlp::BufMut) { + match self.tx_type { + // For legacy transactions + 0 => self.payload.encode(out), + // For EIP-2718 typed transactions + tx_type => { + // prepend the EIP-2718 transaction type + out.put_u8(tx_type); + // append the RLP-encoded payload + self.payload.encode(out); + } + } + } + + /// Returns the length of the encoded receipt in bytes. + #[inline] + fn length(&self) -> usize { + let mut payload_length = self.payload.length(); + if self.tx_type != 0 { + payload_length += 1; + } + payload_length + } +} + +impl Receipt { + /// Constructs a new [Receipt]. + /// + /// This function also computes the `logs_bloom` based on the provided logs. + pub fn new(tx_type: u8, success: bool, cumulative_gas_used: U256, logs: Vec) -> Receipt { + let mut logs_bloom = Bloom::default(); + for log in &logs { + logs_bloom.accrue(BloomInput::Raw(log.address.as_slice())); + for topic in &log.topics { + logs_bloom.accrue(BloomInput::Raw(topic.as_slice())); + } + } + + Receipt { + tx_type, + payload: ReceiptPayload { + success, + cumulative_gas_used, + logs_bloom, + logs, + }, + } + } +} + +// test vectors from https://github.com/ethereum/go-ethereum/blob/c40ab6af72ce282020d03c33e8273ea9b03d58f6/core/types/receipt_test.go +#[cfg(test)] +mod tests { + use hex_literal::hex; + use serde_json::json; + + use super::*; + + #[test] + fn legacy() { + let expected = hex!("f901c58001b9010000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000010000080000000000000000000004000000000000000000000000000040000000000000000000000000000800000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000f8bef85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100fff85d940000000000000000000000000000000000000111f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff"); + let receipt = Receipt::new( + 0, + false, + U256::from(1), + serde_json::from_value(json!([ + { + "address": "0x0000000000000000000000000000000000000011", + "topics": [ + "0x000000000000000000000000000000000000000000000000000000000000dead", + "0x000000000000000000000000000000000000000000000000000000000000beef" + ], + "data": "0x0100ff" + }, + { + "address": "0x0000000000000000000000000000000000000111", + "topics": [ + "0x000000000000000000000000000000000000000000000000000000000000dead", + "0x000000000000000000000000000000000000000000000000000000000000beef" + ], + "data": "0x0100ff" + } + ])) + .unwrap(), + ); + let mut data = vec![]; + receipt.encode(&mut data); + + assert_eq!(data, expected); + } + + #[test] + fn eip2930() { + let expected = hex!("01f901c58001b9010000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000010000080000000000000000000004000000000000000000000000000040000000000000000000000000000800000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000f8bef85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100fff85d940000000000000000000000000000000000000111f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff"); + let receipt = Receipt::new( + 1, + false, + U256::from(1), + serde_json::from_value(json!([ + { + "address": "0x0000000000000000000000000000000000000011", + "topics": [ + "0x000000000000000000000000000000000000000000000000000000000000dead", + "0x000000000000000000000000000000000000000000000000000000000000beef" + ], + "data": "0x0100ff" + }, + { + "address": "0x0000000000000000000000000000000000000111", + "topics": [ + "0x000000000000000000000000000000000000000000000000000000000000dead", + "0x000000000000000000000000000000000000000000000000000000000000beef" + ], + "data": "0x0100ff" + } + ])) + .unwrap(), + ); + let mut data = vec![]; + receipt.encode(&mut data); + + assert_eq!(data, expected); + } + + #[test] + fn eip1559() { + let expected = hex!("02f901c58001b9010000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000010000080000000000000000000004000000000000000000000000000040000000000000000000000000000800000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000f8bef85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100fff85d940000000000000000000000000000000000000111f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff"); + let receipt = Receipt::new( + 2, + false, + U256::from(1), + serde_json::from_value(json!([ + { + "address": "0x0000000000000000000000000000000000000011", + "topics": [ + "0x000000000000000000000000000000000000000000000000000000000000dead", + "0x000000000000000000000000000000000000000000000000000000000000beef" + ], + "data": "0x0100ff" + }, + { + "address": "0x0000000000000000000000000000000000000111", + "topics": [ + "0x000000000000000000000000000000000000000000000000000000000000dead", + "0x000000000000000000000000000000000000000000000000000000000000beef" + ], + "data": "0x0100ff" + } + ])) + .unwrap(), + ); + let mut data = vec![]; + receipt.encode(&mut data); + + assert_eq!(data, expected); + } +} diff --git a/lib/src/primitives/revm.rs b/lib/src/primitives/revm.rs new file mode 100644 index 00000000..a680061f --- /dev/null +++ b/lib/src/primitives/revm.rs @@ -0,0 +1,47 @@ +// Copyright 2024 RISC Zero, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Convert to revm types. + +extern crate alloc; +extern crate core; + +pub use alloc::{ + boxed::Box, + format, + string::{String, ToString}, + vec, + vec::Vec, +}; +pub use core::{ + convert::From, + default::Default, + option::{Option, Option::*}, + result::{Result, Result::*}, +}; + +use revm_primitives::Log as RevmLog; + +use crate::primitives::receipt::Log; + +/// Provides a conversion from `RevmLog` to the local [Log]. +impl From for Log { + fn from(log: RevmLog) -> Self { + Log { + address: log.address, + topics: log.data.topics().to_vec(), + data: log.data.data, + } + } +} diff --git a/lib/src/primitives/signature.rs b/lib/src/primitives/signature.rs new file mode 100644 index 00000000..88d34e8b --- /dev/null +++ b/lib/src/primitives/signature.rs @@ -0,0 +1,82 @@ +// use revm_primitives::U256; + +// The order of the secp256k1 curve, divided by two. Signatures that should be checked +// according to EIP-2 should have an S value less than or equal to this. +// +// `57896044618658097711785492504343953926418782139537452191302581570759080747168` +// const SECP256K1N_HALF: U256 = U256::from_be_bytes([ +// 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +// 0xFF, 0xFF, 0x5D, 0x57, 0x6E, 0x73, 0x57, 0xA4, 0x50, 0x1D, 0xDF, 0xE9, 0x2F, 0x46, +// 0x68, 0x1B, 0x20, 0xA0, ]); + +// Recovers the address of the sender using secp256k1 pubkey recovery. +// +// Converts the public key into an ethereum address by hashing the public key with +// keccak256. +// +// This does not ensure that the `s` value in the signature is low, and _just_ wraps the +// underlying secp256k1 library. +// pub fn recover_signer_unchecked_crypto(sig: &[u8; 65], msg: &[u8; 32]) -> +// Result { #[cfg(target_os = "zkvm")] +// { +// let pubkey = sp1_precompiles::secp256k1::ecrecover(sig, msg).unwrap(); +// return Ok(public_key_bytes_to_address(&pubkey)); +// } +// { +// let recid = RecoveryId::from_byte(sig[64]).expect("recovery ID is valid"); +// let sig = K256Signature::from_slice(&sig.as_slice()[..64])?; +// let recovered_key = VerifyingKey::recover_from_prehash(&msg[..], &sig, recid)?; +// let pubkey = PublicKey::from(&recovered_key); +// Ok(public_key_to_address(pubkey)) +// } +// } +// +// Recover signer from message hash, _without ensuring that the signature has a low `s` +// value_. +// +// Using this for signature validation will succeed, even if the signature is malleable or +// not compliant with EIP-2. This is provided for compatibility with old signatures which +// have large `s` values. +// pub fn recover_signer_unchecked(&self, hash: B256) -> Option
{ +// let mut sig: [u8; 65] = [0; 65]; +// +// sig[0..32].copy_from_slice(&self.r.to_be_bytes::<32>()); +// sig[32..64].copy_from_slice(&self.s.to_be_bytes::<32>()); +// sig[64] = self.odd_y_parity as u8; +// +// NOTE: we are removing error from underlying crypto library as it will restrain +// primitive errors and we care only if recovery is passing or not. +// recover_signer_unchecked_crypto(&sig, &hash.0).ok() +// } +// +// Recover signer address from message hash. This ensures that the signature S value is +// greater than `secp256k1n / 2`, as specified in +// [EIP-2](https://eips.ethereum.org/EIPS/eip-2). +// +// If the S value is too large, then this will return `None` +// pub fn recover_signer(&self, hash: B256) -> Option
{ +// if self.s > SECP256K1N_HALF { +// return None +// } +// +// self.recover_signer_unchecked(hash) +// } +// +// +// Converts a public key into an ethereum address by hashing the encoded public key with +// keccak256. +// pub fn public_key_to_address(public: PublicKey) -> Address { +// let pubkey_bytes = +// public.to_encoded_point(false).as_bytes().try_into().expect("The slice has 65 +// bytes"); public_key_bytes_to_address(&pubkey_bytes); +// // strip out the first byte because that should be the +// SECP256K1_TAG_PUBKEY_UNCOMPRESSED // tag returned by libsecp's uncompressed pubkey +// serialization let hash = keccak(&public.serialize_uncompressed()[1..]); +// Address::from_slice(&hash[12..]) +// } + +// fn public_key_bytes_to_address(public: &[u8; 65]) -> Address { +// // Strip out first byte of sec1 encoded pubkey +// let hash = keccak(&public[1..]); +// Address::from_slice(&hash[12..]) +// } diff --git a/lib/src/protocol_instance.rs b/lib/src/protocol_instance.rs index ffc9f3b8..23f136c1 100644 --- a/lib/src/protocol_instance.rs +++ b/lib/src/protocol_instance.rs @@ -3,7 +3,6 @@ use alloy_primitives::{Address, TxHash, B256}; use alloy_sol_types::SolValue; use anyhow::{ensure, Result}; use c_kzg::{Blob, KzgCommitment, KzgSettings}; -use raiko_primitives::keccak::keccak; use sha2::{Digest as _, Sha256}; use super::utils::ANCHOR_GAS_LIMIT; @@ -12,6 +11,7 @@ use crate::no_std::*; use crate::{ consts::{SupportedChainSpecs, VerifierType}, input::{BlockMetadata, EthDeposit, GuestInput, Transition}, + primitives::keccak::keccak, utils::HeaderHasher, }; @@ -205,10 +205,12 @@ fn bytes_to_bytes32(input: &[u8]) -> [u8; 32] { mod tests { use alloy_primitives::{address, b256}; use alloy_sol_types::SolCall; - use raiko_primitives::keccak; use super::*; - use crate::input::{proveBlockCall, TierProof}; + use crate::{ + input::{proveBlockCall, TierProof}, + primitives::keccak, + }; #[test] fn bytes_to_bytes32_test() { diff --git a/lib/src/utils.rs b/lib/src/utils.rs index 75fe7489..abd8b4f6 100644 --- a/lib/src/utils.rs +++ b/lib/src/utils.rs @@ -11,13 +11,13 @@ use anyhow::{anyhow, bail, ensure, Context, Result}; use lazy_static::lazy_static; use libflate::zlib::Decoder as zlibDecoder; use libflate::zlib::Encoder as zlibEncoder; -use raiko_primitives::{keccak256, B256}; #[cfg(not(feature = "std"))] use crate::no_std::*; use crate::{ consts::{ChainSpec, Network}, input::{decode_anchor, GuestInput}, + primitives::{keccak256, B256}, }; pub const ANCHOR_GAS_LIMIT: u64 = 250_000; diff --git a/provers/powdr/Cargo.toml b/provers/powdr/Cargo.toml index e5f6e052..ee1bba8e 100644 --- a/provers/powdr/Cargo.toml +++ b/provers/powdr/Cargo.toml @@ -12,13 +12,11 @@ path = "src/main.rs" [dependencies] -raiko-primitives = { path = "../../primitives" } raiko-lib = { path = "../../lib", features = ["taiko"] } -powdr-riscv-runtime ={ git = "https://github.com/ceciliaz030/powdr", branch = "+nightly" } +powdr-riscv-runtime = { git = "https://github.com/ceciliaz030/powdr", branch = "+nightly" } [workspace] # cargo build --release -Z build-std=core,alloc --target riscv32imac-unknown-none-elf --lib - diff --git a/provers/powdr/src/main.rs b/provers/powdr/src/main.rs index 6e53b157..a9b0cfeb 100644 --- a/provers/powdr/src/main.rs +++ b/provers/powdr/src/main.rs @@ -2,10 +2,9 @@ extern crate alloc; use alloc::{collections::BTreeMap, vec}; -use raiko_primitives::U256; -use raiko_lib::consts::ChainSpec; use powdr_riscv_runtime; +use raiko_lib::{consts::ChainSpec, primitives::U256}; #[no_mangle] -fn main() { -} \ No newline at end of file +fn main() {} + diff --git a/provers/risc0/driver/Cargo.toml b/provers/risc0/driver/Cargo.toml index 812c8256..ceb91170 100644 --- a/provers/risc0/driver/Cargo.toml +++ b/provers/risc0/driver/Cargo.toml @@ -10,13 +10,12 @@ harness = true [dependencies] raiko-lib = { workspace = true, optional = true } -raiko-primitives = { workspace = true, optional = true } risc0-zkvm = { workspace = true, optional = true } bonsai-sdk = { workspace = true, optional = true } alloy-primitives = { workspace = true, optional = true } -alloy-sol-types = { workspace = true, optional = true} +alloy-sol-types = { workspace = true, optional = true } ethers-contract = { workspace = true, optional = true } ethers-core = { workspace = true, optional = true } @@ -33,13 +32,12 @@ bincode = { workspace = true, optional = true } bytemuck = { workspace = true, optional = true } typetag = { workspace = true, optional = true } serde_with = { workspace = true, optional = true } -serde_json = { workspace = true, optional = true } +serde_json = { workspace = true, optional = true } hex = { workspace = true, optional = true } [features] enable = [ "raiko-lib", - "raiko-primitives", "risc0-zkvm", "bonsai-sdk", "alloy-primitives", @@ -61,4 +59,5 @@ enable = [ "hex", ] cuda = ["risc0-zkvm?/cuda"] -metal = ["risc0-zkvm?/metal"] \ No newline at end of file +metal = ["risc0-zkvm?/metal"] + diff --git a/provers/risc0/driver/src/bonsai.rs b/provers/risc0/driver/src/bonsai.rs index f93d8de0..5fd03c0a 100644 --- a/provers/risc0/driver/src/bonsai.rs +++ b/provers/risc0/driver/src/bonsai.rs @@ -1,6 +1,5 @@ use log::{debug, error, info, warn}; -use raiko_lib::prover::Prover; -use raiko_primitives::keccak::keccak; +use raiko_lib::{primitives::keccak::keccak, prover::Prover}; use risc0_zkvm::{ compute_image_id, is_dev_mode, serde::to_vec, sha::Digest, Assumption, ExecutorEnv, ExecutorImpl, Receipt, diff --git a/provers/risc0/driver/src/lib.rs b/provers/risc0/driver/src/lib.rs index 17d1a483..fd8ef842 100644 --- a/provers/risc0/driver/src/lib.rs +++ b/provers/risc0/driver/src/lib.rs @@ -8,22 +8,24 @@ use hex::ToHex; use raiko_lib::{ input::{GuestInput, GuestOutput}, + primitives::keccak::keccak, protocol_instance::ProtocolInstance, prover::{to_proof, Proof, Prover, ProverConfig, ProverResult}, }; -use raiko_primitives::keccak::keccak; use risc0_zkvm::{serde::to_vec, sha::Digest}; use serde::{Deserialize, Serialize}; use serde_with::serde_as; use tracing::info as traicing_info; +use crate::{ + methods::risc0_guest::{RISC0_GUEST_ELF, RISC0_GUEST_ID}, + snarks::verify_groth16_snark, +}; +pub use bonsai::*; + pub mod bonsai; pub mod methods; pub mod snarks; -use crate::snarks::verify_groth16_snark; -use bonsai::maybe_prove; -pub use bonsai::*; -use methods::risc0_guest::{RISC0_GUEST_ELF, RISC0_GUEST_ID}; #[serde_as] #[derive(Clone, Debug, Serialize, Deserialize)] diff --git a/provers/risc0/driver/src/snarks.rs b/provers/risc0/driver/src/snarks.rs index d4204bee..779e427a 100644 --- a/provers/risc0/driver/src/snarks.rs +++ b/provers/risc0/driver/src/snarks.rs @@ -21,6 +21,8 @@ use ethers_contract::abigen; use ethers_core::types::H160; use ethers_providers::{Http, Provider, RetryClient}; use log::{error, info}; +use raiko_lib::primitives::keccak::keccak; +use risc0_zkvm::Receipt; use risc0_zkvm::{ sha::{Digest, Digestible}, Groth16Seal, @@ -85,9 +87,6 @@ impl From for Seal { } } -use raiko_primitives::keccak::keccak; -use risc0_zkvm::Receipt; - pub async fn stark2snark( image_id: Digest, stark_uuid: String, diff --git a/provers/risc0/guest/Cargo.lock b/provers/risc0/guest/Cargo.lock index bc5bf897..6e93e10c 100644 --- a/provers/risc0/guest/Cargo.lock +++ b/provers/risc0/guest/Cargo.lock @@ -46,21 +46,6 @@ dependencies = [ "sha2 0.10.8", ] -[[package]] -name = "alloy-dyn-abi" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "545885d9b0b2c30fd344ae291439b4bfe59e48dd62fbc862f8503d98088967dc" -dependencies = [ - "alloy-json-abi", - "alloy-primitives", - "alloy-sol-type-parser", - "alloy-sol-types", - "const-hex", - "itoa", - "winnow 0.6.8", -] - [[package]] name = "alloy-eips" version = "0.1.0" @@ -85,17 +70,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "alloy-json-abi" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "786689872ec4e7d354810ab0dffd48bb40b838c047522eb031cbd47d15634849" -dependencies = [ - "alloy-primitives", - "alloy-sol-type-parser", - "serde", -] - [[package]] name = "alloy-json-rpc" version = "0.1.0" @@ -243,15 +217,6 @@ dependencies = [ "syn-solidity", ] -[[package]] -name = "alloy-sol-type-parser" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8e71ea68e780cc203919e03f69f59e7afe92d2696fb1dcb6662f61e4031b6" -dependencies = [ - "winnow 0.6.8", -] - [[package]] name = "alloy-sol-types" version = "0.7.2" @@ -1744,6 +1709,7 @@ name = "raiko-lib" version = "0.1.0" dependencies = [ "alloy-consensus", + "alloy-eips", "alloy-network", "alloy-primitives", "alloy-rlp", @@ -1760,36 +1726,19 @@ dependencies = [ "libflate", "log", "once_cell", - "raiko-primitives", "revm", + "revm-primitives", + "rlp", "serde", "serde_json", "serde_with", "sha2 0.10.8", + "sha3", "thiserror", "thiserror-no-std", "url", ] -[[package]] -name = "raiko-primitives" -version = "0.1.0" -dependencies = [ - "alloy-dyn-abi", - "alloy-eips", - "alloy-primitives", - "alloy-rlp", - "alloy-rlp-derive", - "alloy-rpc-types", - "alloy-sol-types", - "anyhow", - "revm-primitives", - "rlp", - "serde", - "sha3", - "thiserror", -] - [[package]] name = "rand" version = "0.8.5" @@ -1992,7 +1941,6 @@ dependencies = [ "harness-core", "k256 0.13.3 (git+https://github.com/risc0/RustCrypto-elliptic-curves?tag=k256/v0.13.3-risczero.0)", "raiko-lib", - "raiko-primitives", "revm-precompile", "revm-primitives", "risc0-zkvm", @@ -2555,7 +2503,7 @@ checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" dependencies = [ "indexmap 2.2.6", "toml_datetime", - "winnow 0.5.40", + "winnow", ] [[package]] @@ -2834,12 +2782,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "winnow" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3c52e9c97a68071b23e836c9380edae937f17b9c4667bd021973efc689f618d" - [[package]] name = "wyz" version = "0.5.1" diff --git a/provers/risc0/guest/Cargo.toml b/provers/risc0/guest/Cargo.toml index 9f0690cf..37edfb44 100644 --- a/provers/risc0/guest/Cargo.toml +++ b/provers/risc0/guest/Cargo.toml @@ -18,9 +18,11 @@ name = "ecdsa" path = "src/benchmark/ecdsa.rs" [dependencies] -raiko-primitives = { path = "../../../primitives" } raiko-lib = { path = "../../../lib", features = ["std", "risc0"] } -risc0-zkvm = { version = "0.21.0", default-features = false, features = ['std', "getrandom"] } +risc0-zkvm = { version = "0.21.0", default-features = false, features = [ + 'std', + "getrandom", +] } revm-precompile = { git = "https://github.com/taikoxyz/revm.git", branch = "v35_taiko_v2", default-features = false, features = [ "taiko", "std", @@ -32,7 +34,7 @@ k256 = { git = "https://github.com/risc0/RustCrypto-elliptic-curves", tag = "k25 sha2 = { git = "https://github.com/risc0/RustCrypto-hashes", tag = "sha2-v0.10.6-risczero.0" } crypto-bigint = { git = "https://github.com/risc0/RustCrypto-crypto-bigint", tag = "v0.5.2-risczero.0" } harness-core = { path = "../../../harness/core" } -harness = { path = "../../../harness/macro", features = ["risc0"]} +harness = { path = "../../../harness/macro", features = ["risc0"] } [patch.crates-io] # Can't use ZkOp since it's big int optimization used everywhere diff --git a/provers/risc0/guest/src/zk_op.rs b/provers/risc0/guest/src/zk_op.rs index b1157903..8a9b8406 100644 --- a/provers/risc0/guest/src/zk_op.rs +++ b/provers/risc0/guest/src/zk_op.rs @@ -74,9 +74,8 @@ harness::zk_suits!( pub mod tests { #[test] pub fn test_sha256() { - use harness::*; - use raiko_primitives::hex; + use raiko_lib::primitives::hex; use risc0_sha2::{Digest, Sha256}; let test_ves = [ diff --git a/provers/sgx/guest/Cargo.toml b/provers/sgx/guest/Cargo.toml index cec84822..d52e62ba 100644 --- a/provers/sgx/guest/Cargo.toml +++ b/provers/sgx/guest/Cargo.toml @@ -7,7 +7,6 @@ edition = "2021" [dependencies] raiko-lib = { workspace = true, features = ["sgx"] } -raiko-primitives = { workspace = true } tokio = { workspace = true } anyhow = { workspace = true } thiserror = { workspace = true } diff --git a/provers/sgx/guest/src/one_shot.rs b/provers/sgx/guest/src/one_shot.rs index dd9db865..b7b62808 100644 --- a/provers/sgx/guest/src/one_shot.rs +++ b/provers/sgx/guest/src/one_shot.rs @@ -11,9 +11,9 @@ use raiko_lib::{ builder::{BlockBuilderStrategy, TaikoStrategy}, consts::VerifierType, input::GuestInput, + primitives::Address, protocol_instance::ProtocolInstance, }; -use raiko_primitives::Address; use secp256k1::{KeyPair, SecretKey}; use serde::Serialize; diff --git a/provers/sgx/guest/src/signature.rs b/provers/sgx/guest/src/signature.rs index 9474cea2..a0b831b1 100644 --- a/provers/sgx/guest/src/signature.rs +++ b/provers/sgx/guest/src/signature.rs @@ -1,6 +1,6 @@ use std::{fs, path::Path}; -use raiko_primitives::{keccak256, Address, Signature, B256}; +use raiko_lib::primitives::{keccak256, Address, Signature, B256}; use rand_core::OsRng; use secp256k1::{ ecdsa::{RecoverableSignature, RecoveryId}, diff --git a/provers/sgx/prover/Cargo.toml b/provers/sgx/prover/Cargo.toml index 604ff1b6..efe39e08 100644 --- a/provers/sgx/prover/Cargo.toml +++ b/provers/sgx/prover/Cargo.toml @@ -7,7 +7,6 @@ edition = "2021" [dependencies] raiko-lib = { workspace = true, optional = true } -raiko-primitives = { workspace = true, optional = true } serde = { workspace = true, optional = true } serde_json = { workspace = true, optional = true } @@ -28,13 +27,5 @@ anyhow = { workspace = true } [features] default = ["dep:pem"] -enable = [ - "raiko-lib", - "raiko-primitives", - "serde", - "serde_json", - "serde_with", - "bincode", - "tokio", -] +enable = ["raiko-lib", "serde", "serde_json", "serde_with", "bincode", "tokio"] docker_build = [] diff --git a/provers/sgx/prover/src/sgx_register_utils.rs b/provers/sgx/prover/src/sgx_register_utils.rs index efc285e6..51710421 100644 --- a/provers/sgx/prover/src/sgx_register_utils.rs +++ b/provers/sgx/prover/src/sgx_register_utils.rs @@ -5,12 +5,11 @@ use alloy_sol_types::sol; use alloy_transport_http::Http; use anyhow::Result; use pem::parse_many; -use raiko_primitives::{ +use raiko_lib::primitives::{ alloy_eips::{BlockId, BlockNumberOrTag}, hex, Address, Bytes, FixedBytes, U256, }; -use std::{env, path::Path}; -use std::{fs, io}; +use std::{env, fs, io, path::Path}; use url::Url; const REGISTERED_FILE: &str = "registered"; @@ -335,8 +334,7 @@ pub async fn register_sgx_instance( #[cfg(test)] mod test { - - use raiko_primitives::address; + use raiko_lib::primitives::address; use super::*; diff --git a/provers/sgx/setup/Cargo.toml b/provers/sgx/setup/Cargo.toml index 2800fc97..2ad17999 100644 --- a/provers/sgx/setup/Cargo.toml +++ b/provers/sgx/setup/Cargo.toml @@ -10,8 +10,7 @@ edition = "2021" sgx-prover = { path = "../prover", optional = true } # raiko -raiko-lib = { workspace = true } -raiko-primitives = { workspace = true, features = ["c-kzg"] } +raiko-lib = { workspace = true, features = ["c-kzg"] } # alloy alloy-rlp = { workspace = true }