diff --git a/Cargo.toml b/Cargo.toml index 8781d1ee6..914c0450a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,8 +57,8 @@ keywords = ["blockchain", "zksync", "zk", "risc-v"] categories = ["cryptography"] [workspace.dependencies] -zksync_os_evm_errors = { version = "0.0.10", default-features = false } -zksync_os_interface = { version = "0.0.10"} +zksync_os_evm_errors = { git = "https://github.com/matter-labs/zksync-os-interface", branch = "alocascio-prover-input-gen", default-features = false } +zksync_os_interface = { git = "https://github.com/matter-labs/zksync-os-interface", branch = "alocascio-prover-input-gen", version = "0.0.10"} risc_v_simulator = { git = "https://github.com/matter-labs/zksync-airbender", tag = "v0.4.3"} blake2s_u32 = { git = "https://github.com/matter-labs/zksync-airbender", tag = "v0.4.3"} diff --git a/api/src/lib.rs b/api/src/lib.rs index 913ebd84c..13aecf3ae 100644 --- a/api/src/lib.rs +++ b/api/src/lib.rs @@ -1,54 +1,5 @@ #![feature(allocator_api)] -use std::{path::PathBuf, str::FromStr}; - -use forward_system::run::{ - test_impl::{InMemoryPreimageSource, InMemoryTree}, - BlockContext, StorageCommitment, -}; -use oracle_provider::ReadWitnessSource; -use zksync_os_interface::traits::TxListSource; -pub mod helpers; - /// Runs the batch, and returns the output (that contains gas usage, transaction status etc.). -pub use forward_system::run::run_block; -use zk_ee::common_structs::da_commitment_scheme::DACommitmentScheme; -use zk_ee::common_structs::ProofData; - -/// Runs a block in RISC-V - using zksync_os binary - and returns the -/// witness that can be passed to the prover subsystem. -pub fn run_block_generate_witness( - block_context: BlockContext, - tree: InMemoryTree, - preimage_source: InMemoryPreimageSource, - tx_source: TxListSource, - proof_data: ProofData, - da_commitment_scheme: DACommitmentScheme, - zksync_os_bin_path: &str, -) -> Vec { - use forward_system::run::*; - - let oracle = make_oracle_for_proofs_and_dumps_for_init_data( - block_context, - tree, - preimage_source, - tx_source, - Some(proof_data), - Some(da_commitment_scheme), - false, - ); - - // We'll wrap the source, to collect all the reads. - let copy_source = ReadWitnessSource::new(oracle); - - let items = copy_source.get_read_items(); - // By default - enable diagnostics is false (which makes the test run faster). - let path = PathBuf::from_str(zksync_os_bin_path).unwrap(); - let output = zksync_os_runner::run(path, None, 1 << 36, copy_source); - - // We return 0s in case of failure. - assert_ne!(output, [0u32; 8]); - - let result = items.borrow().clone(); - result -} +pub use forward_system::run::{generate_proof_input, run_block}; +pub mod helpers; diff --git a/basic_bootloader/src/bootloader/gas_helpers.rs b/basic_bootloader/src/bootloader/gas_helpers.rs index 813635f1d..67f4f8fdc 100644 --- a/basic_bootloader/src/bootloader/gas_helpers.rs +++ b/basic_bootloader/src/bootloader/gas_helpers.rs @@ -1,6 +1,7 @@ use constants::{CALLDATA_NON_ZERO_BYTE_GAS_COST, CALLDATA_ZERO_BYTE_GAS_COST}; use evm_interpreter::native_resource_constants::COPY_BYTE_NATIVE_COST; use evm_interpreter::ERGS_PER_GAS; +use metadata::basic_metadata::ZkSpecificPricingMetadata; use zk_ee::internal_error; use zk_ee::system::errors::internal::InternalError; use zk_ee::system::{Computational, Resources}; @@ -156,9 +157,12 @@ pub fn get_resources_to_charge_for_pubdata( system: &mut System, native_per_pubdata: U256, base_pubdata: Option, -) -> Result<(u64, S::Resources), InternalError> { +) -> Result<(u64, S::Resources), InternalError> +where + S::Metadata: ZkSpecificPricingMetadata, +{ let current_pubdata_spent = system - .net_pubdata_used()? + .net_pubdata_used(system.repeated_write_index_encoding_length())? .saturating_sub(base_pubdata.unwrap_or(0)); let native_per_pubdata = u256_to_u64_saturated(&native_per_pubdata); let native = current_pubdata_spent @@ -181,7 +185,10 @@ pub fn check_enough_resources_for_pubdata( native_per_pubdata: U256, resources: &S::Resources, base_pubdata: Option, -) -> Result<(bool, S::Resources, u64), InternalError> { +) -> Result<(bool, S::Resources, u64), InternalError> +where + S::Metadata: ZkSpecificPricingMetadata, +{ let (pubdata_used, resources_for_pubdata) = get_resources_to_charge_for_pubdata(system, native_per_pubdata, base_pubdata)?; let _ = system.get_logger().write_fmt(format_args!( diff --git a/basic_system/src/system_functions/mod.rs b/basic_system/src/system_functions/mod.rs index eb5d6574e..fded162dc 100644 --- a/basic_system/src/system_functions/mod.rs +++ b/basic_system/src/system_functions/mod.rs @@ -29,9 +29,9 @@ fn bytereverse(input: &mut [u8]) { /// No std system functions implementations. /// All of them are following EVM specs(for precompiles and keccak opcode). /// -pub struct NoStdSystemFunctions; +pub struct NoStdSystemFunctions; -impl SystemFunctions for NoStdSystemFunctions { +impl SystemFunctions for NoStdSystemFunctions { type Keccak256 = keccak256::Keccak256Impl; type Sha256 = sha256::Sha256Impl; type Secp256k1ECRecover = ecrecover::EcRecoverImpl; @@ -47,6 +47,8 @@ impl SystemFunctions for NoStdSystemFunctions { type PointEvaluation = point_evaluation::PointEvaluationImpl; } -impl SystemFunctionsExt for NoStdSystemFunctions { - type ModExp = modexp::ModExpImpl; +impl SystemFunctionsExt + for NoStdSystemFunctions +{ + type ModExp = modexp::ModExpImpl; } diff --git a/basic_system/src/system_functions/modexp/delegation/bigint.rs b/basic_system/src/system_functions/modexp/advice/bigint.rs similarity index 90% rename from basic_system/src/system_functions/modexp/delegation/bigint.rs rename to basic_system/src/system_functions/modexp/advice/bigint.rs index 4b2b9b33d..af39d7f3c 100644 --- a/basic_system/src/system_functions/modexp/delegation/bigint.rs +++ b/basic_system/src/system_functions/modexp/advice/bigint.rs @@ -1,15 +1,15 @@ // Representation of big integers using primitives that are friendly for our delegations extern crate alloc; -#[cfg(any(all(target_arch = "riscv32", feature = "proving"), test))] -use super::super::{ModExpAdviceParams, MODEXP_ADVICE_QUERY_ID}; +use super::super::MODEXP_ADVICE_QUERY_ID; use super::u256::*; +use crate::system_functions::modexp::ModExpAdviceParams64; use alloc::vec::Vec; use core::alloc::Allocator; use core::fmt::Debug; use core::mem::MaybeUninit; use crypto::{bigint_op_delegation_raw, bigint_op_delegation_with_carry_bit_raw, BigIntOps}; -#[cfg(any(all(target_arch = "riscv32", feature = "proving"), test))] +use ruint::aliases::U256; use zk_ee::oracle::IOOracle; // There is a small choice to make - either we do exponentiation walking as via LE or BE exponent. @@ -812,32 +812,32 @@ pub(crate) mod naive_advisor { } } -#[cfg(any(all(target_arch = "riscv32", feature = "proving"), test))] pub(crate) struct OracleAdvisor<'a, O: IOOracle> { pub(crate) inner: &'a mut O, } -#[cfg(any(all(target_arch = "riscv32", feature = "proving"), test))] fn write_bigint( it: &mut impl ExactSizeIterator, mut to_consume: usize, dst: &mut BigintRepr, ) { - const { - assert!(core::mem::size_of::() == core::mem::size_of::()); - } // NOTE: even if oracle overstates the number of digits (so - iterator length), it is not important // as long as caller checks that number of digits is within bounds of soundness unsafe { - let num_digits = to_consume.next_multiple_of(8) / 8; + const BIGINT_DIGIT_USIZE_SIZE: usize = U256::BYTES / core::mem::size_of::(); + let num_digits = + to_consume.next_multiple_of(BIGINT_DIGIT_USIZE_SIZE) / BIGINT_DIGIT_USIZE_SIZE; let dst_capacity = dst.clear_as_capacity_mut(); for dst in dst_capacity[..num_digits].iter_mut() { - let dst: *mut u32 = dst.as_mut_ptr().cast::<[u32; 8]>().cast(); - for i in 0..8 { + let dst: *mut usize = dst + .as_mut_ptr() + .cast::<[usize; BIGINT_DIGIT_USIZE_SIZE]>() + .cast(); + for i in 0..BIGINT_DIGIT_USIZE_SIZE { if to_consume > 0 { to_consume -= 1; let digit = it.next().unwrap(); - dst.add(i).write(digit as u32); + dst.add(i).write(digit); } else { dst.add(i).write(0); } @@ -848,7 +848,6 @@ fn write_bigint( } } -#[cfg(any(all(target_arch = "riscv32", feature = "proving"), test))] impl<'a, O: IOOracle> ModexpAdvisor for OracleAdvisor<'a, O> { fn get_reduction_op_advice( &mut self, @@ -857,37 +856,79 @@ impl<'a, O: IOOracle> ModexpAdvisor for OracleAdvisor<'a, O> { quotient_dst: &mut BigintRepr, remainder_dst: &mut BigintRepr, ) { - let arg: ModExpAdviceParams = { - let a_len = a.digits; - let a_ptr = a.backing.as_ptr(); - - let modulus_len = m.digits; - let modulus_ptr = m.backing.as_ptr(); - - assert!(modulus_len > 0); - - ModExpAdviceParams { - op: 0, - a_ptr: a_ptr.addr() as u32, - a_len: a_len as u32, - b_ptr: 0, - b_len: 0, - modulus_ptr: modulus_ptr.addr() as u32, - modulus_len: modulus_len as u32, - } + // We use different advice params depending on architecture + // Both are mostly the same, main difference is the width of pointers + #[cfg(target_arch = "riscv32")] + let (mut it, q_len, r_len) = { + use crate::system_functions::modexp::ModExpAdviceParams; + let arg: ModExpAdviceParams = { + let a_len = a.digits; + let a_ptr = a.backing.as_ptr(); + + let modulus_len = m.digits; + let modulus_ptr = m.backing.as_ptr(); + + assert!(modulus_len > 0); + + ModExpAdviceParams { + op: 0, + a_ptr: a_ptr.addr() as u32, + a_len: a_len as u32, + b_ptr: 0, + b_len: 0, + modulus_ptr: modulus_ptr.addr() as u32, + modulus_len: modulus_len as u32, + } + }; + // We assume that oracle's response is well-formed lengths-wise, and we will check value-wise separately + let mut it = self + .inner + .raw_query( + MODEXP_ADVICE_QUERY_ID, + &((&arg as *const ModExpAdviceParams).addr() as u32), + ) + .unwrap(); + let q_len = it.next().expect("quotient length"); + let r_len = it.next().expect("remainder length"); + (it, q_len, r_len) }; - // We assume that oracle's response is well-formed lengths-wise, and we will check value-wise separately - let mut it = self - .inner - .raw_query( - MODEXP_ADVICE_QUERY_ID, - &((&arg as *const ModExpAdviceParams).addr() as u32), - ) - .unwrap(); - - let q_len = it.next().expect("quotient length"); - let r_len = it.next().expect("remainder length"); + #[cfg(not(target_arch = "riscv32"))] + let (mut it, q_len, r_len) = { + let arg: ModExpAdviceParams64 = { + let a_len = a.digits; + let a_ptr = a.backing.as_ptr(); + + let modulus_len = m.digits; + let modulus_ptr = m.backing.as_ptr(); + + assert!(modulus_len > 0); + + ModExpAdviceParams64 { + op: 0, + a_ptr: a_ptr.addr() as u64, + a_len: a_len as u64, + b_ptr: 0, + b_len: 0, + modulus_ptr: modulus_ptr.addr() as u64, + modulus_len: modulus_len as u64, + } + }; + // We assume that oracle's response is well-formed lengths-wise, and we will check value-wise separately + let mut it = self + .inner + .raw_query( + MODEXP_ADVICE_QUERY_ID, + &((&arg as *const ModExpAdviceParams64).addr() as u64), + ) + .unwrap(); + // Oracle provides lengths as u32, so in this case they are + // packed into a single usize + let packed_lens = it.next().expect("packed lengths"); + let q_len = (packed_lens & 0xFFFF_FFFF) as usize; + let r_len = (packed_lens >> 32) as usize; + (it, q_len / 2, r_len / 2) + }; let max_quotient_digits = if a.digits < m.digits { 0 @@ -899,10 +940,6 @@ impl<'a, O: IOOracle> ModexpAdvisor for OracleAdvisor<'a, O> { let max_remainder_digits = m.digits; - const { - assert!(core::mem::size_of::() == core::mem::size_of::()); - } - // check that hint is "sane" in upper bound assert!(q_len.next_multiple_of(8) / 8 <= max_quotient_digits); diff --git a/basic_system/src/system_functions/modexp/delegation/mod.rs b/basic_system/src/system_functions/modexp/advice/mod.rs similarity index 99% rename from basic_system/src/system_functions/modexp/delegation/mod.rs rename to basic_system/src/system_functions/modexp/advice/mod.rs index aa13cffcb..34cf985f2 100644 --- a/basic_system/src/system_functions/modexp/delegation/mod.rs +++ b/basic_system/src/system_functions/modexp/advice/mod.rs @@ -11,7 +11,6 @@ use zk_ee::system::logger::Logger; #[cfg(feature = "testing")] use zk_ee::system::logger::NullLogger; -#[cfg(any(all(target_arch = "riscv32", feature = "proving"), test))] pub(super) fn modexp( base: &[u8], exp: &[u8], diff --git a/basic_system/src/system_functions/modexp/delegation/u256.rs b/basic_system/src/system_functions/modexp/advice/u256.rs similarity index 100% rename from basic_system/src/system_functions/modexp/delegation/u256.rs rename to basic_system/src/system_functions/modexp/advice/u256.rs diff --git a/basic_system/src/system_functions/modexp/mod.rs b/basic_system/src/system_functions/modexp/mod.rs index 32a9caabe..298cf617b 100644 --- a/basic_system/src/system_functions/modexp/mod.rs +++ b/basic_system/src/system_functions/modexp/mod.rs @@ -25,29 +25,32 @@ pub const MODEXP_ADVICE_QUERY_ID: u32 = ADVICE_SUBSPACE_MASK | 0x10; /// Used to request division advice for big integer operations during modexp #[repr(C)] #[derive(Debug, Default)] -pub struct ModExpAdviceParams { - pub op: u32, // Operation type (0 = division) - pub a_ptr: u32, // Pointer to dividend - pub a_len: u32, // Length of dividend in words - pub b_ptr: u32, // Pointer to divisor - pub b_len: u32, // Length of divisor in words - pub modulus_ptr: u32, // Pointer to modulus - pub modulus_len: u32, // Length of modulus in words +pub struct ModExpAdviceParamsGeneric { + pub op: W, // Operation type (0 = division) + pub a_ptr: W, // Pointer to dividend + pub a_len: W, // Length of dividend in words + pub b_ptr: W, // Pointer to divisor + pub b_len: W, // Length of divisor in words + pub modulus_ptr: W, // Pointer to modulus + pub modulus_len: W, // Length of modulus in words } -#[cfg(any( - all(target_arch = "riscv32", feature = "proving"), - test, - feature = "testing" -))] -pub mod delegation; +/// Used for proving (RISC-V 32-bit) +pub type ModExpAdviceParams = ModExpAdviceParamsGeneric; + +/// Used for native execution (64-bit) +pub type ModExpAdviceParams64 = ModExpAdviceParamsGeneric; + +pub mod advice; /// /// modexp system function implementation. /// -pub struct ModExpImpl; +pub struct ModExpImpl; -impl SystemFunctionExt for ModExpImpl { +impl SystemFunctionExt + for ModExpImpl +{ /// If the input size is less than expected - it will be padded with zeroes. /// If the input size is greater - redundant bytes will be ignored. /// @@ -71,7 +74,9 @@ impl SystemFunctionExt for ModExpImpl { allocator: A, ) -> Result<(), SubsystemError> { cycle_marker::wrap_with_resources!("modexp", resources, { - modexp_as_system_function_inner(input, output, resources, oracle, logger, allocator) + modexp_as_system_function_inner::<_, _, _, _, _, USE_ADVICE>( + input, output, resources, oracle, logger, allocator, + ) }) } } @@ -106,6 +111,9 @@ fn modexp_as_system_function_inner< D: ?Sized + TryExtend, A: Allocator + Clone, R: Resources, + // Toggle delegation-based implementation for forward run, to be able + // to capture oracle queries. + const USE_ADVICE: bool, >( input: &[u8], dst: &mut D, @@ -211,7 +219,7 @@ fn modexp_as_system_function_inner< // Call the modexp. #[cfg(any(all(target_arch = "riscv32", feature = "proving"), test))] - let output = self::delegation::modexp( + let output = self::advice::modexp( base.as_slice(), exponent.as_slice(), modulus.as_slice(), @@ -221,12 +229,23 @@ fn modexp_as_system_function_inner< ); #[cfg(not(any(all(target_arch = "riscv32", feature = "proving"), test)))] - let output = ::modexp::modexp( - base.as_slice(), - exponent.as_slice(), - modulus.as_slice(), - allocator, - ); + let output = if USE_ADVICE { + self::advice::modexp( + base.as_slice(), + exponent.as_slice(), + modulus.as_slice(), + oracle, + logger, + allocator, + ) + } else { + ::modexp::modexp( + base.as_slice(), + exponent.as_slice(), + modulus.as_slice(), + allocator, + ) + }; if output.len() >= mod_len { // truncate diff --git a/basic_system/src/system_implementation/flat_storage_model/account_cache.rs b/basic_system/src/system_implementation/flat_storage_model/account_cache.rs index 07db948a6..30f02bc3c 100644 --- a/basic_system/src/system_implementation/flat_storage_model/account_cache.rs +++ b/basic_system/src/system_implementation/flat_storage_model/account_cache.rs @@ -405,7 +405,7 @@ impl< }) } - pub fn calculate_pubdata_used_by_tx(&self) -> u32 { + pub fn calculate_pubdata_used_by_tx(&self, repeated_write_index_encoding_length: u8) -> u32 { let mut visited_elements = BTreeSet::new_in(self.alloc.clone()); let mut pubdata_used = 0u32; @@ -432,8 +432,15 @@ impl< continue; } + let is_initial_write = initial.appearance() == Appearance::Unset; + if current.value() != at_tx_start.value() { - pubdata_used += 32; // key + pubdata_used += if is_initial_write { + // Address + 20 + } else { + repeated_write_index_encoding_length as u32 + }; pubdata_used += AccountProperties::diff_compression_length( at_tx_start.value(), current.value(), diff --git a/basic_system/src/system_implementation/flat_storage_model/mod.rs b/basic_system/src/system_implementation/flat_storage_model/mod.rs index 33236d8bd..f42d9cb24 100644 --- a/basic_system/src/system_implementation/flat_storage_model/mod.rs +++ b/basic_system/src/system_implementation/flat_storage_model/mod.rs @@ -16,6 +16,7 @@ pub use self::account_cache_entry::*; pub use self::preimage_cache::*; pub use self::simple_growable_storage::*; pub use self::storage_cache::*; +use alloc::collections::BTreeMap; use core::alloc::Allocator; use crypto::MiniDigest; use ruint::aliases::B160; @@ -114,23 +115,31 @@ impl< } } - fn pubdata_used_by_tx(&self) -> u32 { - self.account_data_cache.calculate_pubdata_used_by_tx() - + self.storage_cache.calculate_pubdata_used_by_tx() + fn pubdata_used_by_tx(&self, repeated_write_index_encoding_length: u8) -> u32 { + self.account_data_cache + .calculate_pubdata_used_by_tx(repeated_write_index_encoding_length) + + self + .storage_cache + .calculate_pubdata_used_by_tx(repeated_write_index_encoding_length) } /// Standard finish method that completes storage model processing. fn finish( self, oracle: &mut impl IOOracle, - state_commitment: Option<&mut Self::StorageCommitment>, - pubdata_dst: &mut T, + state_commitment_and_pubdata_dst: Option<(&mut Self::StorageCommitment, &mut T)>, result_keeper: &mut impl IOResultKeeper, + repeated_write_index_encoding_length: u8, logger: &mut impl Logger, ) -> Result<(), InternalError> { // Complete the finalization but discard the returned storage cache - let _ = - self.finish_internal(oracle, state_commitment, pubdata_dst, result_keeper, logger)?; + let _ = self.finish_internal( + oracle, + state_commitment_and_pubdata_dst, + result_keeper, + repeated_write_index_encoding_length, + logger, + )?; Ok(()) } @@ -142,14 +151,19 @@ impl< fn finish_and_calculate_state_diffs_hash( self, oracle: &mut impl IOOracle, - state_commitment: Option<&mut Self::StorageCommitment>, - pubdata_dst: &mut T, + state_commitment_and_pubdata_dst: Option<(&mut Self::StorageCommitment, &mut T)>, result_keeper: &mut impl IOResultKeeper, + repeated_write_index_encoding_length: u8, logger: &mut impl Logger, ) -> Result { // First complete the normal storage finalization process - let storage_cache = - self.finish_internal(oracle, state_commitment, pubdata_dst, result_keeper, logger)?; + let storage_cache = self.finish_internal( + oracle, + state_commitment_and_pubdata_dst, + result_keeper, + repeated_write_index_encoding_length, + logger, + )?; let mut hasher = crypto::blake2s::Blake2s256::new(); let mut state_diffs_hasher = crypto::blake2s::Blake2s256::new(); @@ -507,21 +521,188 @@ impl< const PROOF_ENV: bool, > FlatTreeWithAccountsUnderHashesStorageModel { + fn is_account_write(key: &WarmStorageKey) -> bool { + key.address == ACCOUNT_PROPERTIES_STORAGE_ADDRESS + } + + fn is_slot_write(key: &WarmStorageKey) -> bool { + key.address != ACCOUNT_PROPERTIES_STORAGE_ADDRESS + } + + /// + /// Adds storage diffs header to pubdata. + /// The format is: + /// - total number of diffs (u32, big-endian) + /// - number of initial account writes (u32, big-endian) + /// - number of initial slot writes (u32, big-endian) + /// - encoding length for indices for repeated writes (u8) + /// + fn add_diffs_header_to_pubdata( + pubdata_dst: &mut T, + result_keeper: &mut impl IOResultKeeper<::IOTypes>, + storage_cache: &NewStorageWithAccountPropertiesUnderHash, + repeated_write_index_encoding_length: u8, + ) -> Result<(), InternalError> { + let encoded_state_diffs_count = + (storage_cache.net_diffs_iter().count() as u32).to_be_bytes(); + let encoded_initial_account_writes_count = (storage_cache + .net_diffs_iter() + .filter(|(k, v)| Self::is_account_write(k) && v.is_new_storage_slot) + .count() as u32) + .to_be_bytes(); + let encoded_initial_slot_writes_count = (storage_cache + .net_diffs_iter() + .filter(|(k, v)| Self::is_slot_write(k) && v.is_new_storage_slot) + .count() as u32) + .to_be_bytes(); + pubdata_dst.write(&encoded_state_diffs_count); + result_keeper.pubdata(&encoded_state_diffs_count); + pubdata_dst.write(&encoded_initial_account_writes_count); + result_keeper.pubdata(&encoded_initial_account_writes_count); + pubdata_dst.write(&encoded_initial_slot_writes_count); + result_keeper.pubdata(&encoded_initial_slot_writes_count); + pubdata_dst.write(&[repeated_write_index_encoding_length]); + result_keeper.pubdata(&[repeated_write_index_encoding_length]); + Ok(()) + } + + /// + /// Add storage diffs to pubdata with optimal compression. + /// The order of adding diffs is: + /// 1. Initial account writes + /// 2. Initial slot writes + /// 3. Repeated writes (both account and slot writes) + /// + fn filter_and_add_diffs_to_pubdata( + account_data_cache: &mut NewModelAccountCache, + storage_cache: &mut NewStorageWithAccountPropertiesUnderHash, + preimages_cache: &mut BytecodeAndAccountDataPreimagesStorage, + oracle: &mut impl IOOracle, + pubdata_dst: &mut T, + result_keeper: &mut impl IOResultKeeper<::IOTypes>, + repeated_write_index_encoding_length: u8, + key_to_index_cache: &BTreeMap, + ) -> Result<(), ()> { + let mut hasher = crypto::blake2s::Blake2s256::new(); + + // First, add diffs for initial account writes + for (k, v) in storage_cache.net_diffs_iter() { + if Self::is_account_write(&k) && v.is_new_storage_slot { + // Add address to pubdata + let address = B160::try_from_be_slice(&k.key.as_u8_ref()[12..]).unwrap(); + let address_bytes = address.to_be_bytes::<{ B160::BYTES }>(); + pubdata_dst.write(&address_bytes); + result_keeper.pubdata(&address_bytes); + + // Add diff itself + let account_address = address.into(); + let cache_item = account_data_cache.cache.get(&account_address).ok_or(())?; + let (l, r) = cache_item.get_initial_and_last_values().ok_or(())?; + AccountProperties::diff_compression::( + l.value(), + r.value(), + r.metadata().not_publish_bytecode, + pubdata_dst, + result_keeper, + preimages_cache, + oracle, + ) + .map_err(|_| ())?; + } + } + + // Second, add diffs for initial slot writes + for (k, v) in storage_cache.net_diffs_iter() { + if Self::is_slot_write(&k) && v.is_new_storage_slot { + // Add key to pubdata + let derived_key = + derive_flat_storage_key_with_hasher(&k.address, &k.key, &mut hasher); + pubdata_dst.write(&derived_key.as_u8_ref()); + result_keeper.pubdata(&derived_key.as_u8_ref()); + + // Add diff itself + ValueDiffCompressionStrategy::optimal_compression( + &v.initial_value, + &v.current_value, + pubdata_dst, + result_keeper, + ); + } + } + + // Third, add diffs for repeated writes + for (k, v) in storage_cache.net_diffs_iter() { + if !v.is_new_storage_slot { + if Self::is_account_write(&k) { + // Add index to pubdata + let derived_key = + derive_flat_storage_key_with_hasher(&k.address, &k.key, &mut hasher); + let index = key_to_index_cache.get(&derived_key).ok_or(())?; + write_index_fixed_width( + index, + repeated_write_index_encoding_length, + pubdata_dst, + result_keeper, + )?; + + // Add diff itself + let address = B160::try_from_be_slice(&k.key.as_u8_ref()[12..]).unwrap(); + let account_address = address.into(); + let cache_item = account_data_cache.cache.get(&account_address).ok_or(())?; + let (l, r) = cache_item.get_initial_and_last_values().ok_or(())?; + AccountProperties::diff_compression::( + l.value(), + r.value(), + r.metadata().not_publish_bytecode, + pubdata_dst, + result_keeper, + preimages_cache, + oracle, + ) + .map_err(|_| ())?; + } else { + // Add index to pubdata + let derived_key = + derive_flat_storage_key_with_hasher(&k.address, &k.key, &mut hasher); + let index = key_to_index_cache.get(&derived_key).ok_or(())?; + write_index_fixed_width( + index, + repeated_write_index_encoding_length, + pubdata_dst, + result_keeper, + )?; + + // Add diff itself + ValueDiffCompressionStrategy::optimal_compression( + &v.initial_value, + &v.current_value, + pubdata_dst, + result_keeper, + ); + } + } + } + Ok(()) + } + /// Internal implementation shared by both `finish` and `finish_state_diffs_hash`. /// /// This method performs the complete storage finalization process: /// 1. Persists account changes to storage cache /// 2. Returns uncompressed state diffs to the result keeper - /// 3. Computes and commits compressed pubdata - /// 4. Verifies and applies all storage reads/writes to the state commitment + /// 3. Verifies and applies all storage reads/writes to the state commitment + /// 4. Computes and commits compressed pubdata /// /// Returns the final storage cache for further processing by the caller. fn finish_internal( self, oracle: &mut impl IOOracle, - state_commitment: Option<&mut ::StorageCommitment>, - pubdata_dst: &mut T, + state_commitment_and_pubdata_dst: Option<( + &mut ::StorageCommitment, + &mut T, + )>, result_keeper: &mut impl IOResultKeeper<::IOTypes>, + repeated_write_index_encoding_length: u8, logger: &mut impl Logger, ) -> Result, InternalError> { let Self { @@ -530,7 +711,7 @@ impl< mut account_data_cache, allocator, } = self; - // flush accounts into storage + // 1. flush accounts into storage account_data_cache .persist_changes( &mut storage_cache, @@ -540,7 +721,7 @@ impl< ) .expect("must persist changes from account cache"); - // 1. Return uncompressed state diffs for sequencer + // 2. Return uncompressed state diffs for sequencer result_keeper.storage_diffs(storage_cache.net_diffs_iter().map(|(k, v)| { let WarmStorageKey { address, key } = k; let value = v.current_value; @@ -548,65 +729,35 @@ impl< })); preimages_cache.report_new_preimages(result_keeper)?; - // 2. Commit to/return compressed pubdata - let encdoded_state_diffs_count = - (storage_cache.net_diffs_iter().count() as u32).to_be_bytes(); - pubdata_dst.write(&encdoded_state_diffs_count); - result_keeper.pubdata(&encdoded_state_diffs_count); - - let mut hasher = crypto::blake2s::Blake2s256::new(); - storage_cache - .0 - .cache - .apply_to_all_updated_elements::<_, ()>(|l, r, k| { - // Skip on empty diff - if l.value() == r.value() { - return Ok(()); - } - // TODO(EVM-1074): use tree index instead of key for repeated writes - let derived_key = - derive_flat_storage_key_with_hasher(&k.address, &k.key, &mut hasher); - pubdata_dst.write(derived_key.as_u8_ref()); - result_keeper.pubdata(derived_key.as_u8_ref()); - - // we publish preimages for account details - if k.address == ACCOUNT_PROPERTIES_STORAGE_ADDRESS { - let account_address = B160::try_from_be_slice(&k.key.as_u8_ref()[12..]) - .unwrap() - .into(); - let cache_item = account_data_cache.cache.get(&account_address).ok_or(())?; - let (l, r) = cache_item.get_initial_and_last_values().ok_or(())?; - AccountProperties::diff_compression::( - l.value(), - r.value(), - r.metadata().not_publish_bytecode, - pubdata_dst, - result_keeper, - &mut preimages_cache, - oracle, - ) - .map_err(|_| ())?; - } else { - ValueDiffCompressionStrategy::optimal_compression( - l.value(), - r.value(), - pubdata_dst, - result_keeper, - ); - } - Ok(()) - }) - .map_err(|_| internal_error!("Failed to compute pubdata"))?; - - // 3. Verify/apply reads and writes - cycle_marker::wrap!("verify_and_apply_batch", { - if let Some(state_commitment) = state_commitment { + if let Some((state_commitment, pubdata_dst)) = state_commitment_and_pubdata_dst { + // 3. Verify/apply reads and writes + let key_to_index_cache = cycle_marker::wrap!("verify_and_apply_batch", { let it = storage_cache.net_accesses_iter(); state_commitment.verify_and_apply_batch(oracle, it, allocator, logger) - } else { - Ok(()) - } - })?; + })?; + + // 4. Commit to/return compressed pubdata + // Header contains counts for total diffs, initial account writes and initial slot writes + Self::add_diffs_header_to_pubdata( + pubdata_dst, + result_keeper, + &storage_cache, + repeated_write_index_encoding_length, + )?; + + // Add diffs themselves + Self::filter_and_add_diffs_to_pubdata( + &mut account_data_cache, + &mut storage_cache, + &mut preimages_cache, + oracle, + pubdata_dst, + result_keeper, + repeated_write_index_encoding_length, + &key_to_index_cache, + ) + .map_err(|_| internal_error!("Failed to compute pubdata"))?; + } Ok(storage_cache) } @@ -661,3 +812,35 @@ impl< Ok(()) } } + +#[inline] +const fn required_be_len_u64(x: &u64) -> u8 { + let bits = 64 - x.leading_zeros(); + ((bits + 7) / 8) as u8 +} + +/// Write a single index using a fixed big-endian width (no metadata). +/// Fails if `width < required_be_len_u64(index)` (would truncate). +#[inline] +fn write_index_fixed_width( + index: &u64, + repeated_write_index_encoding_length: u8, // 0..=8; 0 means write nothing + pubdata_dst: &mut T, + result_keeper: &mut impl IOResultKeeper, +) -> Result<(), ()> { + if repeated_write_index_encoding_length > 8 { + return Err(()); + } + let need = required_be_len_u64(index); + if need > repeated_write_index_encoding_length { + return Err(()); + } // would lose high bits + + let be = index.to_be_bytes(); + let start = 8usize.saturating_sub(repeated_write_index_encoding_length as usize); + let tail = &be[start..]; + + pubdata_dst.write(tail); + result_keeper.pubdata(tail); + Ok(()) +} diff --git a/basic_system/src/system_implementation/flat_storage_model/simple_growable_storage.rs b/basic_system/src/system_implementation/flat_storage_model/simple_growable_storage.rs index 3f525b2dd..9e67bba32 100644 --- a/basic_system/src/system_implementation/flat_storage_model/simple_growable_storage.rs +++ b/basic_system/src/system_implementation/flat_storage_model/simple_growable_storage.rs @@ -189,7 +189,7 @@ impl StateRootView for FlatStorageCommitm source: impl Iterator + Clone, allocator: A, logger: &mut impl Logger, - ) -> Result<(), InternalError> { + ) -> Result, InternalError> { // we know that for our storage model we have a luxury of amortizing all new writes, // so our strategy is to: // - verify all reads @@ -214,7 +214,7 @@ impl StateRootView for FlatStorageCommitm // If there was no IO, just return if source.clone().next().is_none() { - return Ok(()); + return Ok(key_to_index_cache); } let reads_iter = source @@ -777,7 +777,7 @@ impl StateRootView for FlatStorageCommitm // root should not change in such case } - Ok(()) + Ok(key_to_index_cache) } } diff --git a/basic_system/src/system_implementation/flat_storage_model/storage_cache.rs b/basic_system/src/system_implementation/flat_storage_model/storage_cache.rs index b2370bc64..cbeb46130 100644 --- a/basic_system/src/system_implementation/flat_storage_model/storage_cache.rs +++ b/basic_system/src/system_implementation/flat_storage_model/storage_cache.rs @@ -641,7 +641,7 @@ impl< .filter(|(_, v)| v.current_value != v.initial_value) } - pub fn calculate_pubdata_used_by_tx(&self) -> u32 { + pub fn calculate_pubdata_used_by_tx(&self, repeated_write_index_encoding_length: u8) -> u32 { let mut visited_elements = BTreeSet::new_in(self.0.alloc.clone()); let mut pubdata_used = 0u32; @@ -672,10 +672,15 @@ impl< if current_value == initial_value { continue; } + let is_initial_write = element_history.initial().appearance() == Appearance::Unset; if at_tx_start_value != current_value { - // TODO(EVM-1074): use tree index instead of key for repeated writes - pubdata_used += 32; // key + pubdata_used += if is_initial_write { + // Full key + 32 + } else { + repeated_write_index_encoding_length as u32 + }; pubdata_used += ValueDiffCompressionStrategy::optimal_compression_length( at_tx_start_value, current_value, diff --git a/basic_system/src/system_implementation/system/da_commitment_generator/blob_commitment_generator/commitment_and_proof_advice.rs b/basic_system/src/system_implementation/system/da_commitment_generator/blob_commitment_generator/commitment_and_proof_advice.rs index 2d1c57d88..f7b44830f 100644 --- a/basic_system/src/system_implementation/system/da_commitment_generator/blob_commitment_generator/commitment_and_proof_advice.rs +++ b/basic_system/src/system_implementation/system/da_commitment_generator/blob_commitment_generator/commitment_and_proof_advice.rs @@ -70,7 +70,6 @@ pub struct OracleBasedBlobCommitmentAndProofAdvisor<'a, O: zk_ee::oracle::IOOrac pub oracle: &'a mut O, } -// Right now, we run it only on RISC-V, but in theory, it should be possible to make it runnable on native arch as well impl<'a, O: zk_ee::oracle::IOOracle> BlobCommitmentAndProofAdvisor for OracleBasedBlobCommitmentAndProofAdvisor<'a, O> { diff --git a/basic_system/src/system_implementation/system/io_subsystem.rs b/basic_system/src/system_implementation/system/io_subsystem.rs index f333ccfc8..e2fd4c26e 100644 --- a/basic_system/src/system_implementation/system/io_subsystem.rs +++ b/basic_system/src/system_implementation/system/io_subsystem.rs @@ -28,6 +28,7 @@ use zk_ee::common_structs::ProofData; use zk_ee::common_structs::L2_TO_L1_LOG_SERIALIZE_SIZE; use zk_ee::interface_error; use zk_ee::oracle::basic_queries::ZKProofDataQuery; +use zk_ee::oracle::query_ids::DISCONNECT_ORACLE_QUERY_ID; use zk_ee::oracle::simple_oracle_query::SimpleOracleQuery; use zk_ee::out_of_ergs_error; use zk_ee::system::metadata::zk_metadata::BlockMetadataFromOracle; @@ -359,8 +360,13 @@ impl< ) } - fn net_pubdata_used(&self) -> Result { - Ok(self.storage.pubdata_used_by_tx() as u64 + fn net_pubdata_used( + &self, + repeated_write_index_encoding_length: u8, + ) -> Result { + Ok(self + .storage + .pubdata_used_by_tx(repeated_write_index_encoding_length) as u64 + self.logs_storage.calculate_pubdata_used_by_tx()? as u64) } @@ -465,13 +471,12 @@ impl< result_keeper.pubdata(&block_metadata.timestamp.to_be_bytes()); // dump pubdata and state diffs self.storage - .finish( + .finish::( &mut self.oracle, - // no storage commitment + // no storage commitment or pubdata (hash) destination needed None, - // we don't need to append pubdata to the hash - &mut NopCommitmentGenerator, result_keeper, + block_metadata.repeated_write_index_encoding_length, &mut logger, ) .expect("Failed to finish storage"); @@ -531,6 +536,9 @@ impl< // finishing IO, applying changes let mut da_commitment_generator = crate::system_implementation::system::da_commitment_generator::Blake2sCommitmentGenerator::new(); // Write version byte first to enable future pubdata format upgrades + result_keeper.pubdata(&[PUBDATA_ENCODING_VERSION]); + result_keeper.pubdata(current_block_hash.as_u8_ref()); + result_keeper.pubdata(&block_metadata.timestamp.to_be_bytes()); da_commitment_generator.write(&[PUBDATA_ENCODING_VERSION]); da_commitment_generator.write(current_block_hash.as_u8_ref()); da_commitment_generator.write(&block_metadata.timestamp.to_be_bytes()); @@ -539,9 +547,9 @@ impl< self.storage .finish( &mut self.oracle, - Some(&mut state_commitment), - &mut da_commitment_generator, + Some((&mut state_commitment, &mut da_commitment_generator)), result_keeper, + block_metadata.repeated_write_index_encoding_length, &mut logger, ) .expect("Failed to finish storage"); @@ -588,6 +596,11 @@ impl< blocks_output: block_output.hash().into(), }; + #[allow(unused_must_use)] + self.oracle + .raw_query_with_empty_input(DISCONNECT_ORACLE_QUERY_ID) + .expect("must disconnect an oracle before performing arbitrary CSR access"); + (self.oracle, public_input.hash().into()) } } @@ -607,7 +620,7 @@ impl< O: IOOracle, > FinishIO for FullIO { - type FinalData = (O, Bytes32); + type FinalData = (O, Bytes32, public_input::BatchOutput); fn finish( mut self, block_metadata: BlockMetadataFromOracle, @@ -647,6 +660,9 @@ impl< .unwrap(); // Write version byte first to enable future pubdata format upgrades + result_keeper.pubdata(&[PUBDATA_ENCODING_VERSION]); + result_keeper.pubdata(current_block_hash.as_u8_ref()); + result_keeper.pubdata(&block_metadata.timestamp.to_be_bytes()); da_commitment_generator.write(&[PUBDATA_ENCODING_VERSION]); da_commitment_generator.write(current_block_hash.as_u8_ref()); da_commitment_generator.write(&block_metadata.timestamp.to_be_bytes()); @@ -655,9 +671,9 @@ impl< self.storage .finish_and_calculate_state_diffs_hash( &mut self.oracle, - Some(&mut state_commitment), - da_commitment_generator.as_mut(), + Some((&mut state_commitment, da_commitment_generator.as_mut())), result_keeper, + block_metadata.repeated_write_index_encoding_length, &mut logger, ) .expect("Failed to finish storage") @@ -665,9 +681,9 @@ impl< self.storage .finish( &mut self.oracle, - Some(&mut state_commitment), - da_commitment_generator.as_mut(), + Some((&mut state_commitment, da_commitment_generator.as_mut())), result_keeper, + block_metadata.repeated_write_index_encoding_length, &mut logger, ) .expect("Failed to finish storage"); @@ -733,10 +749,15 @@ impl< "PI calculation: final batch public input hash {public_input_hash:?}\n", )); + #[allow(unused_must_use)] + self.oracle + .raw_query_with_empty_input(DISCONNECT_ORACLE_QUERY_ID) + .expect("must disconnect an oracle before performing arbitrary CSR access"); + if cfg!(feature = "state-diffs-pi") { - (self.oracle, state_diffs_hash) + (self.oracle, state_diffs_hash, batch_output) } else { - (self.oracle, public_input_hash) + (self.oracle, public_input_hash, batch_output) } } } @@ -848,9 +869,12 @@ where self.storage .finish( &mut self.oracle, - Some(&mut state_commitment), - builder.da_commitment_generator.as_mut().unwrap().as_mut(), + Some(( + &mut state_commitment, + builder.da_commitment_generator.as_mut().unwrap().as_mut(), + )), &mut NopResultKeeper, + block_metadata.repeated_write_index_encoding_length, &mut NullLogger, ) .expect("Failed to finish storage"); @@ -892,6 +916,11 @@ where upgrade_tx_hash, ); + #[allow(unused_must_use)] + self.oracle + .raw_query_with_empty_input(DISCONNECT_ORACLE_QUERY_ID) + .expect("must disconnect an oracle before performing arbitrary CSR access"); + self.oracle } } diff --git a/basic_system/src/system_implementation/system/pubdata.rs b/basic_system/src/system_implementation/system/pubdata.rs index b6a165f26..457c530a9 100644 --- a/basic_system/src/system_implementation/system/pubdata.rs +++ b/basic_system/src/system_implementation/system/pubdata.rs @@ -1,5 +1,4 @@ //! Pubdata related definitions and functionality. /// Version byte for pubdata encoding format. -/// Version 1: Initial versioned pubdata format -pub const PUBDATA_ENCODING_VERSION: u8 = 1; +pub const PUBDATA_ENCODING_VERSION: u8 = 2; diff --git a/callable_oracles/src/arithmetic/mod.rs b/callable_oracles/src/arithmetic/mod.rs index c5be8b62a..e1faa7a0e 100644 --- a/callable_oracles/src/arithmetic/mod.rs +++ b/callable_oracles/src/arithmetic/mod.rs @@ -1,12 +1,57 @@ -use basic_system::system_functions::modexp::{ModExpAdviceParams, MODEXP_ADVICE_QUERY_ID}; +use basic_system::system_functions::modexp::{ + ModExpAdviceParams, ModExpAdviceParams64, MODEXP_ADVICE_QUERY_ID, +}; use oracle_provider::OracleQueryProcessor; use risc_v_simulator::abstractions::memory::MemorySource; +use crate::read_u64_words; use crate::utils::{ evaluate::{read_memory_as_u64, read_struct}, usize_slice_iterator::UsizeSliceIteratorOwned, }; +struct ArithmeticQueryOutput { + quotient: Vec, + remainder: Vec, +} + +impl ArithmeticQueryOutput { + fn into_usize_iterator(self) -> Box + 'static> { + // Trim zeros + fn strip_leading_zeroes(input: &[u64]) -> &[u64] { + let mut digits = input.len(); + for el in input.iter().rev() { + if *el == 0 { + digits -= 1; + } else { + break; + } + } + &input[..digits] + } + let quotient = strip_leading_zeroes(&self.quotient); + let remainder = strip_leading_zeroes(&self.remainder); + + // account for usize being u64 here + let q_len_in_u32_words = quotient.len() * 2; + let r_len_in_u32_words = remainder.len() * 2; + // account for LE, and we will ask quotient first, then remainder + let header = [(q_len_in_u32_words as u64) | ((r_len_in_u32_words as u64) << 32)]; + + let r = header + .iter() + .chain(quotient.iter()) + .chain(remainder.iter()) + .map(|x| *x as usize) + .collect::>(); + let r = Vec::into_boxed_slice(r); + + let n = UsizeSliceIteratorOwned::new(r); + + Box::new(n) + } +} + pub struct ArithmeticQuery { _marker: std::marker::PhantomData, } @@ -58,37 +103,75 @@ impl OracleQueryProcessor for ArithmeticQuery { ruint::algorithms::div(&mut n, &mut d); - // Trim zeros - fn strip_leading_zeroes(input: &[u64]) -> &[u64] { - let mut digits = input.len(); - for el in input.iter().rev() { - if *el == 0 { - digits -= 1; - } else { - break; - } - } - &input[..digits] + ArithmeticQueryOutput { + quotient: n, + remainder: d, + } + .into_usize_iterator() + } +} + +/// Query processor to be used for prover input native run +/// Works in a similar way as the ArithmeticQuery, but with +/// 64 bit pointers. Importantly, the query response is the +/// same. +/// +/// This processor explicitly reads the process memory +/// using a raw pointer to get the input. +pub struct NativeArithmeticQuery { + _marker: std::marker::PhantomData, +} + +impl Default for NativeArithmeticQuery { + fn default() -> Self { + Self { + _marker: std::marker::PhantomData, } - let quotient = strip_leading_zeroes(&n); - let remainder = strip_leading_zeroes(&d); + } +} - // account for usize being u64 here - let q_len_in_u32_words = quotient.len() * 2; - let r_len_in_u32_words = remainder.len() * 2; - // account for LE, and we will ask quotient first, then remainder - let header = [(q_len_in_u32_words as u64) | ((r_len_in_u32_words as u64) << 32)]; +impl OracleQueryProcessor for NativeArithmeticQuery { + fn supported_query_ids(&self) -> Vec { + vec![MODEXP_ADVICE_QUERY_ID] + } - let r = header - .iter() - .chain(quotient.iter()) - .chain(remainder.iter()) - .map(|x| *x as usize) - .collect::>(); - let r = Vec::into_boxed_slice(r); + fn process_buffered_query( + &mut self, + query_id: u32, + query: Vec, + _memory: &M, + ) -> Box + 'static> { + debug_assert!(self.supports_query_id(query_id)); - let n = UsizeSliceIteratorOwned::new(r); + let mut it = query.into_iter(); - Box::new(n) + let arg_ptr = it.next().expect("A u64 should've been passed in."); + + assert!(it.next().is_none(), "A single ptr should've been passed."); + + let arg = unsafe { + let p = arg_ptr as *const ModExpAdviceParams64; + core::ptr::read_unaligned(p) + }; + + assert!(arg.a_ptr > 0); + assert!(arg.a_len > 0); + + assert_eq!(arg.b_ptr, 0); + assert_eq!(arg.b_len, 0); + + assert!(arg.modulus_ptr > 0); + assert!(arg.modulus_len > 0); + + let mut n: Vec = unsafe { read_u64_words(arg.a_ptr, arg.a_len * 4) }; + let mut d: Vec = unsafe { read_u64_words(arg.modulus_ptr, arg.modulus_len * 4) }; + + ruint::algorithms::div(&mut n, &mut d); + + ArithmeticQueryOutput { + quotient: n, + remainder: d, + } + .into_usize_iterator() } } diff --git a/callable_oracles/src/blob_kzg_commitment/mod.rs b/callable_oracles/src/blob_kzg_commitment/mod.rs index 412e61545..6d329d58e 100644 --- a/callable_oracles/src/blob_kzg_commitment/mod.rs +++ b/callable_oracles/src/blob_kzg_commitment/mod.rs @@ -63,6 +63,58 @@ impl OracleQueryProcessor for BlobCommitmentAndProofQuery } } +/// Query processor to be used for prover input native run +/// Works in a similar way as the NativeBlobCommitmentAndProof, but with +/// 64 bit pointers. Importantly, the query response is the +/// same. +/// +/// This processor explicitly reads the process memory +/// using a raw pointer to get the input. +pub struct NativeBlobCommitmentAndProofQuery { + _marker: std::marker::PhantomData, +} + +impl Default for NativeBlobCommitmentAndProofQuery { + fn default() -> Self { + Self { + _marker: std::marker::PhantomData, + } + } +} + +impl OracleQueryProcessor for NativeBlobCommitmentAndProofQuery { + fn supported_query_ids(&self) -> Vec { + vec![BLOB_COMMITMENT_AND_PROOF_QUERY_ID] + } + + fn process_buffered_query( + &mut self, + query_id: u32, + query: Vec, + _memory: &M, + ) -> Box + 'static> { + debug_assert!(self.supports_query_id(query_id)); + + // this query processor supposed to work only on "host" architecture, which is always 64 bit + const { assert!(8 == core::mem::size_of::()) }; + let mut it = query.into_iter(); + + let data_ptr = it.next().unwrap(); + let data_len = it.next().unwrap(); + assert!( + it.next().is_none(), + "A single RISC-V ptr should've been passed." + ); + let data = unsafe { crate::read_u8_words(data_ptr as u64, data_len as u64) }; + let result = blob_kzg_commitment_and_proof(&data); + + let r = result.iter().collect::>(); + let r = Vec::into_boxed_slice(r); + let n = UsizeSliceIteratorOwned::new(r); + Box::new(n) + } +} + /// /// Calculate kzg commitment and proof at the point `blake2s(versioned_hash & data)` for blob created from passed data. /// diff --git a/callable_oracles/src/lib.rs b/callable_oracles/src/lib.rs index bd63748d2..8c834c0bd 100644 --- a/callable_oracles/src/lib.rs +++ b/callable_oracles/src/lib.rs @@ -59,3 +59,23 @@ impl UsizeDeserializable for MemoryRegionDescriptionParams { Ok(new) } } + +#[inline(always)] +unsafe fn read_u64_words(ptr_u64: u64, len_words_u64: u64) -> Vec { + if ptr_u64 == 0 || len_words_u64 == 0 { + return vec![]; + } + let addr = ptr_u64 as usize; + let len_words = len_words_u64 as usize; + core::slice::from_raw_parts(addr as *const u64, len_words).to_vec() +} + +#[inline(always)] +unsafe fn read_u8_words(ptr_u64: u64, len_words_u8: u64) -> Vec { + if ptr_u64 == 0 || len_words_u8 == 0 { + return vec![]; + } + let addr = ptr_u64 as usize; + let len_words = len_words_u8 as usize; + core::slice::from_raw_parts(addr as *const u8, len_words).to_vec() +} diff --git a/cycle_marker/src/lib.rs b/cycle_marker/src/lib.rs index a08909f01..13f8b9df9 100644 --- a/cycle_marker/src/lib.rs +++ b/cycle_marker/src/lib.rs @@ -158,6 +158,74 @@ macro_rules! wrap_with_resources { }}; } +// Snapshotting mechanism, used for tests +// We run multiple native runs of the program, so labels can be duplicated. +// This is a way to ignore some of those side effects. +#[cfg(not(target_arch = "riscv32"))] +pub struct Snapshot { + labels_len: usize, + #[cfg(feature = "log_to_file")] + file_len: u64, +} + +#[cfg(target_arch = "riscv32")] +pub struct Snapshot; + +#[cfg(not(target_arch = "riscv32"))] +pub fn snapshot() -> Snapshot { + let labels_len = LABELS.with(|l| l.borrow().len()); + + #[cfg(feature = "log_to_file")] + let file_len = MARKER_FILE.with(|f| { + use std::io::Seek; + use std::io::SeekFrom; + + let mut file = f.borrow_mut(); + // Get current position; writing always appends, so this is effectively the "index" + file.seek(SeekFrom::Current(0)) + .expect("Failed to seek marker file") + }); + + Snapshot { + labels_len, + #[cfg(feature = "log_to_file")] + file_len, + } +} + +#[cfg(target_arch = "riscv32")] +pub fn snapshot() -> Snapshot { + Snapshot +} + +#[cfg(not(target_arch = "riscv32"))] +pub fn revert(snap: Snapshot) { + // Restore LABELS length + LABELS.with(|l| { + let mut v = l.borrow_mut(); + if v.len() > snap.labels_len { + v.truncate(snap.labels_len); + } + }); + + // Restore file length/position if logging to file + #[cfg(feature = "log_to_file")] + { + use std::io::{Seek, SeekFrom}; + + MARKER_FILE.with(|f| { + let mut file = f.borrow_mut(); + file.set_len(snap.file_len) + .expect("Failed to truncate marker file"); + file.seek(SeekFrom::Start(snap.file_len)) + .expect("Failed to seek marker file"); + }); + } +} + +#[cfg(target_arch = "riscv32")] +pub fn revert(_: Snapshot) {} + #[cfg(all(feature = "use_risc_v_simulator", not(target_arch = "riscv32")))] pub fn print_cycle_markers() -> Option { const BLAKE_DELEGATION_ID: u32 = 1991; diff --git a/docs/l1_integration.md b/docs/l1_integration.md index 0a20c18b4..1f90398cb 100644 --- a/docs/l1_integration.md +++ b/docs/l1_integration.md @@ -137,14 +137,42 @@ Pubdata is the data we should publish with the chain DA, for rollups it should b Also, in the future, it can be a third-party DA solution such as Celestia, Avail, etc. Or validiums shouldn't post any data at all. Pubdata includes data needed to restore the full chain state(everything under commitment mentioned in the public input section) and data for messaging. -Now it includes(for each block): -- pubdata encoding version (currently 0x01) + +The first byte of pubdata is reserved for versioning. Current version is 2. + +### Version 1 + +For every block, the pubdata is: + +- pubdata encoding version (0x01) - block hash - block timestamp (8 bytes, BE) -- state diffs, for the current storage model it's: +- state diffs: - compressed diffs for contracts storage slots changed during block execution - accounts changes(nonce, balance, bytecode) - l2 -> l1 logs - l2 -> l1 messages Block number can be derived from pubdata implicitly. + +### Version 2 + +For every block, the pubdata is: + +- pubdata encoding version (0x02) +- block hash +- block timestamp (8 bytes, BE) +- state diffs: + - header: + - total_diffs (4 bytes, BE) + - initial account writes (4 bytes, BE): number of new accounts written to, + - initial slot writes (4 bytes, BE): number of new storage slots written to (not including those used to store account properties) + - repeated write index encoding length (1 byte): fixed width (in the range 0..=8 for indices used in repeated writes). + - diffs body: + - initial account writes: each write has a 20 byte address followed by account changes (nonce, balance, bytecode) + - initial contract storage slot writes: each write has a 32 byte key followed by the compressed value diff + - repeated writes: each write has an index in the tree (encoded using the fixed length from the header in BE) and the diff for either slot or account diff +- l2 -> l1 logs +- l2 -> l1 messages + +Block number can be derived from pubdata implicitly. diff --git a/forward_system/src/run/convert.rs b/forward_system/src/run/convert.rs index 381ead5be..c346414c0 100644 --- a/forward_system/src/run/convert.rs +++ b/forward_system/src/run/convert.rs @@ -30,6 +30,8 @@ impl FromInterface for BlockMetadataFromOracle { gas_limit: value.gas_limit, pubdata_limit: value.pubdata_limit, mix_hash: value.mix_hash, + // TODO: add this to interface + repeated_write_index_encoding_length: 5, } } } diff --git a/forward_system/src/run/mod.rs b/forward_system/src/run/mod.rs index 26339875a..8664ffe25 100644 --- a/forward_system/src/run/mod.rs +++ b/forward_system/src/run/mod.rs @@ -21,17 +21,21 @@ use crate::run::query_processors::ZKProofDataResponder; use crate::run::query_processors::{BlockMetadataResponder, DACommitmentSchemeResponder}; use crate::run::result_keeper::ForwardRunningResultKeeper; use crate::system::bootloader::run_forward; +use crate::system::bootloader::run_prover_input_no_panic; use crate::system::system::CallSimulationBootloader; use crate::system::system::CallSimulationSystem; use crate::system::system::ForwardRunningSystem; use basic_bootloader::bootloader::config::{ BasicBootloaderCallSimulationConfig, BasicBootloaderForwardSimulationConfig, + BasicBootloaderProvingExecutionConfig, }; use errors::ForwardSubsystemError; use oracle_provider::MemorySource; use oracle_provider::ReadWitnessSource; use oracle_provider::ZkEENonDeterminismSource; +use result_keeper::ProverInputResultKeeper; use zk_ee::common_structs::ProofData; +use zk_ee::system::tracer::NopTracer; use zk_ee::system::tracer::Tracer; use zk_ee::utils::Bytes32; @@ -45,7 +49,6 @@ pub use preimage_source::PreimageSource; use zk_ee::wrap_error; use zksync_os_interface::traits::EncodedTx; -use std::path::PathBuf; pub use tx_result_callback::TxResultCallback; pub use tx_source::NextTxResponse; pub use tx_source::TxSource; @@ -94,16 +97,21 @@ pub fn run_block( - zk_os_program_path: PathBuf, +// Returns (prover_input, block_output, pubdata) +pub fn generate_proof_input< + T: ReadStorageTree, + PS: PreimageSource, + TS: TxSource, + TR: TxResultCallback, +>( block_context: BlockContext, proof_data: ProofData, da_commitment_scheme: DACommitmentScheme, tree: T, preimage_source: PS, tx_source: TS, -) -> Result, ForwardSubsystemError> { + tx_result_callback: TR, +) -> Result<(Vec, BlockOutput, Vec), ForwardSubsystemError> { let block_metadata_responder = BlockMetadataResponder { block_metadata: block_context, }; @@ -129,19 +137,25 @@ pub fn generate_proof_input( + copy_source, + &mut result_keeper, + &mut tracer, + ) + .map_err(|e| wrap_error!(e))?; + // Take pubdata, as it's not part of BlockOutput + let pubdata = std::mem::take(&mut result_keeper.pubdata); + + Ok((prover_input, result_keeper.into(), pubdata)) } // TODO(EVM-1184): in future we should generate input per batch @@ -220,6 +234,7 @@ pub fn make_oracle_for_proofs_and_dumps< proof_data: Option>, da_commitment_scheme: Option, add_uart: bool, + use_native_callable_oracles: bool, ) -> ZkEENonDeterminismSource { make_oracle_for_proofs_and_dumps_for_init_data( block_context, @@ -229,6 +244,7 @@ pub fn make_oracle_for_proofs_and_dumps< proof_data, da_commitment_scheme, add_uart, + use_native_callable_oracles, ) } @@ -245,6 +261,7 @@ pub fn make_oracle_for_proofs_and_dumps_for_init_data< proof_data: Option>, da_commitment_scheme: Option, add_uart: bool, + use_native_callable_oracles: bool, ) -> ZkEENonDeterminismSource { let block_metadata_responder = BlockMetadataResponder { block_metadata: block_context, @@ -269,10 +286,18 @@ pub fn make_oracle_for_proofs_and_dumps_for_init_data< oracle.add_external_processor(tree_responder); oracle.add_external_processor(zk_proof_data_responder); oracle.add_external_processor(da_commitment_scheme_responder); - oracle.add_external_processor(callable_oracles::arithmetic::ArithmeticQuery::default()); - oracle.add_external_processor( - callable_oracles::blob_kzg_commitment::BlobCommitmentAndProofQuery::default(), - ); + if use_native_callable_oracles { + oracle + .add_external_processor(callable_oracles::arithmetic::NativeArithmeticQuery::default()); + oracle.add_external_processor( + callable_oracles::blob_kzg_commitment::NativeBlobCommitmentAndProofQuery::default(), + ); + } else { + oracle.add_external_processor(callable_oracles::arithmetic::ArithmeticQuery::default()); + oracle.add_external_processor( + callable_oracles::blob_kzg_commitment::BlobCommitmentAndProofQuery::default(), + ); + } if add_uart { let uart_responder = UARTPrintResponder; diff --git a/forward_system/src/run/output.rs b/forward_system/src/run/output.rs index b483a9dcd..617ad68e6 100644 --- a/forward_system/src/run/output.rs +++ b/forward_system/src/run/output.rs @@ -24,6 +24,8 @@ use zk_ee::types_config::EthereumIOTypesConfig; pub use zksync_os_interface::types::BlockOutput; use zksync_os_interface::types::L2ToL1LogWithPreimage; +use super::result_keeper::ProverInputResultKeeper; + pub type TxResult = Result; /// Extension trait to create `StorageWrite` from components. @@ -53,7 +55,6 @@ impl From> for BlockOutput storage_writes, tx_results, new_preimages, - pubdata, .. } = value; @@ -134,12 +135,17 @@ impl From> for BlockOutput storage_writes, account_diffs, published_preimages, - pubdata, computaional_native_used: block_computaional_native_used, } } } +impl From> for BlockOutput { + fn from(value: ProverInputResultKeeper) -> Self { + BlockOutput::from(value.forward_running_rk) + } +} + /// Extract account diffs from a BlockOutput. /// /// This method processes the published preimages and storage writes to extract diff --git a/forward_system/src/run/result_keeper.rs b/forward_system/src/run/result_keeper.rs index 00dbe314e..8e81d6e46 100644 --- a/forward_system/src/run/result_keeper.rs +++ b/forward_system/src/run/result_keeper.rs @@ -24,8 +24,6 @@ pub struct ForwardRunningResultKeeper { Result, >, pub new_preimages: Vec<(Bytes32, Vec, PreimageType)>, - pub pubdata: Vec, - pub tx_result_callback: TR, } @@ -38,7 +36,6 @@ impl ForwardRunningResultKeeper { storage_writes: vec![], tx_results: vec![], new_preimages: vec![], - pubdata: vec![], tx_result_callback, } } @@ -50,7 +47,7 @@ impl IOResultKeeper fn events<'a>( &mut self, iter: impl Iterator< - Item = GenericEventContentWithTxRef<'a, { MAX_EVENT_TOPICS }, EthereumIOTypesConfig>, + Item = GenericEventContentWithTxRef<'a, MAX_EVENT_TOPICS, EthereumIOTypesConfig>, >, ) { self.events = iter @@ -84,10 +81,6 @@ impl IOResultKeeper .map(|(hash, preimage, preimage_type)| (*hash, preimage.to_vec(), preimage_type)) .collect(); } - - fn pubdata(&mut self, value: &[u8]) { - self.pubdata.extend_from_slice(value); - } } impl ResultKeeperExt for ForwardRunningResultKeeper { @@ -123,3 +116,84 @@ impl ResultKeeperExt for ForwardRunningResultKeeper { .sum() } } + +/// +/// Result keeper for prover input run. +/// Adds pubdata to ForwardRunningResultKeeper +/// +pub struct ProverInputResultKeeper { + pub forward_running_rk: ForwardRunningResultKeeper, + pub pubdata: Vec, +} + +impl ProverInputResultKeeper { + pub fn new(tx_result_callback: TR) -> Self { + Self { + forward_running_rk: ForwardRunningResultKeeper::new(tx_result_callback), + pubdata: vec![], + } + } +} + +// Delegate to ForwardRunningResultKeeper, except for pubdata +impl IOResultKeeper for ProverInputResultKeeper { + fn events<'a>( + &mut self, + iter: impl Iterator< + Item = GenericEventContentWithTxRef<'a, MAX_EVENT_TOPICS, EthereumIOTypesConfig>, + >, + ) { + // Have to reimplement, as delegating it causes an ICE... + self.forward_running_rk.events = iter + .map(|e| GenericEventContent { + tx_number: e.tx_number, + address: *e.address, + topics: e.topics.clone(), + data: UsizeAlignedByteBox::from_slice_in(e.data, Global), + }) + .collect(); + } + + fn logs<'a>( + &mut self, + iter: impl Iterator>, + ) { + self.forward_running_rk.logs(iter) + } + + fn storage_diffs(&mut self, iter: impl Iterator) { + self.forward_running_rk.storage_diffs(iter) + } + + fn new_preimages<'a>( + &mut self, + iter: impl Iterator, + ) { + self.forward_running_rk.new_preimages(iter) + } + + fn pubdata(&mut self, value: &[u8]) { + self.pubdata.extend_from_slice(value); + } +} + +// Delegate to ForwardRunningResultKeeper +impl ResultKeeperExt for ProverInputResultKeeper { + fn tx_processed( + &mut self, + tx_result: Result< + TxProcessingOutput, + basic_bootloader::bootloader::errors::InvalidTransaction, + >, + ) { + self.forward_running_rk.tx_processed(tx_result) + } + + fn block_sealed(&mut self, block_header: BlockHeader) { + self.forward_running_rk.block_sealed(block_header) + } + + fn get_gas_used(&self) -> u64 { + self.forward_running_rk.get_gas_used() + } +} diff --git a/forward_system/src/system/bootloader.rs b/forward_system/src/system/bootloader.rs index c13bd7fb6..2aaab25c8 100644 --- a/forward_system/src/system/bootloader.rs +++ b/forward_system/src/system/bootloader.rs @@ -3,6 +3,7 @@ use basic_bootloader::bootloader::config::BasicBootloaderExecutionConfig; use basic_bootloader::bootloader::errors::BootloaderSubsystemError; use basic_bootloader::bootloader::result_keeper::ResultKeeperExt; use oracle_provider::DummyMemorySource; +use oracle_provider::ReadWitnessSource; use oracle_provider::ZkEENonDeterminismSource; use zk_ee::system::tracer::Tracer; @@ -27,3 +28,12 @@ pub fn run_forward_no_panic( ) -> Result<(), BootloaderSubsystemError> { ForwardBootloader::run_prepared::(oracle, result_keeper, tracer).map(|_| ()) } + +pub fn run_prover_input_no_panic( + oracle: ReadWitnessSource, + result_keeper: &mut impl ResultKeeperExt, + tracer: &mut impl Tracer, +) -> Result, BootloaderSubsystemError> { + ProverInputBootloader::run_prepared::(oracle, result_keeper, tracer) + .map(|o| o.0.get_read_items().borrow().clone()) +} diff --git a/forward_system/src/system/system.rs b/forward_system/src/system/system.rs index 428c478f2..df90a5c12 100644 --- a/forward_system/src/system/system.rs +++ b/forward_system/src/system/system.rs @@ -19,11 +19,11 @@ type Logger = crate::system::logger::StdIOLogger; #[cfg(feature = "no_print")] type Logger = zk_ee::system::NullLogger; -pub struct ForwardSystemTypes(O); +pub struct ForwardSystemTypes(O); type Native = zk_ee::reference_implementations::DecreasingNative; -impl SystemTypes for ForwardSystemTypes { +impl SystemTypes for ForwardSystemTypes { type IOTypes = EthereumIOTypesConfig; type Resources = BaseResources; type IO = FullIO< @@ -33,21 +33,29 @@ impl SystemTypes for ForwardSystemTypes { VecStackFactory, 0, O, - false, + PROOF_ENV, >; - type SystemFunctions = NoStdSystemFunctions; - type SystemFunctionsExt = NoStdSystemFunctions; + // For PROOF_ENV=true, we can enable delegation-based modexp to capture prover input. + type SystemFunctions = NoStdSystemFunctions; + type SystemFunctionsExt = NoStdSystemFunctions; type Allocator = Global; type Logger = Logger; type Metadata = zk_ee::system::metadata::zk_metadata::ZkMetadata; } -impl EthereumLikeTypes for ForwardSystemTypes {} +impl EthereumLikeTypes for ForwardSystemTypes {} -pub type ForwardRunningSystem = ForwardSystemTypes>; +pub type ForwardRunningSystem = + ForwardSystemTypes, false>; -pub type CallSimulationSystem = ForwardSystemTypes>; +pub type CallSimulationSystem = + ForwardSystemTypes, false>; + +pub type ProverInputSystem = + ForwardSystemTypes, true>; pub type ForwardBootloader = BasicBootloader; pub type CallSimulationBootloader = BasicBootloader; + +pub type ProverInputBootloader = BasicBootloader; diff --git a/oracle_provider/src/lib.rs b/oracle_provider/src/lib.rs index 5d3308baf..17af59550 100644 --- a/oracle_provider/src/lib.rs +++ b/oracle_provider/src/lib.rs @@ -322,3 +322,42 @@ impl NonDeterminismCSRSource for ReadWitnessSource { self.original_source.write_with_memory_access(memory, value); } } + +impl IOOracle for ReadWitnessSource { + type RawIterator<'a> = + as IOOracle>::RawIterator<'a>; + + fn raw_query<'a, I>( + &'a mut self, + query_type: u32, + input: &I, + ) -> Result, InternalError> + where + I: UsizeSerializable + UsizeDeserializable, + { + let inner = self.original_source.raw_query(query_type, input)?; + // First add the length of the iterator. + let len = inner.len(); + { + let mut read_items = self.read_items.borrow_mut(); + // Len is multiplied by 2 to account for 32/64-bit mismatch + let len_u32 = u32::try_from(len * 2).expect("iterator length does not fit into u32"); + read_items.push(len_u32); + } + let read_items = Rc::clone(&self.read_items); + let wrapped: Self::RawIterator<'a> = Box::new(inner.inspect(move |v| { + record_usize_as_u32_words(&mut read_items.borrow_mut(), *v); + })); + + Ok(wrapped) + } +} + +fn record_usize_as_u32_words(dst: &mut Vec, value: usize) { + { + let v = value as u64; + // LE + dst.push((v & 0xFFFF_FFFF) as u32); + dst.push((v >> 32) as u32); + } +} diff --git a/proof_running_system/src/system/bootloader.rs b/proof_running_system/src/system/bootloader.rs index 07698a0f5..df8300802 100644 --- a/proof_running_system/src/system/bootloader.rs +++ b/proof_running_system/src/system/bootloader.rs @@ -5,7 +5,6 @@ use basic_bootloader::bootloader::config::BasicBootloaderProvingExecutionConfig; use core::alloc::Allocator; use core::mem::MaybeUninit; use zk_ee::memory::ZSTAllocator; -use zk_ee::oracle::query_ids::DISCONNECT_ORACLE_QUERY_ID; use zk_ee::oracle::IOOracle; use zk_ee::system::tracer::NopTracer; use zk_ee::system::{logger::Logger, NopResultKeeper}; @@ -179,17 +178,13 @@ pub fn run_proving_inner< let _ = L::default().write_fmt(format_args!("IO implementer init is complete")); // Load all transactions from oracle and apply them. - let (mut oracle, public_input) = ProvingBootloader::::run_prepared::< - BasicBootloaderProvingExecutionConfig, - >(oracle, &mut NopResultKeeper, &mut NopTracer::default()) - .expect("Tried to prove a failing batch"); - - // disconnect oracle before returning, if some other post-logic is needed that doesn't use Oracle trait - // TODO: check this is the intended behaviour (ignoring the result) - #[allow(unused_must_use)] - oracle - .raw_query_with_empty_input(DISCONNECT_ORACLE_QUERY_ID) - .expect("must disconnect an oracle before performing arbitrary CSR access"); + let (_oracle, public_input, _batch_output) = + ProvingBootloader::::run_prepared::( + oracle, + &mut NopResultKeeper, + &mut NopTracer::default(), + ) + .expect("Tried to prove a failing batch"); unsafe { core::mem::transmute(public_input) } } @@ -224,12 +219,6 @@ pub fn run_proving_inner< upgrade_tx_hash, &mut batch_pi_builder, ); - // we do this query for consistency with block based input generation(there is empty iterator as response to this query) - // but during proving this request shouldn't have the effect with "u32 array based" oracle - #[allow(unused_must_use)] - oracle - .raw_query_with_empty_input(DISCONNECT_ORACLE_QUERY_ID) - .expect("must disconnect an oracle before performing arbitrary CSR access"); } unsafe { diff --git a/proof_running_system/src/system/mod.rs b/proof_running_system/src/system/mod.rs index c2aa4d8f4..9c845e2a8 100644 --- a/proof_running_system/src/system/mod.rs +++ b/proof_running_system/src/system/mod.rs @@ -42,8 +42,8 @@ impl SystemTypes for ProofRunningSystemTypes; - type SystemFunctions = NoStdSystemFunctions; - type SystemFunctionsExt = NoStdSystemFunctions; + type SystemFunctions = NoStdSystemFunctions; + type SystemFunctionsExt = NoStdSystemFunctions; type Allocator = BootloaderAllocator; type Logger = L; type Metadata = zk_ee::system::metadata::zk_metadata::ZkMetadata; diff --git a/storage_models/src/common_structs/traits/storage_model.rs b/storage_models/src/common_structs/traits/storage_model.rs index 7856db417..65b95f340 100644 --- a/storage_models/src/common_structs/traits/storage_model.rs +++ b/storage_models/src/common_structs/traits/storage_model.rs @@ -215,15 +215,15 @@ pub trait StorageModel: Sized + SnapshottableIo { fn construct(init_data: Self::InitData, allocator: Self::Allocator) -> Self; /// Get amount of pubdata needed to encode current tx diff in bytes. - fn pubdata_used_by_tx(&self) -> u32; + fn pubdata_used_by_tx(&self, repeated_write_index_encoding_length: u8) -> u32; /// Used for testing to compare state diffs between forwards and proving runs. fn finish_and_calculate_state_diffs_hash( self, oracle: &mut impl IOOracle, - state_commitment: Option<&mut Self::StorageCommitment>, - pubdata_dst: &mut T, + state_commitment_and_pubdata_dst: Option<(&mut Self::StorageCommitment, &mut T)>, result_keeper: &mut impl IOResultKeeper, + repeated_write_index_encoding_length: u8, logger: &mut impl Logger, ) -> Result; @@ -238,9 +238,9 @@ pub trait StorageModel: Sized + SnapshottableIo { fn finish( self, oracle: &mut impl IOOracle, // oracle is needed here to prove tree - state_commitment: Option<&mut Self::StorageCommitment>, - pubdata_dst: &mut T, + state_commitment_and_pubdata_dst: Option<(&mut Self::StorageCommitment, &mut T)>, result_keeper: &mut impl IOResultKeeper, + repeated_write_index_encoding_length: u8, logger: &mut impl Logger, ) -> Result<(), InternalError>; diff --git a/tests/evm_tester/Cargo.lock b/tests/evm_tester/Cargo.lock index ed066f6f1..6c842fdeb 100644 --- a/tests/evm_tester/Cargo.lock +++ b/tests/evm_tester/Cargo.lock @@ -8798,14 +8798,12 @@ dependencies = [ [[package]] name = "zksync_os_evm_errors" version = "0.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b3ad55a8aa3498e304565de55b61dc930bac227c8c5e81284b623c69f2143d2" +source = "git+https://github.com/matter-labs/zksync-os-interface?branch=alocascio-prover-input-gen#16f2306bb4c3b6cb7465f6809637661ff4f2f86f" [[package]] name = "zksync_os_interface" version = "0.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3476c481db1ea2db28ed8cbdfef391576288cb9e13241f7944274b72cf1e251e" +source = "git+https://github.com/matter-labs/zksync-os-interface?branch=alocascio-prover-input-gen#16f2306bb4c3b6cb7465f6809637661ff4f2f86f" dependencies = [ "alloy-consensus", "alloy-primitives", diff --git a/tests/fuzzer/fuzz/fuzz_targets/bootloader/common/mod.rs b/tests/fuzzer/fuzz/fuzz_targets/bootloader/common/mod.rs index 800cd1ccc..71788387e 100644 --- a/tests/fuzzer/fuzz/fuzz_targets/bootloader/common/mod.rs +++ b/tests/fuzzer/fuzz/fuzz_targets/bootloader/common/mod.rs @@ -214,6 +214,7 @@ pub fn mock_oracle() -> ( init_data, None, true, + false, ), ) } @@ -275,6 +276,7 @@ pub fn mock_oracle_balance( init_data, None, true, + false, ), ) } diff --git a/tests/fuzzer/fuzz/fuzz_targets/precompiles_diff/modexp.rs b/tests/fuzzer/fuzz/fuzz_targets/precompiles_diff/modexp.rs index 4c2727cc4..b8981df1a 100644 --- a/tests/fuzzer/fuzz/fuzz_targets/precompiles_diff/modexp.rs +++ b/tests/fuzzer/fuzz/fuzz_targets/precompiles_diff/modexp.rs @@ -1,18 +1,18 @@ #![no_main] #![feature(allocator_api)] +use arbitrary::{Arbitrary, Unstructured}; +use basic_system::system_functions::modexp::ModExpImpl; +use basic_system_proving::system_functions::modexp::advice::delegated_modexp_with_naive_advisor; use libfuzzer_sys::fuzz_target; use revm::primitives::U256; use revm_precompile::modexp::berlin_run; -use arbitrary::{Arbitrary, Unstructured}; -use zk_ee::system::Resource; -use basic_system_proving::system_functions::modexp::delegation::delegated_modexp_with_naive_advisor; -use zk_ee::system::base_system_functions::ModExpErrors; -use zk_ee::system::errors::subsystem::SubsystemError; -use basic_system::system_functions::modexp::ModExpImpl; use zk_ee::reference_implementations::BaseResources; -use zk_ee::system::{SystemFunction,SystemFunctionExt}; use zk_ee::reference_implementations::DecreasingNative; +use zk_ee::system::base_system_functions::ModExpErrors; +use zk_ee::system::errors::subsystem::SubsystemError; +use zk_ee::system::Resource; +use zk_ee::system::{SystemFunction, SystemFunctionExt}; #[derive(Arbitrary, Debug, Clone, Copy)] enum LenMode { @@ -63,8 +63,8 @@ struct Input { // Raw entropy to fill buffers base_seed: Vec, - exp_seed: Vec, - mod_seed: Vec, + exp_seed: Vec, + mod_seed: Vec, // Random lengths used when LenMode::Random bl_rand: u32, @@ -95,13 +95,17 @@ fn shape_exponent(mut exp: Vec, ek: ExpKind) -> Vec { ExpKind::Random => exp, ExpKind::Zero => vec![0u8; exp.len()], ExpKind::One => { - if exp.is_empty() { return exp; } + if exp.is_empty() { + return exp; + } let mut v = vec![0u8; exp.len()]; *v.last_mut().unwrap() = 1; v } ExpKind::Two => { - if exp.is_empty() { return exp; } + if exp.is_empty() { + return exp; + } let mut v = vec![0u8; exp.len()]; *v.last_mut().unwrap() = 2; v @@ -112,8 +116,8 @@ fn shape_exponent(mut exp: Vec, ek: ExpKind) -> Vec { } let mut v = vec![0u8; exp.len()]; let n = v.len(); - v[n-3] = 0x01; - v[n-1] = 0x01; + v[n - 3] = 0x01; + v[n - 1] = 0x01; v } } @@ -124,20 +128,26 @@ fn shape_modulus(mut m: Vec, mk: ModKind) -> Vec { ModKind::Random => m, ModKind::Zero => vec![0u8; m.len()], ModKind::One => { - if m.is_empty() { return m; } + if m.is_empty() { + return m; + } let mut v = vec![0u8; m.len()]; *v.last_mut().unwrap() = 1; v } ModKind::PowerOfTwo => { - if m.is_empty() { return m; } + if m.is_empty() { + return m; + } let mut v = vec![0u8; m.len()]; let bit = 1u8 << (v.len() as u8 % 8); *v.last_mut().unwrap() = bit.max(1); v } ModKind::Odd => { - if m.is_empty() { return m; } + if m.is_empty() { + return m; + } let mut v = m; *v.last_mut().unwrap() |= 1; v @@ -157,12 +167,12 @@ fn fill_len(u: &mut Unstructured<'_>, len: usize, seed: &[u8]) -> Vec { fn mutate_len(chosen: u32, mode: LenMode, rnd: u32) -> u32 { match mode { - LenMode::Exact => chosen, + LenMode::Exact => chosen, LenMode::OffByOneShort => chosen.saturating_sub(1), - LenMode::OffByOneLong => chosen.saturating_add(1).min(MAX_COMPONENT_LEN), - LenMode::MuchShorter => chosen / 2, - LenMode::MuchLonger => chosen.saturating_mul(2).min(MAX_COMPONENT_LEN), - LenMode::Random => rnd.min(MAX_DECL_LEN), + LenMode::OffByOneLong => chosen.saturating_add(1).min(MAX_COMPONENT_LEN), + LenMode::MuchShorter => chosen / 2, + LenMode::MuchLonger => chosen.saturating_mul(2).min(MAX_COMPONENT_LEN), + LenMode::Random => rnd.min(MAX_DECL_LEN), } } @@ -179,21 +189,21 @@ fn normalize_be(s: &[u8]) -> &[u8] { fn build_input_bytes( mut u: &mut Unstructured<'_>, - i: &Input + i: &Input, ) -> (Vec, Vec, Vec, Vec) { let bl = i.bl as usize; let el = i.el as usize; let ml = i.ml as usize; let base = fill_len(&mut u, bl, &i.base_seed); - let exp_raw = fill_len(&mut u, el, &i.exp_seed); - let mod_raw = fill_len(&mut u, ml, &i.mod_seed); + let exp_raw = fill_len(&mut u, el, &i.exp_seed); + let mod_raw = fill_len(&mut u, ml, &i.mod_seed); let mbl = mutate_len(bl as u32, i.bl_lm, i.bl_rand); let mel = mutate_len(el as u32, i.el_lm, i.el_rand); let mml = mutate_len(ml as u32, i.ml_lm, i.ml_rand); - let exp = shape_exponent(exp_raw.clone(), i.ek); + let exp = shape_exponent(exp_raw.clone(), i.ek); let modu = shape_modulus(mod_raw.clone(), i.mk); let mut out = Vec::with_capacity(96 + bl + el + ml); @@ -206,7 +216,9 @@ fn build_input_bytes( if let Some(t) = i.max_len { let t = t as usize; - if t < out.len() { out.truncate(t); } + if t < out.len() { + out.truncate(t); + } } (out, base, exp_raw, mod_raw) } @@ -244,7 +256,7 @@ fn fuzz(data: &[u8]) { pub fn modexp_forward(src: &[u8], dst: &mut Vec) -> Result<(), SubsystemError> { let allocator = std::alloc::Global; let mut resource = as Resource>::FORMAL_INFINITE; - ModExpImpl::execute( + ModExpImpl::::execute( &src, dst, &mut resource, @@ -264,11 +276,15 @@ struct DummyOracle {} impl zk_ee::oracle::IOOracle for DummyOracle { type RawIterator<'a> = Box + 'static>; - fn raw_query<'a, I: zk_ee::oracle::usize_serialization::UsizeSerializable + zk_ee::oracle::usize_serialization::UsizeDeserializable>( + fn raw_query< + 'a, + I: zk_ee::oracle::usize_serialization::UsizeSerializable + + zk_ee::oracle::usize_serialization::UsizeDeserializable, + >( &'a mut self, _query_type: u32, _input: &I, ) -> Result, zk_ee::system::errors::internal::InternalError> { unreachable!("oracle should not be consulted on native targets"); } -} \ No newline at end of file +} diff --git a/tests/fuzzer/fuzz/fuzz_targets/system_functions/modexp.rs b/tests/fuzzer/fuzz/fuzz_targets/system_functions/modexp.rs index 8bc59ec25..da934d203 100644 --- a/tests/fuzzer/fuzz/fuzz_targets/system_functions/modexp.rs +++ b/tests/fuzzer/fuzz/fuzz_targets/system_functions/modexp.rs @@ -97,7 +97,7 @@ fn fuzz(data: &[u8]) { let mut dst = dst.clone(); - let _ = ModExpImpl::execute( + let _ = ModExpImpl::::execute( &src.to_bytes().as_slice()[0..src.n], &mut dst, &mut resource, @@ -113,7 +113,11 @@ struct DummyOracle {} impl zk_ee::oracle::IOOracle for DummyOracle { type RawIterator<'a> = Box + 'static>; - fn raw_query<'a, I: zk_ee::oracle::usize_serialization::UsizeSerializable + zk_ee::oracle::usize_serialization::UsizeDeserializable>( + fn raw_query< + 'a, + I: zk_ee::oracle::usize_serialization::UsizeSerializable + + zk_ee::oracle::usize_serialization::UsizeDeserializable, + >( &'a mut self, _query_type: u32, _input: &I, @@ -125,4 +129,4 @@ impl zk_ee::oracle::IOOracle for DummyOracle { fuzz_target!(|data: &[u8]| { // call fuzzing in a separate function, so we can see its coverage fuzz(data); -}); \ No newline at end of file +}); diff --git a/tests/fuzzer/fuzz/fuzz_targets/system_functions/modexp_delegation.rs b/tests/fuzzer/fuzz/fuzz_targets/system_functions/modexp_delegation.rs index e8eac8008..e5f5f3033 100644 --- a/tests/fuzzer/fuzz/fuzz_targets/system_functions/modexp_delegation.rs +++ b/tests/fuzzer/fuzz/fuzz_targets/system_functions/modexp_delegation.rs @@ -2,7 +2,7 @@ #![feature(allocator_api)] use arbitrary::{Arbitrary, Unstructured}; -use basic_system::system_functions::modexp::delegation::delegated_modexp_with_naive_advisor; +use basic_system::system_functions::modexp::advice::delegated_modexp_with_naive_advisor; use libfuzzer_sys::fuzz_target; use ruint::aliases::U256; use std::alloc::Global; diff --git a/tests/instances/eth_runner/Cargo.lock b/tests/instances/eth_runner/Cargo.lock index 616ee269e..bc47e96a0 100644 --- a/tests/instances/eth_runner/Cargo.lock +++ b/tests/instances/eth_runner/Cargo.lock @@ -8670,14 +8670,12 @@ dependencies = [ [[package]] name = "zksync_os_evm_errors" version = "0.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b3ad55a8aa3498e304565de55b61dc930bac227c8c5e81284b623c69f2143d2" +source = "git+https://github.com/matter-labs/zksync-os-interface?branch=alocascio-prover-input-gen#16f2306bb4c3b6cb7465f6809637661ff4f2f86f" [[package]] name = "zksync_os_interface" version = "0.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3476c481db1ea2db28ed8cbdfef391576288cb9e13241f7944274b72cf1e251e" +source = "git+https://github.com/matter-labs/zksync-os-interface?branch=alocascio-prover-input-gen#16f2306bb4c3b6cb7465f6809637661ff4f2f86f" dependencies = [ "alloy-consensus", "alloy-primitives", diff --git a/tests/instances/header/src/lib.rs b/tests/instances/header/src/lib.rs index 0034bab80..aa4207480 100644 --- a/tests/instances/header/src/lib.rs +++ b/tests/instances/header/src/lib.rs @@ -25,7 +25,7 @@ fn test_block_header_invariants() { // TODO: enable when this is implemented // assert_ne!(header.transactions_root, Bytes32::ZERO); // assert_ne!(header.receipts_root, Bytes32::ZERO); - assert_eq!(header.number, 0); + assert_eq!(header.number, 1); assert_eq!(header.gas_limit, MAX_BLOCK_GAS_LIMIT); assert!( header.gas_used @@ -63,7 +63,7 @@ fn test_block_header_invariants() { // TODO: enable when this is implemented // assert_ne!(header.transactions_root, Bytes32::ZERO); // assert_ne!(header.receipts_root, Bytes32::ZERO); - assert_eq!(header.number, 1); + assert_eq!(header.number, 2); assert_eq!(header.gas_limit, gas_limit); assert!( header.gas_used diff --git a/tests/instances/multiblock_batch/src/lib.rs b/tests/instances/multiblock_batch/src/lib.rs index cc697336e..94b9a4833 100644 --- a/tests/instances/multiblock_batch/src/lib.rs +++ b/tests/instances/multiblock_batch/src/lib.rs @@ -88,10 +88,7 @@ fn run_multiblock_batch_proof_run(da_commitment_scheme: DACommitmentScheme) { let batch_input = generate_batch_proof_input( vec![block1_result.2.as_slice(), block2_result.2.as_slice()], da_commitment_scheme, - vec![ - block1_result.0.pubdata.as_slice(), - block2_result.0.pubdata.as_slice(), - ], + vec![block1_result.3.as_slice(), block2_result.3.as_slice()], ); let multinblock_program_path = PathBuf::from(std::env::var("CARGO_WORKSPACE_DIR").unwrap()) diff --git a/tests/instances/transactions/src/lib.rs b/tests/instances/transactions/src/lib.rs index a68e01de2..7ae666f7f 100644 --- a/tests/instances/transactions/src/lib.rs +++ b/tests/instances/transactions/src/lib.rs @@ -8,7 +8,7 @@ use alloy::signers::local::PrivateKeySigner; use rig::alloy::consensus::TxEip7702; use rig::alloy::primitives::{address, b256}; use rig::alloy::rpc::types::{AccessList, AccessListItem, TransactionRequest}; -use rig::basic_system::system_implementation::system::pubdata::PUBDATA_ENCODING_VERSION; +use rig::basic_system::system_implementation::system::pubdata::{self, PUBDATA_ENCODING_VERSION}; use rig::ruint::aliases::{B160, U256}; use rig::zksync_os_interface::error::InvalidTransaction; use rig::{alloy, zksync_web3_rs, Chain}; @@ -1319,11 +1319,12 @@ fn test_check_pubdata_encoding_version() { ..Default::default() }; // Check tx succeeds - let result = chain.run_block(vec![tx], Some(block_context), None, run_config()); + let (result, pubdata) = + chain.run_block_get_pubdata(vec![tx], Some(block_context), None, run_config()); let res0 = result.tx_results.first().expect("Must have a tx result"); assert!(res0.as_ref().is_ok(), "Tx should succeed"); - assert_eq!(result.pubdata[0], PUBDATA_ENCODING_VERSION); + assert_eq!(pubdata[0], PUBDATA_ENCODING_VERSION); } #[test] @@ -1363,12 +1364,13 @@ fn test_check_pubdata_has_timestamp() { ..Default::default() }; // Check tx succeeds - let result = chain.run_block(vec![tx], Some(block_context), None, run_config()); + let (result, pubdata) = + chain.run_block_get_pubdata(vec![tx], Some(block_context), None, run_config()); let res0 = result.tx_results.first().expect("Must have a tx result"); assert!(res0.as_ref().is_ok(), "Tx should succeed"); // Pubdata format is [VERSION(1)][BLOCK_HASH(32)][TIMESTAMP(8)][DIFFS...] - let pubdata_timestamp_bytes = &result.pubdata.as_slice()[33..41]; + let pubdata_timestamp_bytes = &pubdata.as_slice()[33..41]; let pubdata_timestamp = u64::from_be_bytes( pubdata_timestamp_bytes .try_into() diff --git a/tests/instances/unit/src/initial_slot_regression.rs b/tests/instances/unit/src/initial_slot_regression.rs index 642825c4a..e6cf66d14 100644 --- a/tests/instances/unit/src/initial_slot_regression.rs +++ b/tests/instances/unit/src/initial_slot_regression.rs @@ -133,6 +133,7 @@ impl TestingOracleFactory for InvalidInitialValueOracleFactory { proof_data: Option>>, da_commitment_scheme: Option, _add_uart: bool, + _use_native_callable_oracles: bool, ) -> ZkEENonDeterminismSource { // Create a malicious oracle manually instead of using the default factory let block_metadata_responder = BlockMetadataResponder { block_metadata }; diff --git a/tests/instances/unit/src/lib.rs b/tests/instances/unit/src/lib.rs index 5eed863f7..6a16f9d03 100644 --- a/tests/instances/unit/src/lib.rs +++ b/tests/instances/unit/src/lib.rs @@ -1,3 +1,4 @@ mod initial_slot_regression; mod kzg_blobs; +mod pubdata_format; mod tracer; diff --git a/tests/instances/unit/src/pubdata_format.rs b/tests/instances/unit/src/pubdata_format.rs new file mode 100644 index 000000000..8dde80be9 --- /dev/null +++ b/tests/instances/unit/src/pubdata_format.rs @@ -0,0 +1,232 @@ +#![cfg(test)] + +use rig::alloy::consensus::TxEip1559; +use rig::alloy::primitives::{address, TxKind}; +use rig::ruint::aliases::{B160, U256}; +use rig::BlockContext; +use rig::Chain; + +fn run_config() -> Option { + Some(rig::chain::RunConfig { + app: Some("for_tests".to_string()), + only_forward: false, + check_storage_diff_hashes: true, + ..Default::default() + }) +} + +struct PubdataParser { + pub pubdata: Vec, + index: usize, +} + +impl PubdataParser { + fn new(pubdata: Vec) -> Self { + Self { pubdata, index: 0 } + } + + // Generic reading function + fn read(&mut self, len: usize) -> &[u8] { + let slice = &self.pubdata[self.index..(self.index + len)]; + self.index += len; + slice + } + + fn read_byte(&mut self) -> u8 { + self.read(1)[0] + } + + fn read_u32(&mut self) -> u32 { + u32::from_be_bytes( + self.read(4) + .try_into() + .expect("Slice with incorrect length"), + ) + } + + fn read_u64(&mut self) -> u64 { + u64::from_be_bytes( + self.read(8) + .try_into() + .expect("Slice with incorrect length"), + ) + } + + fn read_u256(&mut self) -> U256 { + U256::from_be_bytes::<32>(self.read(32).try_into().unwrap()) + } + + fn read_address(&mut self) -> B160 { + B160::from_be_bytes::<20>(self.read(20).try_into().unwrap()) + } + + fn parse_and_validate_value_diff(&mut self, initial: U256, end: U256) { + let metadata = self.read_byte(); + + // Strategy 0: Nothing (full 32-byte value follows) + if metadata == 0u8 { + let full = self.read_u256(); + assert_eq!( + full, end, + "Nothing compression: decoded value does not match expected end" + ); + return; + } + + // Lower 3 bits = strategy, upper 5 bits = length in bytes + let strategy = metadata & 0b0000_0111; + let length = (metadata >> 3) as usize; + + // By construction, encoder only emits length < 32 for these strategies + assert!( + length < 32, + "Invalid length {length} in value diff metadata" + ); + + // Read the (possibly zero-length) payload + let mut buf = [0u8; 32]; + if length > 0 { + let bytes = self.read(length); + buf[32 - length..].copy_from_slice(bytes); + } + let value = U256::from_be_bytes::<32>(buf); + + match strategy { + // Add: end = initial + value + 1 => { + let decoded = initial + value; + assert_eq!( + decoded, end, + "Add compression: decoded value does not match expected end" + ); + } + // Sub: end = initial - value + 2 => { + let decoded = initial - value; + assert_eq!( + decoded, end, + "Sub compression: decoded value does not match expected end" + ); + } + // Transform: end = value (independent of initial) + 3 => { + assert_eq!( + value, end, + "Transform compression: decoded value does not match expected end" + ); + } + _ => { + panic!("Unknown value diff compression strategy {strategy}"); + } + } + } +} + +#[test] +fn test_check_pubdata_format_diffs() { + let mut chain = Chain::empty(None); + let wallet = chain.random_signer(); + let from = wallet.address(); + let target_address = address!("4242000000000000000000000000000000000000"); + + // Set balance for the contract address + chain.set_balance(B160::from_be_bytes(from.into_array()), U256::from(u64::MAX)); + let value = U256::from(42); + + let tx = { + let tx = TxEip1559 { + chain_id: 37u64, + nonce: 0, + max_fee_per_gas: 100_000, + max_priority_fee_per_gas: 100_000, + gas_limit: 75_000, + to: TxKind::Call(target_address), + value, + input: Default::default(), + access_list: Default::default(), + }; + rig::utils::sign_and_encode_alloy_tx(tx, &wallet) + }; + + let native_price = U256::from(100); + let pubdata_price = U256::from(2); + let timestamp: u64 = 42; + + let block_context = BlockContext { + native_price, + pubdata_price, + eip1559_basefee: U256::from(1), + timestamp, + ..Default::default() + }; + let coinbase = block_context.coinbase; + // Check tx succeeds + let (result, pubdata) = + chain.run_block_get_pubdata(vec![tx], Some(block_context), None, run_config()); + let res0 = result.tx_results.first().expect("Must have a tx result"); + assert!(res0.as_ref().is_ok(), "Tx should succeed"); + + // Helper to read pubdata + let mut parser = PubdataParser::new(pubdata); + + // Parse pubdata header + // Pubdata format is [VERSION(1)][BLOCK_HASH(32)][TIMESTAMP(8)][DIFFS...] + let pubdata_version = parser.read_byte(); + assert_eq!(pubdata_version, 2, "Pubdata version mismatch"); + let pubdata_block_hash: [u8; 32] = parser.read(32).to_vec().try_into().unwrap(); + assert_eq!( + result.header.hash().0, + pubdata_block_hash, + "Block hashes do not match" + ); + let pubdata_timestamp = parser.read_u64(); + assert_eq!(timestamp, pubdata_timestamp, "Timestamps do not match"); + + // Parse diffs header + // Diffs header is: [TOTAL_NB_DIFFS(4), NB_ACCOUNT_INITIAL_WRITES(4), NB_SLOT_INITIAL_WRITES(4), INDEX_LENGTH(1)] + let total_nb_diffs = parser.read_u32(); + // Diffs should be: + // - repeated write to sender + // - initial write to target + // - initial write to coinbase + assert_eq!(total_nb_diffs, 3, "Total number of diffs mismatch"); + let nb_account_initial_writes = parser.read_u32(); + assert_eq!( + nb_account_initial_writes, 2, + "Account initial writes mismatch" + ); + let nb_slot_initial_writes = parser.read_u32(); + assert_eq!(nb_slot_initial_writes, 0, "Slot initial writes mismatch"); + let index_length = parser.read_byte(); + assert_eq!(index_length, 5, "Index length mismatch"); + + // Parse diffs: + // initial account writes, initial slot writes (empty), repeated write + // First is coinbase write + let address = parser.read_address(); + assert_eq!(address, coinbase); + // metadata byte should be 0b00010100, as it's a balance change + let diff_metadata_byte = parser.read_byte(); + assert_eq!(diff_metadata_byte, 0b00010100); + let coinbase_balance_after = chain.get_account_properties(&coinbase).balance; + parser.parse_and_validate_value_diff(U256::ZERO, coinbase_balance_after); + // Second is target write + let address = parser.read_address(); + assert_eq!(address, B160::from_be_bytes(target_address.into_array())); + // metadata byte should be 0b00010100, as it's a balance change + let diff_metadata_byte = parser.read_byte(); + assert_eq!(diff_metadata_byte, 0b00010100); + parser.parse_and_validate_value_diff(U256::ZERO, value); + // Third is the repeated write for from + let _index = parser.read(index_length as usize); + // metadata byte should be 0b00011100, as it's a nonce and balance change + let diff_metadata_byte = parser.read_byte(); + assert_eq!(diff_metadata_byte, 0b00011100); + // nonce + parser.parse_and_validate_value_diff(U256::ZERO, U256::ONE); + // value + let from_balance_after = chain + .get_account_properties(&B160::from_be_bytes(from.into_array())) + .balance; + parser.parse_and_validate_value_diff(U256::from(u64::MAX), from_balance_after); +} diff --git a/tests/instances/unit/src/tracer/evm_opcodes_logger.rs b/tests/instances/unit/src/tracer/evm_opcodes_logger.rs index 61070a046..67e1d4338 100644 --- a/tests/instances/unit/src/tracer/evm_opcodes_logger.rs +++ b/tests/instances/unit/src/tracer/evm_opcodes_logger.rs @@ -47,7 +47,7 @@ fn run_chain_with_tracer( let result = chain.run_block_with_extra_stats(vec![encoded_tx], None, None, None, tracer); assert!(result.is_ok(), "Block execution should succeed"); - let (block_output, _, _) = result.unwrap(); + let (block_output, _, _, _) = result.unwrap(); assert!( block_output.tx_results[0].is_ok(), "Transaction should succeed. Result: {:?}", diff --git a/tests/instances/unit/src/tracer/mod.rs b/tests/instances/unit/src/tracer/mod.rs index f062ff0b5..6dff7a115 100644 --- a/tests/instances/unit/src/tracer/mod.rs +++ b/tests/instances/unit/src/tracer/mod.rs @@ -46,7 +46,7 @@ pub(crate) fn run_chain_with_tracer( let result = chain.run_block_with_extra_stats(vec![encoded_tx], None, None, None, tracer); assert!(result.is_ok(), "Block execution should succeed"); - let (block_output, _, _) = result.unwrap(); + let (block_output, _, _, _) = result.unwrap(); assert!( block_output.tx_results[0].is_ok(), "Transaction should succeed. Result: {:?}", diff --git a/tests/instances/unit/src/tracer/tracer_event_hook.rs b/tests/instances/unit/src/tracer/tracer_event_hook.rs index ff11dba25..a2de3a4f0 100644 --- a/tests/instances/unit/src/tracer/tracer_event_hook.rs +++ b/tests/instances/unit/src/tracer/tracer_event_hook.rs @@ -152,7 +152,7 @@ fn test_event_hook() { let result = chain.run_block_with_extra_stats(vec![encoded_tx], None, None, None, &mut tracer); assert!(result.is_ok(), "Block execution should succeed"); - let (block_output, _, _) = result.unwrap(); + let (block_output, _, _, _) = result.unwrap(); assert!( block_output.tx_results[0].is_ok(), "Transaction should succeed with correct tracer calls. Result: {:?}", diff --git a/tests/instances/unit/src/tracer/tracer_storage_hooks.rs b/tests/instances/unit/src/tracer/tracer_storage_hooks.rs index e66e205f2..b77d1ba08 100644 --- a/tests/instances/unit/src/tracer/tracer_storage_hooks.rs +++ b/tests/instances/unit/src/tracer/tracer_storage_hooks.rs @@ -159,7 +159,7 @@ fn test_storage_hooks() { let result = chain.run_block_with_extra_stats(vec![encoded_tx], None, None, None, &mut tracer); assert!(result.is_ok(), "Block execution should succeed"); - let (block_output, _, _) = result.unwrap(); + let (block_output, _, _, _) = result.unwrap(); assert!( block_output.tx_results[0].is_ok(), "Transaction should succeed with correct tracer calls. Result: {:?}", diff --git a/tests/rig/src/chain.rs b/tests/rig/src/chain.rs index 30e5428e9..bdde61f10 100644 --- a/tests/rig/src/chain.rs +++ b/tests/rig/src/chain.rs @@ -11,13 +11,14 @@ use basic_system::system_implementation::flat_storage_model::{ }; use ethers::signers::LocalWallet; use forward_system::run::result_keeper::ForwardRunningResultKeeper; +use forward_system::run::result_keeper::ProverInputResultKeeper; use forward_system::run::test_impl::{InMemoryPreimageSource, InMemoryTree, NoopTxCallback}; use forward_system::system::bootloader::run_forward_no_panic; +use forward_system::system::bootloader::run_prover_input_no_panic; use forward_system::system::system::ForwardRunningSystem; use log::{debug, info, trace}; use oracle_provider::MemorySource; use oracle_provider::{ReadWitnessSource, ZkEENonDeterminismSource}; -use risc_v_simulator::abstractions::memory::VectorMemoryImpl; use risc_v_simulator::sim::{DiagnosticsConfig, ProfilerConfig}; use ruint::aliases::{B160, B256, U256}; use std::collections::HashMap; @@ -47,6 +48,7 @@ pub trait TestingOracleFactory { proof_data: Option>>, da_commitment_scheme: Option, add_uart: bool, + use_native_callable_oracles: bool, ) -> ZkEENonDeterminismSource; } @@ -65,6 +67,7 @@ impl TestingOracleFactory proof_data: Option>>, da_commitment_scheme: Option, add_uart: bool, + use_native_callable_oracles: bool, ) -> ZkEENonDeterminismSource { forward_system::run::make_oracle_for_proofs_and_dumps( block_metadata, @@ -74,6 +77,7 @@ impl TestingOracleFactory proof_data, da_commitment_scheme, add_uart, + use_native_callable_oracles, ) } } @@ -85,7 +89,7 @@ pub struct Chain { state_tree: InMemoryTree, pub preimage_source: InMemoryPreimageSource, chain_id: u64, - previous_block_number: Option, + previous_block_number: u64, block_hashes: [U256; 256], block_timestamp: u64, } @@ -148,7 +152,7 @@ impl Chain { inner: HashMap::new(), }, chain_id: chain_id.unwrap_or(37), - previous_block_number: None, + previous_block_number: 0, block_hashes: [U256::ZERO; 256], block_timestamp: 0, } @@ -171,7 +175,7 @@ impl Chain { inner: HashMap::new(), }, chain_id: chain_id.unwrap_or(37), - previous_block_number: None, + previous_block_number: 0, block_hashes: [U256::ZERO; 256], block_timestamp: 0, } @@ -186,52 +190,17 @@ pub struct BlockExtraStats { impl Chain { pub fn set_last_block_number(&mut self, prev: u64) { - self.previous_block_number = Some(prev) + self.previous_block_number = prev } pub fn next_block_number(&self) -> u64 { - self.previous_block_number.map(|n| n + 1).unwrap_or(0) + self.previous_block_number + 1 } pub fn set_block_hashes(&mut self, block_hashes: [U256; 256]) { self.block_hashes = block_hashes } - /// TODO: duplicated from API, unify. - /// Runs a block in riscV - using zksync_os binary - and returns the - /// witness that can be passed to the prover subsystem. - pub fn run_block_generate_witness( - oracle: ZkEENonDeterminismSource, - app: &Option, - ) -> Vec { - // We'll wrap the source, to collect all the reads. - let copy_source = ReadWitnessSource::new(oracle); - let items = copy_source.get_read_items(); - // By default - enable diagnostics is false (which makes the test run faster). - let path = get_zksync_os_img_path(app); - - let diagnostics_config = if FLAMEGRAPH { - let mut profiler_config = ProfilerConfig::new("flamegraph.svg".into()); - profiler_config.frequency_recip = 10; - - Some(profiler_config).map(|cfg| { - let mut diagnostics_cfg = DiagnosticsConfig::new(get_zksync_os_sym_path(app)); - diagnostics_cfg.profiler_config = Some(cfg); - diagnostics_cfg - }) - } else { - None - }; - - let output = zksync_os_runner::run(path, diagnostics_config, 1 << 36, copy_source); - - // We return 0s in case of failure. - assert_ne!(output, [0u32; 8]); - - let result = items.borrow().clone(); - result - } - /// /// Simulate block, do not validate transactions /// @@ -253,6 +222,7 @@ impl Chain { gas_limit: block_context.gas_limit, pubdata_limit: block_context.pubdata_limit, mix_hash: block_context.mix_hash, + repeated_write_index_encoding_length: 5, }; let tx_source = TxListSource { transactions: transactions.into(), @@ -311,6 +281,25 @@ impl Chain { .0 } + pub fn run_block_get_pubdata( + &mut self, + transactions: Vec, + block_context: Option, + da_commitment_scheme: Option, + run_config: Option, + ) -> (BlockOutput, Vec) { + let (r, _, _, pubdata) = self + .run_block_with_extra_stats( + transactions, + block_context, + da_commitment_scheme, + run_config, + &mut NopTracer::default(), + ) + .unwrap(); + (r, pubdata) + } + /// /// Run block with given transactions, block context, and custom oracle factory. /// If block context is `None` default testing values will be used. @@ -365,7 +354,7 @@ impl Chain { da_commitment_scheme: Option, run_config: Option, tracer: &mut impl Tracer, - ) -> Result<(BlockOutput, BlockExtraStats, Vec), BootloaderSubsystemError> { + ) -> Result<(BlockOutput, BlockExtraStats, Vec, Vec), BootloaderSubsystemError> { let factory = DefaultOracleFactory::; self.run_inner( transactions, @@ -388,7 +377,7 @@ impl Chain { run_config: Option, tracer: &mut impl Tracer, oracle_factory: &OF, - ) -> Result<(BlockOutput, BlockExtraStats, Vec), BootloaderSubsystemError> { + ) -> Result<(BlockOutput, BlockExtraStats, Vec, Vec), BootloaderSubsystemError> { self.run_inner( transactions, block_context, @@ -408,7 +397,7 @@ impl Chain { run_config: RunConfig, oracle_factory: &OF, tracer: &mut impl Tracer, - ) -> Result<(BlockOutput, BlockExtraStats, Vec), BootloaderSubsystemError> { + ) -> Result<(BlockOutput, BlockExtraStats, Vec, Vec), BootloaderSubsystemError> { let RunConfig { profiler_config, witness_output_file, @@ -429,6 +418,7 @@ impl Chain { gas_limit: block_context.gas_limit, pubdata_limit: block_context.pubdata_limit, mix_hash: block_context.mix_hash, + repeated_write_index_encoding_length: 5, }; let state_commitment = FlatStorageCommitment::<{ TREE_HEIGHT }> { root: *self.state_tree.storage_tree.root(), @@ -452,6 +442,7 @@ impl Chain { Some(proof_data), Some(da_commitment_scheme), true, + false, ); let forward_oracle = oracle_factory.create_oracle( @@ -462,6 +453,18 @@ impl Chain { Some(proof_data), Some(da_commitment_scheme), true, + false, + ); + + let prover_input_oracle = oracle_factory.create_oracle( + block_metadata, + self.state_tree.clone(), + self.preimage_source.clone(), + tx_source.clone(), + Some(proof_data), + Some(da_commitment_scheme), + false, + true, ); #[cfg(feature = "simulate_witness_gen")] @@ -480,6 +483,9 @@ impl Chain { // forward run let mut result_keeper = ForwardRunningResultKeeper::new(NoopTxCallback); + // Avoid capturing markers from second round, it duplicate them + #[cfg(feature = "cycle_marker")] + let snapshot = cycle_marker::snapshot(); // we use proving config here for benchmarking, // although sequencer can have extra optimizations run_forward_no_panic::( @@ -487,8 +493,32 @@ impl Chain { &mut result_keeper, tracer, )?; + #[cfg(feature = "cycle_marker")] + cycle_marker::revert(snapshot); + + let mut result_keeper_prover_input = ProverInputResultKeeper::new(NoopTxCallback); + + let copy_source = ReadWitnessSource::new(prover_input_oracle); + let mut tracer = NopTracer::default(); + let prover_input_forward = run_prover_input_no_panic::< + BasicBootloaderProvingExecutionConfig, + >( + copy_source, &mut result_keeper_prover_input, &mut tracer + )?; + + if let Some(path) = witness_output_file { + let mut file = File::create(&path).expect("should create file"); + let witness: Vec = prover_input_forward + .iter() + .flat_map(|x| x.to_be_bytes()) + .collect(); + let hex = hex::encode(witness); + file.write_all(hex.as_bytes()) + .expect("should write to file"); + } let block_output: BlockOutput = result_keeper.into(); + let pubdata = result_keeper_prover_input.pubdata; trace!( "{}Block output:{} \n{:#?}", @@ -516,7 +546,7 @@ impl Chain { } // update state - self.previous_block_number = Some(self.next_block_number()); + self.previous_block_number = self.next_block_number(); self.block_timestamp = block_context.timestamp; for i in 0..255 { self.block_hashes[i] = self.block_hashes[i + 1]; @@ -539,105 +569,95 @@ impl Chain { } let proof_input = if !only_forward { - if let Some(path) = witness_output_file { - let result = Self::run_block_generate_witness::(oracle, &app); - let mut file = File::create(&path).expect("should create file"); - let witness: Vec = result.iter().flat_map(|x| x.to_be_bytes()).collect(); - let hex = hex::encode(witness); - file.write_all(hex.as_bytes()) - .expect("should write to file"); - result - } else { - // We'll wrap the source, to collect all the reads. - let copy_source = ReadWitnessSource::new(oracle); - let items = copy_source.get_read_items(); - - let diagnostics_config = profiler_config.map(|cfg| { - let mut diagnostics_cfg = DiagnosticsConfig::new(get_zksync_os_sym_path(&app)); - diagnostics_cfg.profiler_config = Some(cfg); - diagnostics_cfg - }); - - let now = std::time::Instant::now(); - let (proof_output, block_effective) = { - zksync_os_runner::run_and_get_effective_cycles( - get_zksync_os_img_path(&app), - diagnostics_config, - 1 << 36, - copy_source, - ) - }; + // We'll wrap the source, to collect all the reads. + let copy_source = ReadWitnessSource::new(oracle); + let items = copy_source.get_read_items(); - info!( - "Simulator without witness tracing executed over {:?}", - now.elapsed() - ); - stats.effective_used = block_effective; - - #[cfg(feature = "simulate_witness_gen")] - { - zksync_os_runner::simulate_witness_tracing( - get_zksync_os_img_path(), - source_for_witness_bench, - ) - } + let diagnostics_config = profiler_config.map(|cfg| { + let mut diagnostics_cfg = DiagnosticsConfig::new(get_zksync_os_sym_path(&app)); + diagnostics_cfg.profiler_config = Some(cfg); + diagnostics_cfg + }); + + let now = std::time::Instant::now(); + let (proof_output, block_effective) = { + zksync_os_runner::run_and_get_effective_cycles( + get_zksync_os_img_path(&app), + diagnostics_config, + 1 << 36, + copy_source, + ) + }; + + info!( + "Simulator without witness tracing executed over {:?}", + now.elapsed() + ); + stats.effective_used = block_effective; + + #[cfg(feature = "simulate_witness_gen")] + { + zksync_os_runner::simulate_witness_tracing( + get_zksync_os_img_path(), + source_for_witness_bench, + ) + } - // dump csr reads if env var set - if let Ok(output_csr) = std::env::var("CSR_READS_DUMP") { - // Save the read elements into a file - that can be later read with the tools/cli from zksync-airbender. - let mut file = - File::create(&output_csr).expect("Failed to create csr reads file"); - // Write each u32 as an 8-character hexadecimal string without newlines - for num in items.borrow().iter() { - write!(file, "{num:08X}").expect("Failed to write to file"); - } - debug!( - "Successfully wrote {} u32 csr reads elements to file: {}", - items.borrow().len(), - output_csr - ); + // dump csr reads if env var set + if let Ok(output_csr) = std::env::var("CSR_READS_DUMP") { + // Save the read elements into a file - that can be later read with the tools/cli from zksync-airbender. + let mut file = File::create(&output_csr).expect("Failed to create csr reads file"); + // Write each u32 as an 8-character hexadecimal string without newlines + for num in items.borrow().iter() { + write!(file, "{num:08X}").expect("Failed to write to file"); } - - let proof_input = items.borrow().iter().copied().collect::>(); - debug!( - "{}Proof running output{} = 0x", - colors::GREEN, - colors::RESET + "Successfully wrote {} u32 csr reads elements to file: {}", + items.borrow().len(), + output_csr ); - for word in proof_output.into_iter() { - debug!("{word:08x}"); - } + } - // Ensure that proof running didn't fail: check that output is not zero - assert!(proof_output.into_iter().any(|word| word != 0)); - let proof_output_u8: [u8; 32] = unsafe { core::mem::transmute(proof_output) }; - - if check_storage_diff_hashes { - // Also ensure that storage diff hash matches - use crypto::MiniDigest; - let mut hasher = crypto::blake2s::Blake2s256::new(); - for StorageWrite { key, value, .. } in block_output.storage_writes.iter() { - hasher.update(key.0.as_ref()); - hasher.update(value.0.as_ref()); - } - let forward_storage_diff_hash = hasher.finalize(); - info!( - "Forward storage diff hash: 0x{}", - hex::encode(forward_storage_diff_hash.as_ref()) - ); - assert_eq!(proof_output_u8, forward_storage_diff_hash); - - #[cfg(feature = "e2e_proving")] - run_prover(items.borrow().as_slice()); + let proof_input = items.borrow().iter().copied().collect::>(); + + debug!( + "{}Proof running output{} = 0x", + colors::GREEN, + colors::RESET + ); + for word in proof_output.into_iter() { + debug!("{word:08x}"); + } + + // Ensure that proof running didn't fail: check that output is not zero + assert!(proof_output.into_iter().any(|word| word != 0)); + let proof_output_u8: [u8; 32] = unsafe { core::mem::transmute(proof_output) }; + + if check_storage_diff_hashes { + // Also ensure that storage diff hash matches + use crypto::MiniDigest; + let mut hasher = crypto::blake2s::Blake2s256::new(); + for StorageWrite { key, value, .. } in block_output.storage_writes.iter() { + hasher.update(key.0.as_ref()); + hasher.update(value.0.as_ref()); } + let forward_storage_diff_hash = hasher.finalize(); + info!( + "Forward storage diff hash: 0x{}", + hex::encode(forward_storage_diff_hash.as_ref()) + ); + assert_eq!(proof_output_u8, forward_storage_diff_hash); - proof_input + #[cfg(feature = "e2e_proving")] + run_prover(items.borrow().as_slice()); } + + assert_eq!(prover_input_forward, proof_input); + proof_input } else { vec![] }; - Ok((block_output, stats, proof_input)) + Ok((block_output, stats, proof_input, pubdata)) } pub fn get_account_properties(&mut self, address: &B160) -> AccountProperties { diff --git a/tests/rig/src/testing_utils.rs b/tests/rig/src/testing_utils.rs index 4f04ff631..84f776c03 100644 --- a/tests/rig/src/testing_utils.rs +++ b/tests/rig/src/testing_utils.rs @@ -81,7 +81,7 @@ pub fn call_address_and_measure_gas_cost( let mut tracer = CallTracer::default(); - let (output, _, _) = chain + let (output, _, _, _) = chain .run_block_with_extra_stats(transactions, None, None, None, &mut tracer) .expect("Should succeed"); diff --git a/zk_ee/src/common_structs/state_root_view.rs b/zk_ee/src/common_structs/state_root_view.rs index b1d9dc6e7..61168dba6 100644 --- a/zk_ee/src/common_structs/state_root_view.rs +++ b/zk_ee/src/common_structs/state_root_view.rs @@ -1,7 +1,9 @@ use crate::oracle::usize_serialization::{UsizeDeserializable, UsizeSerializable}; use crate::system::errors::internal::InternalError; use crate::system::logger::Logger; +use crate::utils::Bytes32; use crate::{oracle::IOOracle, types_config::SystemIOTypesConfig}; +use alloc::collections::BTreeMap; use core::alloc::Allocator; #[derive(Clone, Copy, Debug)] @@ -30,5 +32,5 @@ pub trait StateRootView: source: impl Iterator + Clone, allocator: A, logger: &mut impl Logger, - ) -> Result<(), InternalError>; + ) -> Result, InternalError>; } diff --git a/zk_ee/src/system/io.rs b/zk_ee/src/system/io.rs index 079b05f26..d3d8ea078 100644 --- a/zk_ee/src/system/io.rs +++ b/zk_ee/src/system/io.rs @@ -125,7 +125,10 @@ pub trait IOSubsystem: Sized { DeconstructionSubsystemError, >; - fn net_pubdata_used(&self) -> Result; + fn net_pubdata_used( + &self, + repeated_write_index_encoding_length: u8, + ) -> Result; /// Starts a new "local" frame that does not track memory (like `near_call` in the EraVM). /// Returns a snapshot to which the system can rollback to on frame finish. diff --git a/zk_ee/src/system/metadata/basic_metadata.rs b/zk_ee/src/system/metadata/basic_metadata.rs index d21d81e09..2672646ff 100644 --- a/zk_ee/src/system/metadata/basic_metadata.rs +++ b/zk_ee/src/system/metadata/basic_metadata.rs @@ -82,6 +82,9 @@ pub trait ZkSpecificPricingMetadata { /// Price in base token of 1 byte of pubdata. fn get_pubdata_price(&self) -> U256; + + /// Length in bytes of the encoding of repeated write indices in storage diffs. + fn repeated_write_index_encoding_length(&self) -> u8; } /// Convenience super-trait for environments that expose both block- and tx-level diff --git a/zk_ee/src/system/metadata/system_metadata.rs b/zk_ee/src/system/metadata/system_metadata.rs index 9392086b9..6110bb556 100644 --- a/zk_ee/src/system/metadata/system_metadata.rs +++ b/zk_ee/src/system/metadata/system_metadata.rs @@ -100,6 +100,9 @@ impl< fn get_pubdata_price(&self) -> U256 { self.block_level.get_pubdata_price() } + fn repeated_write_index_encoding_length(&self) -> u8 { + self.block_level.repeated_write_index_encoding_length() + } } impl< diff --git a/zk_ee/src/system/metadata/zk_metadata.rs b/zk_ee/src/system/metadata/zk_metadata.rs index 9c8368c17..773c51c47 100644 --- a/zk_ee/src/system/metadata/zk_metadata.rs +++ b/zk_ee/src/system/metadata/zk_metadata.rs @@ -124,6 +124,8 @@ pub struct BlockMetadataFromOracle { /// Source of randomness, currently holds the value /// of prevRandao. pub mix_hash: U256, + // Length in bytes used to encode indices for repeated storage writes in the block. + pub repeated_write_index_encoding_length: u8, } impl BasicBlockMetadata for BlockMetadataFromOracle { @@ -195,6 +197,9 @@ impl ZkSpecificPricingMetadata for BlockMetadataFromOracle { fn get_pubdata_limit(&self) -> u64 { self.pubdata_limit } + fn repeated_write_index_encoding_length(&self) -> u8 { + self.repeated_write_index_encoding_length + } } impl BlockMetadataFromOracle { @@ -211,6 +216,7 @@ impl BlockMetadataFromOracle { coinbase: B160::ZERO, block_hashes: BlockHashes::default(), mix_hash: U256::ONE, + repeated_write_index_encoding_length: 4, } } } @@ -219,7 +225,8 @@ impl UsizeSerializable for BlockMetadataFromOracle { const USIZE_LEN: usize = ::USIZE_LEN * (4 + BLOCK_HASHES_WINDOW_SIZE) + ::USIZE_LEN * 5 - + ::USIZE_LEN; + + ::USIZE_LEN + + ::USIZE_LEN; fn iter(&self) -> impl ExactSizeIterator { ExactSizeChain::new( @@ -232,26 +239,29 @@ impl UsizeSerializable for BlockMetadataFromOracle { ExactSizeChain::new( ExactSizeChain::new( ExactSizeChain::new( - UsizeSerializable::iter(&self.eip1559_basefee), - UsizeSerializable::iter(&self.pubdata_price), + ExactSizeChain::new( + UsizeSerializable::iter(&self.eip1559_basefee), + UsizeSerializable::iter(&self.pubdata_price), + ), + UsizeSerializable::iter(&self.native_price), ), - UsizeSerializable::iter(&self.native_price), + UsizeSerializable::iter(&self.block_number), ), - UsizeSerializable::iter(&self.block_number), + UsizeSerializable::iter(&self.timestamp), ), - UsizeSerializable::iter(&self.timestamp), + UsizeSerializable::iter(&self.chain_id), ), - UsizeSerializable::iter(&self.chain_id), + UsizeSerializable::iter(&self.gas_limit), ), - UsizeSerializable::iter(&self.gas_limit), + UsizeSerializable::iter(&self.pubdata_limit), ), - UsizeSerializable::iter(&self.pubdata_limit), + UsizeSerializable::iter(&self.coinbase), ), - UsizeSerializable::iter(&self.coinbase), + UsizeSerializable::iter(&self.block_hashes), ), - UsizeSerializable::iter(&self.block_hashes), + UsizeSerializable::iter(&self.mix_hash), ), - UsizeSerializable::iter(&self.mix_hash), + UsizeSerializable::iter(&self.repeated_write_index_encoding_length), ) } } @@ -271,6 +281,7 @@ impl UsizeDeserializable for BlockMetadataFromOracle { let coinbase = UsizeDeserializable::from_iter(src)?; let block_hashes = UsizeDeserializable::from_iter(src)?; let mix_hash = UsizeDeserializable::from_iter(src)?; + let repeated_write_index_encoding_length = UsizeDeserializable::from_iter(src)?; let new = Self { eip1559_basefee, @@ -284,6 +295,7 @@ impl UsizeDeserializable for BlockMetadataFromOracle { coinbase, block_hashes, mix_hash, + repeated_write_index_encoding_length, }; Ok(new) diff --git a/zk_ee/src/system/mod.rs b/zk_ee/src/system/mod.rs index d38c27737..b169948a4 100644 --- a/zk_ee/src/system/mod.rs +++ b/zk_ee/src/system/mod.rs @@ -166,8 +166,12 @@ impl System { self.metadata.set_transaction_metadata(tx_level_metadata); } - pub fn net_pubdata_used(&self) -> Result { - self.io.net_pubdata_used() + pub fn net_pubdata_used( + &self, + repeated_write_index_encoding_length: u8, + ) -> Result { + self.io + .net_pubdata_used(repeated_write_index_encoding_length) } } @@ -186,6 +190,10 @@ where pub fn get_pubdata_price(&self) -> ruint::aliases::U256 { self.metadata.get_pubdata_price() } + + pub fn repeated_write_index_encoding_length(&self) -> u8 { + self.metadata.repeated_write_index_encoding_length() + } } impl System diff --git a/zksync_os/Cargo.lock b/zksync_os/Cargo.lock index 9b9bcfb9d..93d5cebc3 100644 --- a/zksync_os/Cargo.lock +++ b/zksync_os/Cargo.lock @@ -1115,5 +1115,4 @@ dependencies = [ [[package]] name = "zksync_os_evm_errors" version = "0.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b3ad55a8aa3498e304565de55b61dc930bac227c8c5e81284b623c69f2143d2" +source = "git+https://github.com/matter-labs/zksync-os-interface?branch=alocascio-prover-input-gen#16f2306bb4c3b6cb7465f6809637661ff4f2f86f"