Skip to content

Commit

Permalink
feat: Integrate tracers and implement circuits tracer in vm2 (#2653)
Browse files Browse the repository at this point in the history
## What ❔

Integrates tracers support into the codebase and implements the circuits
tracer.

## Why ❔

Tracers are required for some VM applications, e.g. to determine batch
seal criteria and for tracing calls.

## Checklist

- [x] PR title corresponds to the body of PR (we generate changelog
entries from PRs).
- [x] Documentation comments have been added / updated.
- [x] Code has been formatted via `zk fmt` and `zk lint`.

---------

Co-authored-by: Joonatan Saarhelo <[email protected]>
  • Loading branch information
montekki and joonazan committed Sep 4, 2024
1 parent b2dd9a5 commit 87b02e3
Show file tree
Hide file tree
Showing 16 changed files with 301 additions and 49 deletions.
11 changes: 10 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ zk_evm_1_4_1 = { package = "zk_evm", version = "0.141.0" }
zk_evm_1_5_0 = { package = "zk_evm", version = "=0.150.4" }

# New VM; pinned to a specific commit because of instability
vm2 = { git = "https://github.com/matter-labs/vm2.git", rev = "2276b7b5af520fca0477bdafe43781b51896d235" }
vm2 = { git = "https://github.com/matter-labs/vm2.git", rev = "4ef15d46410ffc11744771a3a6c7c09dd9470c90" }

# Consensus dependencies.
zksync_concurrency = "=0.1.0-rc.11"
Expand Down
5 changes: 5 additions & 0 deletions core/lib/multivm/src/versions/shadow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,11 @@ impl DivergenceErrors {
let shadow_logs = UniqueStorageLogs::new(&shadow_result.logs.storage_logs);
self.check_match("logs.storage_logs", &main_logs, &shadow_logs);
self.check_match("refunds", &main_result.refunds, &shadow_result.refunds);
self.check_match(
"statistics.circuit_statistic",
&main_result.statistics.circuit_statistic,
&shadow_result.statistics.circuit_statistic,
);
self.check_match(
"gas_remaining",
&main_result.statistics.gas_remaining,
Expand Down
157 changes: 157 additions & 0 deletions core/lib/multivm/src/versions/vm_fast/circuits_tracer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
use circuit_sequencer_api_1_5_0::{geometry_config::get_geometry_config, toolset::GeometryConfig};
use vm2::{CycleStats, Opcode, OpcodeType, StateInterface, Tracer};
use zksync_vm_interface::CircuitStatistic;

use crate::vm_latest::tracers::circuits_capacity::*;

#[derive(Debug, Default, Clone, PartialEq)]
pub(crate) struct CircuitsTracer {
main_vm_cycles: u32,
ram_permutation_cycles: u32,
storage_application_cycles: u32,
storage_sorter_cycles: u32,
code_decommitter_cycles: u32,
code_decommitter_sorter_cycles: u32,
log_demuxer_cycles: u32,
events_sorter_cycles: u32,
keccak256_cycles: u32,
ecrecover_cycles: u32,
sha256_cycles: u32,
secp256k1_verify_cycles: u32,
transient_storage_checker_cycles: u32,
}

impl Tracer for CircuitsTracer {
fn after_instruction<OP: OpcodeType, S: StateInterface>(&mut self, _state: &mut S) {
self.main_vm_cycles += 1;

match OP::VALUE {
Opcode::Nop
| Opcode::Add
| Opcode::Sub
| Opcode::Mul
| Opcode::Div
| Opcode::Jump
| Opcode::Xor
| Opcode::And
| Opcode::Or
| Opcode::ShiftLeft
| Opcode::ShiftRight
| Opcode::RotateLeft
| Opcode::RotateRight
| Opcode::PointerAdd
| Opcode::PointerSub
| Opcode::PointerPack
| Opcode::PointerShrink => {
self.ram_permutation_cycles += RICH_ADDRESSING_OPCODE_RAM_CYCLES;
}
Opcode::This
| Opcode::Caller
| Opcode::CodeAddress
| Opcode::ContextMeta
| Opcode::ErgsLeft
| Opcode::SP
| Opcode::ContextU128
| Opcode::SetContextU128
| Opcode::AuxMutating0
| Opcode::IncrementTxNumber
| Opcode::Ret(_)
| Opcode::NearCall => {
self.ram_permutation_cycles += AVERAGE_OPCODE_RAM_CYCLES;
}
Opcode::StorageRead => {
self.ram_permutation_cycles += STORAGE_READ_RAM_CYCLES;
self.log_demuxer_cycles += STORAGE_READ_LOG_DEMUXER_CYCLES;
self.storage_sorter_cycles += STORAGE_READ_STORAGE_SORTER_CYCLES;
}
Opcode::TransientStorageRead => {
self.ram_permutation_cycles += TRANSIENT_STORAGE_READ_RAM_CYCLES;
self.log_demuxer_cycles += TRANSIENT_STORAGE_READ_LOG_DEMUXER_CYCLES;
self.transient_storage_checker_cycles +=
TRANSIENT_STORAGE_READ_TRANSIENT_STORAGE_CHECKER_CYCLES;
}
Opcode::StorageWrite => {
self.ram_permutation_cycles += STORAGE_WRITE_RAM_CYCLES;
self.log_demuxer_cycles += STORAGE_WRITE_LOG_DEMUXER_CYCLES;
self.storage_sorter_cycles += STORAGE_WRITE_STORAGE_SORTER_CYCLES;
}
Opcode::TransientStorageWrite => {
self.ram_permutation_cycles += TRANSIENT_STORAGE_WRITE_RAM_CYCLES;
self.log_demuxer_cycles += TRANSIENT_STORAGE_WRITE_LOG_DEMUXER_CYCLES;
self.transient_storage_checker_cycles +=
TRANSIENT_STORAGE_WRITE_TRANSIENT_STORAGE_CHECKER_CYCLES;
}
Opcode::L2ToL1Message | Opcode::Event => {
self.ram_permutation_cycles += EVENT_RAM_CYCLES;
self.log_demuxer_cycles += EVENT_LOG_DEMUXER_CYCLES;
self.events_sorter_cycles += EVENT_EVENTS_SORTER_CYCLES;
}
Opcode::PrecompileCall => {
self.ram_permutation_cycles += PRECOMPILE_RAM_CYCLES;
self.log_demuxer_cycles += PRECOMPILE_LOG_DEMUXER_CYCLES;
}
Opcode::Decommit => {
// Note, that for decommit the log demuxer circuit is not used.
self.ram_permutation_cycles += LOG_DECOMMIT_RAM_CYCLES;
self.code_decommitter_sorter_cycles += LOG_DECOMMIT_DECOMMITTER_SORTER_CYCLES;
}
Opcode::FarCall(_) => {
self.ram_permutation_cycles += FAR_CALL_RAM_CYCLES;
self.code_decommitter_sorter_cycles += FAR_CALL_CODE_DECOMMITTER_SORTER_CYCLES;
self.storage_sorter_cycles += FAR_CALL_STORAGE_SORTER_CYCLES;
self.log_demuxer_cycles += FAR_CALL_LOG_DEMUXER_CYCLES;
}
Opcode::AuxHeapWrite | Opcode::HeapWrite /* StaticMemoryWrite */ => {
self.ram_permutation_cycles += UMA_WRITE_RAM_CYCLES;
}
Opcode::AuxHeapRead | Opcode::HeapRead | Opcode::PointerRead /* StaticMemoryRead */ => {
self.ram_permutation_cycles += UMA_READ_RAM_CYCLES;
}
}
}

fn on_extra_prover_cycles(&mut self, stats: CycleStats) {
match stats {
CycleStats::Keccak256(cycles) => self.keccak256_cycles += cycles,
CycleStats::Sha256(cycles) => self.sha256_cycles += cycles,
CycleStats::EcRecover(cycles) => self.ecrecover_cycles += cycles,
CycleStats::Secp256k1Verify(cycles) => self.secp256k1_verify_cycles += cycles,
CycleStats::Decommit(cycles) => self.code_decommitter_cycles += cycles,
CycleStats::StorageRead => self.storage_application_cycles += 1,
CycleStats::StorageWrite => self.storage_application_cycles += 2,
}
}
}

impl CircuitsTracer {
pub(crate) fn circuit_statistic(&self) -> CircuitStatistic {
CircuitStatistic {
main_vm: self.main_vm_cycles as f32 / GEOMETRY_CONFIG.cycles_per_vm_snapshot as f32,
ram_permutation: self.ram_permutation_cycles as f32
/ GEOMETRY_CONFIG.cycles_per_ram_permutation as f32,
storage_application: self.storage_application_cycles as f32
/ GEOMETRY_CONFIG.cycles_per_storage_application as f32,
storage_sorter: self.storage_sorter_cycles as f32
/ GEOMETRY_CONFIG.cycles_per_storage_sorter as f32,
code_decommitter: self.code_decommitter_cycles as f32
/ GEOMETRY_CONFIG.cycles_per_code_decommitter as f32,
code_decommitter_sorter: self.code_decommitter_sorter_cycles as f32
/ GEOMETRY_CONFIG.cycles_code_decommitter_sorter as f32,
log_demuxer: self.log_demuxer_cycles as f32
/ GEOMETRY_CONFIG.cycles_per_log_demuxer as f32,
events_sorter: self.events_sorter_cycles as f32
/ GEOMETRY_CONFIG.cycles_per_events_or_l1_messages_sorter as f32,
keccak256: self.keccak256_cycles as f32
/ GEOMETRY_CONFIG.cycles_per_keccak256_circuit as f32,
ecrecover: self.ecrecover_cycles as f32
/ GEOMETRY_CONFIG.cycles_per_ecrecover_circuit as f32,
sha256: self.sha256_cycles as f32 / GEOMETRY_CONFIG.cycles_per_sha256_circuit as f32,
secp256k1_verify: self.secp256k1_verify_cycles as f32
/ GEOMETRY_CONFIG.cycles_per_secp256r1_verify_circuit as f32,
transient_storage_checker: self.transient_storage_checker_cycles as f32
/ GEOMETRY_CONFIG.cycles_per_transient_storage_sorter as f32,
}
}
}

const GEOMETRY_CONFIG: GeometryConfig = get_geometry_config();
1 change: 1 addition & 0 deletions core/lib/multivm/src/versions/vm_fast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ pub use self::vm::Vm;

mod bootloader_state;
mod bytecode;
mod circuits_tracer;
mod events;
mod glue;
mod hook;
Expand Down
10 changes: 5 additions & 5 deletions core/lib/multivm/src/versions/vm_fast/tests/code_oracle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,11 +207,11 @@ fn refunds_in_code_oracle() {

let account = &mut vm.rich_accounts[0];
if decommit {
let (_, is_fresh) = vm
.vm
.inner
.world_diff
.decommit_opcode(&mut vm.vm.world, h256_to_u256(normal_zkevm_bytecode_hash));
let (_, is_fresh) = vm.vm.inner.world_diff.decommit_opcode(
&mut vm.vm.world,
&mut vm.vm.tracer,
h256_to_u256(normal_zkevm_bytecode_hash),
);
assert!(is_fresh);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::{
VmExecutionMode, VmExecutionResultAndLogs, VmInterface, VmInterfaceExt,
VmInterfaceHistoryEnabled, VmRevertReason,
},
vm_fast::Vm,
vm_fast::{circuits_tracer::CircuitsTracer, vm::World, Vm},
};

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -185,14 +185,14 @@ impl TransactionTestInfo {

// TODO this doesn't include all the state of ModifiedWorld
#[derive(Debug, PartialEq)]
struct VmStateDump {
state: vm2::State,
struct VmStateDump<S> {
state: vm2::State<CircuitsTracer, World<S, CircuitsTracer>>,
storage_writes: Vec<((H160, U256), U256)>,
events: Box<[vm2::Event]>,
}

impl<S: ReadStorage> Vm<S> {
fn dump_state(&self) -> VmStateDump {
impl<S: ReadStorage + Clone + PartialEq> Vm<S> {
fn dump_state(&self) -> VmStateDump<S> {
VmStateDump {
state: self.inner.state.clone(),
storage_writes: self
Expand Down
5 changes: 4 additions & 1 deletion core/lib/multivm/src/versions/vm_fast/tests/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ use crate::interface::storage::ReadStorage;
pub(crate) static BASE_SYSTEM_CONTRACTS: Lazy<BaseSystemContracts> =
Lazy::new(BaseSystemContracts::load_from_disk);

pub(crate) fn verify_required_memory(state: &State, required_values: Vec<(U256, HeapId, u32)>) {
pub(crate) fn verify_required_memory<T, W>(
state: &State<T, W>,
required_values: Vec<(U256, HeapId, u32)>,
) {
for (required_value, memory_page, cell) in required_values {
let current_value = state.heaps[memory_page].read_u256(cell * 32);
assert_eq!(current_value, required_value);
Expand Down
Loading

0 comments on commit 87b02e3

Please sign in to comment.