Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: fvs circuit statistics tracers interface #55

Merged
merged 28 commits into from
Sep 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
9f62573
expose last precompile cycles
montekki Aug 15, 2024
d26c04d
missed value in dummy
montekki Aug 15, 2024
b16f6b2
current frame
montekki Aug 15, 2024
876d9c5
set decommit cycles
montekki Aug 16, 2024
96ab5be
fix clippy
montekki Aug 16, 2024
e0b7ec1
expose cycles statistics
montekki Aug 21, 2024
c2f1f6e
adds code decommitter stats
montekki Aug 21, 2024
b69c067
rollback statistics
montekki Aug 21, 2024
e36d1d2
storage stats
montekki Aug 21, 2024
c707e3a
left field behind
montekki Aug 21, 2024
d083074
use existing write and read storage structures
montekki Aug 23, 2024
f633403
expose returntype
montekki Aug 26, 2024
d2c8118
clean up StateInterface
joonazan Aug 28, 2024
f7f2fd1
remove last_precompile_cycles
montekki Aug 29, 2024
d8062ca
don't roll back cycle counts; they are only meaningful for one tx
joonazan Aug 29, 2024
8f22384
optimize RollbackableSet
joonazan Aug 29, 2024
1e8aa42
tally storage cycles at the end; fix bugs in read_storage_slots
joonazan Aug 29, 2024
44aa7cc
decommitted code is not a storage slot
joonazan Aug 29, 2024
4e3bb96
count storage cycles on the fly because order matters
joonazan Aug 30, 2024
6a2805d
count decommit cycles of Decommit opcode, too
joonazan Sep 3, 2024
77f2677
clone new field
joonazan Sep 3, 2024
c727b54
remove weird PartialEq
joonazan Sep 3, 2024
0f2a580
remove unnecessary rev
joonazan Sep 3, 2024
c1dd0a8
remove unused method
joonazan Sep 3, 2024
8c9c706
don't store cycle counts in the vm
joonazan Sep 3, 2024
8dd24f2
expose cyclestats
joonazan Sep 3, 2024
bc008f1
less different variants of boilerplate
joonazan Sep 3, 2024
cfa7531
reverse add so it works like rust std sets
joonazan Sep 3, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions eravm-stable-interface/src/state_interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pub trait StateInterface {
fn read_register(&self, register: u8) -> (U256, bool);
fn set_register(&mut self, register: u8, value: U256, is_pointer: bool);

fn current_frame(&mut self) -> impl CallframeInterface + '_;
fn number_of_callframes(&self) -> usize;
/// zero is the current frame, one is the frame before that etc.
fn callframe(&mut self, n: usize) -> impl CallframeInterface + '_;
Expand Down Expand Up @@ -132,6 +133,10 @@ impl StateInterface for DummyState {
unimplemented!()
}

fn current_frame(&mut self) -> impl CallframeInterface + '_ {
DummyState
}

fn number_of_callframes(&self) -> usize {
unimplemented!()
}
Expand Down
18 changes: 18 additions & 0 deletions eravm-stable-interface/src/tracer_interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,19 @@ impl<T: opcodes::TypeLevelReturnType> OpcodeType for opcodes::Ret<T> {
pub trait Tracer {
fn before_instruction<OP: OpcodeType, S: StateInterface>(&mut self, _state: &mut S) {}
fn after_instruction<OP: OpcodeType, S: StateInterface>(&mut self, _state: &mut S) {}

fn on_extra_prover_cycles(&mut self, _stats: CycleStats) {}
joonazan marked this conversation as resolved.
Show resolved Hide resolved
}

#[derive(Debug, Clone, Copy)]
pub enum CycleStats {
Keccak256(u32),
Sha256(u32),
EcRecover(u32),
Secp256k1Verify(u32),
Decommit(u32),
StorageRead,
StorageWrite,
}

impl Tracer for () {}
Expand All @@ -223,6 +236,11 @@ impl<A: Tracer, B: Tracer> Tracer for (A, B) {
self.0.after_instruction::<OP, S>(state);
self.1.after_instruction::<OP, S>(state);
}

fn on_extra_prover_cycles(&mut self, stats: CycleStats) {
self.0.on_extra_prover_cycles(stats);
self.1.on_extra_prover_cycles(stats);
}
}

