diff --git a/core/lib/multivm/src/versions/vm_latest/old_vm/oracles/decommitter.rs b/core/lib/multivm/src/versions/vm_latest/old_vm/oracles/decommitter.rs index 85b18e203ce2..7c7dc6995d14 100644 --- a/core/lib/multivm/src/versions/vm_latest/old_vm/oracles/decommitter.rs +++ b/core/lib/multivm/src/versions/vm_latest/old_vm/oracles/decommitter.rs @@ -30,7 +30,7 @@ pub struct DecommitterOracle { /// Stores pages of memory where certain code hashes have already been decommitted. /// It is expected that they all are present in the DB. // `decommitted_code_hashes` history is necessary - pub decommitted_code_hashes: HistoryRecorder, HistoryEnabled>, + pub decommitted_code_hashes: HistoryRecorder>, HistoryEnabled>, /// Stores history of decommitment requests. decommitment_requests: HistoryRecorder, H>, } @@ -89,7 +89,7 @@ impl DecommitterOracle { pub fn get_decommitted_code_hashes_with_history( &self, - ) -> &HistoryRecorder, HistoryEnabled> { + ) -> &HistoryRecorder>, HistoryEnabled> { &self.decommitted_code_hashes } @@ -108,7 +108,7 @@ impl DecommitterOracle { .map(|(_, value)| value.len() * std::mem::size_of::()) .sum::(); let decommitted_code_hashes_size = - self.decommitted_code_hashes.inner().len() * std::mem::size_of::<(U256, u32)>(); + self.decommitted_code_hashes.inner().len() * std::mem::size_of::<(U256, Option)>(); known_bytecodes_size + decommitted_code_hashes_size } @@ -132,7 +132,7 @@ impl DecommitterOracle { ); let decommitted_code_hashes_size = self.decommitted_code_hashes.borrow_history(|h| h.len(), 0) - * std::mem::size_of::< as WithHistory>::HistoryRecord>(); + * std::mem::size_of::<> as WithHistory>::HistoryRecord>(); known_bytecodes_stack_size + known_bytecodes_heap_size + decommitted_code_hashes_size } @@ -172,6 +172,7 @@ impl DecommittmentProcess .inner() .get(&stored_hash) .copied() + .flatten() { partial_query.is_fresh = false; partial_query.memory_page = MemoryPage(memory_page); @@ -179,6 +180,8 @@ impl DecommittmentProcess Ok(partial_query) } else { partial_query.is_fresh = true; + self.decommitted_code_hashes + .insert(stored_hash, None, partial_query.timestamp); Ok(partial_query) } @@ -216,7 +219,7 @@ impl DecommittmentProcess rw_flag: true, }; self.decommitted_code_hashes - .insert(stored_hash, page_to_use.0, timestamp); + .insert(stored_hash, Some(page_to_use.0), timestamp); // Copy the bytecode (that is stored in 'values' Vec) into the memory page. if B { diff --git a/core/lib/multivm/src/versions/vm_latest/tests/get_used_contracts.rs b/core/lib/multivm/src/versions/vm_latest/tests/get_used_contracts.rs index 7bc08b6fb495..1798c700ea2d 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/get_used_contracts.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/get_used_contracts.rs @@ -1,6 +1,14 @@ -use std::collections::{HashMap, HashSet}; +use std::{ + collections::{HashMap, HashSet}, + str::FromStr, +}; use itertools::Itertools; +use zk_evm_1_5_0::{ + abstractions::DecommittmentProcessor, + aux_structures::{DecommittmentQuery, MemoryPage, Timestamp}, + zkevm_opcode_defs::{VersionedHashHeader, VersionedHashNormalizedPreimage}, +}; use zksync_state::WriteStorage; use zksync_system_constants::CONTRACT_DEPLOYER_ADDRESS; use zksync_test_account::Account; @@ -91,6 +99,47 @@ fn test_get_used_contracts() { } } +#[test] +fn test_contract_is_used_right_after_prepare_to_decommit() { + let mut vm = VmTesterBuilder::new(HistoryDisabled) + .with_empty_in_memory_storage() + .with_execution_mode(TxExecutionMode::VerifyExecute) + .build(); + + assert!(vm.vm.get_used_contracts().is_empty()); + + let bytecode_hash = + U256::from_str("0x100067ff3124f394104ab03481f7923f0bc4029a2aa9d41cc1d848c81257185") + .unwrap(); + vm.vm + .state + .decommittment_processor + .populate(vec![(bytecode_hash, vec![])], Timestamp(0)); + + let header = hex::decode("0100067f").unwrap(); + let normalized_preimage = + hex::decode("f3124f394104ab03481f7923f0bc4029a2aa9d41cc1d848c81257185").unwrap(); + vm.vm + .state + .decommittment_processor + .prepare_to_decommit( + 0, + DecommittmentQuery { + header: VersionedHashHeader(header.try_into().unwrap()), + normalized_preimage: VersionedHashNormalizedPreimage( + normalized_preimage.try_into().unwrap(), + ), + timestamp: Timestamp(0), + memory_page: MemoryPage(0), + decommitted_length: 0, + is_fresh: false, + }, + ) + .unwrap(); + + assert_eq!(vm.vm.get_used_contracts(), vec![bytecode_hash]); +} + fn known_bytecodes_without_aa_code( vm: &Vm, ) -> HashMap> { diff --git a/core/lib/multivm/src/versions/vm_latest/tests/tester/inner_state.rs b/core/lib/multivm/src/versions/vm_latest/tests/tester/inner_state.rs index 102822351366..2a6fead8cf9c 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/tester/inner_state.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/tester/inner_state.rs @@ -43,7 +43,7 @@ pub(crate) struct DecommitterTestInnerState { /// so we just compare the modified keys. This is reasonable enough. pub(crate) modified_storage_keys: ModifiedKeysMap, pub(crate) known_bytecodes: HistoryRecorder>, H>, - pub(crate) decommitted_code_hashes: HistoryRecorder, HistoryEnabled>, + pub(crate) decommitted_code_hashes: HistoryRecorder>, HistoryEnabled>, } #[derive(Clone, PartialEq, Debug)] diff --git a/core/lib/multivm/src/versions/vm_latest/tracers/circuits_tracer.rs b/core/lib/multivm/src/versions/vm_latest/tracers/circuits_tracer.rs index 7c3012d03f11..4d5dc0b13273 100644 --- a/core/lib/multivm/src/versions/vm_latest/tracers/circuits_tracer.rs +++ b/core/lib/multivm/src/versions/vm_latest/tracers/circuits_tracer.rs @@ -177,20 +177,21 @@ impl CircuitsTracer { .decommitted_code_hashes .history(); for (_, history_event) in &history[last_decommitment_history_entry_checked..] { - // We assume that only insertions may happen during a single VM inspection. - assert!(history_event.value.is_none()); - let bytecode_len = state - .decommittment_processor - .known_bytecodes - .inner() - .get(&history_event.key) - .expect("Bytecode must be known at this point") - .len(); + // We update cycles once per bytecode when it is actually decommitted. + if history_event.value.is_some() { + let bytecode_len = state + .decommittment_processor + .known_bytecodes + .inner() + .get(&history_event.key) + .expect("Bytecode must be known at this point") + .len(); - // Each cycle of `CodeDecommitter` processes 2 words. - // If the number of words in bytecode is odd, then number of cycles must be rounded up. - let decommitter_cycles_used = (bytecode_len + 1) / 2; - self.statistics.code_decommitter_cycles += decommitter_cycles_used as u32; + // Each cycle of `CodeDecommitter` processes 2 words. + // If the number of words in bytecode is odd, then number of cycles must be rounded up. + let decommitter_cycles_used = (bytecode_len + 1) / 2; + self.statistics.code_decommitter_cycles += decommitter_cycles_used as u32; + } } self.last_decommitment_history_entry_checked = Some(history.len()); }