From 63d1f529ca8f2125e6effa8bc3baef45bc7ef602 Mon Sep 17 00:00:00 2001 From: perekopskiy <53865202+perekopskiy@users.noreply.github.com> Date: Fri, 23 Feb 2024 12:34:18 +0200 Subject: [PATCH] feat(vm): integrate new vm version (#1215) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ Integrates new VM version ## Why ❔ ## Checklist - [ ] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [ ] Tests for the changes have been added / updated. - [ ] Documentation comments have been added / updated. - [ ] Code has been formatted via `zk fmt` and `zk lint`. - [ ] Spellcheck has been run via `zk spellcheck`. - [ ] Linkcheck has been run via `zk linkcheck`. --- core/lib/commitment_utils/src/lib.rs | 4 +- core/lib/contracts/src/lib.rs | 14 ++ core/lib/multivm/src/glue/history_mode.rs | 22 +- core/lib/multivm/src/glue/tracers/mod.rs | 23 +- .../src/glue/types/vm/storage_query.rs | 9 + core/lib/multivm/src/lib.rs | 2 +- .../multivm/src/tracers/call_tracer/mod.rs | 1 + .../src/tracers/call_tracer/vm_1_4_1/mod.rs | 209 ++++++++++++++++++ .../multivm/src/tracers/multivm_dispatcher.rs | 10 +- .../src/tracers/storage_invocation/mod.rs | 1 + .../storage_invocation/vm_1_4_1/mod.rs | 35 +++ core/lib/multivm/src/tracers/validator/mod.rs | 1 + .../src/tracers/validator/vm_1_4_1/mod.rs | 201 +++++++++++++++++ .../src/tracers/validator/vm_latest/mod.rs | 10 +- core/lib/multivm/src/utils.rs | 65 ++++-- core/lib/multivm/src/versions/mod.rs | 1 + .../vm_1_4_1/implementation/snapshots.rs | 10 +- core/lib/multivm/src/versions/vm_1_4_1/mod.rs | 2 - .../vm_1_4_1/tracers/pubdata_tracer.rs | 17 -- .../src/versions/vm_1_4_1/tracers/refunds.rs | 2 +- .../src/versions/vm_1_4_1/utils/fee.rs | 36 +-- core/lib/multivm/src/versions/vm_1_4_1/vm.rs | 4 +- .../vm_latest/implementation/execution.rs | 8 +- .../versions/vm_latest/implementation/gas.rs | 2 +- .../vm_latest/implementation/statistics.rs | 2 +- .../vm_latest/tests/tester/inner_state.rs | 2 +- .../src/versions/vm_latest/utils/fee.rs | 36 +-- core/lib/multivm/src/versions/vm_latest/vm.rs | 4 +- core/lib/multivm/src/vm_instance.rs | 10 +- core/lib/types/src/protocol_version.rs | 7 +- core/lib/types/src/vm_version.rs | 3 +- .../api_server/execution_sandbox/tracers.rs | 2 +- .../src/api_server/tx_sender/mod.rs | 7 +- .../ts-integration/tests/api/web3.test.ts | 22 +- .../fee_estimate.yul/fee_estimate.yul.zbin | Bin 0 -> 65888 bytes .../vm_1_4_2/gas_test.yul/gas_test.yul.zbin | Bin 0 -> 64352 bytes .../playground_batch.yul.zbin | Bin 0 -> 66144 bytes .../proved_batch.yul/proved_batch.yul.zbin | Bin 0 -> 64928 bytes 38 files changed, 635 insertions(+), 149 deletions(-) create mode 100644 core/lib/multivm/src/tracers/call_tracer/vm_1_4_1/mod.rs create mode 100644 core/lib/multivm/src/tracers/storage_invocation/vm_1_4_1/mod.rs create mode 100644 core/lib/multivm/src/tracers/validator/vm_1_4_1/mod.rs create mode 100644 etc/multivm_bootloaders/vm_1_4_2/fee_estimate.yul/fee_estimate.yul.zbin create mode 100644 etc/multivm_bootloaders/vm_1_4_2/gas_test.yul/gas_test.yul.zbin create mode 100644 etc/multivm_bootloaders/vm_1_4_2/playground_batch.yul/playground_batch.yul.zbin create mode 100644 etc/multivm_bootloaders/vm_1_4_2/proved_batch.yul/proved_batch.yul.zbin diff --git a/core/lib/commitment_utils/src/lib.rs b/core/lib/commitment_utils/src/lib.rs index cfdfa23e81d5..c420233369f4 100644 --- a/core/lib/commitment_utils/src/lib.rs +++ b/core/lib/commitment_utils/src/lib.rs @@ -24,7 +24,7 @@ pub fn events_queue_commitment( .collect(), ), )), - VmVersion::Vm1_4_1 => Some(H256( + VmVersion::Vm1_4_1 | VmVersion::Vm1_4_2 => Some(H256( zkevm_test_harness_1_4_1::witness::utils::events_queue_commitment_fixed( &events_queue .iter() @@ -55,7 +55,7 @@ pub fn bootloader_initial_content_commitment( &full_bootloader_memory, ), )), - VmVersion::Vm1_4_1 => Some(H256( + VmVersion::Vm1_4_1 | VmVersion::Vm1_4_2 => Some(H256( zkevm_test_harness_1_4_1::witness::utils::initial_heap_content_commitment_fixed( &full_bootloader_memory, ), diff --git a/core/lib/contracts/src/lib.rs b/core/lib/contracts/src/lib.rs index 065008d5dbce..fe390b4cd9e2 100644 --- a/core/lib/contracts/src/lib.rs +++ b/core/lib/contracts/src/lib.rs @@ -312,6 +312,13 @@ impl BaseSystemContracts { BaseSystemContracts::load_with_bootloader(bootloader_bytecode) } + pub fn playground_post_1_4_2() -> Self { + let bootloader_bytecode = read_zbin_bytecode( + "etc/multivm_bootloaders/vm_1_4_2/playground_batch.yul/playground_batch.yul.zbin", + ); + BaseSystemContracts::load_with_bootloader(bootloader_bytecode) + } + pub fn estimate_gas_pre_virtual_blocks() -> Self { let bootloader_bytecode = read_zbin_bytecode( "etc/multivm_bootloaders/vm_1_3_2/fee_estimate.yul/fee_estimate.yul.zbin", @@ -354,6 +361,13 @@ impl BaseSystemContracts { BaseSystemContracts::load_with_bootloader(bootloader_bytecode) } + pub fn estimate_gas_post_1_4_2() -> Self { + let bootloader_bytecode = read_zbin_bytecode( + "etc/multivm_bootloaders/vm_1_4_2/fee_estimate.yul/fee_estimate.yul.zbin", + ); + BaseSystemContracts::load_with_bootloader(bootloader_bytecode) + } + pub fn hashes(&self) -> BaseSystemContractsHashes { BaseSystemContractsHashes { bootloader: self.bootloader.hash, diff --git a/core/lib/multivm/src/glue/history_mode.rs b/core/lib/multivm/src/glue/history_mode.rs index f820477c10a5..45de90fc446d 100644 --- a/core/lib/multivm/src/glue/history_mode.rs +++ b/core/lib/multivm/src/glue/history_mode.rs @@ -8,13 +8,15 @@ pub trait HistoryMode: + GlueInto + GlueInto + GlueInto + + GlueInto { type VmM6Mode: crate::vm_m6::HistoryMode; type Vm1_3_2Mode: crate::vm_1_3_2::HistoryMode; type VmVirtualBlocksMode: crate::vm_virtual_blocks::HistoryMode; type VmVirtualBlocksRefundsEnhancement: crate::vm_refunds_enhancement::HistoryMode; type VmBoojumIntegration: crate::vm_boojum_integration::HistoryMode; - type Vm1_4_1: crate::vm_latest::HistoryMode; + type Vm1_4_1: crate::vm_1_4_1::HistoryMode; + type Vm1_4_2: crate::vm_latest::HistoryMode; } impl GlueFrom for crate::vm_m6::HistoryEnabled { @@ -47,6 +49,12 @@ impl GlueFrom for crate::vm_boojum_integration } } +impl GlueFrom for crate::vm_1_4_1::HistoryEnabled { + fn glue_from(_: crate::vm_latest::HistoryEnabled) -> Self { + Self + } +} + impl GlueFrom for crate::vm_m6::HistoryDisabled { fn glue_from(_: crate::vm_latest::HistoryDisabled) -> Self { Self @@ -79,13 +87,20 @@ impl GlueFrom for crate::vm_boojum_integratio } } +impl GlueFrom for crate::vm_1_4_1::HistoryDisabled { + fn glue_from(_: crate::vm_latest::HistoryDisabled) -> Self { + Self + } +} + impl HistoryMode for crate::vm_latest::HistoryEnabled { type VmM6Mode = crate::vm_m6::HistoryEnabled; type Vm1_3_2Mode = crate::vm_1_3_2::HistoryEnabled; type VmVirtualBlocksMode = crate::vm_virtual_blocks::HistoryEnabled; type VmVirtualBlocksRefundsEnhancement = crate::vm_refunds_enhancement::HistoryEnabled; type VmBoojumIntegration = crate::vm_boojum_integration::HistoryEnabled; - type Vm1_4_1 = crate::vm_latest::HistoryEnabled; + type Vm1_4_1 = crate::vm_1_4_1::HistoryEnabled; + type Vm1_4_2 = crate::vm_latest::HistoryEnabled; } impl HistoryMode for crate::vm_latest::HistoryDisabled { @@ -94,5 +109,6 @@ impl HistoryMode for crate::vm_latest::HistoryDisabled { type VmVirtualBlocksMode = crate::vm_virtual_blocks::HistoryDisabled; type VmVirtualBlocksRefundsEnhancement = crate::vm_refunds_enhancement::HistoryDisabled; type VmBoojumIntegration = crate::vm_boojum_integration::HistoryDisabled; - type Vm1_4_1 = crate::vm_latest::HistoryDisabled; + type Vm1_4_1 = crate::vm_1_4_1::HistoryDisabled; + type Vm1_4_2 = crate::vm_latest::HistoryDisabled; } diff --git a/core/lib/multivm/src/glue/tracers/mod.rs b/core/lib/multivm/src/glue/tracers/mod.rs index 3ca26892113c..46a7acf0665f 100644 --- a/core/lib/multivm/src/glue/tracers/mod.rs +++ b/core/lib/multivm/src/glue/tracers/mod.rs @@ -41,6 +41,7 @@ pub trait MultiVMTracer: + IntoVmVirtualBlocksTracer + IntoVmRefundsEnhancementTracer + IntoVmBoojumIntegrationTracer + + IntoVm1_4_1IntegrationTracer + IntoOldVmTracer { fn into_tracer_pointer(self) -> MultiVmTracerPointer @@ -52,7 +53,7 @@ pub trait MultiVMTracer: } pub trait IntoLatestTracer { - fn latest(&self) -> crate::vm_latest::TracerPointer; + fn latest(&self) -> crate::vm_latest::TracerPointer; } pub trait IntoVmVirtualBlocksTracer { @@ -73,6 +74,10 @@ pub trait IntoVmBoojumIntegrationTracer { ) -> Box>; } +pub trait IntoVm1_4_1IntegrationTracer { + fn vm_1_4_1(&self) -> Box>; +} + /// Into tracers for old VM versions. /// Even though number of tracers is limited, we still need to have this trait to be able to convert /// tracers to old VM tracers. @@ -89,9 +94,9 @@ impl IntoLatestTracer for T where S: WriteStorage, H: HistoryMode, - T: crate::vm_latest::VmTracer + Clone + 'static, + T: crate::vm_latest::VmTracer + Clone + 'static, { - fn latest(&self) -> crate::vm_latest::TracerPointer { + fn latest(&self) -> crate::vm_latest::TracerPointer { Box::new(self.clone()) } } @@ -138,6 +143,17 @@ where } } +impl IntoVm1_4_1IntegrationTracer for T +where + S: WriteStorage, + H: HistoryMode, + T: crate::vm_1_4_1::VmTracer + Clone + 'static, +{ + fn vm_1_4_1(&self) -> Box> { + Box::new(self.clone()) + } +} + impl MultiVMTracer for T where S: WriteStorage, @@ -146,6 +162,7 @@ where + IntoVmVirtualBlocksTracer + IntoVmRefundsEnhancementTracer + IntoVmBoojumIntegrationTracer + + IntoVm1_4_1IntegrationTracer + IntoOldVmTracer, { } diff --git a/core/lib/multivm/src/glue/types/vm/storage_query.rs b/core/lib/multivm/src/glue/types/vm/storage_query.rs index 21a10947e09b..19c543954e35 100644 --- a/core/lib/multivm/src/glue/types/vm/storage_query.rs +++ b/core/lib/multivm/src/glue/types/vm/storage_query.rs @@ -56,6 +56,15 @@ impl GlueFrom for St } } +impl GlueFrom for StorageLogQuery { + fn glue_from(value: crate::vm_1_4_1::utils::logs::StorageLogQuery) -> Self { + Self { + log_query: value.log_query.glue_into(), + log_type: value.log_type, + } + } +} + impl GlueFrom for StorageLogQuery { fn glue_from(value: crate::vm_latest::utils::logs::StorageLogQuery) -> Self { Self { diff --git a/core/lib/multivm/src/lib.rs b/core/lib/multivm/src/lib.rs index aa55026d88fe..b8278257f526 100644 --- a/core/lib/multivm/src/lib.rs +++ b/core/lib/multivm/src/lib.rs @@ -7,7 +7,7 @@ pub use zkevm_test_harness_1_4_1 as zkevm_test_harness_latest; pub use zksync_types::vm_version::VmVersion; pub use self::versions::{ - vm_1_3_2, vm_boojum_integration, vm_latest, vm_m5, vm_m6, vm_refunds_enhancement, + vm_1_3_2, vm_1_4_1, vm_boojum_integration, vm_latest, vm_m5, vm_m6, vm_refunds_enhancement, vm_virtual_blocks, }; pub use crate::{ diff --git a/core/lib/multivm/src/tracers/call_tracer/mod.rs b/core/lib/multivm/src/tracers/call_tracer/mod.rs index b3a98902f4b9..989db54d968f 100644 --- a/core/lib/multivm/src/tracers/call_tracer/mod.rs +++ b/core/lib/multivm/src/tracers/call_tracer/mod.rs @@ -6,6 +6,7 @@ use zksync_types::vm_trace::Call; use crate::{glue::tracers::IntoOldVmTracer, tracers::call_tracer::metrics::CALL_METRICS}; mod metrics; +pub mod vm_1_4_1; pub mod vm_boojum_integration; pub mod vm_latest; pub mod vm_refunds_enhancement; diff --git a/core/lib/multivm/src/tracers/call_tracer/vm_1_4_1/mod.rs b/core/lib/multivm/src/tracers/call_tracer/vm_1_4_1/mod.rs new file mode 100644 index 000000000000..54809168fca8 --- /dev/null +++ b/core/lib/multivm/src/tracers/call_tracer/vm_1_4_1/mod.rs @@ -0,0 +1,209 @@ +use zk_evm_1_4_1::{ + tracing::{AfterExecutionData, VmLocalStateData}, + zkevm_opcode_defs::{ + FarCallABI, FatPointer, Opcode, RetOpcode, CALL_IMPLICIT_CALLDATA_FAT_PTR_REGISTER, + RET_IMPLICIT_RETURNDATA_PARAMS_REGISTER, + }, +}; +use zksync_state::{StoragePtr, WriteStorage}; +use zksync_system_constants::CONTRACT_DEPLOYER_ADDRESS; +use zksync_types::{ + vm_trace::{Call, CallType}, + zk_evm_types::FarCallOpcode, + U256, +}; + +use crate::{ + glue::GlueInto, + interface::{ + tracer::VmExecutionStopReason, traits::tracers::dyn_tracers::vm_1_4_1::DynTracer, + VmRevertReason, + }, + tracers::call_tracer::CallTracer, + vm_1_4_1::{BootloaderState, HistoryMode, SimpleMemory, VmTracer, ZkSyncVmState}, +}; + +impl DynTracer> for CallTracer { + fn after_execution( + &mut self, + state: VmLocalStateData<'_>, + data: AfterExecutionData, + memory: &SimpleMemory, + _storage: StoragePtr, + ) { + match data.opcode.variant.opcode { + Opcode::NearCall(_) => { + self.increase_near_call_count(); + } + Opcode::FarCall(far_call) => { + // We use parent gas for properly calculating gas used in the trace. + let current_ergs = state.vm_local_state.callstack.current.ergs_remaining; + let parent_gas = state + .vm_local_state + .callstack + .inner + .last() + .map(|call| call.ergs_remaining + current_ergs) + .unwrap_or(current_ergs); + + let mut current_call = Call { + r#type: CallType::Call(far_call.glue_into()), + gas: 0, + parent_gas, + ..Default::default() + }; + + self.handle_far_call_op_code_vm_1_4_1(state, memory, &mut current_call); + self.push_call_and_update_stats(current_call, 0); + } + Opcode::Ret(ret_code) => { + self.handle_ret_op_code_vm_1_4_1(state, memory, ret_code); + } + _ => {} + }; + } +} + +impl VmTracer for CallTracer { + fn after_vm_execution( + &mut self, + _state: &mut ZkSyncVmState, + _bootloader_state: &BootloaderState, + _stop_reason: VmExecutionStopReason, + ) { + self.store_result() + } +} + +impl CallTracer { + fn handle_far_call_op_code_vm_1_4_1( + &mut self, + state: VmLocalStateData<'_>, + memory: &SimpleMemory, + current_call: &mut Call, + ) { + let current = state.vm_local_state.callstack.current; + // All calls from the actual users are mimic calls, + // so we need to check that the previous call was to the deployer. + // Actually it's a call of the constructor. + // And at this stage caller is user and callee is deployed contract. + let call_type = if let CallType::Call(far_call) = current_call.r#type { + if matches!(far_call, FarCallOpcode::Mimic) { + let previous_caller = state + .vm_local_state + .callstack + .inner + .last() + .map(|call| call.this_address) + // Actually it's safe to just unwrap here, because we have at least one call in the stack + // But i want to be sure that we will not have any problems in the future + .unwrap_or(current.this_address); + if previous_caller == CONTRACT_DEPLOYER_ADDRESS { + CallType::Create + } else { + CallType::Call(far_call) + } + } else { + CallType::Call(far_call) + } + } else { + unreachable!() + }; + let calldata = if current.code_page.0 == 0 || current.ergs_remaining == 0 { + vec![] + } else { + let packed_abi = + state.vm_local_state.registers[CALL_IMPLICIT_CALLDATA_FAT_PTR_REGISTER as usize]; + assert!(packed_abi.is_pointer); + let far_call_abi = FarCallABI::from_u256(packed_abi.value); + memory.read_unaligned_bytes( + far_call_abi.memory_quasi_fat_pointer.memory_page as usize, + far_call_abi.memory_quasi_fat_pointer.start as usize, + far_call_abi.memory_quasi_fat_pointer.length as usize, + ) + }; + + current_call.input = calldata; + current_call.r#type = call_type; + current_call.from = current.msg_sender; + current_call.to = current.this_address; + current_call.value = U256::from(current.context_u128_value); + current_call.gas = current.ergs_remaining; + } + + fn save_output_vm_1_4_1( + &mut self, + state: VmLocalStateData<'_>, + memory: &SimpleMemory, + ret_opcode: RetOpcode, + current_call: &mut Call, + ) { + let fat_data_pointer = + state.vm_local_state.registers[RET_IMPLICIT_RETURNDATA_PARAMS_REGISTER as usize]; + + // if `fat_data_pointer` is not a pointer then there is no output + let output = if fat_data_pointer.is_pointer { + let fat_data_pointer = FatPointer::from_u256(fat_data_pointer.value); + if !fat_data_pointer.is_trivial() { + Some(memory.read_unaligned_bytes( + fat_data_pointer.memory_page as usize, + fat_data_pointer.start as usize, + fat_data_pointer.length as usize, + )) + } else { + None + } + } else { + None + }; + + match ret_opcode { + RetOpcode::Ok => { + current_call.output = output.unwrap_or_default(); + } + RetOpcode::Revert => { + if let Some(output) = output { + current_call.revert_reason = + Some(VmRevertReason::from(output.as_slice()).to_string()); + } else { + current_call.revert_reason = Some("Unknown revert reason".to_string()); + } + } + RetOpcode::Panic => { + current_call.error = Some("Panic".to_string()); + } + } + } + + fn handle_ret_op_code_vm_1_4_1( + &mut self, + state: VmLocalStateData<'_>, + memory: &SimpleMemory, + ret_opcode: RetOpcode, + ) { + let Some(mut current_call) = self.stack.pop() else { + return; + }; + + if current_call.near_calls_after > 0 { + current_call.near_calls_after -= 1; + self.push_call_and_update_stats(current_call.farcall, current_call.near_calls_after); + return; + } + + current_call.farcall.gas_used = current_call + .farcall + .parent_gas + .saturating_sub(state.vm_local_state.callstack.current.ergs_remaining); + + self.save_output_vm_1_4_1(state, memory, ret_opcode, &mut current_call.farcall); + + // If there is a parent call, push the current call to it + // Otherwise, push the current call to the stack, because it's the top level call + if let Some(parent_call) = self.stack.last_mut() { + parent_call.farcall.calls.push(current_call.farcall); + } else { + self.push_call_and_update_stats(current_call.farcall, current_call.near_calls_after); + } + } +} diff --git a/core/lib/multivm/src/tracers/multivm_dispatcher.rs b/core/lib/multivm/src/tracers/multivm_dispatcher.rs index 0e8585884fa5..b616c571f77c 100644 --- a/core/lib/multivm/src/tracers/multivm_dispatcher.rs +++ b/core/lib/multivm/src/tracers/multivm_dispatcher.rs @@ -30,7 +30,7 @@ impl Default for TracerDispatcher { } impl From> - for crate::vm_latest::TracerDispatcher + for crate::vm_latest::TracerDispatcher { fn from(value: TracerDispatcher) -> Self { Self::new(value.tracers.into_iter().map(|x| x.latest()).collect()) @@ -51,6 +51,14 @@ impl From> } } +impl From> + for crate::vm_1_4_1::TracerDispatcher +{ + fn from(value: TracerDispatcher) -> Self { + Self::new(value.tracers.into_iter().map(|x| x.vm_1_4_1()).collect()) + } +} + impl From> for crate::vm_refunds_enhancement::TracerDispatcher { diff --git a/core/lib/multivm/src/tracers/storage_invocation/mod.rs b/core/lib/multivm/src/tracers/storage_invocation/mod.rs index db4e936e0112..210a175507b9 100644 --- a/core/lib/multivm/src/tracers/storage_invocation/mod.rs +++ b/core/lib/multivm/src/tracers/storage_invocation/mod.rs @@ -1,5 +1,6 @@ use crate::{glue::tracers::IntoOldVmTracer, tracers::old_tracers::OldTracers}; +pub mod vm_1_4_1; pub mod vm_boojum_integration; pub mod vm_latest; pub mod vm_refunds_enhancement; diff --git a/core/lib/multivm/src/tracers/storage_invocation/vm_1_4_1/mod.rs b/core/lib/multivm/src/tracers/storage_invocation/vm_1_4_1/mod.rs new file mode 100644 index 000000000000..be3a30adb1d4 --- /dev/null +++ b/core/lib/multivm/src/tracers/storage_invocation/vm_1_4_1/mod.rs @@ -0,0 +1,35 @@ +use zksync_state::WriteStorage; + +use crate::{ + interface::{ + tracer::{TracerExecutionStatus, TracerExecutionStopReason}, + traits::tracers::dyn_tracers::vm_1_4_1::DynTracer, + Halt, + }, + tracers::storage_invocation::StorageInvocations, + vm_1_4_1::{BootloaderState, HistoryMode, SimpleMemory, VmTracer, ZkSyncVmState}, +}; + +impl DynTracer> for StorageInvocations {} + +impl VmTracer for StorageInvocations { + fn finish_cycle( + &mut self, + state: &mut ZkSyncVmState, + _bootloader_state: &mut BootloaderState, + ) -> TracerExecutionStatus { + let current = state + .storage + .storage + .get_ptr() + .borrow() + .missed_storage_invocations(); + + if current >= self.limit { + return TracerExecutionStatus::Stop(TracerExecutionStopReason::Abort( + Halt::TracerCustom("Storage invocations limit reached".to_string()), + )); + } + TracerExecutionStatus::Continue + } +} diff --git a/core/lib/multivm/src/tracers/validator/mod.rs b/core/lib/multivm/src/tracers/validator/mod.rs index c56424f3013e..caf9dee30c9d 100644 --- a/core/lib/multivm/src/tracers/validator/mod.rs +++ b/core/lib/multivm/src/tracers/validator/mod.rs @@ -19,6 +19,7 @@ use crate::{ }; mod types; +mod vm_1_4_1; mod vm_boojum_integration; mod vm_latest; mod vm_refunds_enhancement; diff --git a/core/lib/multivm/src/tracers/validator/vm_1_4_1/mod.rs b/core/lib/multivm/src/tracers/validator/vm_1_4_1/mod.rs new file mode 100644 index 000000000000..81d885fa788e --- /dev/null +++ b/core/lib/multivm/src/tracers/validator/vm_1_4_1/mod.rs @@ -0,0 +1,201 @@ +use zk_evm_1_4_1::{ + tracing::{BeforeExecutionData, VmLocalStateData}, + zkevm_opcode_defs::{ContextOpcode, FarCallABI, LogOpcode, Opcode}, +}; +use zksync_state::{StoragePtr, WriteStorage}; +use zksync_system_constants::KECCAK256_PRECOMPILE_ADDRESS; +use zksync_types::{ + get_code_key, vm_trace::ViolatedValidationRule, AccountTreeId, StorageKey, H256, +}; +use zksync_utils::{h256_to_account_address, u256_to_account_address, u256_to_h256}; + +use crate::{ + interface::{ + traits::tracers::dyn_tracers::vm_1_4_1::DynTracer, + types::tracer::{TracerExecutionStatus, TracerExecutionStopReason}, + Halt, + }, + tracers::validator::{ + types::{NewTrustedValidationItems, ValidationTracerMode}, + ValidationRoundResult, ValidationTracer, + }, + vm_1_4_1::{ + tracers::utils::{ + computational_gas_price, get_calldata_page_via_abi, print_debug_if_needed, VmHook, + }, + BootloaderState, SimpleMemory, VmTracer, ZkSyncVmState, + }, + HistoryMode, +}; + +impl ValidationTracer { + fn check_user_restrictions_vm_1_4_1( + &mut self, + state: VmLocalStateData<'_>, + data: BeforeExecutionData, + memory: &SimpleMemory, + storage: StoragePtr, + ) -> ValidationRoundResult { + if self.computational_gas_used > self.computational_gas_limit { + return Err(ViolatedValidationRule::TookTooManyComputationalGas( + self.computational_gas_limit, + )); + } + + let opcode_variant = data.opcode.variant; + match opcode_variant.opcode { + Opcode::FarCall(_) => { + let packed_abi = data.src0_value.value; + let call_destination_value = data.src1_value.value; + + let called_address = u256_to_account_address(&call_destination_value); + let far_call_abi = FarCallABI::from_u256(packed_abi); + + if called_address == KECCAK256_PRECOMPILE_ADDRESS + && far_call_abi.memory_quasi_fat_pointer.length == 64 + { + let calldata_page = get_calldata_page_via_abi( + &far_call_abi, + state.vm_local_state.callstack.current.base_memory_page, + ); + let calldata = memory.read_unaligned_bytes( + calldata_page as usize, + far_call_abi.memory_quasi_fat_pointer.start as usize, + 64, + ); + + let slot_to_add = + self.slot_to_add_from_keccak_call(&calldata, self.user_address); + + if let Some(slot) = slot_to_add { + return Ok(NewTrustedValidationItems { + new_allowed_slots: vec![slot], + ..Default::default() + }); + } + } else if called_address != self.user_address { + let code_key = get_code_key(&called_address); + let code = storage.borrow_mut().read_value(&code_key); + + if code == H256::zero() { + // The users are not allowed to call contracts with no code + return Err(ViolatedValidationRule::CalledContractWithNoCode( + called_address, + )); + } + } + } + Opcode::Context(context) => { + match context { + ContextOpcode::Meta => { + return Err(ViolatedValidationRule::TouchedUnallowedContext); + } + ContextOpcode::ErgsLeft => { + // TODO (SMA-1168): implement the correct restrictions for the gas left opcode. + } + _ => {} + } + } + Opcode::Log(LogOpcode::StorageRead) => { + let key = data.src0_value.value; + let this_address = state.vm_local_state.callstack.current.this_address; + let msg_sender = state.vm_local_state.callstack.current.msg_sender; + + if !self.is_allowed_storage_read(storage.clone(), this_address, key, msg_sender) { + return Err(ViolatedValidationRule::TouchedUnallowedStorageSlots( + this_address, + key, + )); + } + + if self.trusted_address_slots.contains(&(this_address, key)) { + let storage_key = + StorageKey::new(AccountTreeId::new(this_address), u256_to_h256(key)); + + let value = storage.borrow_mut().read_value(&storage_key); + + return Ok(NewTrustedValidationItems { + new_trusted_addresses: vec![h256_to_account_address(&value)], + ..Default::default() + }); + } + } + _ => {} + } + + Ok(Default::default()) + } +} + +impl DynTracer> + for ValidationTracer +{ + fn before_execution( + &mut self, + state: VmLocalStateData<'_>, + data: BeforeExecutionData, + memory: &SimpleMemory, + storage: StoragePtr, + ) { + // For now, we support only validations for users. + if let ValidationTracerMode::UserTxValidation = self.validation_mode { + self.computational_gas_used = self + .computational_gas_used + .saturating_add(computational_gas_price(state, &data)); + + let validation_round_result = + self.check_user_restrictions_vm_1_4_1(state, data, memory, storage); + self.process_validation_round_result(validation_round_result); + } + + let hook = VmHook::from_opcode_memory(&state, &data); + print_debug_if_needed(&hook, &state, memory); + + let current_mode = self.validation_mode; + match (current_mode, hook) { + (ValidationTracerMode::NoValidation, VmHook::AccountValidationEntered) => { + // Account validation can be entered when there is no prior validation (i.e. "nested" validations are not allowed) + self.validation_mode = ValidationTracerMode::UserTxValidation; + } + (ValidationTracerMode::NoValidation, VmHook::PaymasterValidationEntered) => { + // Paymaster validation can be entered when there is no prior validation (i.e. "nested" validations are not allowed) + self.validation_mode = ValidationTracerMode::PaymasterTxValidation; + } + (_, VmHook::AccountValidationEntered | VmHook::PaymasterValidationEntered) => { + panic!( + "Unallowed transition inside the validation tracer. Mode: {:#?}, hook: {:#?}", + self.validation_mode, hook + ); + } + (_, VmHook::NoValidationEntered) => { + // Validation can be always turned off + self.validation_mode = ValidationTracerMode::NoValidation; + } + (_, VmHook::ValidationStepEndeded) => { + // The validation step has ended. + self.should_stop_execution = true; + } + (_, _) => { + // The hook is not relevant to the validation tracer. Ignore. + } + } + } +} + +impl VmTracer for ValidationTracer { + fn finish_cycle( + &mut self, + _state: &mut ZkSyncVmState, + _bootloader_state: &mut BootloaderState, + ) -> TracerExecutionStatus { + if self.should_stop_execution { + return TracerExecutionStatus::Stop(TracerExecutionStopReason::Finish); + } + if let Some(result) = self.result.get() { + return TracerExecutionStatus::Stop(TracerExecutionStopReason::Abort( + Halt::TracerCustom(format!("Validation error: {:#?}", result)), + )); + } + TracerExecutionStatus::Continue + } +} diff --git a/core/lib/multivm/src/tracers/validator/vm_latest/mod.rs b/core/lib/multivm/src/tracers/validator/vm_latest/mod.rs index 095f1a20b38b..a2096ee68961 100644 --- a/core/lib/multivm/src/tracers/validator/vm_latest/mod.rs +++ b/core/lib/multivm/src/tracers/validator/vm_latest/mod.rs @@ -33,7 +33,7 @@ impl ValidationTracer { &mut self, state: VmLocalStateData<'_>, data: BeforeExecutionData, - memory: &SimpleMemory, + memory: &SimpleMemory, storage: StoragePtr, ) -> ValidationRoundResult { if self.computational_gas_used > self.computational_gas_limit { @@ -127,14 +127,14 @@ impl ValidationTracer { } } -impl DynTracer> +impl DynTracer> for ValidationTracer { fn before_execution( &mut self, state: VmLocalStateData<'_>, data: BeforeExecutionData, - memory: &SimpleMemory, + memory: &SimpleMemory, storage: StoragePtr, ) { // For now, we support only validations for users. @@ -182,10 +182,10 @@ impl DynTracer> } } -impl VmTracer for ValidationTracer { +impl VmTracer for ValidationTracer { fn finish_cycle( &mut self, - _state: &mut ZkSyncVmState, + _state: &mut ZkSyncVmState, _bootloader_state: &mut BootloaderState, ) -> TracerExecutionStatus { if self.should_stop_execution { diff --git a/core/lib/multivm/src/utils.rs b/core/lib/multivm/src/utils.rs index 1488ee3df19d..9f5ae5540c39 100644 --- a/core/lib/multivm/src/utils.rs +++ b/core/lib/multivm/src/utils.rs @@ -1,4 +1,7 @@ -use zksync_types::{fee_model::BatchFeeInput, VmVersion, U256}; +use zksync_types::{ + fee_model::{BatchFeeInput, L1PeggedBatchFeeModelInput, PubdataIndependentBatchFeeModelInput}, + VmVersion, U256, +}; use crate::vm_latest::L1BatchEnv; @@ -38,7 +41,10 @@ pub fn derive_base_fee_and_gas_per_pubdata( batch_fee_input.into_l1_pegged(), ) } - VmVersion::Vm1_4_1 => crate::vm_latest::utils::fee::derive_base_fee_and_gas_per_pubdata( + VmVersion::Vm1_4_1 => crate::vm_1_4_1::utils::fee::derive_base_fee_and_gas_per_pubdata( + batch_fee_input.into_pubdata_independent(), + ), + VmVersion::Vm1_4_2 => crate::vm_latest::utils::fee::derive_base_fee_and_gas_per_pubdata( batch_fee_input.into_pubdata_independent(), ), } @@ -62,7 +68,8 @@ pub fn get_batch_base_fee(l1_batch_env: &L1BatchEnv, vm_version: VmVersion) -> u VmVersion::VmBoojumIntegration => { crate::vm_boojum_integration::utils::fee::get_batch_base_fee(l1_batch_env) } - VmVersion::Vm1_4_1 => crate::vm_latest::utils::fee::get_batch_base_fee(l1_batch_env), + VmVersion::Vm1_4_1 => crate::vm_1_4_1::utils::fee::get_batch_base_fee(l1_batch_env), + VmVersion::Vm1_4_2 => crate::vm_latest::utils::fee::get_batch_base_fee(l1_batch_env), } } @@ -75,14 +82,37 @@ pub fn adjust_pubdata_price_for_tx( if U256::from(derive_base_fee_and_gas_per_pubdata(batch_fee_input, vm_version).1) <= tx_gas_per_pubdata_limit { + // gas per pubdata is already smaller than or equal to `tx_gas_per_pubdata_limit`. return batch_fee_input; } - // The latest VM supports adjusting the pubdata price for all the types of the fee models. - crate::vm_latest::utils::fee::adjust_pubdata_price_for_tx( - batch_fee_input, - tx_gas_per_pubdata_limit, - ) + match batch_fee_input { + BatchFeeInput::L1Pegged(fee_input) => { + // `gasPerPubdata = ceil(17 * l1gasprice / fair_l2_gas_price)` + // `gasPerPubdata <= 17 * l1gasprice / fair_l2_gas_price + 1` + // `fair_l2_gas_price(gasPerPubdata - 1) / 17 <= l1gasprice` + let new_l1_gas_price = U256::from(fee_input.fair_l2_gas_price) + * (tx_gas_per_pubdata_limit - U256::from(1u32)) + / U256::from(17); + + BatchFeeInput::L1Pegged(L1PeggedBatchFeeModelInput { + l1_gas_price: new_l1_gas_price.as_u64(), + ..fee_input + }) + } + BatchFeeInput::PubdataIndependent(fee_input) => { + // `gasPerPubdata = ceil(fair_pubdata_price / fair_l2_gas_price)` + // `gasPerPubdata <= fair_pubdata_price / fair_l2_gas_price + 1` + // `fair_l2_gas_price(gasPerPubdata - 1) <= fair_pubdata_price` + let new_fair_pubdata_price = U256::from(fee_input.fair_l2_gas_price) + * (tx_gas_per_pubdata_limit - U256::from(1u32)); + + BatchFeeInput::PubdataIndependent(PubdataIndependentBatchFeeModelInput { + fair_pubdata_price: new_fair_pubdata_price.as_u64(), + ..fee_input + }) + } + } } pub fn derive_overhead( @@ -140,7 +170,8 @@ pub fn derive_overhead( ), ) } - VmVersion::Vm1_4_1 => crate::vm_latest::utils::overhead::derive_overhead(encoded_len), + VmVersion::Vm1_4_1 => crate::vm_1_4_1::utils::overhead::derive_overhead(encoded_len), + VmVersion::Vm1_4_2 => crate::vm_latest::utils::overhead::derive_overhead(encoded_len), } } @@ -162,7 +193,8 @@ pub fn get_bootloader_encoding_space(version: VmVersion) -> u32 { VmVersion::VmBoojumIntegration => { crate::vm_boojum_integration::constants::BOOTLOADER_TX_ENCODING_SPACE } - VmVersion::Vm1_4_1 => crate::vm_latest::constants::BOOTLOADER_TX_ENCODING_SPACE, + VmVersion::Vm1_4_1 => crate::vm_1_4_1::constants::BOOTLOADER_TX_ENCODING_SPACE, + VmVersion::Vm1_4_2 => crate::vm_latest::constants::BOOTLOADER_TX_ENCODING_SPACE, } } @@ -180,7 +212,8 @@ pub fn get_bootloader_max_txs_in_batch(version: VmVersion) -> usize { crate::vm_refunds_enhancement::constants::MAX_TXS_IN_BLOCK } VmVersion::VmBoojumIntegration => crate::vm_boojum_integration::constants::MAX_TXS_IN_BLOCK, - VmVersion::Vm1_4_1 => crate::vm_latest::constants::MAX_TXS_IN_BATCH, + VmVersion::Vm1_4_1 => crate::vm_1_4_1::constants::MAX_TXS_IN_BATCH, + VmVersion::Vm1_4_2 => crate::vm_latest::constants::MAX_TXS_IN_BATCH, } } @@ -199,7 +232,8 @@ pub fn gas_bootloader_batch_tip_overhead(version: VmVersion) -> u32 { VmVersion::VmBoojumIntegration => { crate::vm_boojum_integration::constants::BOOTLOADER_BATCH_TIP_OVERHEAD } - VmVersion::Vm1_4_1 => crate::vm_latest::constants::BOOTLOADER_BATCH_TIP_OVERHEAD, + VmVersion::Vm1_4_1 => crate::vm_1_4_1::constants::BOOTLOADER_BATCH_TIP_OVERHEAD, + VmVersion::Vm1_4_2 => crate::vm_latest::constants::BOOTLOADER_BATCH_TIP_OVERHEAD, } } @@ -220,6 +254,7 @@ pub fn get_max_gas_per_pubdata_byte(version: VmVersion) -> u64 { crate::vm_boojum_integration::constants::MAX_GAS_PER_PUBDATA_BYTE } VmVersion::Vm1_4_1 => crate::vm_latest::constants::MAX_GAS_PER_PUBDATA_BYTE, + VmVersion::Vm1_4_2 => crate::vm_1_4_1::constants::MAX_GAS_PER_PUBDATA_BYTE, } } @@ -241,7 +276,8 @@ pub fn get_used_bootloader_memory_bytes(version: VmVersion) -> usize { VmVersion::VmBoojumIntegration => { crate::vm_boojum_integration::constants::USED_BOOTLOADER_MEMORY_BYTES } - VmVersion::Vm1_4_1 => crate::vm_latest::constants::USED_BOOTLOADER_MEMORY_BYTES, + VmVersion::Vm1_4_1 => crate::vm_1_4_1::constants::USED_BOOTLOADER_MEMORY_BYTES, + VmVersion::Vm1_4_2 => crate::vm_latest::constants::USED_BOOTLOADER_MEMORY_BYTES, } } @@ -263,6 +299,7 @@ pub fn get_used_bootloader_memory_words(version: VmVersion) -> usize { VmVersion::VmBoojumIntegration => { crate::vm_boojum_integration::constants::USED_BOOTLOADER_MEMORY_WORDS } - VmVersion::Vm1_4_1 => crate::vm_latest::constants::USED_BOOTLOADER_MEMORY_WORDS, + VmVersion::Vm1_4_1 => crate::vm_1_4_1::constants::USED_BOOTLOADER_MEMORY_WORDS, + VmVersion::Vm1_4_2 => crate::vm_latest::constants::USED_BOOTLOADER_MEMORY_WORDS, } } diff --git a/core/lib/multivm/src/versions/mod.rs b/core/lib/multivm/src/versions/mod.rs index 0fc9111aa9a2..a13946c3852c 100644 --- a/core/lib/multivm/src/versions/mod.rs +++ b/core/lib/multivm/src/versions/mod.rs @@ -1,4 +1,5 @@ pub mod vm_1_3_2; +pub mod vm_1_4_1; pub mod vm_boojum_integration; pub mod vm_latest; pub mod vm_m5; diff --git a/core/lib/multivm/src/versions/vm_1_4_1/implementation/snapshots.rs b/core/lib/multivm/src/versions/vm_1_4_1/implementation/snapshots.rs index 2682b6183c52..08e4e1c9e49e 100644 --- a/core/lib/multivm/src/versions/vm_1_4_1/implementation/snapshots.rs +++ b/core/lib/multivm/src/versions/vm_1_4_1/implementation/snapshots.rs @@ -4,11 +4,7 @@ use vise::{Buckets, EncodeLabelSet, EncodeLabelValue, Family, Histogram, Metrics use zk_evm_1_4_1::aux_structures::Timestamp; use zksync_state::WriteStorage; -use crate::vm_1_4_1::{ - old_vm::{history_recorder::HistoryEnabled, oracles::OracleWithHistory}, - types::internals::VmSnapshot, - vm::Vm, -}; +use crate::vm_1_4_1::{old_vm::oracles::OracleWithHistory, types::internals::VmSnapshot, vm::Vm}; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, EncodeLabelSet, EncodeLabelValue)] #[metrics(label = "stage", rename_all = "snake_case")] @@ -22,7 +18,7 @@ enum RollbackStage { } #[derive(Debug, Metrics)] -#[metrics(prefix = "server_vm")] +#[metrics(prefix = "server_vm_1_4_1")] struct VmMetrics { #[metrics(buckets = Buckets::LATENCIES)] rollback_time: Family>, @@ -32,7 +28,7 @@ struct VmMetrics { static METRICS: vise::Global = vise::Global::new(); /// Implementation of VM related to rollbacks inside virtual machine -impl Vm { +impl Vm { pub(crate) fn make_snapshot_inner(&mut self) { self.snapshots.push(VmSnapshot { // Vm local state contains O(1) various parameters (registers/etc). diff --git a/core/lib/multivm/src/versions/vm_1_4_1/mod.rs b/core/lib/multivm/src/versions/vm_1_4_1/mod.rs index c3df28f6c31c..83693e4b24e9 100644 --- a/core/lib/multivm/src/versions/vm_1_4_1/mod.rs +++ b/core/lib/multivm/src/versions/vm_1_4_1/mod.rs @@ -28,8 +28,6 @@ pub mod constants; mod implementation; mod old_vm; mod oracles; -#[cfg(test)] -mod tests; pub(crate) mod tracers; mod types; pub mod utils; diff --git a/core/lib/multivm/src/versions/vm_1_4_1/tracers/pubdata_tracer.rs b/core/lib/multivm/src/versions/vm_1_4_1/tracers/pubdata_tracer.rs index c69daa0927b5..831466be318c 100644 --- a/core/lib/multivm/src/versions/vm_1_4_1/tracers/pubdata_tracer.rs +++ b/core/lib/multivm/src/versions/vm_1_4_1/tracers/pubdata_tracer.rs @@ -57,23 +57,6 @@ impl PubdataTracer { } } - // Creates the pubdata tracer with constant state diffs. - // To be used in tests only. - #[cfg(test)] - pub(crate) fn new_with_forced_state_diffs( - l1_batch_env: L1BatchEnv, - execution_mode: VmExecutionMode, - forced_state_diffs: Vec, - ) -> Self { - Self { - l1_batch_env, - pubdata_info_requested: false, - execution_mode, - enforced_state_diffs: Some(forced_state_diffs), - _phantom_data: Default::default(), - } - } - // Packs part of L1 Messenger total pubdata that corresponds to // `L2toL1Logs` sent in the block fn get_total_user_logs( diff --git a/core/lib/multivm/src/versions/vm_1_4_1/tracers/refunds.rs b/core/lib/multivm/src/versions/vm_1_4_1/tracers/refunds.rs index 9fba21c42078..6f942a807e78 100644 --- a/core/lib/multivm/src/versions/vm_1_4_1/tracers/refunds.rs +++ b/core/lib/multivm/src/versions/vm_1_4_1/tracers/refunds.rs @@ -212,7 +212,7 @@ impl VmTracer for RefundsTracer { ]); #[derive(Debug, Metrics)] - #[metrics(prefix = "vm")] + #[metrics(prefix = "vm_1_4_1")] struct RefundMetrics { #[metrics(buckets = PERCENT_BUCKETS)] refund: Family>, diff --git a/core/lib/multivm/src/versions/vm_1_4_1/utils/fee.rs b/core/lib/multivm/src/versions/vm_1_4_1/utils/fee.rs index beb85468241b..6498a86d3536 100644 --- a/core/lib/multivm/src/versions/vm_1_4_1/utils/fee.rs +++ b/core/lib/multivm/src/versions/vm_1_4_1/utils/fee.rs @@ -1,8 +1,5 @@ //! Utility functions for vm -use zksync_types::{ - fee_model::{BatchFeeInput, PubdataIndependentBatchFeeModelInput}, - U256, -}; +use zksync_types::fee_model::PubdataIndependentBatchFeeModelInput; use zksync_utils::ceil_div; use crate::vm_1_4_1::{constants::MAX_GAS_PER_PUBDATA_BYTE, L1BatchEnv}; @@ -37,34 +34,3 @@ pub(crate) fn get_batch_base_fee(l1_batch_env: &L1BatchEnv) -> u64 { derive_base_fee_and_gas_per_pubdata(l1_batch_env.fee_input.into_pubdata_independent()); base_fee } - -/// Changes the fee model output so that the expected gas per pubdata is smaller than or the `tx_gas_per_pubdata_limit`. -/// This function expects that the currently expected gas per pubdata is greater than the `tx_gas_per_pubdata_limit`. -pub(crate) fn adjust_pubdata_price_for_tx( - mut batch_fee_input: BatchFeeInput, - tx_gas_per_pubdata_limit: U256, -) -> BatchFeeInput { - match &mut batch_fee_input { - BatchFeeInput::L1Pegged(fee_input) => { - // `gasPerPubdata = ceil(17 * l1gasprice / fair_l2_gas_price)` - // `gasPerPubdata <= 17 * l1gasprice / fair_l2_gas_price + 1` - // `fair_l2_gas_price(gasPerPubdata - 1) / 17 <= l1gasprice` - let new_l1_gas_price = U256::from(fee_input.fair_l2_gas_price) - * (tx_gas_per_pubdata_limit - U256::from(1u32)) - / U256::from(17); - - fee_input.l1_gas_price = new_l1_gas_price.as_u64(); - } - BatchFeeInput::PubdataIndependent(fee_input) => { - // `gasPerPubdata = ceil(fair_pubdata_price / fair_l2_gas_price)` - // `gasPerPubdata <= fair_pubdata_price / fair_l2_gas_price + 1` - // `fair_l2_gas_price(gasPerPubdata - 1) <= fair_pubdata_price` - let new_fair_pubdata_price = U256::from(fee_input.fair_l2_gas_price) - * (tx_gas_per_pubdata_limit - U256::from(1u32)); - - fee_input.fair_pubdata_price = new_fair_pubdata_price.as_u64(); - } - } - - batch_fee_input -} diff --git a/core/lib/multivm/src/versions/vm_1_4_1/vm.rs b/core/lib/multivm/src/versions/vm_1_4_1/vm.rs index d87b8d96586c..5684c8821abb 100644 --- a/core/lib/multivm/src/versions/vm_1_4_1/vm.rs +++ b/core/lib/multivm/src/versions/vm_1_4_1/vm.rs @@ -16,7 +16,7 @@ use crate::{ }, vm_1_4_1::{ bootloader_state::BootloaderState, - old_vm::{events::merge_events, history_recorder::HistoryEnabled}, + old_vm::events::merge_events, tracers::dispatcher::TracerDispatcher, types::internals::{new_vm_state, VmSnapshot, ZkSyncVmState}, }, @@ -187,7 +187,7 @@ impl VmInterface for Vm { } /// Methods of vm, which required some history manipulations -impl VmInterfaceHistoryEnabled for Vm { +impl VmInterfaceHistoryEnabled for Vm { /// Create snapshot of current vm state and push it into the memory fn make_snapshot(&mut self) { self.make_snapshot_inner() diff --git a/core/lib/multivm/src/versions/vm_latest/implementation/execution.rs b/core/lib/multivm/src/versions/vm_latest/implementation/execution.rs index 0c836f64097f..d5d61aceafcf 100644 --- a/core/lib/multivm/src/versions/vm_latest/implementation/execution.rs +++ b/core/lib/multivm/src/versions/vm_latest/implementation/execution.rs @@ -20,7 +20,7 @@ use crate::{ impl Vm { pub(crate) fn inspect_inner( &mut self, - dispatcher: TracerDispatcher, + dispatcher: TracerDispatcher, execution_mode: VmExecutionMode, custom_pubdata_tracer: Option>, ) -> VmExecutionResultAndLogs { @@ -44,14 +44,14 @@ impl Vm { /// Collect the result from the default tracers. fn inspect_and_collect_results( &mut self, - dispatcher: TracerDispatcher, + dispatcher: TracerDispatcher, execution_mode: VmExecutionMode, with_refund_tracer: bool, custom_pubdata_tracer: Option>, ) -> (VmExecutionStopReason, VmExecutionResultAndLogs) { let refund_tracers = with_refund_tracer.then_some(RefundsTracer::new(self.batch_env.clone())); - let mut tx_tracer: DefaultExecutionTracer = DefaultExecutionTracer::new( + let mut tx_tracer: DefaultExecutionTracer = DefaultExecutionTracer::new( self.system_env.default_validation_computational_gas_limit, execution_mode, dispatcher, @@ -104,7 +104,7 @@ impl Vm { /// Execute vm with given tracers until the stop reason is reached. fn execute_with_default_tracer( &mut self, - tracer: &mut DefaultExecutionTracer, + tracer: &mut DefaultExecutionTracer, ) -> VmExecutionStopReason { tracer.initialize_tracer(&mut self.state); let result = loop { diff --git a/core/lib/multivm/src/versions/vm_latest/implementation/gas.rs b/core/lib/multivm/src/versions/vm_latest/implementation/gas.rs index 13932dd3c382..184430568cbc 100644 --- a/core/lib/multivm/src/versions/vm_latest/implementation/gas.rs +++ b/core/lib/multivm/src/versions/vm_latest/implementation/gas.rs @@ -9,7 +9,7 @@ use crate::{ impl Vm { pub(crate) fn calculate_computational_gas_used( &self, - tracer: &DefaultExecutionTracer, + tracer: &DefaultExecutionTracer, gas_remaining_before: u32, spent_pubdata_counter_before: u32, ) -> u32 { diff --git a/core/lib/multivm/src/versions/vm_latest/implementation/statistics.rs b/core/lib/multivm/src/versions/vm_latest/implementation/statistics.rs index 3e969d951b51..f0984d02dc18 100644 --- a/core/lib/multivm/src/versions/vm_latest/implementation/statistics.rs +++ b/core/lib/multivm/src/versions/vm_latest/implementation/statistics.rs @@ -18,7 +18,7 @@ impl Vm { &self, timestamp_initial: Timestamp, cycles_initial: u32, - tracer: &DefaultExecutionTracer, + tracer: &DefaultExecutionTracer, gas_remaining_before: u32, gas_remaining_after: u32, spent_pubdata_counter_before: u32, 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 dbe0afa33fa1..829aa93b13da 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 @@ -79,7 +79,7 @@ pub(crate) struct VmInstanceInnerState { impl Vm { // Dump inner state of the VM. - pub(crate) fn dump_inner_state(&self) -> VmInstanceInnerState { + pub(crate) fn dump_inner_state(&self) -> VmInstanceInnerState { let event_sink = self.state.event_sink.clone(); let precompile_processor_state = PrecompileProcessorTestInnerState { timestamp_history: self.state.precompiles_processor.timestamp_history.clone(), diff --git a/core/lib/multivm/src/versions/vm_latest/utils/fee.rs b/core/lib/multivm/src/versions/vm_latest/utils/fee.rs index ea4de7204434..c17e585330c9 100644 --- a/core/lib/multivm/src/versions/vm_latest/utils/fee.rs +++ b/core/lib/multivm/src/versions/vm_latest/utils/fee.rs @@ -1,8 +1,5 @@ //! Utility functions for vm -use zksync_types::{ - fee_model::{BatchFeeInput, PubdataIndependentBatchFeeModelInput}, - U256, -}; +use zksync_types::fee_model::PubdataIndependentBatchFeeModelInput; use zksync_utils::ceil_div; use crate::vm_latest::{constants::MAX_GAS_PER_PUBDATA_BYTE, L1BatchEnv}; @@ -37,34 +34,3 @@ pub(crate) fn get_batch_base_fee(l1_batch_env: &L1BatchEnv) -> u64 { derive_base_fee_and_gas_per_pubdata(l1_batch_env.fee_input.into_pubdata_independent()); base_fee } - -/// Changes the fee model output so that the expected gas per pubdata is smaller than or the `tx_gas_per_pubdata_limit`. -/// This function expects that the currently expected gas per pubdata is greater than the `tx_gas_per_pubdata_limit`. -pub(crate) fn adjust_pubdata_price_for_tx( - mut batch_fee_input: BatchFeeInput, - tx_gas_per_pubdata_limit: U256, -) -> BatchFeeInput { - match &mut batch_fee_input { - BatchFeeInput::L1Pegged(fee_input) => { - // `gasPerPubdata = ceil(17 * l1gasprice / fair_l2_gas_price)` - // `gasPerPubdata <= 17 * l1gasprice / fair_l2_gas_price + 1` - // `fair_l2_gas_price(gasPerPubdata - 1) / 17 <= l1gasprice` - let new_l1_gas_price = U256::from(fee_input.fair_l2_gas_price) - * (tx_gas_per_pubdata_limit - U256::from(1u32)) - / U256::from(17); - - fee_input.l1_gas_price = new_l1_gas_price.as_u64(); - } - BatchFeeInput::PubdataIndependent(fee_input) => { - // `gasPerPubdata = ceil(fair_pubdata_price / fair_l2_gas_price)` - // `gasPerPubdata <= fair_pubdata_price / fair_l2_gas_price + 1` - // `fair_l2_gas_price(gasPerPubdata - 1) <= fair_pubdata_price` - let new_fair_pubdata_price = U256::from(fee_input.fair_l2_gas_price) - * (tx_gas_per_pubdata_limit - U256::from(1u32)); - - fee_input.fair_pubdata_price = new_fair_pubdata_price.as_u64(); - } - } - - batch_fee_input -} diff --git a/core/lib/multivm/src/versions/vm_latest/vm.rs b/core/lib/multivm/src/versions/vm_latest/vm.rs index f570bdac72ee..a2257f96b5c3 100644 --- a/core/lib/multivm/src/versions/vm_latest/vm.rs +++ b/core/lib/multivm/src/versions/vm_latest/vm.rs @@ -29,7 +29,7 @@ use crate::{ pub struct Vm { pub(crate) bootloader_state: BootloaderState, // Current state and oracles of virtual machine - pub(crate) state: ZkSyncVmState, + pub(crate) state: ZkSyncVmState, pub(crate) storage: StoragePtr, pub(crate) system_env: SystemEnv, pub(crate) batch_env: L1BatchEnv, @@ -39,7 +39,7 @@ pub struct Vm { } impl VmInterface for Vm { - type TracerDispatcher = TracerDispatcher; + type TracerDispatcher = TracerDispatcher; fn new(batch_env: L1BatchEnv, system_env: SystemEnv, storage: StoragePtr) -> Self { let (state, bootloader_state) = new_vm_state(storage.clone(), &system_env, &batch_env); diff --git a/core/lib/multivm/src/vm_instance.rs b/core/lib/multivm/src/vm_instance.rs index a742a65f73a4..cbea46649284 100644 --- a/core/lib/multivm/src/vm_instance.rs +++ b/core/lib/multivm/src/vm_instance.rs @@ -20,7 +20,8 @@ pub enum VmInstance { VmVirtualBlocks(crate::vm_virtual_blocks::Vm), VmVirtualBlocksRefundsEnhancement(crate::vm_refunds_enhancement::Vm), VmBoojumIntegration(crate::vm_boojum_integration::Vm), - Vm1_4_1(crate::vm_latest::Vm), + Vm1_4_1(crate::vm_1_4_1::Vm), + Vm1_4_2(crate::vm_latest::Vm), } macro_rules! dispatch_vm { @@ -33,6 +34,7 @@ macro_rules! dispatch_vm { VmInstance::VmVirtualBlocksRefundsEnhancement(vm) => vm.$function($($params)*), VmInstance::VmBoojumIntegration(vm) => vm.$function($($params)*), VmInstance::Vm1_4_1(vm) => vm.$function($($params)*), + VmInstance::Vm1_4_2(vm) => vm.$function($($params)*), } }; } @@ -205,9 +207,13 @@ impl VmInstance { VmInstance::VmBoojumIntegration(vm) } VmVersion::Vm1_4_1 => { - let vm = crate::vm_latest::Vm::new(l1_batch_env, system_env, storage_view); + let vm = crate::vm_1_4_1::Vm::new(l1_batch_env, system_env, storage_view); VmInstance::Vm1_4_1(vm) } + VmVersion::Vm1_4_2 => { + let vm = crate::vm_latest::Vm::new(l1_batch_env, system_env, storage_view); + VmInstance::Vm1_4_2(vm) + } } } } diff --git a/core/lib/types/src/protocol_version.rs b/core/lib/types/src/protocol_version.rs index 639ff7f6192b..5d8b982aef53 100644 --- a/core/lib/types/src/protocol_version.rs +++ b/core/lib/types/src/protocol_version.rs @@ -42,6 +42,7 @@ pub enum ProtocolVersionId { Version19, Version20, Version21, + Version22, } impl ProtocolVersionId { @@ -78,7 +79,8 @@ impl ProtocolVersionId { ProtocolVersionId::Version18 => VmVersion::VmBoojumIntegration, ProtocolVersionId::Version19 => VmVersion::VmBoojumIntegration, ProtocolVersionId::Version20 => VmVersion::Vm1_4_1, - ProtocolVersionId::Version21 => VmVersion::Vm1_4_1, + ProtocolVersionId::Version21 => VmVersion::Vm1_4_2, + ProtocolVersionId::Version22 => VmVersion::Vm1_4_2, } } @@ -764,7 +766,8 @@ impl From for VmVersion { ProtocolVersionId::Version18 => VmVersion::VmBoojumIntegration, ProtocolVersionId::Version19 => VmVersion::VmBoojumIntegration, ProtocolVersionId::Version20 => VmVersion::Vm1_4_1, - ProtocolVersionId::Version21 => VmVersion::Vm1_4_1, + ProtocolVersionId::Version21 => VmVersion::Vm1_4_2, + ProtocolVersionId::Version22 => VmVersion::Vm1_4_2, } } } diff --git a/core/lib/types/src/vm_version.rs b/core/lib/types/src/vm_version.rs index 2a4e9dc3ef2e..5fa6c76b933f 100644 --- a/core/lib/types/src/vm_version.rs +++ b/core/lib/types/src/vm_version.rs @@ -9,11 +9,12 @@ pub enum VmVersion { VmVirtualBlocksRefundsEnhancement, VmBoojumIntegration, Vm1_4_1, + Vm1_4_2, } impl VmVersion { /// Returns the latest supported VM version. pub const fn latest() -> VmVersion { - Self::Vm1_4_1 + Self::Vm1_4_2 } } diff --git a/core/lib/zksync_core/src/api_server/execution_sandbox/tracers.rs b/core/lib/zksync_core/src/api_server/execution_sandbox/tracers.rs index e6add9a9e3b1..58d573796316 100644 --- a/core/lib/zksync_core/src/api_server/execution_sandbox/tracers.rs +++ b/core/lib/zksync_core/src/api_server/execution_sandbox/tracers.rs @@ -14,7 +14,7 @@ pub(crate) enum ApiTracer { impl ApiTracer { pub fn into_boxed< S: WriteStorage, - H: HistoryMode + multivm::HistoryMode + 'static, + H: HistoryMode + multivm::HistoryMode + 'static, >( self, ) -> MultiVmTracerPointer { diff --git a/core/lib/zksync_core/src/api_server/tx_sender/mod.rs b/core/lib/zksync_core/src/api_server/tx_sender/mod.rs index ba1463038277..cceea1fa16e2 100644 --- a/core/lib/zksync_core/src/api_server/tx_sender/mod.rs +++ b/core/lib/zksync_core/src/api_server/tx_sender/mod.rs @@ -63,6 +63,8 @@ pub struct MultiVMBaseSystemContracts { pub(crate) post_allowlist_removal: BaseSystemContracts, /// Contracts to be used after the 1.4.1 upgrade pub(crate) post_1_4_1: BaseSystemContracts, + /// Contracts to be used after the 1.4.2 upgrade + pub(crate) post_1_4_2: BaseSystemContracts, } impl MultiVMBaseSystemContracts { @@ -88,7 +90,8 @@ impl MultiVMBaseSystemContracts { | ProtocolVersionId::Version17 => self.post_virtual_blocks_finish_upgrade_fix, ProtocolVersionId::Version18 => self.post_boojum, ProtocolVersionId::Version19 => self.post_allowlist_removal, - ProtocolVersionId::Version20 | ProtocolVersionId::Version21 => self.post_1_4_1, + ProtocolVersionId::Version20 => self.post_1_4_1, + ProtocolVersionId::Version21 | ProtocolVersionId::Version22 => self.post_1_4_2, } } } @@ -121,6 +124,7 @@ impl ApiContracts { post_boojum: BaseSystemContracts::estimate_gas_post_boojum(), post_allowlist_removal: BaseSystemContracts::estimate_gas_post_allowlist_removal(), post_1_4_1: BaseSystemContracts::estimate_gas_post_1_4_1(), + post_1_4_2: BaseSystemContracts::estimate_gas_post_1_4_2(), }, eth_call: MultiVMBaseSystemContracts { pre_virtual_blocks: BaseSystemContracts::playground_pre_virtual_blocks(), @@ -130,6 +134,7 @@ impl ApiContracts { post_boojum: BaseSystemContracts::playground_post_boojum(), post_allowlist_removal: BaseSystemContracts::playground_post_allowlist_removal(), post_1_4_1: BaseSystemContracts::playground_post_1_4_1(), + post_1_4_2: BaseSystemContracts::playground_post_1_4_2(), }, } } diff --git a/core/tests/ts-integration/tests/api/web3.test.ts b/core/tests/ts-integration/tests/api/web3.test.ts index 185c9d3dd079..8cdde989a9e3 100644 --- a/core/tests/ts-integration/tests/api/web3.test.ts +++ b/core/tests/ts-integration/tests/api/web3.test.ts @@ -729,12 +729,24 @@ describe('web3 API compatibility tests', () => { let latestBlock = await alice.provider.getBlock('latest'); // Check API returns identical logs by block number and block hash. - const getLogsByNumber = await alice.provider.getLogs({ - fromBlock: latestBlock.number, - toBlock: latestBlock.number + // Logs can have different `l1BatchNumber` field though, + // if L1 batch was sealed in between API requests are processed. + const getLogsByNumber = ( + await alice.provider.getLogs({ + fromBlock: latestBlock.number, + toBlock: latestBlock.number + }) + ).map((x) => { + x.l1BatchNumber = 0; // Set bogus value. + return x; }); - const getLogsByHash = await alice.provider.getLogs({ - blockHash: latestBlock.hash + const getLogsByHash = ( + await alice.provider.getLogs({ + blockHash: latestBlock.hash + }) + ).map((x) => { + x.l1BatchNumber = 0; // Set bogus value. + return x; }); await expect(getLogsByNumber).toEqual(getLogsByHash); diff --git a/etc/multivm_bootloaders/vm_1_4_2/fee_estimate.yul/fee_estimate.yul.zbin b/etc/multivm_bootloaders/vm_1_4_2/fee_estimate.yul/fee_estimate.yul.zbin new file mode 100644 index 0000000000000000000000000000000000000000..a020cd118c7a8081b0a5c127e0b906093b9718e1 GIT binary patch literal 65888 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

p7C3`2p#>sJ~b+ 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 WPi}bCk@tM;$It)4#i!hN>;DH3AJ~!r literal 0 HcmV?d00001 diff --git a/etc/multivm_bootloaders/vm_1_4_2/proved_batch.yul/proved_batch.yul.zbin b/etc/multivm_bootloaders/vm_1_4_2/proved_batch.yul/proved_batch.yul.zbin new file mode 100644 index 0000000000000000000000000000000000000000..1ad3bd50502a9e0f9eea7ec2564dc1c2ea9531a9 GIT binary patch literal 64928 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