#[cfg(test)]
Expand Down
40 changes: 29 additions & 11 deletions src/decommit.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
use crate::{program::Program, world_diff::WorldDiff, World};
use eravm_stable_interface::{CycleStats, Tracer};
use u256::{H160, U256};
use zkevm_opcode_defs::{
ethereum_types::Address, system_params::DEPLOYER_SYSTEM_CONTRACT_ADDRESS_LOW,
};

impl WorldDiff {
pub(crate) fn decommit<T>(
pub(crate) fn decommit<T: Tracer>(
&mut self,
world: &mut impl World<T>,
tracer: &mut T,
address: U256,
default_aa_code_hash: [u8; 32],
evm_interpreter_code_hash: [u8; 32],
Expand All @@ -19,8 +21,12 @@ impl WorldDiff {
let mut is_evm = false;

let mut code_info = {
let code_info =
self.read_storage_without_refund(world, deployer_system_contract_address, address);
let code_info = self.read_storage_without_refund(
world,
tracer,
deployer_system_contract_address,
address,
);
let mut code_info_bytes = [0; 32];
code_info.to_big_endian(&mut code_info_bytes);

Expand Down Expand Up @@ -81,21 +87,25 @@ impl WorldDiff {
/// Returns the decommitted contract code and a flag set to `true` if this is a fresh decommit (i.e.,
/// the code wasn't decommitted previously in the same VM run).
#[doc(hidden)] // should be used for testing purposes only; can break VM operation otherwise
pub fn decommit_opcode<T>(
pub fn decommit_opcode<T: Tracer>(
&mut self,
world: &mut impl World<T>,
tracer: &mut T,
code_hash: U256,
) -> (Vec<u8>, bool) {
let was_decommitted = self.decommitted_hashes.as_ref().get(&code_hash) == Some(&true);
if !was_decommitted {
self.decommitted_hashes.insert(code_hash, true);
let is_new = self.decommitted_hashes.insert(code_hash, true) != Some(true);
let code = world.decommit_code(code_hash);
if is_new {
// Decommitter can process two words per cycle
tracer.on_extra_prover_cycles(CycleStats::Decommit((code.len() as u32 + 63) / 64));
}
(world.decommit_code(code_hash), !was_decommitted)
(code, is_new)
}

pub(crate) fn pay_for_decommit<T, W: World<T>>(
pub(crate) fn pay_for_decommit<T: Tracer, W: World<T>>(
&mut self,
world: &mut W,
tracer: &mut T,
decommit: UnpaidDecommit,
gas: &mut u32,
) -> Option<Program<T, W>> {
Expand All @@ -107,9 +117,17 @@ impl WorldDiff {
return None;
}

self.decommitted_hashes.insert(decommit.code_key, true);
let is_new = self.decommitted_hashes.insert(decommit.code_key, true) != Some(true);
*gas -= decommit.cost;
Some(world.decommit(decommit.code_key))

let decommit = world.decommit(decommit.code_key);
if is_new {
tracer.on_extra_prover_cycles(CycleStats::Decommit(
(decommit.code_page().len() as u32 + 1) / 2,
));
}

Some(decommit)
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/instruction_handlers/binop.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::common::instruction_boilerplate;
use super::common::boilerplate;
use crate::{
addressing_modes::{
AbsoluteStack, Addressable, AdvanceStackPointer, AnyDestination, AnySource, Arguments,
Expand Down Expand Up @@ -28,7 +28,7 @@ fn binop<
world: &mut W,
tracer: &mut T,
) -> ExecutionStatus {
instruction_boilerplate::<Op, _, _>(vm, world, tracer, |vm, args, _| {
boilerplate::<Op, _, _>(vm, world, tracer, |vm, args| {
let a = In1::get(args, &mut vm.state);
let b = Register2::get(args, &mut vm.state);
let (a, b) = if SWAP { (b, a) } else { (a, b) };
Expand Down
27 changes: 20 additions & 7 deletions src/instruction_handlers/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,41 @@ use crate::{addressing_modes::Arguments, instruction::ExecutionStatus, VirtualMa
use eravm_stable_interface::{opcodes, OpcodeType, Tracer};

#[inline(always)]
pub(crate) fn instruction_boilerplate<Opcode: OpcodeType, T: Tracer, W>(
pub(crate) fn boilerplate<Opcode: OpcodeType, T: Tracer, W>(
vm: &mut VirtualMachine<T, W>,
world: &mut W,
tracer: &mut T,
business_logic: impl FnOnce(&mut VirtualMachine<T, W>, &Arguments, &mut W),
business_logic: impl FnOnce(&mut VirtualMachine<T, W>, &Arguments),
) -> ExecutionStatus {
instruction_boilerplate_ext::<Opcode, T, W>(vm, world, tracer, |vm, args, _, world| {
business_logic(vm, args, world);
full_boilerplate::<Opcode, T, W>(vm, world, tracer, |vm, args, _, _| {
business_logic(vm, args);
ExecutionStatus::Running
})
}

#[inline(always)]
pub(crate) fn instruction_boilerplate_ext<Opcode: OpcodeType, T: Tracer, W>(
pub(crate) fn boilerplate_ext<Opcode: OpcodeType, T: Tracer, W>(
vm: &mut VirtualMachine<T, W>,
world: &mut W,
tracer: &mut T,
business_logic: impl FnOnce(&mut VirtualMachine<T, W>, &Arguments, &mut W, &mut T),
) -> ExecutionStatus {
full_boilerplate::<Opcode, T, W>(vm, world, tracer, |vm, args, world, tracer| {
business_logic(vm, args, world, tracer);
ExecutionStatus::Running
})
}

#[inline(always)]
pub(crate) fn full_boilerplate<Opcode: OpcodeType, T: Tracer, W>(
vm: &mut VirtualMachine<T, W>,
world: &mut W,
tracer: &mut T,
business_logic: impl FnOnce(
&mut VirtualMachine<T, W>,
&Arguments,
&mut T,
&mut W,
&mut T,
) -> ExecutionStatus,
) -> ExecutionStatus {
let args = unsafe { &(*vm.state.current_frame.pc).arguments };
Expand All @@ -41,7 +54,7 @@ pub(crate) fn instruction_boilerplate_ext<Opcode: OpcodeType, T: Tracer, W>(
if args.predicate().satisfied(&vm.state.flags) {
tracer.before_instruction::<Opcode, _>(vm);
vm.state.current_frame.pc = unsafe { vm.state.current_frame.pc.add(1) };
let result = business_logic(vm, args, tracer, world);
let result = business_logic(vm, args, world, tracer);
tracer.after_instruction::<Opcode, _>(vm);
result
} else {
Expand Down
12 changes: 6 additions & 6 deletions src/instruction_handlers/context.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::common::instruction_boilerplate;
use super::common::boilerplate;
use crate::{
addressing_modes::{Arguments, Destination, Register1, Source},
decommit::address_into_u256,
Expand All @@ -18,7 +18,7 @@ fn context<T: Tracer, W, Op: ContextOp>(
world: &mut W,
tracer: &mut T,
) -> ExecutionStatus {
instruction_boilerplate::<Op, _, _>(vm, world, tracer, |vm, args, _| {
boilerplate::<Op, _, _>(vm, world, tracer, |vm, args| {
let result = Op::get(&vm.state);
Register1::set(args, &mut vm.state, result)
})
Expand Down Expand Up @@ -69,7 +69,7 @@ fn context_meta<T: Tracer, W>(
world: &mut W,
tracer: &mut T,
) -> ExecutionStatus {
instruction_boilerplate::<opcodes::ContextMeta, _, _>(vm, world, tracer, |vm, args, _| {
boilerplate::<opcodes::ContextMeta, _, _>(vm, world, tracer, |vm, args| {
let result = VmMetaParameters {
heap_size: vm.state.current_frame.heap_size,
aux_heap_size: vm.state.current_frame.aux_heap_size,
Expand All @@ -94,7 +94,7 @@ fn set_context_u128<T: Tracer, W>(
world: &mut W,
tracer: &mut T,
) -> ExecutionStatus {
instruction_boilerplate::<opcodes::SetContextU128, _, _>(vm, world, tracer, |vm, args, _| {
boilerplate::<opcodes::SetContextU128, _, _>(vm, world, tracer, |vm, args| {
let value = Register1::get(args, &mut vm.state).low_u128();
vm.state.set_context_u128(value);
})
Expand All @@ -105,7 +105,7 @@ fn increment_tx_number<T: Tracer, W>(
world: &mut W,
tracer: &mut T,
) -> ExecutionStatus {
instruction_boilerplate::<opcodes::IncrementTxNumber, _, _>(vm, world, tracer, |vm, _, _| {
boilerplate::<opcodes::IncrementTxNumber, _, _>(vm, world, tracer, |vm, _| {
vm.start_new_tx();
})
}
Expand All @@ -115,7 +115,7 @@ fn aux_mutating<T: Tracer, W>(
world: &mut W,
tracer: &mut T,
) -> ExecutionStatus {
instruction_boilerplate::<opcodes::AuxMutating0, _, _>(vm, world, tracer, |_, _, _| {
boilerplate::<opcodes::AuxMutating0, _, _>(vm, world, tracer, |_, _| {
// This instruction just crashes or nops
})
}
Expand Down
6 changes: 3 additions & 3 deletions src/instruction_handlers/decommit.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::common::instruction_boilerplate;
use super::common::boilerplate_ext;
use crate::{
addressing_modes::{Arguments, Destination, Register1, Register2, Source},
fat_pointer::FatPointer,
Expand All @@ -14,7 +14,7 @@ fn decommit<T: Tracer, W: World<T>>(
world: &mut W,
tracer: &mut T,
) -> ExecutionStatus {
instruction_boilerplate::<opcodes::Decommit, _, _>(vm, world, tracer, |vm, args, world| {
boilerplate_ext::<opcodes::Decommit, _, _>(vm, world, tracer, |vm, args, world, tracer| {
let code_hash = Register1::get(args, &mut vm.state);
let extra_cost = Register2::get(args, &mut vm.state).low_u32();

Expand All @@ -32,7 +32,7 @@ fn decommit<T: Tracer, W: World<T>>(
return;
}

let (program, is_fresh) = vm.world_diff.decommit_opcode(world, code_hash);
let (program, is_fresh) = vm.world_diff.decommit_opcode(world, tracer, code_hash);
if !is_fresh {
vm.state.current_frame.gas += extra_cost;
}
Expand Down
6 changes: 3 additions & 3 deletions src/instruction_handlers/event.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::common::instruction_boilerplate;
use super::common::boilerplate_ext;
use crate::{
addressing_modes::{Arguments, Immediate1, Register1, Register2, Source},
instruction::ExecutionStatus,
Expand All @@ -14,7 +14,7 @@ fn event<T: Tracer, W>(
world: &mut W,
tracer: &mut T,
) -> ExecutionStatus {
instruction_boilerplate::<opcodes::Event, _, _>(vm, world, tracer, |vm, args, _| {
boilerplate_ext::<opcodes::Event, _, _>(vm, world, tracer, |vm, args, _, _| {
if vm.state.current_frame.address == H160::from_low_u64_be(ADDRESS_EVENT_WRITER as u64) {
let key = Register1::get(args, &mut vm.state);
let value = Register2::get(args, &mut vm.state);
Expand All @@ -36,7 +36,7 @@ fn l2_to_l1<T: Tracer, W>(
world: &mut W,
tracer: &mut T,
) -> ExecutionStatus {
instruction_boilerplate::<opcodes::L2ToL1Message, _, _>(vm, world, tracer, |vm, args, _| {
boilerplate_ext::<opcodes::L2ToL1Message, _, _>(vm, world, tracer, |vm, args, _, _| {
let key = Register1::get(args, &mut vm.state);
let value = Register2::get(args, &mut vm.state);
let is_service = Immediate1::get(args, &mut vm.state).low_u32() == 1;
Expand Down
10 changes: 5 additions & 5 deletions src/instruction_handlers/far_call.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use super::{
common::instruction_boilerplate_ext,
common::boilerplate_ext,
heap_access::grow_heap,
ret::{panic_from_failed_far_call, RETURN_COST},
AuxHeap, Heap,
Expand Down Expand Up @@ -43,7 +43,7 @@ fn far_call<
world: &mut W,
tracer: &mut T,
) -> ExecutionStatus {
instruction_boilerplate_ext::<FarCall<M>, _, _>(vm, world, tracer, |vm, args, tracer, world| {
boilerplate_ext::<FarCall<M>, _, _>(vm, world, tracer, |vm, args, world, tracer| {
let (raw_abi, raw_abi_is_pointer) = Register1::get_with_pointer_flag(args, &mut vm.state);

let address_mask: U256 = U256::MAX >> (256 - 160);
Expand All @@ -65,6 +65,7 @@ fn far_call<
let failing_part = (|| {
let decommit_result = vm.world_diff.decommit(
world,
tracer,
destination_address,
vm.settings.default_aa_code_hash,
vm.settings.evm_interpreter_code_hash,
Expand Down Expand Up @@ -92,6 +93,7 @@ fn far_call<
let (unpaid_decommit, is_evm) = decommit_result?;
let program = vm.world_diff.pay_for_decommit(
world,
tracer,
unpaid_decommit,
&mut vm.state.current_frame.gas,
)?;
Expand All @@ -107,7 +109,7 @@ fn far_call<
let Some((calldata, program, is_evm_interpreter)) = failing_part else {
vm.state.current_frame.gas += new_frame_gas.saturating_sub(RETURN_COST);
panic_from_failed_far_call(vm, tracer, exception_handler);
return ExecutionStatus::Running;
return;
};

let stipend = if is_evm_interpreter {
Expand Down Expand Up @@ -152,8 +154,6 @@ fn far_call<
| u8::from(abi.is_constructor_call);

vm.state.registers[2] = call_type.into();

ExecutionStatus::Running
})
}

Expand Down
8 changes: 4 additions & 4 deletions src/instruction_handlers/heap_access.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::common::{instruction_boilerplate, instruction_boilerplate_ext};
use super::common::{boilerplate, full_boilerplate};
use crate::{
addressing_modes::{
Arguments, Destination, DestinationWriter, Immediate1, Register1, Register2,
Expand Down Expand Up @@ -64,7 +64,7 @@ fn load<T: Tracer, W, H: HeapFromState, In: Source, const INCREMENT: bool>(
world: &mut W,
tracer: &mut T,
) -> ExecutionStatus {
instruction_boilerplate::<H::Read, _, _>(vm, world, tracer, |vm, args, _| {
boilerplate::<H::Read, _, _>(vm, world, tracer, |vm, args| {
// Pointers need not be masked here even though we do not care about them being pointers.
// They will panic, though because they are larger than 2^32.
let (pointer, _) = In::get_with_pointer_flag(args, &mut vm.state);
Expand Down Expand Up @@ -107,7 +107,7 @@ fn store<
world: &mut W,
tracer: &mut T,
) -> ExecutionStatus {
instruction_boilerplate_ext::<H::Write, _, _>(vm, world, tracer, |vm, args, _, _| {
full_boilerplate::<H::Write, _, _>(vm, world, tracer, |vm, args, _, _| {
// Pointers need not be masked here even though we do not care about them being pointers.
// They will panic, though because they are larger than 2^32.
let (pointer, _) = In::get_with_pointer_flag(args, &mut vm.state);
Expand Down Expand Up @@ -165,7 +165,7 @@ fn load_pointer<T: Tracer, W, const INCREMENT: bool>(
world: &mut W,
tracer: &mut T,
) -> ExecutionStatus {
instruction_boilerplate::<opcodes::PointerRead, _, _>(vm, world, tracer, |vm, args, _| {
boilerplate::<opcodes::PointerRead, _, _>(vm, world, tracer, |vm, args| {
let (input, input_is_pointer) = Register1::get_with_pointer_flag(args, &mut vm.state);
if !input_is_pointer {
vm.state.current_frame.pc = &*vm.panic;
Expand Down
Loading
Loading