From c2c6cd0c17ae57df73ec88d4b79ca79232903070 Mon Sep 17 00:00:00 2001 From: Marcos Nicolau Date: Thu, 22 Aug 2024 17:40:03 -0300 Subject: [PATCH 01/32] Bootloader trait --- .../src/versions/era_vm/tracers/traits.rs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 core/lib/multivm/src/versions/era_vm/tracers/traits.rs diff --git a/core/lib/multivm/src/versions/era_vm/tracers/traits.rs b/core/lib/multivm/src/versions/era_vm/tracers/traits.rs new file mode 100644 index 000000000000..0b2774667bde --- /dev/null +++ b/core/lib/multivm/src/versions/era_vm/tracers/traits.rs @@ -0,0 +1,21 @@ +pub use era_vm::tracers::tracer::Tracer; +use era_vm::{state::VMState, Execution, Opcode}; + +pub trait BootloaderTracer { + fn before_bootloader_execution( + &mut self, + opcode: &Opcode, + execution: &mut Execution, + state: &mut VMState, + ); + + fn after_bootloader_execution( + &mut self, + opcode: &Opcode, + execution: &mut Execution, + state: &mut VMState, + ); +} + +pub trait VmTracer: Tracer + BootloaderTracer {} +impl VmTracer for T {} From f09740196e985d9d9b983a12d190e87e72560bd5 Mon Sep 17 00:00:00 2001 From: Marcos Nicolau Date: Thu, 22 Aug 2024 17:41:54 -0300 Subject: [PATCH 02/32] Manager and dispatcher tracers --- .../src/versions/era_vm/tracers/dispatcher.rs | 63 ++++++++ .../src/versions/era_vm/tracers/manager.rs | 140 ++++++++++++++++++ 2 files changed, 203 insertions(+) create mode 100644 core/lib/multivm/src/versions/era_vm/tracers/dispatcher.rs create mode 100644 core/lib/multivm/src/versions/era_vm/tracers/manager.rs diff --git a/core/lib/multivm/src/versions/era_vm/tracers/dispatcher.rs b/core/lib/multivm/src/versions/era_vm/tracers/dispatcher.rs new file mode 100644 index 000000000000..c1bfad35adf8 --- /dev/null +++ b/core/lib/multivm/src/versions/era_vm/tracers/dispatcher.rs @@ -0,0 +1,63 @@ +use super::traits::{BootloaderTracer, VmTracer}; +use era_vm::{state::VMState, tracers::tracer::Tracer, Execution, Opcode}; + +#[derive(Default)] +// dispatcher calls to other tracers +pub struct TracerDispatcher { + tracers: Vec>, +} + +impl Tracer for TracerDispatcher { + fn before_decoding(&mut self, execution: &mut Execution, state: &mut VMState) { + for tracer in self.tracers.iter_mut() { + tracer.before_decoding(execution, state); + } + } + + fn after_decoding(&mut self, opcode: &Opcode, execution: &mut Execution, state: &mut VMState) { + for tracer in self.tracers.iter_mut() { + tracer.after_decoding(opcode, execution, state); + } + } + + fn before_execution( + &mut self, + opcode: &Opcode, + execution: &mut Execution, + state: &mut VMState, + ) { + for tracer in self.tracers.iter_mut() { + tracer.before_execution(opcode, execution, state); + } + } + + fn after_execution(&mut self, opcode: &Opcode, execution: &mut Execution, state: &mut VMState) { + for tracer in self.tracers.iter_mut() { + tracer.after_execution(opcode, execution, state); + } + } +} + +impl BootloaderTracer for TracerDispatcher { + fn before_bootloader_execution( + &mut self, + opcode: &Opcode, + execution: &mut Execution, + state: &mut VMState, + ) { + for tracer in self.tracers.iter_mut() { + tracer.before_bootloader_execution(opcode, execution, state); + } + } + + fn after_bootloader_execution( + &mut self, + opcode: &Opcode, + execution: &mut Execution, + state: &mut VMState, + ) { + for tracer in self.tracers.iter_mut() { + tracer.after_bootloader_execution(opcode, execution, state); + } + } +} diff --git a/core/lib/multivm/src/versions/era_vm/tracers/manager.rs b/core/lib/multivm/src/versions/era_vm/tracers/manager.rs new file mode 100644 index 000000000000..78390dd26180 --- /dev/null +++ b/core/lib/multivm/src/versions/era_vm/tracers/manager.rs @@ -0,0 +1,140 @@ +use era_vm::{execution::Execution, opcode::Opcode, state::VMState, tracers::tracer::Tracer}; +use zksync_state::{ReadStorage, StoragePtr}; + +use super::{ + circuits_tracer::CircuitsTracer, + dispatcher::TracerDispatcher, + pubdata_tracer::PubdataTracer, + refunds_tracer::RefundsTracer, + result_tracer::ResultTracer, + traits::{BootloaderTracer, VmTracer}, +}; + +// this tracer manager is the one that gets called when running the vm +pub struct VmTracerManager { + dispatcher: TracerDispatcher, + result_tracer: ResultTracer, + refund_tracer: Option, + pubdata_tracer: Option, + circuits_tracer: CircuitsTracer, + storage: StoragePtr, +} + +impl Tracer for VmTracerManager { + fn before_decoding(&mut self, execution: &mut Execution, state: &mut VMState) { + // Call the dispatcher to handle all the tracers added to it + self.dispatcher.before_decoding(execution, state); + + // Individual tracers + self.result_tracer.before_decoding(execution, state); + if let Some(refunds_tracer) = &mut self.refund_tracer { + refunds_tracer.before_decoding(execution, state); + } + if let Some(pubdata_tracer) = &mut self.pubdata_tracer { + pubdata_tracer.before_decoding(execution, state); + } + self.circuits_tracer.before_decoding(execution, state); + } + + fn after_decoding(&mut self, opcode: &Opcode, execution: &mut Execution, state: &mut VMState) { + // Call the dispatcher to handle all the tracers added to it + self.dispatcher.after_decoding(opcode, execution, state); + + // Individual tracers + self.result_tracer.after_decoding(opcode, execution, state); + if let Some(refunds_tracer) = &mut self.refund_tracer { + refunds_tracer.after_decoding(opcode, execution, state); + } + if let Some(pubdata_tracer) = &mut self.pubdata_tracer { + pubdata_tracer.after_decoding(opcode, execution, state); + } + self.circuits_tracer + .after_decoding(opcode, execution, state); + } + + fn before_execution( + &mut self, + opcode: &Opcode, + execution: &mut Execution, + state: &mut VMState, + ) { + // Call the dispatcher to handle all the tracers added to it + self.dispatcher.before_execution(opcode, execution, state); + + // Individual tracers + self.result_tracer + .before_execution(opcode, execution, state); + if let Some(refunds_tracer) = &mut self.refund_tracer { + refunds_tracer.before_execution(opcode, execution, state); + } + if let Some(pubdata_tracer) = &mut self.pubdata_tracer { + pubdata_tracer.before_execution(opcode, execution, state); + } + self.circuits_tracer + .before_execution(opcode, execution, state); + } + + fn after_execution(&mut self, opcode: &Opcode, execution: &mut Execution, state: &mut VMState) { + // Call the dispatcher to handle all the tracers added to it + self.dispatcher.after_execution(opcode, execution, state); + + // Individual tracers + self.result_tracer.after_execution(opcode, execution, state); + if let Some(refunds_tracer) = &mut self.refund_tracer { + refunds_tracer.after_execution(opcode, execution, state); + } + if let Some(pubdata_tracer) = &mut self.pubdata_tracer { + pubdata_tracer.after_execution(opcode, execution, state); + } + self.circuits_tracer + .after_execution(opcode, execution, state); + } +} + +impl BootloaderTracer for VmTracerManager { + fn before_bootloader_execution( + &mut self, + opcode: &Opcode, + execution: &mut Execution, + state: &mut VMState, + ) { + // Call the dispatcher to handle all the tracers added to it + self.dispatcher + .before_bootloader_execution(opcode, execution, state); + + // Individual tracers + self.result_tracer + .before_bootloader_execution(opcode, execution, state); + if let Some(refunds_tracer) = &mut self.refund_tracer { + refunds_tracer.before_bootloader_execution(opcode, execution, state); + } + if let Some(pubdata_tracer) = &mut self.pubdata_tracer { + pubdata_tracer.before_bootloader_execution(opcode, execution, state); + } + self.circuits_tracer + .before_bootloader_execution(opcode, execution, state); + } + + fn after_bootloader_execution( + &mut self, + opcode: &Opcode, + execution: &mut Execution, + state: &mut VMState, + ) { + // Call the dispatcher to handle all the tracers added to it + self.dispatcher + .after_bootloader_execution(opcode, execution, state); + + // Individual tracers + self.result_tracer + .after_bootloader_execution(opcode, execution, state); + if let Some(refunds_tracer) = &mut self.refund_tracer { + refunds_tracer.after_bootloader_execution(opcode, execution, state); + } + if let Some(pubdata_tracer) = &mut self.pubdata_tracer { + pubdata_tracer.after_bootloader_execution(opcode, execution, state); + } + self.circuits_tracer + .after_bootloader_execution(opcode, execution, state); + } +} From f5a4b3fad90b1ab580f4a7a69766d0a11e92c506 Mon Sep 17 00:00:00 2001 From: Marcos Nicolau Date: Thu, 22 Aug 2024 17:42:52 -0300 Subject: [PATCH 03/32] Create basic tracers skeleton --- .../era_vm/tracers/circuits_tracer.rs | 54 +++++++++++++++++++ .../src/versions/era_vm/tracers/mod.rs | 7 +++ .../versions/era_vm/tracers/pubdata_tracer.rs | 54 +++++++++++++++++++ .../versions/era_vm/tracers/refunds_tracer.rs | 54 +++++++++++++++++++ .../versions/era_vm/tracers/result_tracer.rs | 54 +++++++++++++++++++ 5 files changed, 223 insertions(+) create mode 100644 core/lib/multivm/src/versions/era_vm/tracers/circuits_tracer.rs create mode 100644 core/lib/multivm/src/versions/era_vm/tracers/mod.rs create mode 100644 core/lib/multivm/src/versions/era_vm/tracers/pubdata_tracer.rs create mode 100644 core/lib/multivm/src/versions/era_vm/tracers/refunds_tracer.rs create mode 100644 core/lib/multivm/src/versions/era_vm/tracers/result_tracer.rs diff --git a/core/lib/multivm/src/versions/era_vm/tracers/circuits_tracer.rs b/core/lib/multivm/src/versions/era_vm/tracers/circuits_tracer.rs new file mode 100644 index 000000000000..154592fbe361 --- /dev/null +++ b/core/lib/multivm/src/versions/era_vm/tracers/circuits_tracer.rs @@ -0,0 +1,54 @@ +use super::traits::{BootloaderTracer, Tracer}; + +pub struct CircuitsTracer {} + +impl Tracer for CircuitsTracer { + fn before_decoding( + &mut self, + execution: &mut era_vm::Execution, + state: &mut era_vm::state::VMState, + ) { + } + + fn after_decoding( + &mut self, + opcode: &era_vm::Opcode, + execution: &mut era_vm::Execution, + state: &mut era_vm::state::VMState, + ) { + } + + fn before_execution( + &mut self, + opcode: &era_vm::Opcode, + execution: &mut era_vm::Execution, + state: &mut era_vm::state::VMState, + ) { + } + + fn after_execution( + &mut self, + opcode: &era_vm::Opcode, + execution: &mut era_vm::Execution, + state: &mut era_vm::state::VMState, + ) { + } +} + +impl BootloaderTracer for CircuitsTracer { + fn before_bootloader_execution( + &mut self, + opcode: &era_vm::Opcode, + execution: &mut era_vm::Execution, + state: &mut era_vm::state::VMState, + ) { + } + + fn after_bootloader_execution( + &mut self, + opcode: &era_vm::Opcode, + execution: &mut era_vm::Execution, + state: &mut era_vm::state::VMState, + ) { + } +} diff --git a/core/lib/multivm/src/versions/era_vm/tracers/mod.rs b/core/lib/multivm/src/versions/era_vm/tracers/mod.rs new file mode 100644 index 000000000000..c03ac29c143f --- /dev/null +++ b/core/lib/multivm/src/versions/era_vm/tracers/mod.rs @@ -0,0 +1,7 @@ +mod circuits_tracer; +mod dispatcher; +pub mod manager; +mod pubdata_tracer; +mod refunds_tracer; +mod result_tracer; +mod traits; diff --git a/core/lib/multivm/src/versions/era_vm/tracers/pubdata_tracer.rs b/core/lib/multivm/src/versions/era_vm/tracers/pubdata_tracer.rs new file mode 100644 index 000000000000..14a733da7196 --- /dev/null +++ b/core/lib/multivm/src/versions/era_vm/tracers/pubdata_tracer.rs @@ -0,0 +1,54 @@ +use super::traits::{BootloaderTracer, Tracer, VmTracer}; + +pub struct PubdataTracer {} + +impl Tracer for PubdataTracer { + fn before_decoding( + &mut self, + execution: &mut era_vm::Execution, + state: &mut era_vm::state::VMState, + ) { + } + + fn after_decoding( + &mut self, + opcode: &era_vm::Opcode, + execution: &mut era_vm::Execution, + state: &mut era_vm::state::VMState, + ) { + } + + fn before_execution( + &mut self, + opcode: &era_vm::Opcode, + execution: &mut era_vm::Execution, + state: &mut era_vm::state::VMState, + ) { + } + + fn after_execution( + &mut self, + opcode: &era_vm::Opcode, + execution: &mut era_vm::Execution, + state: &mut era_vm::state::VMState, + ) { + } +} + +impl BootloaderTracer for PubdataTracer { + fn before_bootloader_execution( + &mut self, + opcode: &era_vm::Opcode, + execution: &mut era_vm::Execution, + state: &mut era_vm::state::VMState, + ) { + } + + fn after_bootloader_execution( + &mut self, + opcode: &era_vm::Opcode, + execution: &mut era_vm::Execution, + state: &mut era_vm::state::VMState, + ) { + } +} diff --git a/core/lib/multivm/src/versions/era_vm/tracers/refunds_tracer.rs b/core/lib/multivm/src/versions/era_vm/tracers/refunds_tracer.rs new file mode 100644 index 000000000000..f7a1d36ab4ef --- /dev/null +++ b/core/lib/multivm/src/versions/era_vm/tracers/refunds_tracer.rs @@ -0,0 +1,54 @@ +use super::traits::{BootloaderTracer, Tracer, VmTracer}; + +pub struct RefundsTracer {} + +impl Tracer for RefundsTracer { + fn before_decoding( + &mut self, + execution: &mut era_vm::Execution, + state: &mut era_vm::state::VMState, + ) { + } + + fn after_decoding( + &mut self, + opcode: &era_vm::Opcode, + execution: &mut era_vm::Execution, + state: &mut era_vm::state::VMState, + ) { + } + + fn before_execution( + &mut self, + opcode: &era_vm::Opcode, + execution: &mut era_vm::Execution, + state: &mut era_vm::state::VMState, + ) { + } + + fn after_execution( + &mut self, + opcode: &era_vm::Opcode, + execution: &mut era_vm::Execution, + state: &mut era_vm::state::VMState, + ) { + } +} + +impl BootloaderTracer for RefundsTracer { + fn before_bootloader_execution( + &mut self, + opcode: &era_vm::Opcode, + execution: &mut era_vm::Execution, + state: &mut era_vm::state::VMState, + ) { + } + + fn after_bootloader_execution( + &mut self, + opcode: &era_vm::Opcode, + execution: &mut era_vm::Execution, + state: &mut era_vm::state::VMState, + ) { + } +} diff --git a/core/lib/multivm/src/versions/era_vm/tracers/result_tracer.rs b/core/lib/multivm/src/versions/era_vm/tracers/result_tracer.rs new file mode 100644 index 000000000000..a9f7c1c5892b --- /dev/null +++ b/core/lib/multivm/src/versions/era_vm/tracers/result_tracer.rs @@ -0,0 +1,54 @@ +use super::traits::{BootloaderTracer, Tracer, VmTracer}; + +pub struct ResultTracer {} + +impl Tracer for ResultTracer { + fn before_decoding( + &mut self, + execution: &mut era_vm::Execution, + state: &mut era_vm::state::VMState, + ) { + } + + fn after_decoding( + &mut self, + opcode: &era_vm::Opcode, + execution: &mut era_vm::Execution, + state: &mut era_vm::state::VMState, + ) { + } + + fn before_execution( + &mut self, + opcode: &era_vm::Opcode, + execution: &mut era_vm::Execution, + state: &mut era_vm::state::VMState, + ) { + } + + fn after_execution( + &mut self, + opcode: &era_vm::Opcode, + execution: &mut era_vm::Execution, + state: &mut era_vm::state::VMState, + ) { + } +} + +impl BootloaderTracer for ResultTracer { + fn before_bootloader_execution( + &mut self, + opcode: &era_vm::Opcode, + execution: &mut era_vm::Execution, + state: &mut era_vm::state::VMState, + ) { + } + + fn after_bootloader_execution( + &mut self, + opcode: &era_vm::Opcode, + execution: &mut era_vm::Execution, + state: &mut era_vm::state::VMState, + ) { + } +} From ff82b18b023a95b717e769af28dab2beaad260c1 Mon Sep 17 00:00:00 2001 From: Marcos Nicolau Date: Thu, 22 Aug 2024 18:32:51 -0300 Subject: [PATCH 04/32] Implement tracers on vm run method --- core/lib/multivm/src/versions/era_vm/mod.rs | 1 + .../era_vm/tracers/circuits_tracer.rs | 6 ++ .../src/versions/era_vm/tracers/dispatcher.rs | 9 ++- .../src/versions/era_vm/tracers/manager.rs | 55 ++++++++++--------- .../src/versions/era_vm/tracers/mod.rs | 10 ++-- .../versions/era_vm/tracers/pubdata_tracer.rs | 6 ++ .../versions/era_vm/tracers/refunds_tracer.rs | 8 ++- .../versions/era_vm/tracers/result_tracer.rs | 6 ++ core/lib/multivm/src/versions/era_vm/vm.rs | 22 +++++++- 9 files changed, 89 insertions(+), 34 deletions(-) diff --git a/core/lib/multivm/src/versions/era_vm/mod.rs b/core/lib/multivm/src/versions/era_vm/mod.rs index ab5d40ae124b..11925ae46f47 100644 --- a/core/lib/multivm/src/versions/era_vm/mod.rs +++ b/core/lib/multivm/src/versions/era_vm/mod.rs @@ -8,5 +8,6 @@ mod refunds; mod snapshot; #[cfg(test)] mod tests; +mod tracers; mod transaction_data; pub mod vm; diff --git a/core/lib/multivm/src/versions/era_vm/tracers/circuits_tracer.rs b/core/lib/multivm/src/versions/era_vm/tracers/circuits_tracer.rs index 154592fbe361..9e53840c19b7 100644 --- a/core/lib/multivm/src/versions/era_vm/tracers/circuits_tracer.rs +++ b/core/lib/multivm/src/versions/era_vm/tracers/circuits_tracer.rs @@ -2,6 +2,12 @@ use super::traits::{BootloaderTracer, Tracer}; pub struct CircuitsTracer {} +impl CircuitsTracer { + pub fn new() -> Self { + Self {} + } +} + impl Tracer for CircuitsTracer { fn before_decoding( &mut self, diff --git a/core/lib/multivm/src/versions/era_vm/tracers/dispatcher.rs b/core/lib/multivm/src/versions/era_vm/tracers/dispatcher.rs index c1bfad35adf8..f1c6c0aadd6b 100644 --- a/core/lib/multivm/src/versions/era_vm/tracers/dispatcher.rs +++ b/core/lib/multivm/src/versions/era_vm/tracers/dispatcher.rs @@ -1,12 +1,19 @@ -use super::traits::{BootloaderTracer, VmTracer}; use era_vm::{state::VMState, tracers::tracer::Tracer, Execution, Opcode}; +use super::traits::{BootloaderTracer, VmTracer}; + #[derive(Default)] // dispatcher calls to other tracers pub struct TracerDispatcher { tracers: Vec>, } +impl TracerDispatcher { + pub fn new(tracers: Vec>) -> Self { + Self { tracers } + } +} + impl Tracer for TracerDispatcher { fn before_decoding(&mut self, execution: &mut Execution, state: &mut VMState) { for tracer in self.tracers.iter_mut() { diff --git a/core/lib/multivm/src/versions/era_vm/tracers/manager.rs b/core/lib/multivm/src/versions/era_vm/tracers/manager.rs index 78390dd26180..f1f60cdf1b4d 100644 --- a/core/lib/multivm/src/versions/era_vm/tracers/manager.rs +++ b/core/lib/multivm/src/versions/era_vm/tracers/manager.rs @@ -2,12 +2,8 @@ use era_vm::{execution::Execution, opcode::Opcode, state::VMState, tracers::trac use zksync_state::{ReadStorage, StoragePtr}; use super::{ - circuits_tracer::CircuitsTracer, - dispatcher::TracerDispatcher, - pubdata_tracer::PubdataTracer, - refunds_tracer::RefundsTracer, - result_tracer::ResultTracer, - traits::{BootloaderTracer, VmTracer}, + circuits_tracer::CircuitsTracer, dispatcher::TracerDispatcher, pubdata_tracer::PubdataTracer, + refunds_tracer::RefundsTracer, result_tracer::ResultTracer, traits::BootloaderTracer, }; // this tracer manager is the one that gets called when running the vm @@ -15,11 +11,28 @@ pub struct VmTracerManager { dispatcher: TracerDispatcher, result_tracer: ResultTracer, refund_tracer: Option, - pubdata_tracer: Option, + pubdata_tracer: PubdataTracer, circuits_tracer: CircuitsTracer, storage: StoragePtr, } +impl VmTracerManager { + pub fn new( + storage: StoragePtr, + dispatcher: TracerDispatcher, + refund_tracer: Option, + ) -> Self { + Self { + dispatcher, + refund_tracer, + circuits_tracer: CircuitsTracer::new(), + result_tracer: ResultTracer::new(), + pubdata_tracer: PubdataTracer::new(), + storage, + } + } +} + impl Tracer for VmTracerManager { fn before_decoding(&mut self, execution: &mut Execution, state: &mut VMState) { // Call the dispatcher to handle all the tracers added to it @@ -30,9 +43,7 @@ impl Tracer for VmTracerManager { if let Some(refunds_tracer) = &mut self.refund_tracer { refunds_tracer.before_decoding(execution, state); } - if let Some(pubdata_tracer) = &mut self.pubdata_tracer { - pubdata_tracer.before_decoding(execution, state); - } + self.pubdata_tracer.before_decoding(execution, state); self.circuits_tracer.before_decoding(execution, state); } @@ -45,9 +56,7 @@ impl Tracer for VmTracerManager { if let Some(refunds_tracer) = &mut self.refund_tracer { refunds_tracer.after_decoding(opcode, execution, state); } - if let Some(pubdata_tracer) = &mut self.pubdata_tracer { - pubdata_tracer.after_decoding(opcode, execution, state); - } + self.pubdata_tracer.after_decoding(opcode, execution, state); self.circuits_tracer .after_decoding(opcode, execution, state); } @@ -67,9 +76,8 @@ impl Tracer for VmTracerManager { if let Some(refunds_tracer) = &mut self.refund_tracer { refunds_tracer.before_execution(opcode, execution, state); } - if let Some(pubdata_tracer) = &mut self.pubdata_tracer { - pubdata_tracer.before_execution(opcode, execution, state); - } + self.pubdata_tracer + .before_execution(opcode, execution, state); self.circuits_tracer .before_execution(opcode, execution, state); } @@ -83,9 +91,8 @@ impl Tracer for VmTracerManager { if let Some(refunds_tracer) = &mut self.refund_tracer { refunds_tracer.after_execution(opcode, execution, state); } - if let Some(pubdata_tracer) = &mut self.pubdata_tracer { - pubdata_tracer.after_execution(opcode, execution, state); - } + self.pubdata_tracer + .after_execution(opcode, execution, state); self.circuits_tracer .after_execution(opcode, execution, state); } @@ -108,9 +115,8 @@ impl BootloaderTracer for VmTracerManager { if let Some(refunds_tracer) = &mut self.refund_tracer { refunds_tracer.before_bootloader_execution(opcode, execution, state); } - if let Some(pubdata_tracer) = &mut self.pubdata_tracer { - pubdata_tracer.before_bootloader_execution(opcode, execution, state); - } + self.pubdata_tracer + .before_bootloader_execution(opcode, execution, state); self.circuits_tracer .before_bootloader_execution(opcode, execution, state); } @@ -131,9 +137,8 @@ impl BootloaderTracer for VmTracerManager { if let Some(refunds_tracer) = &mut self.refund_tracer { refunds_tracer.after_bootloader_execution(opcode, execution, state); } - if let Some(pubdata_tracer) = &mut self.pubdata_tracer { - pubdata_tracer.after_bootloader_execution(opcode, execution, state); - } + self.pubdata_tracer + .after_bootloader_execution(opcode, execution, state); self.circuits_tracer .after_bootloader_execution(opcode, execution, state); } diff --git a/core/lib/multivm/src/versions/era_vm/tracers/mod.rs b/core/lib/multivm/src/versions/era_vm/tracers/mod.rs index c03ac29c143f..e7f7bbf4c270 100644 --- a/core/lib/multivm/src/versions/era_vm/tracers/mod.rs +++ b/core/lib/multivm/src/versions/era_vm/tracers/mod.rs @@ -1,7 +1,7 @@ -mod circuits_tracer; -mod dispatcher; +pub mod circuits_tracer; +pub mod dispatcher; pub mod manager; -mod pubdata_tracer; -mod refunds_tracer; -mod result_tracer; +pub mod pubdata_tracer; +pub mod refunds_tracer; +pub mod result_tracer; mod traits; diff --git a/core/lib/multivm/src/versions/era_vm/tracers/pubdata_tracer.rs b/core/lib/multivm/src/versions/era_vm/tracers/pubdata_tracer.rs index 14a733da7196..380d1fa107b3 100644 --- a/core/lib/multivm/src/versions/era_vm/tracers/pubdata_tracer.rs +++ b/core/lib/multivm/src/versions/era_vm/tracers/pubdata_tracer.rs @@ -2,6 +2,12 @@ use super::traits::{BootloaderTracer, Tracer, VmTracer}; pub struct PubdataTracer {} +impl PubdataTracer { + pub fn new() -> Self { + Self {} + } +} + impl Tracer for PubdataTracer { fn before_decoding( &mut self, diff --git a/core/lib/multivm/src/versions/era_vm/tracers/refunds_tracer.rs b/core/lib/multivm/src/versions/era_vm/tracers/refunds_tracer.rs index f7a1d36ab4ef..864ce7a687a7 100644 --- a/core/lib/multivm/src/versions/era_vm/tracers/refunds_tracer.rs +++ b/core/lib/multivm/src/versions/era_vm/tracers/refunds_tracer.rs @@ -1,7 +1,13 @@ -use super::traits::{BootloaderTracer, Tracer, VmTracer}; +use super::traits::{BootloaderTracer, Tracer}; pub struct RefundsTracer {} +impl RefundsTracer { + pub fn new() -> Self { + Self {} + } +} + impl Tracer for RefundsTracer { fn before_decoding( &mut self, diff --git a/core/lib/multivm/src/versions/era_vm/tracers/result_tracer.rs b/core/lib/multivm/src/versions/era_vm/tracers/result_tracer.rs index a9f7c1c5892b..11804e76a8b4 100644 --- a/core/lib/multivm/src/versions/era_vm/tracers/result_tracer.rs +++ b/core/lib/multivm/src/versions/era_vm/tracers/result_tracer.rs @@ -2,6 +2,12 @@ use super::traits::{BootloaderTracer, Tracer, VmTracer}; pub struct ResultTracer {} +impl ResultTracer { + pub fn new() -> Self { + Self {} + } +} + impl Tracer for ResultTracer { fn before_decoding( &mut self, diff --git a/core/lib/multivm/src/versions/era_vm/vm.rs b/core/lib/multivm/src/versions/era_vm/vm.rs index 1ebd1a2e0baf..addb950e710d 100644 --- a/core/lib/multivm/src/versions/era_vm/vm.rs +++ b/core/lib/multivm/src/versions/era_vm/vm.rs @@ -38,6 +38,9 @@ use super::{ logs::IntoSystemLog, refunds::compute_refund, snapshot::VmSnapshot, + tracers::{ + dispatcher::TracerDispatcher, manager::VmTracerManager, refunds_tracer::RefundsTracer, + }, }; use crate::{ era_vm::{bytecode::compress_bytecodes, transaction_data::TransactionData}, @@ -157,6 +160,7 @@ impl Vm { pub fn run( &mut self, execution_mode: VmExecutionMode, + tracer: TracerDispatcher, track_refunds: bool, ) -> (ExecutionResult, Refunds) { let mut refunds = Refunds { @@ -166,8 +170,18 @@ impl Vm { let mut pubdata_before = self.inner.state.pubdata() as u32; let mut last_tx_result = None; + let refund_tracer = if track_refunds { + Some(RefundsTracer::new()) + } else { + None + }; + + let mut tracer = VmTracerManager::new(self.storage.clone(), tracer, refund_tracer); + loop { - let (result, _blob_tracer) = self.inner.run_program_with_custom_bytecode(); + let result = self + .inner + .run_program_with_custom_bytecode(Some(&mut tracer)); let result = match result { ExecutionOutput::Ok(output) => { @@ -515,7 +529,11 @@ impl VmInterface for Vm { } let snapshot = self.inner.state.snapshot(); - let (result, refunds) = self.run(execution_mode, enable_refund_tracer); + let (result, refunds) = self.run( + execution_mode, + TracerDispatcher::new(vec![]), + enable_refund_tracer, + ); let ignore_world_diff = matches!(execution_mode, VmExecutionMode::OneTx) && matches!(result, ExecutionResult::Halt { .. }); From 24ef9c8cd010fd5ea136b591054bc1b93e027140 Mon Sep 17 00:00:00 2001 From: Marcos Nicolau Date: Fri, 23 Aug 2024 10:33:39 -0300 Subject: [PATCH 05/32] Refactor bootloader tracer --- .../era_vm/tracers/circuits_tracer.rs | 55 ++--------------- .../src/versions/era_vm/tracers/dispatcher.rs | 37 +++++------- .../src/versions/era_vm/tracers/manager.rs | 60 +++++++++---------- .../src/versions/era_vm/tracers/mod.rs | 2 +- .../versions/era_vm/tracers/pubdata_tracer.rs | 55 ++--------------- .../versions/era_vm/tracers/refunds_tracer.rs | 55 ++--------------- .../versions/era_vm/tracers/result_tracer.rs | 55 ++--------------- .../src/versions/era_vm/tracers/traits.rs | 22 ++----- 8 files changed, 69 insertions(+), 272 deletions(-) diff --git a/core/lib/multivm/src/versions/era_vm/tracers/circuits_tracer.rs b/core/lib/multivm/src/versions/era_vm/tracers/circuits_tracer.rs index 9e53840c19b7..082c97bb0e6c 100644 --- a/core/lib/multivm/src/versions/era_vm/tracers/circuits_tracer.rs +++ b/core/lib/multivm/src/versions/era_vm/tracers/circuits_tracer.rs @@ -1,4 +1,6 @@ -use super::traits::{BootloaderTracer, Tracer}; +use zksync_state::ReadStorage; + +use super::traits::{Tracer, VmTracer}; pub struct CircuitsTracer {} @@ -8,53 +10,6 @@ impl CircuitsTracer { } } -impl Tracer for CircuitsTracer { - fn before_decoding( - &mut self, - execution: &mut era_vm::Execution, - state: &mut era_vm::state::VMState, - ) { - } - - fn after_decoding( - &mut self, - opcode: &era_vm::Opcode, - execution: &mut era_vm::Execution, - state: &mut era_vm::state::VMState, - ) { - } +impl Tracer for CircuitsTracer {} - fn before_execution( - &mut self, - opcode: &era_vm::Opcode, - execution: &mut era_vm::Execution, - state: &mut era_vm::state::VMState, - ) { - } - - fn after_execution( - &mut self, - opcode: &era_vm::Opcode, - execution: &mut era_vm::Execution, - state: &mut era_vm::state::VMState, - ) { - } -} - -impl BootloaderTracer for CircuitsTracer { - fn before_bootloader_execution( - &mut self, - opcode: &era_vm::Opcode, - execution: &mut era_vm::Execution, - state: &mut era_vm::state::VMState, - ) { - } - - fn after_bootloader_execution( - &mut self, - opcode: &era_vm::Opcode, - execution: &mut era_vm::Execution, - state: &mut era_vm::state::VMState, - ) { - } -} +impl VmTracer for CircuitsTracer {} diff --git a/core/lib/multivm/src/versions/era_vm/tracers/dispatcher.rs b/core/lib/multivm/src/versions/era_vm/tracers/dispatcher.rs index f1c6c0aadd6b..a53024610589 100644 --- a/core/lib/multivm/src/versions/era_vm/tracers/dispatcher.rs +++ b/core/lib/multivm/src/versions/era_vm/tracers/dispatcher.rs @@ -1,20 +1,23 @@ -use era_vm::{state::VMState, tracers::tracer::Tracer, Execution, Opcode}; +use era_vm::{state::VMState, tracers::tracer::Tracer, vm::ExecutionOutput, Execution, Opcode}; +use zksync_state::ReadStorage; -use super::traits::{BootloaderTracer, VmTracer}; +use crate::era_vm::vm::Vm; + +use super::traits::{ExecutionResult, VmTracer}; #[derive(Default)] // dispatcher calls to other tracers -pub struct TracerDispatcher { - tracers: Vec>, +pub struct TracerDispatcher { + tracers: Vec>>, } -impl TracerDispatcher { - pub fn new(tracers: Vec>) -> Self { +impl TracerDispatcher { + pub fn new(tracers: Vec>>) -> Self { Self { tracers } } } -impl Tracer for TracerDispatcher { +impl Tracer for TracerDispatcher { fn before_decoding(&mut self, execution: &mut Execution, state: &mut VMState) { for tracer in self.tracers.iter_mut() { tracer.before_decoding(execution, state); @@ -45,26 +48,16 @@ impl Tracer for TracerDispatcher { } } -impl BootloaderTracer for TracerDispatcher { - fn before_bootloader_execution( - &mut self, - opcode: &Opcode, - execution: &mut Execution, - state: &mut VMState, - ) { +impl VmTracer for TracerDispatcher { + fn before_bootloader_execution(&mut self, state: &mut Vm) { for tracer in self.tracers.iter_mut() { - tracer.before_bootloader_execution(opcode, execution, state); + tracer.before_bootloader_execution(state); } } - fn after_bootloader_execution( - &mut self, - opcode: &Opcode, - execution: &mut Execution, - state: &mut VMState, - ) { + fn after_bootloader_execution(&mut self, state: &mut Vm, stop_reason: ExecutionResult) { for tracer in self.tracers.iter_mut() { - tracer.after_bootloader_execution(opcode, execution, state); + tracer.after_bootloader_execution(state, stop_reason.clone()); } } } diff --git a/core/lib/multivm/src/versions/era_vm/tracers/manager.rs b/core/lib/multivm/src/versions/era_vm/tracers/manager.rs index f1f60cdf1b4d..ea3553c26e1c 100644 --- a/core/lib/multivm/src/versions/era_vm/tracers/manager.rs +++ b/core/lib/multivm/src/versions/era_vm/tracers/manager.rs @@ -1,14 +1,23 @@ -use era_vm::{execution::Execution, opcode::Opcode, state::VMState, tracers::tracer::Tracer}; +use era_vm::{ + execution::Execution, opcode::Opcode, state::VMState, tracers::tracer::Tracer, + vm::ExecutionOutput, +}; use zksync_state::{ReadStorage, StoragePtr}; +use crate::era_vm::vm::Vm; + use super::{ - circuits_tracer::CircuitsTracer, dispatcher::TracerDispatcher, pubdata_tracer::PubdataTracer, - refunds_tracer::RefundsTracer, result_tracer::ResultTracer, traits::BootloaderTracer, + circuits_tracer::CircuitsTracer, + dispatcher::TracerDispatcher, + pubdata_tracer::PubdataTracer, + refunds_tracer::RefundsTracer, + result_tracer::ResultTracer, + traits::{ExecutionResult, VmTracer}, }; // this tracer manager is the one that gets called when running the vm pub struct VmTracerManager { - dispatcher: TracerDispatcher, + dispatcher: TracerDispatcher, result_tracer: ResultTracer, refund_tracer: Option, pubdata_tracer: PubdataTracer, @@ -19,7 +28,7 @@ pub struct VmTracerManager { impl VmTracerManager { pub fn new( storage: StoragePtr, - dispatcher: TracerDispatcher, + dispatcher: TracerDispatcher, refund_tracer: Option, ) -> Self { Self { @@ -98,48 +107,35 @@ impl Tracer for VmTracerManager { } } -impl BootloaderTracer for VmTracerManager { - fn before_bootloader_execution( - &mut self, - opcode: &Opcode, - execution: &mut Execution, - state: &mut VMState, - ) { +impl VmTracer for VmTracerManager { + fn before_bootloader_execution(&mut self, state: &mut Vm) { // Call the dispatcher to handle all the tracers added to it - self.dispatcher - .before_bootloader_execution(opcode, execution, state); + self.dispatcher.before_bootloader_execution(state); // Individual tracers - self.result_tracer - .before_bootloader_execution(opcode, execution, state); + self.result_tracer.before_bootloader_execution(state); + if let Some(refunds_tracer) = &mut self.refund_tracer { - refunds_tracer.before_bootloader_execution(opcode, execution, state); + refunds_tracer.before_bootloader_execution(state); } - self.pubdata_tracer - .before_bootloader_execution(opcode, execution, state); - self.circuits_tracer - .before_bootloader_execution(opcode, execution, state); + self.pubdata_tracer.before_bootloader_execution(state); + self.circuits_tracer.before_bootloader_execution(state); } - fn after_bootloader_execution( - &mut self, - opcode: &Opcode, - execution: &mut Execution, - state: &mut VMState, - ) { + fn after_bootloader_execution(&mut self, state: &mut Vm, stop_reason: ExecutionResult) { // Call the dispatcher to handle all the tracers added to it self.dispatcher - .after_bootloader_execution(opcode, execution, state); + .after_bootloader_execution(state, stop_reason.clone()); // Individual tracers self.result_tracer - .after_bootloader_execution(opcode, execution, state); + .after_bootloader_execution(state, stop_reason.clone()); if let Some(refunds_tracer) = &mut self.refund_tracer { - refunds_tracer.after_bootloader_execution(opcode, execution, state); + refunds_tracer.after_bootloader_execution(state, stop_reason.clone()); } self.pubdata_tracer - .after_bootloader_execution(opcode, execution, state); + .after_bootloader_execution(state, stop_reason.clone()); self.circuits_tracer - .after_bootloader_execution(opcode, execution, state); + .after_bootloader_execution(state, stop_reason.clone()); } } diff --git a/core/lib/multivm/src/versions/era_vm/tracers/mod.rs b/core/lib/multivm/src/versions/era_vm/tracers/mod.rs index e7f7bbf4c270..6676649e3073 100644 --- a/core/lib/multivm/src/versions/era_vm/tracers/mod.rs +++ b/core/lib/multivm/src/versions/era_vm/tracers/mod.rs @@ -4,4 +4,4 @@ pub mod manager; pub mod pubdata_tracer; pub mod refunds_tracer; pub mod result_tracer; -mod traits; +pub mod traits; diff --git a/core/lib/multivm/src/versions/era_vm/tracers/pubdata_tracer.rs b/core/lib/multivm/src/versions/era_vm/tracers/pubdata_tracer.rs index 380d1fa107b3..3f5b057500ce 100644 --- a/core/lib/multivm/src/versions/era_vm/tracers/pubdata_tracer.rs +++ b/core/lib/multivm/src/versions/era_vm/tracers/pubdata_tracer.rs @@ -1,4 +1,6 @@ -use super::traits::{BootloaderTracer, Tracer, VmTracer}; +use zksync_state::ReadStorage; + +use super::traits::{Tracer, VmTracer}; pub struct PubdataTracer {} @@ -8,53 +10,6 @@ impl PubdataTracer { } } -impl Tracer for PubdataTracer { - fn before_decoding( - &mut self, - execution: &mut era_vm::Execution, - state: &mut era_vm::state::VMState, - ) { - } - - fn after_decoding( - &mut self, - opcode: &era_vm::Opcode, - execution: &mut era_vm::Execution, - state: &mut era_vm::state::VMState, - ) { - } +impl Tracer for PubdataTracer {} - fn before_execution( - &mut self, - opcode: &era_vm::Opcode, - execution: &mut era_vm::Execution, - state: &mut era_vm::state::VMState, - ) { - } - - fn after_execution( - &mut self, - opcode: &era_vm::Opcode, - execution: &mut era_vm::Execution, - state: &mut era_vm::state::VMState, - ) { - } -} - -impl BootloaderTracer for PubdataTracer { - fn before_bootloader_execution( - &mut self, - opcode: &era_vm::Opcode, - execution: &mut era_vm::Execution, - state: &mut era_vm::state::VMState, - ) { - } - - fn after_bootloader_execution( - &mut self, - opcode: &era_vm::Opcode, - execution: &mut era_vm::Execution, - state: &mut era_vm::state::VMState, - ) { - } -} +impl VmTracer for PubdataTracer {} diff --git a/core/lib/multivm/src/versions/era_vm/tracers/refunds_tracer.rs b/core/lib/multivm/src/versions/era_vm/tracers/refunds_tracer.rs index 864ce7a687a7..109def19643e 100644 --- a/core/lib/multivm/src/versions/era_vm/tracers/refunds_tracer.rs +++ b/core/lib/multivm/src/versions/era_vm/tracers/refunds_tracer.rs @@ -1,4 +1,6 @@ -use super::traits::{BootloaderTracer, Tracer}; +use zksync_state::ReadStorage; + +use super::traits::{Tracer, VmTracer}; pub struct RefundsTracer {} @@ -8,53 +10,6 @@ impl RefundsTracer { } } -impl Tracer for RefundsTracer { - fn before_decoding( - &mut self, - execution: &mut era_vm::Execution, - state: &mut era_vm::state::VMState, - ) { - } - - fn after_decoding( - &mut self, - opcode: &era_vm::Opcode, - execution: &mut era_vm::Execution, - state: &mut era_vm::state::VMState, - ) { - } +impl Tracer for RefundsTracer {} - fn before_execution( - &mut self, - opcode: &era_vm::Opcode, - execution: &mut era_vm::Execution, - state: &mut era_vm::state::VMState, - ) { - } - - fn after_execution( - &mut self, - opcode: &era_vm::Opcode, - execution: &mut era_vm::Execution, - state: &mut era_vm::state::VMState, - ) { - } -} - -impl BootloaderTracer for RefundsTracer { - fn before_bootloader_execution( - &mut self, - opcode: &era_vm::Opcode, - execution: &mut era_vm::Execution, - state: &mut era_vm::state::VMState, - ) { - } - - fn after_bootloader_execution( - &mut self, - opcode: &era_vm::Opcode, - execution: &mut era_vm::Execution, - state: &mut era_vm::state::VMState, - ) { - } -} +impl VmTracer for RefundsTracer {} diff --git a/core/lib/multivm/src/versions/era_vm/tracers/result_tracer.rs b/core/lib/multivm/src/versions/era_vm/tracers/result_tracer.rs index 11804e76a8b4..90c9213d75cc 100644 --- a/core/lib/multivm/src/versions/era_vm/tracers/result_tracer.rs +++ b/core/lib/multivm/src/versions/era_vm/tracers/result_tracer.rs @@ -1,4 +1,6 @@ -use super::traits::{BootloaderTracer, Tracer, VmTracer}; +use zksync_state::ReadStorage; + +use super::traits::{Tracer, VmTracer}; pub struct ResultTracer {} @@ -8,53 +10,6 @@ impl ResultTracer { } } -impl Tracer for ResultTracer { - fn before_decoding( - &mut self, - execution: &mut era_vm::Execution, - state: &mut era_vm::state::VMState, - ) { - } - - fn after_decoding( - &mut self, - opcode: &era_vm::Opcode, - execution: &mut era_vm::Execution, - state: &mut era_vm::state::VMState, - ) { - } +impl Tracer for ResultTracer {} - fn before_execution( - &mut self, - opcode: &era_vm::Opcode, - execution: &mut era_vm::Execution, - state: &mut era_vm::state::VMState, - ) { - } - - fn after_execution( - &mut self, - opcode: &era_vm::Opcode, - execution: &mut era_vm::Execution, - state: &mut era_vm::state::VMState, - ) { - } -} - -impl BootloaderTracer for ResultTracer { - fn before_bootloader_execution( - &mut self, - opcode: &era_vm::Opcode, - execution: &mut era_vm::Execution, - state: &mut era_vm::state::VMState, - ) { - } - - fn after_bootloader_execution( - &mut self, - opcode: &era_vm::Opcode, - execution: &mut era_vm::Execution, - state: &mut era_vm::state::VMState, - ) { - } -} +impl VmTracer for ResultTracer {} diff --git a/core/lib/multivm/src/versions/era_vm/tracers/traits.rs b/core/lib/multivm/src/versions/era_vm/tracers/traits.rs index 0b2774667bde..d56ce2b22fb0 100644 --- a/core/lib/multivm/src/versions/era_vm/tracers/traits.rs +++ b/core/lib/multivm/src/versions/era_vm/tracers/traits.rs @@ -1,21 +1,9 @@ +pub use crate::{era_vm::vm::Vm, vm_latest::ExecutionResult}; pub use era_vm::tracers::tracer::Tracer; -use era_vm::{state::VMState, Execution, Opcode}; +use zksync_state::ReadStorage; -pub trait BootloaderTracer { - fn before_bootloader_execution( - &mut self, - opcode: &Opcode, - execution: &mut Execution, - state: &mut VMState, - ); +pub trait VmTracer: Tracer { + fn before_bootloader_execution(&mut self, _state: &mut Vm) {} - fn after_bootloader_execution( - &mut self, - opcode: &Opcode, - execution: &mut Execution, - state: &mut VMState, - ); + fn after_bootloader_execution(&mut self, _state: &mut Vm, _stop_reason: ExecutionResult) {} } - -pub trait VmTracer: Tracer + BootloaderTracer {} -impl VmTracer for T {} From 1bddb9b6b160ddd8017a4890e6a154f4660ebf47 Mon Sep 17 00:00:00 2001 From: Marcos Nicolau Date: Fri, 23 Aug 2024 10:34:42 -0300 Subject: [PATCH 06/32] Implement bootloader tracers in vm --- core/lib/multivm/src/versions/era_vm/vm.rs | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/core/lib/multivm/src/versions/era_vm/vm.rs b/core/lib/multivm/src/versions/era_vm/vm.rs index addb950e710d..2c590797720a 100644 --- a/core/lib/multivm/src/versions/era_vm/vm.rs +++ b/core/lib/multivm/src/versions/era_vm/vm.rs @@ -40,6 +40,7 @@ use super::{ snapshot::VmSnapshot, tracers::{ dispatcher::TracerDispatcher, manager::VmTracerManager, refunds_tracer::RefundsTracer, + traits::VmTracer, }, }; use crate::{ @@ -160,7 +161,7 @@ impl Vm { pub fn run( &mut self, execution_mode: VmExecutionMode, - tracer: TracerDispatcher, + tracer: TracerDispatcher, track_refunds: bool, ) -> (ExecutionResult, Refunds) { let mut refunds = Refunds { @@ -178,25 +179,26 @@ impl Vm { let mut tracer = VmTracerManager::new(self.storage.clone(), tracer, refund_tracer); - loop { + tracer.before_bootloader_execution(self); + let (stop_reason, refunds) = loop { let result = self .inner .run_program_with_custom_bytecode(Some(&mut tracer)); let result = match result { ExecutionOutput::Ok(output) => { - return (ExecutionResult::Success { output }, refunds) + break (ExecutionResult::Success { output }, refunds) } ExecutionOutput::Revert(output) => match TxRevertReason::parse_error(&output) { TxRevertReason::TxReverted(output) => { - return (ExecutionResult::Revert { output }, refunds) + break (ExecutionResult::Revert { output }, refunds) } TxRevertReason::Halt(reason) => { - return (ExecutionResult::Halt { reason }, refunds) + break (ExecutionResult::Halt { reason }, refunds) } }, ExecutionOutput::Panic => { - return ( + break ( ExecutionResult::Halt { reason: if self.inner.execution.gas_left().unwrap() == 0 { Halt::BootloaderOutOfGas @@ -363,7 +365,11 @@ impl Vm { self.write_to_bootloader_heap(memory_to_apply); } } - } + }; + + tracer.after_bootloader_execution(self, stop_reason.clone()); + + (stop_reason, refunds) } fn get_vm_hook_params(&self, heap: &era_vm::execution::Heap) -> Vec { From 6803dc6d290b8a9add45420c6a69e900bae35272 Mon Sep 17 00:00:00 2001 From: Marcos Nicolau Date: Fri, 23 Aug 2024 10:43:48 -0300 Subject: [PATCH 07/32] zk fmt --- .../lib/multivm/src/versions/era_vm/tracers/dispatcher.rs | 3 +-- core/lib/multivm/src/versions/era_vm/tracers/manager.rs | 8 ++------ core/lib/multivm/src/versions/era_vm/tracers/traits.rs | 3 ++- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/core/lib/multivm/src/versions/era_vm/tracers/dispatcher.rs b/core/lib/multivm/src/versions/era_vm/tracers/dispatcher.rs index a53024610589..00f672f57008 100644 --- a/core/lib/multivm/src/versions/era_vm/tracers/dispatcher.rs +++ b/core/lib/multivm/src/versions/era_vm/tracers/dispatcher.rs @@ -1,9 +1,8 @@ use era_vm::{state::VMState, tracers::tracer::Tracer, vm::ExecutionOutput, Execution, Opcode}; use zksync_state::ReadStorage; -use crate::era_vm::vm::Vm; - use super::traits::{ExecutionResult, VmTracer}; +use crate::era_vm::vm::Vm; #[derive(Default)] // dispatcher calls to other tracers diff --git a/core/lib/multivm/src/versions/era_vm/tracers/manager.rs b/core/lib/multivm/src/versions/era_vm/tracers/manager.rs index ea3553c26e1c..0c10f2e02977 100644 --- a/core/lib/multivm/src/versions/era_vm/tracers/manager.rs +++ b/core/lib/multivm/src/versions/era_vm/tracers/manager.rs @@ -1,11 +1,6 @@ -use era_vm::{ - execution::Execution, opcode::Opcode, state::VMState, tracers::tracer::Tracer, - vm::ExecutionOutput, -}; +use era_vm::{execution::Execution, opcode::Opcode, state::VMState, tracers::tracer::Tracer}; use zksync_state::{ReadStorage, StoragePtr}; -use crate::era_vm::vm::Vm; - use super::{ circuits_tracer::CircuitsTracer, dispatcher::TracerDispatcher, @@ -14,6 +9,7 @@ use super::{ result_tracer::ResultTracer, traits::{ExecutionResult, VmTracer}, }; +use crate::era_vm::vm::Vm; // this tracer manager is the one that gets called when running the vm pub struct VmTracerManager { diff --git a/core/lib/multivm/src/versions/era_vm/tracers/traits.rs b/core/lib/multivm/src/versions/era_vm/tracers/traits.rs index d56ce2b22fb0..cca5b5af6184 100644 --- a/core/lib/multivm/src/versions/era_vm/tracers/traits.rs +++ b/core/lib/multivm/src/versions/era_vm/tracers/traits.rs @@ -1,7 +1,8 @@ -pub use crate::{era_vm::vm::Vm, vm_latest::ExecutionResult}; pub use era_vm::tracers::tracer::Tracer; use zksync_state::ReadStorage; +pub use crate::{era_vm::vm::Vm, vm_latest::ExecutionResult}; + pub trait VmTracer: Tracer { fn before_bootloader_execution(&mut self, _state: &mut Vm) {} From 033446562786c46278e33bd724c4ac9e46e49f9a Mon Sep 17 00:00:00 2001 From: Marcos Nicolau Date: Fri, 23 Aug 2024 10:58:35 -0300 Subject: [PATCH 08/32] Add bootloader hook fn to tracer --- core/lib/multivm/src/versions/era_vm/hook.rs | 2 +- .../lib/multivm/src/versions/era_vm/tracers/traits.rs | 4 ++++ core/lib/multivm/src/versions/era_vm/vm.rs | 11 ++--------- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/core/lib/multivm/src/versions/era_vm/hook.rs b/core/lib/multivm/src/versions/era_vm/hook.rs index cf7e78deb21a..39cf3267df6b 100644 --- a/core/lib/multivm/src/versions/era_vm/hook.rs +++ b/core/lib/multivm/src/versions/era_vm/hook.rs @@ -1,6 +1,6 @@ #[derive(Debug)] -pub(crate) enum Hook { +pub enum Hook { AccountValidationEntered, PaymasterValidationEntered, AccountValidationExited, diff --git a/core/lib/multivm/src/versions/era_vm/tracers/traits.rs b/core/lib/multivm/src/versions/era_vm/tracers/traits.rs index cca5b5af6184..2d8650f81c35 100644 --- a/core/lib/multivm/src/versions/era_vm/tracers/traits.rs +++ b/core/lib/multivm/src/versions/era_vm/tracers/traits.rs @@ -1,10 +1,14 @@ pub use era_vm::tracers::tracer::Tracer; use zksync_state::ReadStorage; +use zksync_types::U256; +use crate::era_vm::hook::Hook; pub use crate::{era_vm::vm::Vm, vm_latest::ExecutionResult}; pub trait VmTracer: Tracer { fn before_bootloader_execution(&mut self, _state: &mut Vm) {} fn after_bootloader_execution(&mut self, _state: &mut Vm, _stop_reason: ExecutionResult) {} + + fn bootloader_hook_call(&mut self, _state: &mut Vm, _hook: Hook, _hook_params: &[U256; 3]) {} } diff --git a/core/lib/multivm/src/versions/era_vm/vm.rs b/core/lib/multivm/src/versions/era_vm/vm.rs index 2c590797720a..101f778f8825 100644 --- a/core/lib/multivm/src/versions/era_vm/vm.rs +++ b/core/lib/multivm/src/versions/era_vm/vm.rs @@ -219,6 +219,8 @@ impl Vm { } }; + tracer.bootloader_hook_call(self, Hook::from_u32(result), &self.get_hook_params()); + match Hook::from_u32(result) { Hook::PaymasterValidationEntered => { // unused @@ -372,15 +374,6 @@ impl Vm { (stop_reason, refunds) } - fn get_vm_hook_params(&self, heap: &era_vm::execution::Heap) -> Vec { - (get_vm_hook_start_position_latest()..get_vm_hook_start_position_latest() + 2) - .map(|word| { - let res = heap.read((word * 32) as u32); - res - }) - .collect() - } - pub(crate) fn insert_bytecodes<'a>(&mut self, bytecodes: impl IntoIterator) { for code in bytecodes { let mut program_code = vec![]; From d72b6fca929009f6b6c783f5e069dd75b6fa1122 Mon Sep 17 00:00:00 2001 From: Marcos Nicolau Date: Fri, 23 Aug 2024 11:26:53 -0300 Subject: [PATCH 09/32] Implement refund tracer --- core/lib/multivm/src/versions/era_vm/hook.rs | 2 +- core/lib/multivm/src/versions/era_vm/mod.rs | 1 - .../multivm/src/versions/era_vm/refunds.rs | 66 -------- .../src/versions/era_vm/tracers/manager.rs | 34 ++++- .../versions/era_vm/tracers/refunds_tracer.rs | 143 +++++++++++++++++- core/lib/multivm/src/versions/era_vm/vm.rs | 126 ++++----------- 6 files changed, 201 insertions(+), 171 deletions(-) delete mode 100644 core/lib/multivm/src/versions/era_vm/refunds.rs diff --git a/core/lib/multivm/src/versions/era_vm/hook.rs b/core/lib/multivm/src/versions/era_vm/hook.rs index 39cf3267df6b..348ae11c5324 100644 --- a/core/lib/multivm/src/versions/era_vm/hook.rs +++ b/core/lib/multivm/src/versions/era_vm/hook.rs @@ -1,4 +1,4 @@ -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum Hook { AccountValidationEntered, diff --git a/core/lib/multivm/src/versions/era_vm/mod.rs b/core/lib/multivm/src/versions/era_vm/mod.rs index 11925ae46f47..edb8ee88eec0 100644 --- a/core/lib/multivm/src/versions/era_vm/mod.rs +++ b/core/lib/multivm/src/versions/era_vm/mod.rs @@ -4,7 +4,6 @@ mod event; mod hook; mod initial_bootloader_memory; mod logs; -mod refunds; mod snapshot; #[cfg(test)] mod tests; diff --git a/core/lib/multivm/src/versions/era_vm/refunds.rs b/core/lib/multivm/src/versions/era_vm/refunds.rs deleted file mode 100644 index 524a6ca4c3bc..000000000000 --- a/core/lib/multivm/src/versions/era_vm/refunds.rs +++ /dev/null @@ -1,66 +0,0 @@ -use zksync_types::{H256, U256}; -use zksync_utils::ceil_div_u256; - -use crate::vm_latest::{utils::fee::get_batch_base_fee, L1BatchEnv}; - -pub(crate) fn compute_refund( - l1_batch: &L1BatchEnv, - bootloader_refund: u64, - gas_spent_on_pubdata: u64, - tx_gas_limit: u64, - current_ergs_per_pubdata_byte: u32, - pubdata_published: u32, - tx_hash: H256, -) -> u64 { - let total_gas_spent = tx_gas_limit - bootloader_refund; - - let gas_spent_on_computation = total_gas_spent - .checked_sub(gas_spent_on_pubdata) - .unwrap_or_else(|| { - tracing::error!( - "Gas spent on pubdata is greater than total gas spent. On pubdata: {}, total: {}", - gas_spent_on_pubdata, - total_gas_spent - ); - 0 - }); - - // For now, bootloader charges only for base fee. - let effective_gas_price = get_batch_base_fee(l1_batch); - - let bootloader_eth_price_per_pubdata_byte = - U256::from(effective_gas_price) * U256::from(current_ergs_per_pubdata_byte); - - let fair_eth_price_per_pubdata_byte = U256::from(l1_batch.fee_input.fair_pubdata_price()); - - // For now, L1 originated transactions are allowed to pay less than fair fee per pubdata, - // so we should take it into account. - let eth_price_per_pubdata_byte_for_calculation = std::cmp::min( - bootloader_eth_price_per_pubdata_byte, - fair_eth_price_per_pubdata_byte, - ); - - let fair_fee_eth = U256::from(gas_spent_on_computation) - * U256::from(l1_batch.fee_input.fair_l2_gas_price()) - + U256::from(pubdata_published) * eth_price_per_pubdata_byte_for_calculation; - let pre_paid_eth = U256::from(tx_gas_limit) * U256::from(effective_gas_price); - let refund_eth = pre_paid_eth.checked_sub(fair_fee_eth).unwrap_or_else(|| { - tracing::error!( - "Fair fee is greater than pre paid. Fair fee: {} wei, pre paid: {} wei", - fair_fee_eth, - pre_paid_eth - ); - U256::zero() - }); - - tracing::trace!( - "Fee benchmark for transaction with hash {}", - hex::encode(tx_hash.as_bytes()) - ); - tracing::trace!("Gas Limit: {}", tx_gas_limit); - tracing::trace!("Gas spent on computation: {}", gas_spent_on_computation); - tracing::trace!("Gas spent on pubdata: {}", gas_spent_on_pubdata); - tracing::trace!("Pubdata published: {}", pubdata_published); - - ceil_div_u256(refund_eth, effective_gas_price.into()).as_u64() -} diff --git a/core/lib/multivm/src/versions/era_vm/tracers/manager.rs b/core/lib/multivm/src/versions/era_vm/tracers/manager.rs index 0c10f2e02977..f3f68355d329 100644 --- a/core/lib/multivm/src/versions/era_vm/tracers/manager.rs +++ b/core/lib/multivm/src/versions/era_vm/tracers/manager.rs @@ -13,11 +13,11 @@ use crate::era_vm::vm::Vm; // this tracer manager is the one that gets called when running the vm pub struct VmTracerManager { - dispatcher: TracerDispatcher, - result_tracer: ResultTracer, - refund_tracer: Option, - pubdata_tracer: PubdataTracer, - circuits_tracer: CircuitsTracer, + pub dispatcher: TracerDispatcher, + pub result_tracer: ResultTracer, + pub refund_tracer: Option, + pub pubdata_tracer: PubdataTracer, + pub circuits_tracer: CircuitsTracer, storage: StoragePtr, } @@ -103,7 +103,7 @@ impl Tracer for VmTracerManager { } } -impl VmTracer for VmTracerManager { +impl VmTracer for VmTracerManager { fn before_bootloader_execution(&mut self, state: &mut Vm) { // Call the dispatcher to handle all the tracers added to it self.dispatcher.before_bootloader_execution(state); @@ -134,4 +134,26 @@ impl VmTracer for VmTracerManager { self.circuits_tracer .after_bootloader_execution(state, stop_reason.clone()); } + + fn bootloader_hook_call( + &mut self, + state: &mut Vm, + hook: crate::era_vm::hook::Hook, + hook_params: &[zksync_types::U256; 3], + ) { + // Call the dispatcher to handle all the tracers added to it + self.dispatcher + .bootloader_hook_call(state, hook.clone(), hook_params); + + // Individual tracers + self.result_tracer + .bootloader_hook_call(state, hook.clone(), hook_params); + if let Some(refunds_tracer) = &mut self.refund_tracer { + refunds_tracer.bootloader_hook_call(state, hook.clone(), hook_params); + } + self.pubdata_tracer + .bootloader_hook_call(state, hook.clone(), hook_params); + self.circuits_tracer + .bootloader_hook_call(state, hook.clone(), hook_params); + } } diff --git a/core/lib/multivm/src/versions/era_vm/tracers/refunds_tracer.rs b/core/lib/multivm/src/versions/era_vm/tracers/refunds_tracer.rs index 109def19643e..f22c782ef954 100644 --- a/core/lib/multivm/src/versions/era_vm/tracers/refunds_tracer.rs +++ b/core/lib/multivm/src/versions/era_vm/tracers/refunds_tracer.rs @@ -1,15 +1,152 @@ use zksync_state::ReadStorage; +use zksync_types::{H256, U256}; +use zksync_utils::ceil_div_u256; use super::traits::{Tracer, VmTracer}; +use crate::{ + era_vm::hook::Hook, + vm_latest::{ + constants::{OPERATOR_REFUNDS_OFFSET, TX_GAS_LIMIT_OFFSET}, + utils::fee::get_batch_base_fee, + L1BatchEnv, Refunds, + }, +}; -pub struct RefundsTracer {} +#[derive(Default)] +pub struct RefundsTracer { + pub gas_refunded: u64, + pub operator_suggested_refund: u64, + pubdata_before: u32, +} impl RefundsTracer { pub fn new() -> Self { - Self {} + Self { + gas_refunded: 0, + operator_suggested_refund: 0, + pubdata_before: 0, + } + } +} + +impl Into for RefundsTracer { + fn into(self) -> Refunds { + Refunds { + gas_refunded: self.gas_refunded, + operator_suggested_refund: self.operator_suggested_refund, + } } } impl Tracer for RefundsTracer {} -impl VmTracer for RefundsTracer {} +impl VmTracer for RefundsTracer { + fn before_bootloader_execution(&mut self, vm: &mut super::traits::Vm) { + self.pubdata_before = vm.inner.state.pubdata() as u32; + } + + fn bootloader_hook_call( + &mut self, + vm: &mut super::traits::Vm, + hook: crate::era_vm::hook::Hook, + hook_params: &[zksync_types::U256; 3], + ) { + match hook { + Hook::NotifyAboutRefund => self.gas_refunded = hook_params[0].low_u64(), + Hook::AskOperatorForRefund => { + println!("ENTERINg HERE!"); + let [bootloader_refund, gas_spent_on_pubdata, gas_per_pubdata_byte] = hook_params; + let current_tx_index = vm.bootloader_state.current_tx(); + let tx_description_offset = vm + .bootloader_state + .get_tx_description_offset(current_tx_index); + let tx_gas_limit = vm + .read_heap_word(tx_description_offset + TX_GAS_LIMIT_OFFSET) + .as_u64(); + + let pubdata_published = vm.inner.state.pubdata() as u32; + + self.operator_suggested_refund = compute_refund( + &vm.batch_env, + bootloader_refund.as_u64(), + gas_spent_on_pubdata.as_u64(), + tx_gas_limit, + gas_per_pubdata_byte.low_u32(), + pubdata_published.saturating_sub(self.pubdata_before), + vm.bootloader_state.last_l2_block().txs.last().unwrap().hash, + ); + + self.pubdata_before = pubdata_published; + let refund_value = self.operator_suggested_refund; + vm.write_to_bootloader_heap([( + OPERATOR_REFUNDS_OFFSET + current_tx_index, + refund_value.into(), + )]); + vm.bootloader_state.set_refund_for_current_tx(refund_value); + } + _ => {} + }; + } +} + +pub(crate) fn compute_refund( + l1_batch: &L1BatchEnv, + bootloader_refund: u64, + gas_spent_on_pubdata: u64, + tx_gas_limit: u64, + current_ergs_per_pubdata_byte: u32, + pubdata_published: u32, + tx_hash: H256, +) -> u64 { + let total_gas_spent = tx_gas_limit - bootloader_refund; + + let gas_spent_on_computation = total_gas_spent + .checked_sub(gas_spent_on_pubdata) + .unwrap_or_else(|| { + tracing::error!( + "Gas spent on pubdata is greater than total gas spent. On pubdata: {}, total: {}", + gas_spent_on_pubdata, + total_gas_spent + ); + 0 + }); + + // For now, bootloader charges only for base fee. + let effective_gas_price = get_batch_base_fee(l1_batch); + + let bootloader_eth_price_per_pubdata_byte = + U256::from(effective_gas_price) * U256::from(current_ergs_per_pubdata_byte); + + let fair_eth_price_per_pubdata_byte = U256::from(l1_batch.fee_input.fair_pubdata_price()); + + // For now, L1 originated transactions are allowed to pay less than fair fee per pubdata, + // so we should take it into account. + let eth_price_per_pubdata_byte_for_calculation = std::cmp::min( + bootloader_eth_price_per_pubdata_byte, + fair_eth_price_per_pubdata_byte, + ); + + let fair_fee_eth = U256::from(gas_spent_on_computation) + * U256::from(l1_batch.fee_input.fair_l2_gas_price()) + + U256::from(pubdata_published) * eth_price_per_pubdata_byte_for_calculation; + let pre_paid_eth = U256::from(tx_gas_limit) * U256::from(effective_gas_price); + let refund_eth = pre_paid_eth.checked_sub(fair_fee_eth).unwrap_or_else(|| { + tracing::error!( + "Fair fee is greater than pre paid. Fair fee: {} wei, pre paid: {} wei", + fair_fee_eth, + pre_paid_eth + ); + U256::zero() + }); + + tracing::trace!( + "Fee benchmark for transaction with hash {}", + hex::encode(tx_hash.as_bytes()) + ); + tracing::trace!("Gas Limit: {}", tx_gas_limit); + tracing::trace!("Gas spent on computation: {}", gas_spent_on_computation); + tracing::trace!("Gas spent on pubdata: {}", gas_spent_on_pubdata); + tracing::trace!("Pubdata published: {}", pubdata_published); + + ceil_div_u256(refund_eth, effective_gas_price.into()).as_u64() +} diff --git a/core/lib/multivm/src/versions/era_vm/vm.rs b/core/lib/multivm/src/versions/era_vm/vm.rs index 101f778f8825..843525d12993 100644 --- a/core/lib/multivm/src/versions/era_vm/vm.rs +++ b/core/lib/multivm/src/versions/era_vm/vm.rs @@ -36,7 +36,6 @@ use super::{ hook::Hook, initial_bootloader_memory::bootloader_initial_memory, logs::IntoSystemLog, - refunds::compute_refund, snapshot::VmSnapshot, tracers::{ dispatcher::TracerDispatcher, manager::VmTracerManager, refunds_tracer::RefundsTracer, @@ -50,10 +49,9 @@ use crate::{ }, vm_latest::{ constants::{ - get_vm_hook_position, get_vm_hook_start_position_latest, OPERATOR_REFUNDS_OFFSET, - TX_GAS_LIMIT_OFFSET, VM_HOOK_PARAMS_COUNT, + get_vm_hook_position, get_vm_hook_start_position_latest, VM_HOOK_PARAMS_COUNT, }, - BootloaderMemory, CurrentExecutionState, ExecutionResult, L1BatchEnv, L2BlockEnv, Refunds, + BootloaderMemory, CurrentExecutionState, ExecutionResult, L1BatchEnv, L2BlockEnv, SystemEnv, VmExecutionLogs, VmExecutionMode, VmExecutionResultAndLogs, VmExecutionStatistics, }, @@ -161,53 +159,30 @@ impl Vm { pub fn run( &mut self, execution_mode: VmExecutionMode, - tracer: TracerDispatcher, - track_refunds: bool, - ) -> (ExecutionResult, Refunds) { - let mut refunds = Refunds { - gas_refunded: 0, - operator_suggested_refund: 0, - }; - let mut pubdata_before = self.inner.state.pubdata() as u32; + tracer: &mut impl VmTracer, + ) -> ExecutionResult { let mut last_tx_result = None; - let refund_tracer = if track_refunds { - Some(RefundsTracer::new()) - } else { - None - }; - - let mut tracer = VmTracerManager::new(self.storage.clone(), tracer, refund_tracer); - tracer.before_bootloader_execution(self); - let (stop_reason, refunds) = loop { - let result = self - .inner - .run_program_with_custom_bytecode(Some(&mut tracer)); + let stop_reason = loop { + let result = self.inner.run_program_with_custom_bytecode(Some(tracer)); let result = match result { - ExecutionOutput::Ok(output) => { - break (ExecutionResult::Success { output }, refunds) - } + ExecutionOutput::Ok(output) => break (ExecutionResult::Success { output }), ExecutionOutput::Revert(output) => match TxRevertReason::parse_error(&output) { TxRevertReason::TxReverted(output) => { - break (ExecutionResult::Revert { output }, refunds) - } - TxRevertReason::Halt(reason) => { - break (ExecutionResult::Halt { reason }, refunds) + break (ExecutionResult::Revert { output }) } + TxRevertReason::Halt(reason) => break (ExecutionResult::Halt { reason }), }, ExecutionOutput::Panic => { - break ( - ExecutionResult::Halt { - reason: if self.inner.execution.gas_left().unwrap() == 0 { - Halt::BootloaderOutOfGas - } else { - Halt::VMPanic - }, + break ExecutionResult::Halt { + reason: if self.inner.execution.gas_left().unwrap() == 0 { + Halt::BootloaderOutOfGas + } else { + Halt::VMPanic }, - refunds, - ) + } } ExecutionOutput::SuspendedOnHook { hook, @@ -273,55 +248,10 @@ impl Vm { } }); } - Hook::NotifyAboutRefund => { - if track_refunds { - refunds.gas_refunded = self.get_hook_params()[0].low_u64() - } - } - Hook::AskOperatorForRefund => { - if track_refunds { - let [bootloader_refund, gas_spent_on_pubdata, gas_per_pubdata_byte] = - self.get_hook_params(); - let current_tx_index = self.bootloader_state.current_tx(); - let tx_description_offset = self - .bootloader_state - .get_tx_description_offset(current_tx_index); - let tx_gas_limit = self - .read_heap_word(tx_description_offset + TX_GAS_LIMIT_OFFSET) - .as_u64(); - - let pubdata_published = self.inner.state.pubdata() as u32; - - refunds.operator_suggested_refund = compute_refund( - &self.batch_env, - bootloader_refund.as_u64(), - gas_spent_on_pubdata.as_u64(), - tx_gas_limit, - gas_per_pubdata_byte.low_u32(), - pubdata_published.saturating_sub(pubdata_before), - self.bootloader_state - .last_l2_block() - .txs - .last() - .unwrap() - .hash, - ); - - pubdata_before = pubdata_published; - let refund_value = refunds.operator_suggested_refund; - self.write_to_bootloader_heap([( - OPERATOR_REFUNDS_OFFSET + current_tx_index, - refund_value.into(), - )]); - self.bootloader_state - .set_refund_for_current_tx(refund_value); - } - } Hook::DebugLog => {} Hook::TxHasEnded => { if let VmExecutionMode::OneTx = execution_mode { - let tx_result = last_tx_result.take().unwrap(); - return (tx_result, refunds); + break last_tx_result.take().unwrap(); } } Hook::PubdataRequested => { @@ -366,12 +296,13 @@ impl Vm { apply_pubdata_to_memory(&mut memory_to_apply, pubdata_input); self.write_to_bootloader_heap(memory_to_apply); } + _ => {} } }; tracer.after_bootloader_execution(self, stop_reason.clone()); - (stop_reason, refunds) + stop_reason } pub(crate) fn insert_bytecodes<'a>(&mut self, bytecodes: impl IntoIterator) { @@ -520,19 +451,26 @@ impl VmInterface for Vm { _tracer: Self::TracerDispatcher, execution_mode: VmExecutionMode, ) -> VmExecutionResultAndLogs { - let mut enable_refund_tracer = false; + let mut track_refunds = false; if let VmExecutionMode::OneTx = execution_mode { // Move the pointer to the next transaction self.bootloader_state.move_tx_to_execute_pointer(); - enable_refund_tracer = true; + track_refunds = true; } - let snapshot = self.inner.state.snapshot(); - let (result, refunds) = self.run( - execution_mode, + let refund_tracer = if track_refunds { + Some(RefundsTracer::new()) + } else { + None + }; + let mut tracer = VmTracerManager::new( + self.storage.clone(), TracerDispatcher::new(vec![]), - enable_refund_tracer, + refund_tracer, ); + let snapshot = self.inner.state.snapshot(); + + let result = self.run(execution_mode, &mut tracer); let ignore_world_diff = matches!(execution_mode, VmExecutionMode::OneTx) && matches!(result, ExecutionResult::Halt { .. }); @@ -604,7 +542,7 @@ impl VmInterface for Vm { pubdata_published: (self.inner.state.pubdata() - snapshot.pubdata).max(0) as u32, circuit_statistic: Default::default(), }, - refunds, + refunds: tracer.refund_tracer.unwrap_or_default().into(), } } From 149bb703a4ee67a8a0e2105de21fd8d844a58559 Mon Sep 17 00:00:00 2001 From: Marcos Nicolau Date: Fri, 23 Aug 2024 12:07:43 -0300 Subject: [PATCH 10/32] Implement pubdata tracer --- .../src/versions/era_vm/tracers/dispatcher.rs | 2 +- .../src/versions/era_vm/tracers/manager.rs | 9 +- .../versions/era_vm/tracers/pubdata_tracer.rs | 94 ++++++++++++++++++- core/lib/multivm/src/versions/era_vm/vm.rs | 76 +-------------- 4 files changed, 103 insertions(+), 78 deletions(-) diff --git a/core/lib/multivm/src/versions/era_vm/tracers/dispatcher.rs b/core/lib/multivm/src/versions/era_vm/tracers/dispatcher.rs index 00f672f57008..2731cfd39838 100644 --- a/core/lib/multivm/src/versions/era_vm/tracers/dispatcher.rs +++ b/core/lib/multivm/src/versions/era_vm/tracers/dispatcher.rs @@ -1,4 +1,4 @@ -use era_vm::{state::VMState, tracers::tracer::Tracer, vm::ExecutionOutput, Execution, Opcode}; +use era_vm::{state::VMState, tracers::tracer::Tracer, Execution, Opcode}; use zksync_state::ReadStorage; use super::traits::{ExecutionResult, VmTracer}; diff --git a/core/lib/multivm/src/versions/era_vm/tracers/manager.rs b/core/lib/multivm/src/versions/era_vm/tracers/manager.rs index f3f68355d329..35d1554d0446 100644 --- a/core/lib/multivm/src/versions/era_vm/tracers/manager.rs +++ b/core/lib/multivm/src/versions/era_vm/tracers/manager.rs @@ -9,13 +9,17 @@ use super::{ result_tracer::ResultTracer, traits::{ExecutionResult, VmTracer}, }; -use crate::era_vm::vm::Vm; +use crate::{era_vm::vm::Vm, vm_1_4_1::VmExecutionMode}; // this tracer manager is the one that gets called when running the vm pub struct VmTracerManager { pub dispatcher: TracerDispatcher, pub result_tracer: ResultTracer, + // This tracer is designed specifically for calculating refunds and saves the results to `VmResultAndLogs`. pub refund_tracer: Option, + // The pubdata tracer is responsible for inserting the pubdata packing information into the bootloader + // memory at the end of the batch. Its separation from the custom tracer + // ensures static dispatch, enhancing performance by avoiding dynamic dispatch overhe pub pubdata_tracer: PubdataTracer, pub circuits_tracer: CircuitsTracer, storage: StoragePtr, @@ -23,6 +27,7 @@ pub struct VmTracerManager { impl VmTracerManager { pub fn new( + execution_mode: VmExecutionMode, storage: StoragePtr, dispatcher: TracerDispatcher, refund_tracer: Option, @@ -32,7 +37,7 @@ impl VmTracerManager { refund_tracer, circuits_tracer: CircuitsTracer::new(), result_tracer: ResultTracer::new(), - pubdata_tracer: PubdataTracer::new(), + pubdata_tracer: PubdataTracer::new(execution_mode), storage, } } diff --git a/core/lib/multivm/src/versions/era_vm/tracers/pubdata_tracer.rs b/core/lib/multivm/src/versions/era_vm/tracers/pubdata_tracer.rs index 3f5b057500ce..1e0380c983ad 100644 --- a/core/lib/multivm/src/versions/era_vm/tracers/pubdata_tracer.rs +++ b/core/lib/multivm/src/versions/era_vm/tracers/pubdata_tracer.rs @@ -1,15 +1,101 @@ use zksync_state::ReadStorage; +use zksync_types::{ + event::{ + extract_l2tol1logs_from_l1_messenger, extract_long_l2_to_l1_messages, + L1_MESSENGER_BYTECODE_PUBLICATION_EVENT_SIGNATURE, + }, + L1_MESSENGER_ADDRESS, U256, +}; +use zksync_utils::u256_to_h256; + +use crate::{ + era_vm::{ + bootloader_state::utils::{apply_pubdata_to_memory, PubdataInput}, + event::merge_events, + hook::Hook, + }, + vm_1_4_1::VmExecutionMode, +}; use super::traits::{Tracer, VmTracer}; -pub struct PubdataTracer {} +pub struct PubdataTracer { + execution_mode: VmExecutionMode, + pubdata_before_run: i32, + pub pubdata_published: u32, +} impl PubdataTracer { - pub fn new() -> Self { - Self {} + pub fn new(execution_mode: VmExecutionMode) -> Self { + Self { + execution_mode, + pubdata_before_run: 0, + pubdata_published: 0, + } } } impl Tracer for PubdataTracer {} -impl VmTracer for PubdataTracer {} +impl VmTracer for PubdataTracer { + fn before_bootloader_execution(&mut self, vm: &mut super::traits::Vm) { + self.pubdata_before_run = vm.inner.state.pubdata(); + } + + fn after_bootloader_execution( + &mut self, + vm: &mut super::traits::Vm, + _stop_reason: super::traits::ExecutionResult, + ) { + self.pubdata_published = (vm.inner.state.pubdata() - self.pubdata_before_run).max(0) as u32; + } + + fn bootloader_hook_call( + &mut self, + vm: &mut super::traits::Vm, + hook: Hook, + _hook_params: &[zksync_types::U256; 3], + ) { + if let Hook::PubdataRequested = hook { + if !matches!(self.execution_mode, VmExecutionMode::Batch) { + unreachable!("We do not provide the pubdata when executing the block tip or a single transaction"); + }; + + let events = merge_events(vm.inner.state.events(), vm.batch_env.number); + + let published_bytecodes: Vec> = events + .iter() + .filter(|event| { + // Filter events from the l1 messenger contract that match the expected signature. + event.address == L1_MESSENGER_ADDRESS + && !event.indexed_topics.is_empty() + && event.indexed_topics[0] + == *L1_MESSENGER_BYTECODE_PUBLICATION_EVENT_SIGNATURE + }) + .map(|event| { + let hash = U256::from_big_endian(&event.value[..32]); + vm.storage + .load_factory_dep(u256_to_h256(hash)) + .expect("published unknown bytecode") + .clone() + }) + .collect(); + + let pubdata_input = PubdataInput { + user_logs: extract_l2tol1logs_from_l1_messenger(&events), + l2_to_l1_messages: extract_long_l2_to_l1_messages(&events), + published_bytecodes, + state_diffs: vm.get_storage_diff(), + }; + + // Save the pubdata for the future initial bootloader memory building + vm.bootloader_state.set_pubdata_input(pubdata_input.clone()); + + // Apply the pubdata to the current memory + let mut memory_to_apply = vec![]; + + apply_pubdata_to_memory(&mut memory_to_apply, pubdata_input); + vm.write_to_bootloader_heap(memory_to_apply); + } + } +} diff --git a/core/lib/multivm/src/versions/era_vm/vm.rs b/core/lib/multivm/src/versions/era_vm/vm.rs index 843525d12993..23e97da514e9 100644 --- a/core/lib/multivm/src/versions/era_vm/vm.rs +++ b/core/lib/multivm/src/versions/era_vm/vm.rs @@ -7,10 +7,7 @@ use era_vm::{ use itertools::Itertools; use zksync_state::{ReadStorage, StoragePtr}; use zksync_types::{ - event::{ - extract_l2tol1logs_from_l1_messenger, extract_long_l2_to_l1_messages, - L1_MESSENGER_BYTECODE_PUBLICATION_EVENT_SIGNATURE, - }, + event::extract_l2tol1logs_from_l1_messenger, l1::is_l1_tx_type, l2_to_l1_log::UserL2ToL1Log, utils::key_for_eth_balance, @@ -28,10 +25,7 @@ use zksync_utils::{ }; use super::{ - bootloader_state::{ - utils::{apply_l2_block, apply_pubdata_to_memory, PubdataInput}, - BootloaderState, - }, + bootloader_state::{utils::apply_l2_block, BootloaderState}, event::merge_events, hook::Hook, initial_bootloader_memory::bootloader_initial_memory, @@ -197,9 +191,6 @@ impl Vm { tracer.bootloader_hook_call(self, Hook::from_u32(result), &self.get_hook_params()); match Hook::from_u32(result) { - Hook::PaymasterValidationEntered => { - // unused - } Hook::FinalBatchInfo => { // set fictive l2 block let txs_index = self.bootloader_state.free_tx_index(); @@ -208,21 +199,6 @@ impl Vm { apply_l2_block(&mut memory, l2_block, txs_index); self.write_to_bootloader_heap(memory); } - Hook::AccountValidationEntered => { - // println!("ACCOUNT VALIDATION ENTERED"); - } - Hook::ValidationStepEnded => { - // println!("VALIDATION STEP ENDED"); - } - Hook::AccountValidationExited => { - // println!("ACCOUNT VALIDATION EXITED"); - } - Hook::DebugReturnData => { - // println!("DEBUG RETURN DATA"); - } - Hook::NearCallCatch => { - // println!("NOTIFY ABOUT NEAR CALL CATCH"); - } Hook::PostResult => { let result = self.get_hook_params()[0]; let value = self.get_hook_params()[1]; @@ -248,54 +224,11 @@ impl Vm { } }); } - Hook::DebugLog => {} Hook::TxHasEnded => { if let VmExecutionMode::OneTx = execution_mode { break last_tx_result.take().unwrap(); } } - Hook::PubdataRequested => { - if !matches!(execution_mode, VmExecutionMode::Batch) { - unreachable!("We do not provide the pubdata when executing the block tip or a single transaction"); - } - - let events = merge_events(self.inner.state.events(), self.batch_env.number); - - let published_bytecodes: Vec> = events - .iter() - .filter(|event| { - // Filter events from the l1 messenger contract that match the expected signature. - event.address == L1_MESSENGER_ADDRESS - && !event.indexed_topics.is_empty() - && event.indexed_topics[0] - == *L1_MESSENGER_BYTECODE_PUBLICATION_EVENT_SIGNATURE - }) - .map(|event| { - let hash = U256::from_big_endian(&event.value[..32]); - self.storage - .load_factory_dep(u256_to_h256(hash)) - .expect("published unknown bytecode") - .clone() - }) - .collect(); - - let pubdata_input = PubdataInput { - user_logs: extract_l2tol1logs_from_l1_messenger(&events), - l2_to_l1_messages: extract_long_l2_to_l1_messages(&events), - published_bytecodes, - state_diffs: self.get_storage_diff(), - }; - - // Save the pubdata for the future initial bootloader memory building - self.bootloader_state - .set_pubdata_input(pubdata_input.clone()); - - // Apply the pubdata to the current memory - let mut memory_to_apply = vec![]; - - apply_pubdata_to_memory(&mut memory_to_apply, pubdata_input); - self.write_to_bootloader_heap(memory_to_apply); - } _ => {} } }; @@ -361,7 +294,7 @@ impl Vm { } } - fn get_storage_diff(&mut self) -> Vec { + pub fn get_storage_diff(&mut self) -> Vec { self.inner .state .get_storage_changes() @@ -464,6 +397,7 @@ impl VmInterface for Vm { None }; let mut tracer = VmTracerManager::new( + execution_mode, self.storage.clone(), TracerDispatcher::new(vec![]), refund_tracer, @@ -539,7 +473,7 @@ impl VmInterface for Vm { gas_remaining: 0, computational_gas_used: 0, total_log_queries: 0, - pubdata_published: (self.inner.state.pubdata() - snapshot.pubdata).max(0) as u32, + pubdata_published: tracer.pubdata_tracer.pubdata_published, circuit_statistic: Default::default(), }, refunds: tracer.refund_tracer.unwrap_or_default().into(), From e0a18908172f8cecb25d3bb125acb82818179f0c Mon Sep 17 00:00:00 2001 From: Marcos Nicolau Date: Fri, 23 Aug 2024 12:58:35 -0300 Subject: [PATCH 11/32] Implement result tracer --- .../versions/era_vm/tracers/result_tracer.rs | 48 +++++++++++++++++-- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/core/lib/multivm/src/versions/era_vm/tracers/result_tracer.rs b/core/lib/multivm/src/versions/era_vm/tracers/result_tracer.rs index 90c9213d75cc..13f4a634da49 100644 --- a/core/lib/multivm/src/versions/era_vm/tracers/result_tracer.rs +++ b/core/lib/multivm/src/versions/era_vm/tracers/result_tracer.rs @@ -1,15 +1,55 @@ +use era_vm::value::FatPointer; use zksync_state::ReadStorage; -use super::traits::{Tracer, VmTracer}; +use crate::{era_vm::hook::Hook, interface::VmRevertReason}; -pub struct ResultTracer {} +use super::traits::{ExecutionResult, Tracer, VmTracer}; + +pub struct ResultTracer { + pub last_tx_result: Option, +} impl ResultTracer { pub fn new() -> Self { - Self {} + Self { + last_tx_result: None, + } } } impl Tracer for ResultTracer {} -impl VmTracer for ResultTracer {} +impl VmTracer for ResultTracer { + fn bootloader_hook_call( + &mut self, + vm: &mut super::traits::Vm, + hook: Hook, + hook_params: &[zksync_types::U256; 3], + ) { + if let Hook::PostResult = hook { + let result = hook_params[0]; + let value = hook_params[1]; + let pointer = FatPointer::decode(value); + assert_eq!(pointer.offset, 0); + + let return_data = vm + .inner + .execution + .heaps + .get(pointer.page) + .unwrap() + .read_unaligned_from_pointer(&pointer) + .unwrap(); + + self.last_tx_result = Some(if result.is_zero() { + ExecutionResult::Revert { + output: VmRevertReason::from(return_data.as_slice()), + } + } else { + ExecutionResult::Success { + output: return_data, + } + }); + }; + } +} From 5010795f345cc7766923137b932e28332ecc080a Mon Sep 17 00:00:00 2001 From: Marcos Nicolau Date: Fri, 23 Aug 2024 12:59:00 -0300 Subject: [PATCH 12/32] Move result parsing to tracer manager --- .../src/versions/era_vm/tracers/manager.rs | 66 ++++++++++++- core/lib/multivm/src/versions/era_vm/vm.rs | 97 +++---------------- 2 files changed, 75 insertions(+), 88 deletions(-) diff --git a/core/lib/multivm/src/versions/era_vm/tracers/manager.rs b/core/lib/multivm/src/versions/era_vm/tracers/manager.rs index 35d1554d0446..7ba67a803cce 100644 --- a/core/lib/multivm/src/versions/era_vm/tracers/manager.rs +++ b/core/lib/multivm/src/versions/era_vm/tracers/manager.rs @@ -1,4 +1,7 @@ -use era_vm::{execution::Execution, opcode::Opcode, state::VMState, tracers::tracer::Tracer}; +use era_vm::{ + execution::Execution, opcode::Opcode, state::VMState, tracers::tracer::Tracer, + vm::ExecutionOutput, +}; use zksync_state::{ReadStorage, StoragePtr}; use super::{ @@ -9,10 +12,17 @@ use super::{ result_tracer::ResultTracer, traits::{ExecutionResult, VmTracer}, }; -use crate::{era_vm::vm::Vm, vm_1_4_1::VmExecutionMode}; +use crate::{ + era_vm::{bootloader_state::utils::apply_l2_block, hook::Hook, vm::Vm}, + interface::{Halt, TxRevertReason}, + vm_1_4_1::VmExecutionMode, +}; // this tracer manager is the one that gets called when running the vm +// all the logic of hooks and results parsing is managed from here +// the most important tracers are: `result_tracer`, `refund_tracer`, `pubdata_tracer`, pub struct VmTracerManager { + execution_mode: VmExecutionMode, pub dispatcher: TracerDispatcher, pub result_tracer: ResultTracer, // This tracer is designed specifically for calculating refunds and saves the results to `VmResultAndLogs`. @@ -25,7 +35,7 @@ pub struct VmTracerManager { storage: StoragePtr, } -impl VmTracerManager { +impl VmTracerManager { pub fn new( execution_mode: VmExecutionMode, storage: StoragePtr, @@ -33,6 +43,7 @@ impl VmTracerManager { refund_tracer: Option, ) -> Self { Self { + execution_mode, dispatcher, refund_tracer, circuits_tracer: CircuitsTracer::new(), @@ -41,6 +52,55 @@ impl VmTracerManager { storage, } } + + pub fn after_vm_run( + &mut self, + vm: &mut Vm, + output: ExecutionOutput, + ) -> Option { + match output { + ExecutionOutput::Ok(output) => Some(ExecutionResult::Success { output }), + ExecutionOutput::Revert(output) => match TxRevertReason::parse_error(&output) { + TxRevertReason::TxReverted(output) => (Some(ExecutionResult::Revert { output })), + TxRevertReason::Halt(reason) => Some(ExecutionResult::Halt { reason }), + }, + ExecutionOutput::Panic => Some(ExecutionResult::Halt { + reason: if vm.inner.execution.gas_left().unwrap() == 0 { + Halt::BootloaderOutOfGas + } else { + Halt::VMPanic + }, + }), + ExecutionOutput::SuspendedOnHook { + hook, + pc_to_resume_from, + } => { + vm.suspended_at = pc_to_resume_from; + vm.inner.execution.current_frame_mut().unwrap().pc = vm.suspended_at as u64; + let hook = Hook::from_u32(hook); + match hook { + Hook::TxHasEnded => { + if let VmExecutionMode::OneTx = self.execution_mode { + return self.result_tracer.last_tx_result.take(); + } + } + Hook::FinalBatchInfo => self.set_final_batch_info(vm), + _ => {} + } + self.bootloader_hook_call(vm, hook, &vm.get_hook_params()); + None + } + } + } + + fn set_final_batch_info(&self, vm: &mut Vm) { + // set fictive l2 block + let txs_index = vm.bootloader_state.free_tx_index(); + let l2_block = vm.bootloader_state.insert_fictive_l2_block(); + let mut memory = vec![]; + apply_l2_block(&mut memory, l2_block, txs_index); + vm.write_to_bootloader_heap(memory); + } } impl Tracer for VmTracerManager { diff --git a/core/lib/multivm/src/versions/era_vm/vm.rs b/core/lib/multivm/src/versions/era_vm/vm.rs index 23e97da514e9..32f8af0905ff 100644 --- a/core/lib/multivm/src/versions/era_vm/vm.rs +++ b/core/lib/multivm/src/versions/era_vm/vm.rs @@ -150,92 +150,19 @@ impl VmFactory for Vm { } impl Vm { - pub fn run( - &mut self, - execution_mode: VmExecutionMode, - tracer: &mut impl VmTracer, - ) -> ExecutionResult { - let mut last_tx_result = None; - + pub fn run(&mut self, tracer: &mut VmTracerManager) -> ExecutionResult { tracer.before_bootloader_execution(self); - let stop_reason = loop { - let result = self.inner.run_program_with_custom_bytecode(Some(tracer)); - - let result = match result { - ExecutionOutput::Ok(output) => break (ExecutionResult::Success { output }), - ExecutionOutput::Revert(output) => match TxRevertReason::parse_error(&output) { - TxRevertReason::TxReverted(output) => { - break (ExecutionResult::Revert { output }) - } - TxRevertReason::Halt(reason) => break (ExecutionResult::Halt { reason }), - }, - ExecutionOutput::Panic => { - break ExecutionResult::Halt { - reason: if self.inner.execution.gas_left().unwrap() == 0 { - Halt::BootloaderOutOfGas - } else { - Halt::VMPanic - }, - } - } - ExecutionOutput::SuspendedOnHook { - hook, - pc_to_resume_from, - } => { - self.suspended_at = pc_to_resume_from; - self.inner.execution.current_frame_mut().unwrap().pc = self.suspended_at as u64; - hook - } - }; - - tracer.bootloader_hook_call(self, Hook::from_u32(result), &self.get_hook_params()); - - match Hook::from_u32(result) { - Hook::FinalBatchInfo => { - // set fictive l2 block - let txs_index = self.bootloader_state.free_tx_index(); - let l2_block = self.bootloader_state.insert_fictive_l2_block(); - let mut memory = vec![]; - apply_l2_block(&mut memory, l2_block, txs_index); - self.write_to_bootloader_heap(memory); - } - Hook::PostResult => { - let result = self.get_hook_params()[0]; - let value = self.get_hook_params()[1]; - let pointer = FatPointer::decode(value); - assert_eq!(pointer.offset, 0); - - let return_data = self - .inner - .execution - .heaps - .get(pointer.page) - .unwrap() - .read_unaligned_from_pointer(&pointer) - .unwrap(); - - last_tx_result = Some(if result.is_zero() { - ExecutionResult::Revert { - output: VmRevertReason::from(return_data.as_slice()), - } - } else { - ExecutionResult::Success { - output: return_data, - } - }); - } - Hook::TxHasEnded => { - if let VmExecutionMode::OneTx = execution_mode { - break last_tx_result.take().unwrap(); - } - } - _ => {} - } - }; + loop { + let output = self.inner.run_program_with_custom_bytecode(Some(tracer)); - tracer.after_bootloader_execution(self, stop_reason.clone()); + let result = tracer.after_vm_run(self, output); - stop_reason + if result.is_some() { + let result = result.unwrap(); + tracer.after_bootloader_execution(self, result.clone()); + return result; + } + } } pub(crate) fn insert_bytecodes<'a>(&mut self, bytecodes: impl IntoIterator) { @@ -254,7 +181,7 @@ impl Vm { } } - fn get_hook_params(&self) -> [U256; 3] { + pub fn get_hook_params(&self) -> [U256; 3] { let vm_hooks_param_start = get_vm_hook_start_position_latest(); (vm_hooks_param_start..vm_hooks_param_start + VM_HOOK_PARAMS_COUNT) .map(|word| { @@ -404,7 +331,7 @@ impl VmInterface for Vm { ); let snapshot = self.inner.state.snapshot(); - let result = self.run(execution_mode, &mut tracer); + let result = self.run(&mut tracer); let ignore_world_diff = matches!(execution_mode, VmExecutionMode::OneTx) && matches!(result, ExecutionResult::Halt { .. }); From fe31ef963f286221057fd44127ffef6a954db151 Mon Sep 17 00:00:00 2001 From: Marcos Nicolau Date: Fri, 23 Aug 2024 16:44:46 -0300 Subject: [PATCH 13/32] Implement call tracer for era_vm --- .../src/tracers/call_tracer/era_vm/mod.rs | 194 ++++++++++++++++++ .../multivm/src/tracers/call_tracer/mod.rs | 1 + core/lib/multivm/src/versions/era_vm/mod.rs | 2 +- .../src/versions/era_vm/tests/call_tracer.rs | 98 +++++++++ .../multivm/src/versions/era_vm/tests/mod.rs | 1 + .../src/versions/era_vm/tests/utils.rs | 6 + 6 files changed, 301 insertions(+), 1 deletion(-) create mode 100644 core/lib/multivm/src/tracers/call_tracer/era_vm/mod.rs create mode 100644 core/lib/multivm/src/versions/era_vm/tests/call_tracer.rs diff --git a/core/lib/multivm/src/tracers/call_tracer/era_vm/mod.rs b/core/lib/multivm/src/tracers/call_tracer/era_vm/mod.rs new file mode 100644 index 000000000000..f51284ccf4b2 --- /dev/null +++ b/core/lib/multivm/src/tracers/call_tracer/era_vm/mod.rs @@ -0,0 +1,194 @@ +use era_vm::{value::FatPointer, Execution, Opcode}; +use zkevm_opcode_defs::{Opcode as Variant, RetOpcode}; +use zksync_state::ReadStorage; +use zksync_types::{ + vm_trace::{Call, CallType}, + zk_evm_types::FarCallOpcode, + CONTRACT_DEPLOYER_ADDRESS, U256, +}; + +use super::CallTracer; + +use crate::{ + era_vm::tracers::traits::{Tracer, VmTracer}, + interface::VmRevertReason, +}; + +impl Tracer for CallTracer { + fn after_execution( + &mut self, + opcode: &Opcode, + execution: &mut era_vm::Execution, + state: &mut era_vm::state::VMState, + ) { + match opcode.variant { + Variant::NearCall(_) => { + self.increase_near_call_count(); + } + Variant::FarCall(far_call) => { + // We use parent gas for properly calculating gas used in the trace. + let current_ergs = execution.gas_left().unwrap(); + let parent_gas = execution + .running_contexts + .last() + .map(|call| call.frame.gas_left.0.saturating_add(current_ergs)) + .unwrap_or(current_ergs) as u64; + + // we need to to this cast because `Call` uses another library + let far_call_variant = match far_call as u8 { + 0 => FarCallOpcode::Normal, + 1 => FarCallOpcode::Delegate, + 2 => FarCallOpcode::Mimic, + _ => unreachable!(), + }; + + let mut current_call = Call { + r#type: CallType::Call(far_call_variant), + gas: 0, + parent_gas: parent_gas as u64, + ..Default::default() + }; + + self.handle_far_call_op_code_era(execution, &mut current_call); + self.push_call_and_update_stats(current_call, 0); + } + Variant::Ret(ret_code) => { + self.handle_ret_op_code_era(execution, ret_code); + } + _ => {} + }; + } +} + +impl VmTracer for CallTracer { + fn after_bootloader_execution( + &mut self, + _state: &mut crate::era_vm::vm::Vm, + _stop_reason: crate::vm_1_4_1::ExecutionResult, + ) { + self.store_result(); + } +} + +impl CallTracer { + fn handle_far_call_op_code_era(&mut self, execution: &Execution, current_call: &mut Call) { + // since this is a far_call, the current_context represents the current frame + let current = execution.current_context().unwrap(); + // 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 = execution + .running_contexts + .first() + .map(|call| call.caller) + // 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.caller); + if previous_caller == CONTRACT_DEPLOYER_ADDRESS { + CallType::Create + } else { + CallType::Call(far_call) + } + } else { + CallType::Call(far_call) + } + } else { + unreachable!() + }; + let calldata = if current.heap_id == 0 || current.frame.gas_left.0 == 0 { + vec![] + } else { + let packed_abi = execution.get_register(1); + assert!(packed_abi.is_pointer); + let pointer = FatPointer::decode(packed_abi.value); + execution + .heaps + .get(pointer.page) + .unwrap() + .read_unaligned_from_pointer(&pointer) + .unwrap_or_default() + }; + + current_call.input = calldata; + current_call.r#type = call_type; + current_call.from = current.caller; + current_call.to = current.contract_address; + current_call.value = U256::from(current.context_u128); + current_call.gas = current.frame.gas_left.0 as u64; + } + + fn save_output_era( + &mut self, + execution: &Execution, + ret_opcode: RetOpcode, + current_call: &mut Call, + ) { + let fat_data_pointer = execution.get_register(1); + + // 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::decode(fat_data_pointer.value); + if fat_data_pointer.len == 0 && fat_data_pointer.offset == 0 { + Some( + execution + .heaps + .get(fat_data_pointer.page) + .unwrap() + .read_unaligned_from_pointer(&fat_data_pointer) + .unwrap(), + ) + } 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_era(&mut self, execution: &Execution, 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(execution.gas_left().unwrap() as u64); + + self.save_output_era(execution, 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/call_tracer/mod.rs b/core/lib/multivm/src/tracers/call_tracer/mod.rs index 855768067b8a..1f176ae02e39 100644 --- a/core/lib/multivm/src/tracers/call_tracer/mod.rs +++ b/core/lib/multivm/src/tracers/call_tracer/mod.rs @@ -5,6 +5,7 @@ use zksync_types::vm_trace::Call; use crate::{glue::tracers::IntoOldVmTracer, tracers::call_tracer::metrics::CALL_METRICS}; +pub mod era_vm; mod metrics; pub mod vm_1_4_1; pub mod vm_1_4_2; diff --git a/core/lib/multivm/src/versions/era_vm/mod.rs b/core/lib/multivm/src/versions/era_vm/mod.rs index edb8ee88eec0..b595aaa86d79 100644 --- a/core/lib/multivm/src/versions/era_vm/mod.rs +++ b/core/lib/multivm/src/versions/era_vm/mod.rs @@ -7,6 +7,6 @@ mod logs; mod snapshot; #[cfg(test)] mod tests; -mod tracers; +pub mod tracers; mod transaction_data; pub mod vm; diff --git a/core/lib/multivm/src/versions/era_vm/tests/call_tracer.rs b/core/lib/multivm/src/versions/era_vm/tests/call_tracer.rs new file mode 100644 index 000000000000..9317faddbf5c --- /dev/null +++ b/core/lib/multivm/src/versions/era_vm/tests/call_tracer.rs @@ -0,0 +1,98 @@ +use std::sync::Arc; + +use once_cell::sync::OnceCell; +use zksync_types::{Address, Execute}; + +use crate::{ + era_vm::{ + tests::{ + tester::VmTesterBuilder, + utils::{read_max_depth_contract, read_test_contract}, + }, + tracers::dispatcher::TracerDispatcher, + }, + interface::{TxExecutionMode, VmExecutionMode, VmInterface}, + tracers::CallTracer, + vm_latest::constants::BATCH_COMPUTATIONAL_GAS_LIMIT, +}; + +// This test is ultra slow, so it's ignored by default. +#[test] +#[ignore] +fn test_max_depth() { + let contarct = read_max_depth_contract(); + let address = Address::random(); + let mut vm = VmTesterBuilder::new() + .with_empty_in_memory_storage() + .with_random_rich_accounts(1) + .with_deployer() + .with_bootloader_gas_limit(BATCH_COMPUTATIONAL_GAS_LIMIT) + .with_execution_mode(TxExecutionMode::VerifyExecute) + .with_custom_contracts(vec![(contarct, address, true)]) + .build(); + + let account = &mut vm.rich_accounts[0]; + let tx = account.get_l2_tx_for_execute( + Execute { + contract_address: address, + calldata: vec![], + value: Default::default(), + factory_deps: vec![], + }, + None, + ); + + let result = Arc::new(OnceCell::new()); + let call_tracer = Box::new(CallTracer::new(result.clone())); + vm.vm.push_transaction(tx); + let res = vm.vm.inspect( + TracerDispatcher::new(vec![call_tracer]), + VmExecutionMode::OneTx, + ); + assert!(result.get().is_some()); + assert!(res.result.is_failed()); +} + +#[test] +fn test_basic_behavior() { + let contarct = read_test_contract(); + let address = Address::random(); + let mut vm = VmTesterBuilder::new() + .with_empty_in_memory_storage() + .with_random_rich_accounts(1) + .with_deployer() + .with_bootloader_gas_limit(BATCH_COMPUTATIONAL_GAS_LIMIT) + .with_execution_mode(TxExecutionMode::VerifyExecute) + .with_custom_contracts(vec![(contarct, address, true)]) + .build(); + + let increment_by_6_calldata = + "7cf5dab00000000000000000000000000000000000000000000000000000000000000006"; + + let account = &mut vm.rich_accounts[0]; + let tx = account.get_l2_tx_for_execute( + Execute { + contract_address: address, + calldata: hex::decode(increment_by_6_calldata).unwrap(), + value: Default::default(), + factory_deps: vec![], + }, + None, + ); + + let result = Arc::new(OnceCell::new()); + let call_tracer = Box::new(CallTracer::new(result.clone())); + vm.vm.push_transaction(tx); + let res = vm.vm.inspect( + TracerDispatcher::new(vec![call_tracer]), + VmExecutionMode::OneTx, + ); + + let call_tracer_result = result.get().unwrap(); + + assert_eq!(call_tracer_result.len(), 1); + // Expect that there are a plenty of subcalls underneath. + let subcall = &call_tracer_result[0].calls; + assert!(subcall.len() > 10); + assert!(!res.result.is_failed()); +} diff --git a/core/lib/multivm/src/versions/era_vm/tests/mod.rs b/core/lib/multivm/src/versions/era_vm/tests/mod.rs index a56a3ab3f6a2..32507a29943b 100644 --- a/core/lib/multivm/src/versions/era_vm/tests/mod.rs +++ b/core/lib/multivm/src/versions/era_vm/tests/mod.rs @@ -1,5 +1,6 @@ mod bootloader; mod bytecode_publishing; +mod call_tracer; mod code_oracle; mod default_aa; mod gas_limit; diff --git a/core/lib/multivm/src/versions/era_vm/tests/utils.rs b/core/lib/multivm/src/versions/era_vm/tests/utils.rs index 64d6f1104c54..e1d69d53734d 100644 --- a/core/lib/multivm/src/versions/era_vm/tests/utils.rs +++ b/core/lib/multivm/src/versions/era_vm/tests/utils.rs @@ -171,3 +171,9 @@ pub(crate) fn read_expensive_contract() -> (Vec, Contract) { "etc/contracts-test-data/artifacts-zk/contracts/expensive/expensive.sol/Expensive.json"; (read_bytecode(PATH), load_contract(PATH)) } + +pub(crate) fn read_max_depth_contract() -> Vec { + read_zbin_bytecode( + "core/tests/ts-integration/contracts/zkasm/artifacts/deep_stak.zkasm/deep_stak.zkasm.zbin", + ) +} From 69796c7d3e7192982612415edd98346b807433aa Mon Sep 17 00:00:00 2001 From: Marcos Nicolau Date: Fri, 23 Aug 2024 16:45:13 -0300 Subject: [PATCH 14/32] Accept tracer param in inspect --- .../src/versions/era_vm/tracers/dispatcher.rs | 7 ++++++- .../src/versions/era_vm/tracers/pubdata_tracer.rs | 3 +-- .../src/versions/era_vm/tracers/result_tracer.rs | 3 +-- core/lib/multivm/src/versions/era_vm/vm.rs | 12 ++++-------- 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/core/lib/multivm/src/versions/era_vm/tracers/dispatcher.rs b/core/lib/multivm/src/versions/era_vm/tracers/dispatcher.rs index 2731cfd39838..ee219e614755 100644 --- a/core/lib/multivm/src/versions/era_vm/tracers/dispatcher.rs +++ b/core/lib/multivm/src/versions/era_vm/tracers/dispatcher.rs @@ -4,12 +4,17 @@ use zksync_state::ReadStorage; use super::traits::{ExecutionResult, VmTracer}; use crate::era_vm::vm::Vm; -#[derive(Default)] // dispatcher calls to other tracers pub struct TracerDispatcher { tracers: Vec>>, } +impl Default for TracerDispatcher { + fn default() -> Self { + Self { tracers: vec![] } + } +} + impl TracerDispatcher { pub fn new(tracers: Vec>>) -> Self { Self { tracers } diff --git a/core/lib/multivm/src/versions/era_vm/tracers/pubdata_tracer.rs b/core/lib/multivm/src/versions/era_vm/tracers/pubdata_tracer.rs index 1e0380c983ad..d2b82d86f513 100644 --- a/core/lib/multivm/src/versions/era_vm/tracers/pubdata_tracer.rs +++ b/core/lib/multivm/src/versions/era_vm/tracers/pubdata_tracer.rs @@ -8,6 +8,7 @@ use zksync_types::{ }; use zksync_utils::u256_to_h256; +use super::traits::{Tracer, VmTracer}; use crate::{ era_vm::{ bootloader_state::utils::{apply_pubdata_to_memory, PubdataInput}, @@ -17,8 +18,6 @@ use crate::{ vm_1_4_1::VmExecutionMode, }; -use super::traits::{Tracer, VmTracer}; - pub struct PubdataTracer { execution_mode: VmExecutionMode, pubdata_before_run: i32, diff --git a/core/lib/multivm/src/versions/era_vm/tracers/result_tracer.rs b/core/lib/multivm/src/versions/era_vm/tracers/result_tracer.rs index 13f4a634da49..9a076574ee58 100644 --- a/core/lib/multivm/src/versions/era_vm/tracers/result_tracer.rs +++ b/core/lib/multivm/src/versions/era_vm/tracers/result_tracer.rs @@ -1,9 +1,8 @@ use era_vm::value::FatPointer; use zksync_state::ReadStorage; -use crate::{era_vm::hook::Hook, interface::VmRevertReason}; - use super::traits::{ExecutionResult, Tracer, VmTracer}; +use crate::{era_vm::hook::Hook, interface::VmRevertReason}; pub struct ResultTracer { pub last_tx_result: Option, diff --git a/core/lib/multivm/src/versions/era_vm/vm.rs b/core/lib/multivm/src/versions/era_vm/vm.rs index 32f8af0905ff..1b45fe575fa1 100644 --- a/core/lib/multivm/src/versions/era_vm/vm.rs +++ b/core/lib/multivm/src/versions/era_vm/vm.rs @@ -300,7 +300,7 @@ impl Vm { } impl VmInterface for Vm { - type TracerDispatcher = (); + type TracerDispatcher = TracerDispatcher; fn push_transaction(&mut self, tx: Transaction) { self.push_transaction_inner(tx, 0, true); @@ -308,7 +308,7 @@ impl VmInterface for Vm { fn inspect( &mut self, - _tracer: Self::TracerDispatcher, + tracer: Self::TracerDispatcher, execution_mode: VmExecutionMode, ) -> VmExecutionResultAndLogs { let mut track_refunds = false; @@ -323,12 +323,8 @@ impl VmInterface for Vm { } else { None }; - let mut tracer = VmTracerManager::new( - execution_mode, - self.storage.clone(), - TracerDispatcher::new(vec![]), - refund_tracer, - ); + let mut tracer = + VmTracerManager::new(execution_mode, self.storage.clone(), tracer, refund_tracer); let snapshot = self.inner.state.snapshot(); let result = self.run(&mut tracer); From 43604ebcb7ffb0279aecb8e12051c13e34e2ae42 Mon Sep 17 00:00:00 2001 From: Marcos Nicolau Date: Fri, 23 Aug 2024 19:24:58 -0300 Subject: [PATCH 15/32] Add after_vm function to VmTrait This is to allow any tracer to stop execution --- .../src/tracers/call_tracer/era_vm/mod.rs | 6 +- .../src/versions/era_vm/tracers/dispatcher.rs | 31 +++++- .../src/versions/era_vm/tracers/manager.rs | 101 ++++++++++-------- .../versions/era_vm/tracers/pubdata_tracer.rs | 6 +- .../versions/era_vm/tracers/result_tracer.rs | 45 ++++++-- .../src/versions/era_vm/tracers/traits.rs | 10 +- core/lib/multivm/src/versions/era_vm/vm.rs | 28 +++-- 7 files changed, 143 insertions(+), 84 deletions(-) diff --git a/core/lib/multivm/src/tracers/call_tracer/era_vm/mod.rs b/core/lib/multivm/src/tracers/call_tracer/era_vm/mod.rs index f51284ccf4b2..5a7262c47512 100644 --- a/core/lib/multivm/src/tracers/call_tracer/era_vm/mod.rs +++ b/core/lib/multivm/src/tracers/call_tracer/era_vm/mod.rs @@ -61,11 +61,7 @@ impl Tracer for CallTracer { } impl VmTracer for CallTracer { - fn after_bootloader_execution( - &mut self, - _state: &mut crate::era_vm::vm::Vm, - _stop_reason: crate::vm_1_4_1::ExecutionResult, - ) { + fn after_bootloader_execution(&mut self, _state: &mut crate::era_vm::vm::Vm) { self.store_result(); } } diff --git a/core/lib/multivm/src/versions/era_vm/tracers/dispatcher.rs b/core/lib/multivm/src/versions/era_vm/tracers/dispatcher.rs index ee219e614755..4f00ca535398 100644 --- a/core/lib/multivm/src/versions/era_vm/tracers/dispatcher.rs +++ b/core/lib/multivm/src/versions/era_vm/tracers/dispatcher.rs @@ -1,8 +1,8 @@ use era_vm::{state::VMState, tracers::tracer::Tracer, Execution, Opcode}; use zksync_state::ReadStorage; -use super::traits::{ExecutionResult, VmTracer}; -use crate::era_vm::vm::Vm; +use super::traits::VmTracer; +use crate::{era_vm::vm::Vm, interface::tracer::TracerExecutionStatus}; // dispatcher calls to other tracers pub struct TracerDispatcher { @@ -59,9 +59,32 @@ impl VmTracer for TracerDispatcher { } } - fn after_bootloader_execution(&mut self, state: &mut Vm, stop_reason: ExecutionResult) { + fn after_bootloader_execution(&mut self, state: &mut Vm) { for tracer in self.tracers.iter_mut() { - tracer.after_bootloader_execution(state, stop_reason.clone()); + tracer.after_bootloader_execution(state); } } + + fn bootloader_hook_call( + &mut self, + state: &mut Vm, + hook: crate::era_vm::hook::Hook, + hook_params: &[zksync_types::U256; 3], + ) { + for tracer in self.tracers.iter_mut() { + tracer.bootloader_hook_call(state, hook.clone(), &hook_params); + } + } + + fn after_vm_run( + &mut self, + vm: &mut Vm, + output: era_vm::vm::ExecutionOutput, + ) -> crate::interface::tracer::TracerExecutionStatus { + let mut result = TracerExecutionStatus::Continue; + for tracer in self.tracers.iter_mut() { + result = result.stricter(&tracer.after_vm_run(vm, output.clone())); + } + result + } } diff --git a/core/lib/multivm/src/versions/era_vm/tracers/manager.rs b/core/lib/multivm/src/versions/era_vm/tracers/manager.rs index 7ba67a803cce..e4fa37bf2c49 100644 --- a/core/lib/multivm/src/versions/era_vm/tracers/manager.rs +++ b/core/lib/multivm/src/versions/era_vm/tracers/manager.rs @@ -5,16 +5,12 @@ use era_vm::{ use zksync_state::{ReadStorage, StoragePtr}; use super::{ - circuits_tracer::CircuitsTracer, - dispatcher::TracerDispatcher, - pubdata_tracer::PubdataTracer, - refunds_tracer::RefundsTracer, - result_tracer::ResultTracer, - traits::{ExecutionResult, VmTracer}, + circuits_tracer::CircuitsTracer, dispatcher::TracerDispatcher, pubdata_tracer::PubdataTracer, + refunds_tracer::RefundsTracer, result_tracer::ResultTracer, traits::VmTracer, }; use crate::{ era_vm::{bootloader_state::utils::apply_l2_block, hook::Hook, vm::Vm}, - interface::{Halt, TxRevertReason}, + interface::tracer::{TracerExecutionStatus, TracerExecutionStopReason}, vm_1_4_1::VmExecutionMode, }; @@ -53,24 +49,21 @@ impl VmTracerManager { } } - pub fn after_vm_run( + fn set_final_batch_info(&self, vm: &mut Vm) { + // set fictive l2 block + let txs_index = vm.bootloader_state.free_tx_index(); + let l2_block = vm.bootloader_state.insert_fictive_l2_block(); + let mut memory = vec![]; + apply_l2_block(&mut memory, l2_block, txs_index); + vm.write_to_bootloader_heap(memory); + } + + fn handle_execution_output( &mut self, vm: &mut Vm, output: ExecutionOutput, - ) -> Option { + ) -> TracerExecutionStatus { match output { - ExecutionOutput::Ok(output) => Some(ExecutionResult::Success { output }), - ExecutionOutput::Revert(output) => match TxRevertReason::parse_error(&output) { - TxRevertReason::TxReverted(output) => (Some(ExecutionResult::Revert { output })), - TxRevertReason::Halt(reason) => Some(ExecutionResult::Halt { reason }), - }, - ExecutionOutput::Panic => Some(ExecutionResult::Halt { - reason: if vm.inner.execution.gas_left().unwrap() == 0 { - Halt::BootloaderOutOfGas - } else { - Halt::VMPanic - }, - }), ExecutionOutput::SuspendedOnHook { hook, pc_to_resume_from, @@ -78,29 +71,25 @@ impl VmTracerManager { vm.suspended_at = pc_to_resume_from; vm.inner.execution.current_frame_mut().unwrap().pc = vm.suspended_at as u64; let hook = Hook::from_u32(hook); + self.bootloader_hook_call(vm, hook.clone(), &vm.get_hook_params()); match hook { Hook::TxHasEnded => { if let VmExecutionMode::OneTx = self.execution_mode { - return self.result_tracer.last_tx_result.take(); + TracerExecutionStatus::Stop(TracerExecutionStopReason::Finish) + } else { + TracerExecutionStatus::Continue } } - Hook::FinalBatchInfo => self.set_final_batch_info(vm), - _ => {} + Hook::FinalBatchInfo => { + self.set_final_batch_info(vm); + TracerExecutionStatus::Continue + } + _ => TracerExecutionStatus::Continue, } - self.bootloader_hook_call(vm, hook, &vm.get_hook_params()); - None } + _ => TracerExecutionStatus::Stop(TracerExecutionStopReason::Finish), } } - - fn set_final_batch_info(&self, vm: &mut Vm) { - // set fictive l2 block - let txs_index = vm.bootloader_state.free_tx_index(); - let l2_block = vm.bootloader_state.insert_fictive_l2_block(); - let mut memory = vec![]; - apply_l2_block(&mut memory, l2_block, txs_index); - vm.write_to_bootloader_heap(memory); - } } impl Tracer for VmTracerManager { @@ -183,21 +172,17 @@ impl VmTracer for VmTracerManager { self.circuits_tracer.before_bootloader_execution(state); } - fn after_bootloader_execution(&mut self, state: &mut Vm, stop_reason: ExecutionResult) { + fn after_bootloader_execution(&mut self, state: &mut Vm) { // Call the dispatcher to handle all the tracers added to it - self.dispatcher - .after_bootloader_execution(state, stop_reason.clone()); + self.dispatcher.after_bootloader_execution(state); // Individual tracers - self.result_tracer - .after_bootloader_execution(state, stop_reason.clone()); + self.result_tracer.after_bootloader_execution(state); if let Some(refunds_tracer) = &mut self.refund_tracer { - refunds_tracer.after_bootloader_execution(state, stop_reason.clone()); + refunds_tracer.after_bootloader_execution(state); } - self.pubdata_tracer - .after_bootloader_execution(state, stop_reason.clone()); - self.circuits_tracer - .after_bootloader_execution(state, stop_reason.clone()); + self.pubdata_tracer.after_bootloader_execution(state); + self.circuits_tracer.after_bootloader_execution(state); } fn bootloader_hook_call( @@ -221,4 +206,30 @@ impl VmTracer for VmTracerManager { self.circuits_tracer .bootloader_hook_call(state, hook.clone(), hook_params); } + + fn after_vm_run(&mut self, vm: &mut Vm, output: ExecutionOutput) -> TracerExecutionStatus { + // Call the dispatcher to handle all the tracers added to it + let mut result = self.dispatcher.after_vm_run(vm, output.clone()); + + // Individual tracers + result = self + .result_tracer + .after_vm_run(vm, output.clone()) + .stricter(&result); + if let Some(refunds_tracer) = &mut self.refund_tracer { + result = refunds_tracer + .after_vm_run(vm, output.clone()) + .stricter(&result); + } + result = self + .pubdata_tracer + .after_vm_run(vm, output.clone()) + .stricter(&result); + result = self + .circuits_tracer + .after_vm_run(vm, output.clone()) + .stricter(&result); + + self.handle_execution_output(vm, output).stricter(&result) + } } diff --git a/core/lib/multivm/src/versions/era_vm/tracers/pubdata_tracer.rs b/core/lib/multivm/src/versions/era_vm/tracers/pubdata_tracer.rs index d2b82d86f513..4e98dd28523f 100644 --- a/core/lib/multivm/src/versions/era_vm/tracers/pubdata_tracer.rs +++ b/core/lib/multivm/src/versions/era_vm/tracers/pubdata_tracer.rs @@ -41,11 +41,7 @@ impl VmTracer for PubdataTracer { self.pubdata_before_run = vm.inner.state.pubdata(); } - fn after_bootloader_execution( - &mut self, - vm: &mut super::traits::Vm, - _stop_reason: super::traits::ExecutionResult, - ) { + fn after_bootloader_execution(&mut self, vm: &mut super::traits::Vm) { self.pubdata_published = (vm.inner.state.pubdata() - self.pubdata_before_run).max(0) as u32; } diff --git a/core/lib/multivm/src/versions/era_vm/tracers/result_tracer.rs b/core/lib/multivm/src/versions/era_vm/tracers/result_tracer.rs index 9a076574ee58..ec8fa7dc28f7 100644 --- a/core/lib/multivm/src/versions/era_vm/tracers/result_tracer.rs +++ b/core/lib/multivm/src/versions/era_vm/tracers/result_tracer.rs @@ -1,24 +1,55 @@ -use era_vm::value::FatPointer; +use era_vm::{value::FatPointer, vm::ExecutionOutput}; use zksync_state::ReadStorage; use super::traits::{ExecutionResult, Tracer, VmTracer}; -use crate::{era_vm::hook::Hook, interface::VmRevertReason}; +use crate::{ + era_vm::hook::Hook, + interface::{tracer::TracerExecutionStatus, Halt, TxRevertReason, VmRevertReason}, +}; pub struct ResultTracer { - pub last_tx_result: Option, + pub result: Option, } impl ResultTracer { pub fn new() -> Self { - Self { - last_tx_result: None, - } + Self { result: None } } } impl Tracer for ResultTracer {} impl VmTracer for ResultTracer { + fn after_vm_run( + &mut self, + vm: &mut super::traits::Vm, + output: era_vm::vm::ExecutionOutput, + ) -> TracerExecutionStatus { + let result = match output { + ExecutionOutput::Ok(output) => Some(ExecutionResult::Success { output }), + ExecutionOutput::Revert(output) => match TxRevertReason::parse_error(&output) { + TxRevertReason::TxReverted(output) => Some(ExecutionResult::Revert { output }), + TxRevertReason::Halt(reason) => Some(ExecutionResult::Halt { reason }), + }, + ExecutionOutput::Panic => Some(ExecutionResult::Halt { + reason: if vm.inner.execution.gas_left().unwrap() == 0 { + Halt::BootloaderOutOfGas + } else { + Halt::VMPanic + }, + }), + _ => None, + }; + + // if the result is none, it means the execution has been suspended + // and we don't want to remove the previous value + if result.is_some() { + self.result = result; + } + + TracerExecutionStatus::Continue + } + fn bootloader_hook_call( &mut self, vm: &mut super::traits::Vm, @@ -40,7 +71,7 @@ impl VmTracer for ResultTracer { .read_unaligned_from_pointer(&pointer) .unwrap(); - self.last_tx_result = Some(if result.is_zero() { + self.result = Some(if result.is_zero() { ExecutionResult::Revert { output: VmRevertReason::from(return_data.as_slice()), } diff --git a/core/lib/multivm/src/versions/era_vm/tracers/traits.rs b/core/lib/multivm/src/versions/era_vm/tracers/traits.rs index 2d8650f81c35..cc6d8adb1a74 100644 --- a/core/lib/multivm/src/versions/era_vm/tracers/traits.rs +++ b/core/lib/multivm/src/versions/era_vm/tracers/traits.rs @@ -1,14 +1,20 @@ pub use era_vm::tracers::tracer::Tracer; +use era_vm::vm::ExecutionOutput; use zksync_state::ReadStorage; use zksync_types::U256; -use crate::era_vm::hook::Hook; +use crate::{era_vm::hook::Hook, interface::tracer::TracerExecutionStatus}; pub use crate::{era_vm::vm::Vm, vm_latest::ExecutionResult}; pub trait VmTracer: Tracer { fn before_bootloader_execution(&mut self, _state: &mut Vm) {} - fn after_bootloader_execution(&mut self, _state: &mut Vm, _stop_reason: ExecutionResult) {} + fn after_bootloader_execution(&mut self, _state: &mut Vm) {} fn bootloader_hook_call(&mut self, _state: &mut Vm, _hook: Hook, _hook_params: &[U256; 3]) {} + + // runs after every vm execution or transaction + fn after_vm_run(&mut self, _vm: &mut Vm, _output: ExecutionOutput) -> TracerExecutionStatus { + TracerExecutionStatus::Continue + } } diff --git a/core/lib/multivm/src/versions/era_vm/vm.rs b/core/lib/multivm/src/versions/era_vm/vm.rs index 1b45fe575fa1..3a2c950403d7 100644 --- a/core/lib/multivm/src/versions/era_vm/vm.rs +++ b/core/lib/multivm/src/versions/era_vm/vm.rs @@ -1,9 +1,6 @@ use std::{cell::RefCell, collections::HashMap, rc::Rc}; -use era_vm::{ - rollbacks::Rollbackable, store::StorageKey as EraStorageKey, value::FatPointer, - vm::ExecutionOutput, EraVM, Execution, -}; +use era_vm::{rollbacks::Rollbackable, store::StorageKey as EraStorageKey, EraVM, Execution}; use itertools::Itertools; use zksync_state::{ReadStorage, StoragePtr}; use zksync_types::{ @@ -25,9 +22,8 @@ use zksync_utils::{ }; use super::{ - bootloader_state::{utils::apply_l2_block, BootloaderState}, + bootloader_state::BootloaderState, event::merge_events, - hook::Hook, initial_bootloader_memory::bootloader_initial_memory, logs::IntoSystemLog, snapshot::VmSnapshot, @@ -38,9 +34,7 @@ use super::{ }; use crate::{ era_vm::{bytecode::compress_bytecodes, transaction_data::TransactionData}, - interface::{ - Halt, TxRevertReason, VmFactory, VmInterface, VmInterfaceHistoryEnabled, VmRevertReason, - }, + interface::{tracer::TracerExecutionStatus, VmFactory, VmInterface, VmInterfaceHistoryEnabled}, vm_latest::{ constants::{ get_vm_hook_position, get_vm_hook_start_position_latest, VM_HOOK_PARAMS_COUNT, @@ -150,19 +144,18 @@ impl VmFactory for Vm { } impl Vm { - pub fn run(&mut self, tracer: &mut VmTracerManager) -> ExecutionResult { + pub fn run(&mut self, tracer: &mut impl VmTracer) { tracer.before_bootloader_execution(self); loop { let output = self.inner.run_program_with_custom_bytecode(Some(tracer)); - let result = tracer.after_vm_run(self, output); + let status = tracer.after_vm_run(self, output); - if result.is_some() { - let result = result.unwrap(); - tracer.after_bootloader_execution(self, result.clone()); - return result; + if let TracerExecutionStatus::Stop(_) = status { + break; } } + tracer.after_bootloader_execution(self); } pub(crate) fn insert_bytecodes<'a>(&mut self, bytecodes: impl IntoIterator) { @@ -327,7 +320,10 @@ impl VmInterface for Vm { VmTracerManager::new(execution_mode, self.storage.clone(), tracer, refund_tracer); let snapshot = self.inner.state.snapshot(); - let result = self.run(&mut tracer); + self.run(&mut tracer); + // it is actually safe to unwrap here, since we always expect a result + // the reason we use an option is because we really can't set an initial value in the result tracer + let result = tracer.result_tracer.result.unwrap(); let ignore_world_diff = matches!(execution_mode, VmExecutionMode::OneTx) && matches!(result, ExecutionResult::Halt { .. }); From 8abf5c725da6050fdcbfa6eabe0fc7a2856046a5 Mon Sep 17 00:00:00 2001 From: Marcos Nicolau Date: Sat, 24 Aug 2024 16:26:57 -0300 Subject: [PATCH 16/32] Implement circuits tracer --- .../src/versions/era_vm/tests/circuits.rs | 73 ++++++ .../multivm/src/versions/era_vm/tests/mod.rs | 1 + .../era_vm/tracers/circuits_tracer.rs | 219 +++++++++++++++++- core/lib/multivm/src/versions/era_vm/vm.rs | 4 +- 4 files changed, 293 insertions(+), 4 deletions(-) create mode 100644 core/lib/multivm/src/versions/era_vm/tests/circuits.rs diff --git a/core/lib/multivm/src/versions/era_vm/tests/circuits.rs b/core/lib/multivm/src/versions/era_vm/tests/circuits.rs new file mode 100644 index 000000000000..0e70f6b38104 --- /dev/null +++ b/core/lib/multivm/src/versions/era_vm/tests/circuits.rs @@ -0,0 +1,73 @@ +use zksync_types::{Address, Execute, U256}; + +use crate::{ + era_vm::tests::tester::VmTesterBuilder, + interface::{TxExecutionMode, VmExecutionMode, VmInterface}, + vm_latest::constants::BATCH_COMPUTATIONAL_GAS_LIMIT, +}; + +// Checks that estimated number of circuits for simple transfer doesn't differ much +// from hardcoded expected value. +#[test] +fn test_circuits() { + let mut vm = VmTesterBuilder::new() + .with_empty_in_memory_storage() + .with_random_rich_accounts(1) + .with_deployer() + .with_bootloader_gas_limit(BATCH_COMPUTATIONAL_GAS_LIMIT) + .with_execution_mode(TxExecutionMode::VerifyExecute) + .build(); + + let account = &mut vm.rich_accounts[0]; + let tx = account.get_l2_tx_for_execute( + Execute { + contract_address: Address::random(), + calldata: Vec::new(), + value: U256::from(1u8), + factory_deps: vec![], + }, + None, + ); + vm.vm.push_transaction(tx); + let res = vm.vm.inspect(Default::default(), VmExecutionMode::OneTx); + + let s = res.statistics.circuit_statistic; + // Check `circuit_statistic`. + const EXPECTED: [f32; 13] = [ + 1.34935, 0.15026, 1.66666, 0.00315, 1.0594, 0.00058, 0.00348, 0.00076, 0.11945, 0.14285, + 0.0, 0.0, 0.0, + ]; + let actual = [ + (s.main_vm, "main_vm"), + (s.ram_permutation, "ram_permutation"), + (s.storage_application, "storage_application"), + (s.storage_sorter, "storage_sorter"), + (s.code_decommitter, "code_decommitter"), + (s.code_decommitter_sorter, "code_decommitter_sorter"), + (s.log_demuxer, "log_demuxer"), + (s.events_sorter, "events_sorter"), + (s.keccak256, "keccak256"), + (s.ecrecover, "ecrecover"), + (s.sha256, "sha256"), + (s.secp256k1_verify, "secp256k1_verify"), + (s.transient_storage_checker, "transient_storage_checker"), + ]; + for ((actual, name), expected) in actual.iter().zip(EXPECTED) { + if expected == 0.0 { + assert_eq!( + *actual, expected, + "Check failed for {}, expected {}, actual {}", + name, expected, actual + ); + } else { + let diff = (actual - expected) / expected; + assert!( + diff.abs() < 0.1, + "Check failed for {}, expected {}, actual {}", + name, + expected, + actual + ); + } + } +} diff --git a/core/lib/multivm/src/versions/era_vm/tests/mod.rs b/core/lib/multivm/src/versions/era_vm/tests/mod.rs index 32507a29943b..37d878d0b0c5 100644 --- a/core/lib/multivm/src/versions/era_vm/tests/mod.rs +++ b/core/lib/multivm/src/versions/era_vm/tests/mod.rs @@ -1,6 +1,7 @@ mod bootloader; mod bytecode_publishing; mod call_tracer; +mod circuits; mod code_oracle; mod default_aa; mod gas_limit; diff --git a/core/lib/multivm/src/versions/era_vm/tracers/circuits_tracer.rs b/core/lib/multivm/src/versions/era_vm/tracers/circuits_tracer.rs index 082c97bb0e6c..dc902409fc3c 100644 --- a/core/lib/multivm/src/versions/era_vm/tracers/circuits_tracer.rs +++ b/core/lib/multivm/src/versions/era_vm/tracers/circuits_tracer.rs @@ -1,15 +1,228 @@ +use circuit_sequencer_api_1_5_0::{geometry_config::get_geometry_config, toolset::GeometryConfig}; +use era_vm::{ + opcode::{Opcode, Variant}, + statistics::VmStatistics, +}; +use zkevm_opcode_defs::{LogOpcode, UMAOpcode}; use zksync_state::ReadStorage; +use zksync_types::circuit::CircuitStatistic; use super::traits::{Tracer, VmTracer}; -pub struct CircuitsTracer {} +const GEOMETRY_CONFIG: GeometryConfig = get_geometry_config(); + +// "Rich addressing" opcodes are opcodes that can write their return value/read the input onto the stack +// and so take 1-2 RAM permutations more than an average opcode. +// In the worst case, a rich addressing may take 3 ram permutations +// (1 for reading the opcode, 1 for writing input value, 1 for writing output value). +pub(crate) const RICH_ADDRESSING_OPCODE_RAM_CYCLES: u32 = 3; + +pub(crate) const AVERAGE_OPCODE_RAM_CYCLES: u32 = 1; + +pub(crate) const STORAGE_READ_RAM_CYCLES: u32 = 1; +pub(crate) const STORAGE_READ_LOG_DEMUXER_CYCLES: u32 = 1; +pub(crate) const STORAGE_READ_STORAGE_SORTER_CYCLES: u32 = 1; + +pub(crate) const TRANSIENT_STORAGE_READ_RAM_CYCLES: u32 = 1; +pub(crate) const TRANSIENT_STORAGE_READ_LOG_DEMUXER_CYCLES: u32 = 1; +pub(crate) const TRANSIENT_STORAGE_READ_TRANSIENT_STORAGE_CHECKER_CYCLES: u32 = 1; + +pub(crate) const EVENT_RAM_CYCLES: u32 = 1; +pub(crate) const EVENT_LOG_DEMUXER_CYCLES: u32 = 2; +pub(crate) const EVENT_EVENTS_SORTER_CYCLES: u32 = 2; + +pub(crate) const STORAGE_WRITE_RAM_CYCLES: u32 = 1; +pub(crate) const STORAGE_WRITE_LOG_DEMUXER_CYCLES: u32 = 2; +pub(crate) const STORAGE_WRITE_STORAGE_SORTER_CYCLES: u32 = 2; + +pub(crate) const TRANSIENT_STORAGE_WRITE_RAM_CYCLES: u32 = 1; +pub(crate) const TRANSIENT_STORAGE_WRITE_LOG_DEMUXER_CYCLES: u32 = 2; +pub(crate) const TRANSIENT_STORAGE_WRITE_TRANSIENT_STORAGE_CHECKER_CYCLES: u32 = 2; + +pub(crate) const FAR_CALL_RAM_CYCLES: u32 = 1; +pub(crate) const FAR_CALL_STORAGE_SORTER_CYCLES: u32 = 1; +pub(crate) const FAR_CALL_CODE_DECOMMITTER_SORTER_CYCLES: u32 = 1; +pub(crate) const FAR_CALL_LOG_DEMUXER_CYCLES: u32 = 1; + +// 5 RAM permutations, because: 1 to read opcode + 2 reads + 2 writes. +// 2 reads and 2 writes are needed because unaligned access is implemented with +// aligned queries. +pub(crate) const UMA_WRITE_RAM_CYCLES: u32 = 5; + +// 3 RAM permutations, because: 1 to read opcode + 2 reads. +// 2 reads are needed because unaligned access is implemented with aligned queries. +pub(crate) const UMA_READ_RAM_CYCLES: u32 = 3; + +pub(crate) const PRECOMPILE_RAM_CYCLES: u32 = 1; +pub(crate) const PRECOMPILE_LOG_DEMUXER_CYCLES: u32 = 1; + +pub(crate) const LOG_DECOMMIT_RAM_CYCLES: u32 = 1; +pub(crate) const LOG_DECOMMIT_DECOMMITTER_SORTER_CYCLES: u32 = 1; + +#[derive(Debug, Default, Clone, PartialEq)] +pub struct CircuitsTracer { + rich_addressing_opcodes: u32, + average_opcodes: u32, + storage_reads: u32, + storage_writes: u32, + transient_storage_reads: u32, + transient_storage_writes: u32, + events: u32, + precompile_calls: u32, + decommits: u32, + far_calls: u32, + heap_writes: u32, + heap_reads: u32, +} impl CircuitsTracer { pub fn new() -> Self { - Self {} + Self::default() + } + + pub fn circuit_statistics(&self, vm_statistics: &VmStatistics) -> CircuitStatistic { + let VmStatistics { + code_decommitter_cycles, + ecrecover_cycles, + keccak256_cycles, + secp255r1_verify_cycles: secp256k1_verify_cycles, + sha256_cycles, + storage_application_cycles, + } = *vm_statistics; + + CircuitStatistic { + main_vm: (self.rich_addressing_opcodes + + self.average_opcodes + + self.storage_reads + + self.storage_writes + + self.transient_storage_reads + + self.transient_storage_writes + + self.events + + self.precompile_calls + + self.decommits + + self.far_calls + + self.heap_writes + + self.heap_reads) as f32 + / GEOMETRY_CONFIG.cycles_per_vm_snapshot as f32, + ram_permutation: (self.rich_addressing_opcodes * RICH_ADDRESSING_OPCODE_RAM_CYCLES + + self.average_opcodes * AVERAGE_OPCODE_RAM_CYCLES + + self.storage_reads * STORAGE_READ_RAM_CYCLES + + self.storage_writes * STORAGE_WRITE_RAM_CYCLES + + self.transient_storage_reads * TRANSIENT_STORAGE_READ_RAM_CYCLES + + self.transient_storage_writes * TRANSIENT_STORAGE_WRITE_RAM_CYCLES + + self.events * EVENT_RAM_CYCLES + + self.precompile_calls * PRECOMPILE_RAM_CYCLES + + self.decommits * LOG_DECOMMIT_RAM_CYCLES + + self.far_calls * FAR_CALL_RAM_CYCLES + + self.heap_writes * UMA_WRITE_RAM_CYCLES + + self.heap_reads * UMA_READ_RAM_CYCLES) as f32 + / GEOMETRY_CONFIG.cycles_per_ram_permutation as f32, + storage_application: storage_application_cycles as f32 + / GEOMETRY_CONFIG.cycles_per_storage_application as f32, + storage_sorter: (self.storage_reads * STORAGE_READ_STORAGE_SORTER_CYCLES + + self.storage_writes * STORAGE_WRITE_STORAGE_SORTER_CYCLES + + self.transient_storage_reads + * TRANSIENT_STORAGE_READ_TRANSIENT_STORAGE_CHECKER_CYCLES + + self.transient_storage_writes + * TRANSIENT_STORAGE_WRITE_TRANSIENT_STORAGE_CHECKER_CYCLES + + self.far_calls * FAR_CALL_STORAGE_SORTER_CYCLES) + as f32 + / GEOMETRY_CONFIG.cycles_per_storage_sorter as f32, + code_decommitter: code_decommitter_cycles as f32 + / GEOMETRY_CONFIG.cycles_per_code_decommitter as f32, + code_decommitter_sorter: (self.decommits * LOG_DECOMMIT_DECOMMITTER_SORTER_CYCLES + + self.far_calls * FAR_CALL_CODE_DECOMMITTER_SORTER_CYCLES) + as f32 + / GEOMETRY_CONFIG.cycles_code_decommitter_sorter as f32, + log_demuxer: (self.storage_reads * STORAGE_READ_LOG_DEMUXER_CYCLES + + self.storage_writes * STORAGE_WRITE_LOG_DEMUXER_CYCLES + + self.transient_storage_reads * TRANSIENT_STORAGE_READ_LOG_DEMUXER_CYCLES + + self.transient_storage_writes * TRANSIENT_STORAGE_WRITE_LOG_DEMUXER_CYCLES + + self.events * EVENT_LOG_DEMUXER_CYCLES + + self.precompile_calls * PRECOMPILE_LOG_DEMUXER_CYCLES + + self.far_calls * FAR_CALL_LOG_DEMUXER_CYCLES) as f32 + / GEOMETRY_CONFIG.cycles_per_log_demuxer as f32, + events_sorter: (self.events * EVENT_EVENTS_SORTER_CYCLES) as f32 + / GEOMETRY_CONFIG.cycles_per_events_or_l1_messages_sorter as f32, + keccak256: keccak256_cycles as f32 + / GEOMETRY_CONFIG.cycles_per_keccak256_circuit as f32, + ecrecover: ecrecover_cycles as f32 + / GEOMETRY_CONFIG.cycles_per_ecrecover_circuit as f32, + sha256: sha256_cycles as f32 / GEOMETRY_CONFIG.cycles_per_sha256_circuit as f32, + secp256k1_verify: secp256k1_verify_cycles as f32 + / GEOMETRY_CONFIG.cycles_per_secp256r1_verify_circuit as f32, + transient_storage_checker: (self.transient_storage_reads + * TRANSIENT_STORAGE_READ_TRANSIENT_STORAGE_CHECKER_CYCLES + + self.transient_storage_writes + * TRANSIENT_STORAGE_WRITE_TRANSIENT_STORAGE_CHECKER_CYCLES) + as f32 + / GEOMETRY_CONFIG.cycles_per_transient_storage_sorter as f32, + } } } -impl Tracer for CircuitsTracer {} +impl Tracer for CircuitsTracer { + fn after_execution( + &mut self, + opcode: &Opcode, + _execution: &mut era_vm::Execution, + _state: &mut era_vm::state::VMState, + ) { + match opcode.variant { + Variant::Nop(_) + | Variant::Add(_) + | Variant::Sub(_) + | Variant::Mul(_) + | Variant::Div(_) + | Variant::Jump(_) + | Variant::Shift(_) + | Variant::Binop(_) + | Variant::Ptr(_) => { + self.rich_addressing_opcodes += 1; + } + Variant::Context(_) | Variant::Ret(_) | Variant::NearCall(_) => { + self.average_opcodes += 1; + } + Variant::Log(LogOpcode::StorageRead) => { + self.storage_reads += 1; + } + Variant::Log(LogOpcode::TransientStorageRead) => { + self.transient_storage_reads += 1; + } + Variant::Log(LogOpcode::StorageWrite) => { + self.storage_writes += 1; + } + Variant::Log(LogOpcode::TransientStorageWrite) => { + self.transient_storage_writes += 1; + } + Variant::Log(LogOpcode::ToL1Message) | Variant::Log(LogOpcode::Event) => { + self.events += 1; + } + Variant::Log(LogOpcode::PrecompileCall) => { + self.precompile_calls += 1; + } + Variant::Log(LogOpcode::Decommit) => { + self.decommits += 1; + } + Variant::FarCall(_) => { + self.far_calls += 1; + } + Variant::UMA( + UMAOpcode::AuxHeapWrite | UMAOpcode::HeapWrite | UMAOpcode::StaticMemoryWrite, + ) => { + self.heap_writes += 1; + } + Variant::UMA( + UMAOpcode::AuxHeapRead + | UMAOpcode::HeapRead + | UMAOpcode::FatPointerRead + | UMAOpcode::StaticMemoryRead, + ) => { + self.heap_reads += 1; + } + Variant::Invalid(_) => {} + } + } +} impl VmTracer for CircuitsTracer {} diff --git a/core/lib/multivm/src/versions/era_vm/vm.rs b/core/lib/multivm/src/versions/era_vm/vm.rs index 3a2c950403d7..84d7d99c13c7 100644 --- a/core/lib/multivm/src/versions/era_vm/vm.rs +++ b/core/lib/multivm/src/versions/era_vm/vm.rs @@ -393,7 +393,9 @@ impl VmInterface for Vm { computational_gas_used: 0, total_log_queries: 0, pubdata_published: tracer.pubdata_tracer.pubdata_published, - circuit_statistic: Default::default(), + circuit_statistic: tracer + .circuits_tracer + .circuit_statistics(&self.inner.statistics), }, refunds: tracer.refund_tracer.unwrap_or_default().into(), } From 9b91063fbe508bc06e821e490478c81dbf669b54 Mon Sep 17 00:00:00 2001 From: Marcos Nicolau Date: Sat, 24 Aug 2024 16:28:17 -0300 Subject: [PATCH 17/32] Add precompiles tests --- .../multivm/src/versions/era_vm/tests/mod.rs | 1 + .../src/versions/era_vm/tests/precompiles.rs | 108 ++++++++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 core/lib/multivm/src/versions/era_vm/tests/precompiles.rs diff --git a/core/lib/multivm/src/versions/era_vm/tests/mod.rs b/core/lib/multivm/src/versions/era_vm/tests/mod.rs index 37d878d0b0c5..6a8fde47a9f5 100644 --- a/core/lib/multivm/src/versions/era_vm/tests/mod.rs +++ b/core/lib/multivm/src/versions/era_vm/tests/mod.rs @@ -10,6 +10,7 @@ mod is_write_initial; mod l1_tx_execution; mod l2_blocks; mod nonce_holder; +mod precompiles; mod refunds; mod require_eip712; mod rollbacks; diff --git a/core/lib/multivm/src/versions/era_vm/tests/precompiles.rs b/core/lib/multivm/src/versions/era_vm/tests/precompiles.rs new file mode 100644 index 000000000000..b3008295482a --- /dev/null +++ b/core/lib/multivm/src/versions/era_vm/tests/precompiles.rs @@ -0,0 +1,108 @@ +use zksync_types::{Address, Execute}; + +use crate::{ + era_vm::tests::{tester::VmTesterBuilder, utils::read_precompiles_contract}, + interface::{TxExecutionMode, VmExecutionMode, VmInterface}, + vm_latest::constants::BATCH_COMPUTATIONAL_GAS_LIMIT, +}; + +#[test] +fn test_keccak() { + // Execute special transaction and check that at least 1000 keccak calls were made. + let contract = read_precompiles_contract(); + let address = Address::random(); + let mut vm = VmTesterBuilder::new() + .with_empty_in_memory_storage() + .with_random_rich_accounts(1) + .with_deployer() + .with_bootloader_gas_limit(BATCH_COMPUTATIONAL_GAS_LIMIT) + .with_execution_mode(TxExecutionMode::VerifyExecute) + .with_custom_contracts(vec![(contract, address, true)]) + .build(); + + // calldata for `doKeccak(1000)`. + let keccak1000_calldata = + "370f20ac00000000000000000000000000000000000000000000000000000000000003e8"; + + let account = &mut vm.rich_accounts[0]; + let tx = account.get_l2_tx_for_execute( + Execute { + contract_address: address, + calldata: hex::decode(keccak1000_calldata).unwrap(), + value: Default::default(), + factory_deps: vec![], + }, + None, + ); + vm.vm.push_transaction(tx); + let _ = vm.vm.inspect(Default::default(), VmExecutionMode::OneTx); + + let keccak_count = vm.vm.inner.statistics.keccak256_cycles; + + assert!(keccak_count >= 1000); +} + +#[test] +fn test_sha256() { + // Execute special transaction and check that at least 1000 `sha256` calls were made. + let contract = read_precompiles_contract(); + let address = Address::random(); + let mut vm = VmTesterBuilder::new() + .with_empty_in_memory_storage() + .with_random_rich_accounts(1) + .with_deployer() + .with_bootloader_gas_limit(BATCH_COMPUTATIONAL_GAS_LIMIT) + .with_execution_mode(TxExecutionMode::VerifyExecute) + .with_custom_contracts(vec![(contract, address, true)]) + .build(); + + // calldata for `doSha256(1000)`. + let sha1000_calldata = + "5d0b4fb500000000000000000000000000000000000000000000000000000000000003e8"; + + let account = &mut vm.rich_accounts[0]; + let tx = account.get_l2_tx_for_execute( + Execute { + contract_address: address, + calldata: hex::decode(sha1000_calldata).unwrap(), + value: Default::default(), + factory_deps: vec![], + }, + None, + ); + vm.vm.push_transaction(tx); + let _ = vm.vm.inspect(Default::default(), VmExecutionMode::OneTx); + + let sha_count = vm.vm.inner.statistics.sha256_cycles; + + assert!(sha_count >= 1000); +} + +#[test] +fn test_ecrecover() { + // Execute simple transfer and check that exactly 1 `ecrecover` call was made (it's done during tx validation). + let mut vm = VmTesterBuilder::new() + .with_empty_in_memory_storage() + .with_random_rich_accounts(1) + .with_deployer() + .with_bootloader_gas_limit(BATCH_COMPUTATIONAL_GAS_LIMIT) + .with_execution_mode(TxExecutionMode::VerifyExecute) + .build(); + + let account = &mut vm.rich_accounts[0]; + let tx = account.get_l2_tx_for_execute( + Execute { + contract_address: account.address, + calldata: Vec::new(), + value: Default::default(), + factory_deps: vec![], + }, + None, + ); + vm.vm.push_transaction(tx); + let _ = vm.vm.inspect(Default::default(), VmExecutionMode::OneTx); + + let ecrecover_count = vm.vm.inner.statistics.ecrecover_cycles; + + assert_eq!(ecrecover_count, 1); +} From 6d94011ea8a0f6be7452a97eba114506ec484215 Mon Sep 17 00:00:00 2001 From: Marcos Nicolau Date: Sat, 24 Aug 2024 16:28:46 -0300 Subject: [PATCH 18/32] General tracers refactor --- core/lib/multivm/src/versions/era_vm/tracers/manager.rs | 9 ++++++++- .../src/versions/era_vm/tracers/refunds_tracer.rs | 1 - .../multivm/src/versions/era_vm/tracers/result_tracer.rs | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/core/lib/multivm/src/versions/era_vm/tracers/manager.rs b/core/lib/multivm/src/versions/era_vm/tracers/manager.rs index e4fa37bf2c49..e76bb0e2ebcb 100644 --- a/core/lib/multivm/src/versions/era_vm/tracers/manager.rs +++ b/core/lib/multivm/src/versions/era_vm/tracers/manager.rs @@ -16,10 +16,12 @@ use crate::{ // this tracer manager is the one that gets called when running the vm // all the logic of hooks and results parsing is managed from here -// the most important tracers are: `result_tracer`, `refund_tracer`, `pubdata_tracer`, +// the most important tracers are: `result_tracer`, `refund_tracer`, `pubdata_tracer`, and `circuits_tracer` pub struct VmTracerManager { execution_mode: VmExecutionMode, pub dispatcher: TracerDispatcher, + // this tracer collects the vm results for every transaction + // when the vm stops, the result would be available here pub result_tracer: ResultTracer, // This tracer is designed specifically for calculating refunds and saves the results to `VmResultAndLogs`. pub refund_tracer: Option, @@ -27,6 +29,7 @@ pub struct VmTracerManager { // memory at the end of the batch. Its separation from the custom tracer // ensures static dispatch, enhancing performance by avoiding dynamic dispatch overhe pub pubdata_tracer: PubdataTracer, + // This tracers keeps track of opcodes calls and collects circuits statistics pub circuits_tracer: CircuitsTracer, storage: StoragePtr, } @@ -87,6 +90,7 @@ impl VmTracerManager { _ => TracerExecutionStatus::Continue, } } + // any other output means the vm has finished executing _ => TracerExecutionStatus::Stop(TracerExecutionStopReason::Finish), } } @@ -207,6 +211,9 @@ impl VmTracer for VmTracerManager { .bootloader_hook_call(state, hook.clone(), hook_params); } + // here we apply the stricter, to make sure that the stricter output is returned + // for example: if one tracer output is Continue and the other Finish, Finish is stricter + // so we would return Finish as the final output. fn after_vm_run(&mut self, vm: &mut Vm, output: ExecutionOutput) -> TracerExecutionStatus { // Call the dispatcher to handle all the tracers added to it let mut result = self.dispatcher.after_vm_run(vm, output.clone()); diff --git a/core/lib/multivm/src/versions/era_vm/tracers/refunds_tracer.rs b/core/lib/multivm/src/versions/era_vm/tracers/refunds_tracer.rs index f22c782ef954..ee87e1ce7b90 100644 --- a/core/lib/multivm/src/versions/era_vm/tracers/refunds_tracer.rs +++ b/core/lib/multivm/src/versions/era_vm/tracers/refunds_tracer.rs @@ -54,7 +54,6 @@ impl VmTracer for RefundsTracer { match hook { Hook::NotifyAboutRefund => self.gas_refunded = hook_params[0].low_u64(), Hook::AskOperatorForRefund => { - println!("ENTERINg HERE!"); let [bootloader_refund, gas_spent_on_pubdata, gas_per_pubdata_byte] = hook_params; let current_tx_index = vm.bootloader_state.current_tx(); let tx_description_offset = vm diff --git a/core/lib/multivm/src/versions/era_vm/tracers/result_tracer.rs b/core/lib/multivm/src/versions/era_vm/tracers/result_tracer.rs index ec8fa7dc28f7..859b085f5bd9 100644 --- a/core/lib/multivm/src/versions/era_vm/tracers/result_tracer.rs +++ b/core/lib/multivm/src/versions/era_vm/tracers/result_tracer.rs @@ -38,7 +38,7 @@ impl VmTracer for ResultTracer { Halt::VMPanic }, }), - _ => None, + ExecutionOutput::SuspendedOnHook { .. } => None, }; // if the result is none, it means the execution has been suspended From 36752f36043088ba46416dc08a10d5721c0ed3d5 Mon Sep 17 00:00:00 2001 From: Marcos Nicolau Date: Sat, 24 Aug 2024 19:28:17 -0300 Subject: [PATCH 19/32] Add block_tip test --- .../src/versions/era_vm/tests/block_tip.rs | 413 ++++++++++++++++++ .../multivm/src/versions/era_vm/tests/mod.rs | 1 + .../era_vm/tracers/circuits_tracer.rs | 1 + .../src/versions/era_vm/tracers/manager.rs | 3 +- .../versions/era_vm/tracers/pubdata_tracer.rs | 70 ++- core/lib/multivm/src/versions/era_vm/vm.rs | 96 ++-- 6 files changed, 520 insertions(+), 64 deletions(-) create mode 100644 core/lib/multivm/src/versions/era_vm/tests/block_tip.rs diff --git a/core/lib/multivm/src/versions/era_vm/tests/block_tip.rs b/core/lib/multivm/src/versions/era_vm/tests/block_tip.rs new file mode 100644 index 000000000000..cd06ae73bf59 --- /dev/null +++ b/core/lib/multivm/src/versions/era_vm/tests/block_tip.rs @@ -0,0 +1,413 @@ +use ethabi::Token; +use itertools::Itertools; +use zksync_contracts::load_sys_contract; +use zksync_system_constants::{ + CONTRACT_FORCE_DEPLOYER_ADDRESS, KNOWN_CODES_STORAGE_ADDRESS, L1_MESSENGER_ADDRESS, +}; +use zksync_types::{ + commitment::SerializeCommitment, fee_model::BatchFeeInput, get_code_key, + l2_to_l1_log::L2ToL1Log, writes::StateDiffRecord, Address, Execute, H256, U256, +}; +use zksync_utils::{bytecode::hash_bytecode, u256_to_h256}; + +use super::utils::{get_complex_upgrade_abi, read_complex_upgrade}; +use crate::{ + era_vm::{ + tests::tester::{default_l1_batch, get_empty_storage, VmTesterBuilder}, + tracers::{dispatcher::TracerDispatcher, pubdata_tracer::PubdataTracer}, + }, + interface::{TxExecutionMode, VmExecutionMode, VmInterface}, + vm_latest::{ + constants::{ + BOOTLOADER_BATCH_TIP_CIRCUIT_STATISTICS_OVERHEAD, + BOOTLOADER_BATCH_TIP_METRICS_SIZE_OVERHEAD, BOOTLOADER_BATCH_TIP_OVERHEAD, + MAX_VM_PUBDATA_PER_BATCH, + }, + L1BatchEnv, + }, +}; + +#[derive(Debug, Clone, Default)] +struct L1MessengerTestData { + l2_to_l1_logs: usize, + messages: Vec>, + bytecodes: Vec>, + state_diffs: Vec, +} + +struct MimicCallInfo { + to: Address, + who_to_mimic: Address, + data: Vec, +} + +const CALLS_PER_TX: usize = 1_000; +fn populate_mimic_calls(data: L1MessengerTestData) -> Vec> { + let complex_upgrade = get_complex_upgrade_abi(); + let l1_messenger = load_sys_contract("L1Messenger"); + + let logs_mimic_calls = (0..data.l2_to_l1_logs).map(|_| MimicCallInfo { + to: L1_MESSENGER_ADDRESS, + who_to_mimic: KNOWN_CODES_STORAGE_ADDRESS, + data: l1_messenger + .function("sendL2ToL1Log") + .unwrap() + .encode_input(&[ + Token::Bool(false), + Token::FixedBytes(H256::random().0.to_vec()), + Token::FixedBytes(H256::random().0.to_vec()), + ]) + .unwrap(), + }); + let messages_mimic_calls = data.messages.iter().map(|message| MimicCallInfo { + to: L1_MESSENGER_ADDRESS, + who_to_mimic: KNOWN_CODES_STORAGE_ADDRESS, + data: l1_messenger + .function("sendToL1") + .unwrap() + .encode_input(&[Token::Bytes(message.clone())]) + .unwrap(), + }); + let bytecodes_mimic_calls = data.bytecodes.iter().map(|bytecode| MimicCallInfo { + to: L1_MESSENGER_ADDRESS, + who_to_mimic: KNOWN_CODES_STORAGE_ADDRESS, + data: l1_messenger + .function("requestBytecodeL1Publication") + .unwrap() + .encode_input(&[Token::FixedBytes(hash_bytecode(bytecode).0.to_vec())]) + .unwrap(), + }); + + let encoded_calls = logs_mimic_calls + .chain(messages_mimic_calls) + .chain(bytecodes_mimic_calls) + .map(|call| { + Token::Tuple(vec![ + Token::Address(call.to), + Token::Address(call.who_to_mimic), + Token::Bytes(call.data), + ]) + }) + .chunks(CALLS_PER_TX) + .into_iter() + .map(|chunk| { + complex_upgrade + .function("mimicCalls") + .unwrap() + .encode_input(&[Token::Array(chunk.collect_vec())]) + .unwrap() + }) + .collect_vec(); + + encoded_calls +} + +struct TestStatistics { + pub max_used_gas: u32, + pub circuit_statistics: u64, + pub execution_metrics_size: u64, +} + +struct StatisticsTagged { + pub statistics: TestStatistics, + pub tag: String, +} + +fn execute_test(test_data: L1MessengerTestData) -> TestStatistics { + let mut storage = get_empty_storage(); + let complex_upgrade_code = read_complex_upgrade(); + + // For this test we'll just put the bytecode onto the force deployer address + storage.set_value( + get_code_key(&CONTRACT_FORCE_DEPLOYER_ADDRESS), + hash_bytecode(&complex_upgrade_code), + ); + storage.store_factory_dep(hash_bytecode(&complex_upgrade_code), complex_upgrade_code); + + // We are measuring computational cost, so prices for pubdata don't matter, while they artificially dilute + // the gas limit + + let batch_env = L1BatchEnv { + fee_input: BatchFeeInput::pubdata_independent(100_000, 100_000, 100_000), + ..default_l1_batch(zksync_types::L1BatchNumber(1)) + }; + + let mut vm = VmTesterBuilder::new() + .with_storage(storage) + .with_execution_mode(TxExecutionMode::VerifyExecute) + .with_random_rich_accounts(1) + .with_l1_batch_env(batch_env) + .build(); + + for code in &test_data.bytecodes { + vm.storage + .borrow_mut() + .store_factory_dep(hash_bytecode(code), code.clone()); + } + + let txs_data = populate_mimic_calls(test_data.clone()); + let account = &mut vm.rich_accounts[0]; + + for (i, data) in txs_data.into_iter().enumerate() { + let tx = account.get_l2_tx_for_execute( + Execute { + contract_address: CONTRACT_FORCE_DEPLOYER_ADDRESS, + calldata: data, + value: U256::zero(), + factory_deps: vec![], + }, + None, + ); + + vm.vm.push_transaction(tx); + + let result = vm.vm.execute(VmExecutionMode::OneTx); + assert!( + !result.result.is_failed(), + "Transaction {i} wasn't successful for input: {:#?}", + test_data + ); + } + + // Now we count how much ergs were spent at the end of the batch + // It is assumed that the top level frame is the bootloader + + let ergs_before = vm.vm.inner.execution.gas_left().unwrap(); + + // We ensure that indeed the provided state diffs are used + let pubdata_tracer = PubdataTracer::new_with_forced_state_diffs( + VmExecutionMode::Batch, + test_data.state_diffs.clone(), + ); + + let result = vm.vm.inspect_inner( + TracerDispatcher::default(), + Some(pubdata_tracer), + VmExecutionMode::Batch, + ); + + assert!( + !result.result.is_failed(), + "Batch wasn't successful for input: {:?}", + test_data + ); + + let ergs_after = vm.vm.inner.execution.gas_left().unwrap(); + + assert_eq!( + (ergs_before - ergs_after) as u64, + result.statistics.gas_used + ); + + TestStatistics { + max_used_gas: ergs_before - ergs_after, + circuit_statistics: result.statistics.circuit_statistic.total() as u64, + execution_metrics_size: result.get_execution_metrics(None).size() as u64, + } +} + +fn generate_state_diffs( + repeated_writes: bool, + small_diff: bool, + number_of_state_diffs: usize, +) -> Vec { + (0..number_of_state_diffs) + .map(|i| { + let address = Address::from_low_u64_be(i as u64); + let key = U256::from(i); + let enumeration_index = if repeated_writes { i + 1 } else { 0 }; + + let (initial_value, final_value) = if small_diff { + // As small as it gets, one byte to denote zeroing out the value + (U256::from(1), U256::from(0)) + } else { + // As large as it gets + (U256::from(0), U256::from(2).pow(255.into())) + }; + + StateDiffRecord { + address, + key, + derived_key: u256_to_h256(i.into()).0, + enumeration_index: enumeration_index as u64, + initial_value, + final_value, + } + }) + .collect() +} + +// A valid zkEVM bytecode has odd number of 32 byte words +fn get_valid_bytecode_length(length: usize) -> usize { + // Firstly ensure that the length is divisible by 32 + let length_padded_to_32 = if length % 32 == 0 { + length + } else { + length + 32 - (length % 32) + }; + + // Then we ensure that the number returned by division by 32 is odd + if length_padded_to_32 % 64 == 0 { + length_padded_to_32 + 32 + } else { + length_padded_to_32 + } +} + +#[test] +fn test_dry_run_upper_bound() { + // Some of the pubdata is consumed by constant fields (such as length of messages, number of logs, etc.). + // While this leaves some room for error, at the end of the test we require that the `BOOTLOADER_BATCH_TIP_OVERHEAD` + // is sufficient with a very large margin, so it is okay to ignore 1% of possible pubdata. + const MAX_EFFECTIVE_PUBDATA_PER_BATCH: usize = + (MAX_VM_PUBDATA_PER_BATCH as f64 * 0.99) as usize; + + // We are re-using the `ComplexUpgrade` contract as it already has the `mimicCall` functionality. + // To get the upper bound, we'll try to do the following: + // 1. Max number of logs. + // 2. Lots of small L2->L1 messages / one large L2->L1 message. + // 3. Lots of small bytecodes / one large bytecode. + // 4. Lots of storage slot updates. + + let statistics = vec![ + // max logs + StatisticsTagged { + statistics: execute_test(L1MessengerTestData { + l2_to_l1_logs: MAX_EFFECTIVE_PUBDATA_PER_BATCH / L2ToL1Log::SERIALIZED_SIZE, + ..Default::default() + }), + tag: "max_logs".to_string(), + }, + // max messages + StatisticsTagged { + statistics: execute_test(L1MessengerTestData { + // Each L2->L1 message is accompanied by a Log + its length, which is a 4 byte number, + // so the max number of pubdata is bound by it + messages: vec![ + vec![0; 0]; + MAX_EFFECTIVE_PUBDATA_PER_BATCH / (L2ToL1Log::SERIALIZED_SIZE + 4) + ], + ..Default::default() + }), + tag: "max_messages".to_string(), + }, + // long message + StatisticsTagged { + statistics: execute_test(L1MessengerTestData { + // Each L2->L1 message is accompanied by a Log, so the max number of pubdata is bound by it + messages: vec![vec![0; MAX_EFFECTIVE_PUBDATA_PER_BATCH]; 1], + ..Default::default() + }), + tag: "long_message".to_string(), + }, + // // max bytecodes + StatisticsTagged { + statistics: execute_test(L1MessengerTestData { + // Each bytecode must be at least 32 bytes long. + // Each uncompressed bytecode is accompanied by its length, which is a 4 byte number + bytecodes: vec![vec![0; 32]; MAX_EFFECTIVE_PUBDATA_PER_BATCH / (32 + 4)], + ..Default::default() + }), + tag: "max_bytecodes".to_string(), + }, + // long bytecode + StatisticsTagged { + statistics: execute_test(L1MessengerTestData { + bytecodes: vec![ + vec![0; get_valid_bytecode_length(MAX_EFFECTIVE_PUBDATA_PER_BATCH)]; + 1 + ], + ..Default::default() + }), + tag: "long_bytecode".to_string(), + }, + // lots of small repeated writes + StatisticsTagged { + statistics: execute_test(L1MessengerTestData { + // In theory each state diff can require only 5 bytes to be published (enum index + 4 bytes for the key) + state_diffs: generate_state_diffs(true, true, MAX_EFFECTIVE_PUBDATA_PER_BATCH / 5), + ..Default::default() + }), + tag: "small_repeated_writes".to_string(), + }, + // lots of big repeated writes + StatisticsTagged { + statistics: execute_test(L1MessengerTestData { + // Each big repeated write will approximately require 4 bytes for key + 1 byte for encoding type + 32 bytes for value + state_diffs: generate_state_diffs( + true, + false, + MAX_EFFECTIVE_PUBDATA_PER_BATCH / 37, + ), + ..Default::default() + }), + tag: "big_repeated_writes".to_string(), + }, + // lots of small initial writes + StatisticsTagged { + statistics: execute_test(L1MessengerTestData { + // Each small initial write will take at least 32 bytes for derived key + 1 bytes encoding zeroing out + state_diffs: generate_state_diffs( + false, + true, + MAX_EFFECTIVE_PUBDATA_PER_BATCH / 33, + ), + ..Default::default() + }), + tag: "small_initial_writes".to_string(), + }, + // lots of large initial writes + StatisticsTagged { + statistics: execute_test(L1MessengerTestData { + // Each big write will take at least 32 bytes for derived key + 1 byte for encoding type + 32 bytes for value + state_diffs: generate_state_diffs( + false, + false, + MAX_EFFECTIVE_PUBDATA_PER_BATCH / 65, + ), + ..Default::default() + }), + tag: "big_initial_writes".to_string(), + }, + ]; + + // We use 2x overhead for the batch tip compared to the worst estimated scenario. + let max_used_gas = statistics + .iter() + .map(|s| (s.statistics.max_used_gas, s.tag.clone())) + .max() + .unwrap(); + assert!( + max_used_gas.0 * 3 / 2 <= BOOTLOADER_BATCH_TIP_OVERHEAD, + "BOOTLOADER_BATCH_TIP_OVERHEAD is too low for {} with result {}, BOOTLOADER_BATCH_TIP_OVERHEAD = {}", + max_used_gas.1, + max_used_gas.0, + BOOTLOADER_BATCH_TIP_OVERHEAD + ); + + let circuit_statistics = statistics + .iter() + .map(|s| (s.statistics.circuit_statistics, s.tag.clone())) + .max() + .unwrap(); + assert!( + circuit_statistics.0 * 3 / 2 <= BOOTLOADER_BATCH_TIP_CIRCUIT_STATISTICS_OVERHEAD as u64, + "BOOTLOADER_BATCH_TIP_CIRCUIT_STATISTICS_OVERHEAD is too low for {} with result {}, BOOTLOADER_BATCH_TIP_CIRCUIT_STATISTICS_OVERHEAD = {}", + circuit_statistics.1, + circuit_statistics.0, + BOOTLOADER_BATCH_TIP_CIRCUIT_STATISTICS_OVERHEAD + ); + + let execution_metrics_size = statistics + .iter() + .map(|s| (s.statistics.execution_metrics_size, s.tag.clone())) + .max() + .unwrap(); + assert!( + execution_metrics_size.0 * 3 / 2 <= BOOTLOADER_BATCH_TIP_METRICS_SIZE_OVERHEAD as u64, + "BOOTLOADER_BATCH_TIP_METRICS_SIZE_OVERHEAD is too low for {} with result {}, BOOTLOADER_BATCH_TIP_METRICS_SIZE_OVERHEAD = {}", + execution_metrics_size.1, + execution_metrics_size.0, + BOOTLOADER_BATCH_TIP_METRICS_SIZE_OVERHEAD + ); +} diff --git a/core/lib/multivm/src/versions/era_vm/tests/mod.rs b/core/lib/multivm/src/versions/era_vm/tests/mod.rs index 6a8fde47a9f5..801b6450ef85 100644 --- a/core/lib/multivm/src/versions/era_vm/tests/mod.rs +++ b/core/lib/multivm/src/versions/era_vm/tests/mod.rs @@ -1,3 +1,4 @@ +mod block_tip; mod bootloader; mod bytecode_publishing; mod call_tracer; diff --git a/core/lib/multivm/src/versions/era_vm/tracers/circuits_tracer.rs b/core/lib/multivm/src/versions/era_vm/tracers/circuits_tracer.rs index dc902409fc3c..8ef06818ba4a 100644 --- a/core/lib/multivm/src/versions/era_vm/tracers/circuits_tracer.rs +++ b/core/lib/multivm/src/versions/era_vm/tracers/circuits_tracer.rs @@ -88,6 +88,7 @@ impl CircuitsTracer { secp255r1_verify_cycles: secp256k1_verify_cycles, sha256_cycles, storage_application_cycles, + .. } = *vm_statistics; CircuitStatistic { diff --git a/core/lib/multivm/src/versions/era_vm/tracers/manager.rs b/core/lib/multivm/src/versions/era_vm/tracers/manager.rs index e76bb0e2ebcb..a4c60667862f 100644 --- a/core/lib/multivm/src/versions/era_vm/tracers/manager.rs +++ b/core/lib/multivm/src/versions/era_vm/tracers/manager.rs @@ -40,6 +40,7 @@ impl VmTracerManager { storage: StoragePtr, dispatcher: TracerDispatcher, refund_tracer: Option, + pubdata_tracer: Option, ) -> Self { Self { execution_mode, @@ -47,7 +48,7 @@ impl VmTracerManager { refund_tracer, circuits_tracer: CircuitsTracer::new(), result_tracer: ResultTracer::new(), - pubdata_tracer: PubdataTracer::new(execution_mode), + pubdata_tracer: pubdata_tracer.unwrap_or(PubdataTracer::new(execution_mode)), storage, } } diff --git a/core/lib/multivm/src/versions/era_vm/tracers/pubdata_tracer.rs b/core/lib/multivm/src/versions/era_vm/tracers/pubdata_tracer.rs index 4e98dd28523f..1bb0997b4895 100644 --- a/core/lib/multivm/src/versions/era_vm/tracers/pubdata_tracer.rs +++ b/core/lib/multivm/src/versions/era_vm/tracers/pubdata_tracer.rs @@ -1,14 +1,16 @@ +use itertools::Itertools; use zksync_state::ReadStorage; use zksync_types::{ event::{ extract_l2tol1logs_from_l1_messenger, extract_long_l2_to_l1_messages, L1_MESSENGER_BYTECODE_PUBLICATION_EVENT_SIGNATURE, }, - L1_MESSENGER_ADDRESS, U256, + writes::StateDiffRecord, + AccountTreeId, StorageKey, L1_MESSENGER_ADDRESS, U256, }; use zksync_utils::u256_to_h256; -use super::traits::{Tracer, VmTracer}; +use super::traits::{Tracer, Vm, VmTracer}; use crate::{ era_vm::{ bootloader_state::utils::{apply_pubdata_to_memory, PubdataInput}, @@ -22,6 +24,9 @@ pub struct PubdataTracer { execution_mode: VmExecutionMode, pubdata_before_run: i32, pub pubdata_published: u32, + // this field is to enforce a custom storage diff when setting the pubdata to the bootloader + // this is meant to be used for testing purposes only. + enforced_storage_diff: Option>, } impl PubdataTracer { @@ -30,8 +35,59 @@ impl PubdataTracer { execution_mode, pubdata_before_run: 0, pubdata_published: 0, + enforced_storage_diff: None, } } + + pub fn new_with_forced_state_diffs( + execution_mode: VmExecutionMode, + diff: Vec, + ) -> Self { + Self { + enforced_storage_diff: Some(diff), + ..Self::new(execution_mode) + } + } + + fn get_storage_diff(&mut self, vm: &Vm) -> Vec { + vm.inner + .state + .get_storage_changes() + .iter() + .filter_map(|(storage_key, initial_value, value)| { + let address = storage_key.address; + + if address == L1_MESSENGER_ADDRESS { + return None; + } + + let key = storage_key.key; + + let diff = StateDiffRecord { + key, + address, + derived_key: + zk_evm_1_5_0::aux_structures::LogQuery::derive_final_address_for_params( + &address, &key, + ), + enumeration_index: vm + .storage + .borrow_mut() + .get_enumeration_index(&StorageKey::new( + AccountTreeId::new(address), + u256_to_h256(key), + )) + .unwrap_or_default(), + initial_value: initial_value.unwrap_or_default(), + final_value: value.clone(), + }; + + Some(diff) + }) + // the compressor expects the storage diff to be sorted + .sorted_by(|a, b| a.address.cmp(&b.address).then_with(|| a.key.cmp(&b.key))) + .collect() + } } impl Tracer for PubdataTracer {} @@ -47,7 +103,7 @@ impl VmTracer for PubdataTracer { fn bootloader_hook_call( &mut self, - vm: &mut super::traits::Vm, + vm: &mut Vm, hook: Hook, _hook_params: &[zksync_types::U256; 3], ) { @@ -56,6 +112,12 @@ impl VmTracer for PubdataTracer { unreachable!("We do not provide the pubdata when executing the block tip or a single transaction"); }; + let state_diffs = if let Some(diff) = &self.enforced_storage_diff { + diff.clone() + } else { + self.get_storage_diff(&vm) + }; + let events = merge_events(vm.inner.state.events(), vm.batch_env.number); let published_bytecodes: Vec> = events @@ -80,7 +142,7 @@ impl VmTracer for PubdataTracer { user_logs: extract_l2tol1logs_from_l1_messenger(&events), l2_to_l1_messages: extract_long_l2_to_l1_messages(&events), published_bytecodes, - state_diffs: vm.get_storage_diff(), + state_diffs, }; // Save the pubdata for the future initial bootloader memory building diff --git a/core/lib/multivm/src/versions/era_vm/vm.rs b/core/lib/multivm/src/versions/era_vm/vm.rs index 84d7d99c13c7..c268fb21f3ff 100644 --- a/core/lib/multivm/src/versions/era_vm/vm.rs +++ b/core/lib/multivm/src/versions/era_vm/vm.rs @@ -28,8 +28,8 @@ use super::{ logs::IntoSystemLog, snapshot::VmSnapshot, tracers::{ - dispatcher::TracerDispatcher, manager::VmTracerManager, refunds_tracer::RefundsTracer, - traits::VmTracer, + dispatcher::TracerDispatcher, manager::VmTracerManager, pubdata_tracer::PubdataTracer, + refunds_tracer::RefundsTracer, traits::VmTracer, }, }; use crate::{ @@ -214,46 +214,6 @@ impl Vm { } } - pub fn get_storage_diff(&mut self) -> Vec { - self.inner - .state - .get_storage_changes() - .iter() - .filter_map(|(storage_key, initial_value, value)| { - let address = storage_key.address; - - if address == L1_MESSENGER_ADDRESS { - return None; - } - - let key = storage_key.key; - - let diff = StateDiffRecord { - key, - address, - derived_key: - zk_evm_1_5_0::aux_structures::LogQuery::derive_final_address_for_params( - &address, &key, - ), - enumeration_index: self - .storage - .borrow_mut() - .get_enumeration_index(&StorageKey::new( - AccountTreeId::new(address), - u256_to_h256(key), - )) - .unwrap_or_default(), - initial_value: initial_value.unwrap_or_default(), - final_value: value.clone(), - }; - - Some(diff) - }) - // the compressor expects the storage diff to be sorted - .sorted_by(|a, b| a.address.cmp(&b.address).then_with(|| a.key.cmp(&b.key))) - .collect() - } - pub fn push_transaction_inner(&mut self, tx: Transaction, refund: u64, with_compression: bool) { let tx: TransactionData = tx.into(); let overhead = tx.overhead_gas(); @@ -290,18 +250,11 @@ impl Vm { self.write_to_bootloader_heap(memory); } -} - -impl VmInterface for Vm { - type TracerDispatcher = TracerDispatcher; - - fn push_transaction(&mut self, tx: Transaction) { - self.push_transaction_inner(tx, 0, true); - } - fn inspect( + pub fn inspect_inner( &mut self, - tracer: Self::TracerDispatcher, + tracer: TracerDispatcher, + custom_pubdata_tracer: Option, execution_mode: VmExecutionMode, ) -> VmExecutionResultAndLogs { let mut track_refunds = false; @@ -316,14 +269,23 @@ impl VmInterface for Vm { } else { None }; - let mut tracer = - VmTracerManager::new(execution_mode, self.storage.clone(), tracer, refund_tracer); + let mut tracer = VmTracerManager::new( + execution_mode, + self.storage.clone(), + tracer, + refund_tracer, + custom_pubdata_tracer, + ); let snapshot = self.inner.state.snapshot(); + let ergs_before = self.inner.execution.gas_left().unwrap(); + let monotonic_counter_before = self.inner.statistics.monotonic_counter; + self.run(&mut tracer); // it is actually safe to unwrap here, since we always expect a result // the reason we use an option is because we really can't set an initial value in the result tracer let result = tracer.result_tracer.result.unwrap(); + let ergs_after = self.inner.execution.gas_left().unwrap(); let ignore_world_diff = matches!(execution_mode, VmExecutionMode::OneTx) && matches!(result, ExecutionResult::Halt { .. }); @@ -386,11 +348,11 @@ impl VmInterface for Vm { result, logs, statistics: VmExecutionStatistics { - contracts_used: 0, - cycles_used: 0, - gas_used: 0, - gas_remaining: 0, - computational_gas_used: 0, + contracts_used: self.inner.state.decommitted_hashes().len(), + cycles_used: self.inner.statistics.monotonic_counter - monotonic_counter_before, + gas_used: (ergs_before - ergs_after) as u64, + gas_remaining: ergs_after, + computational_gas_used: ergs_before - ergs_after, total_log_queries: 0, pubdata_published: tracer.pubdata_tracer.pubdata_published, circuit_statistic: tracer @@ -400,6 +362,22 @@ impl VmInterface for Vm { refunds: tracer.refund_tracer.unwrap_or_default().into(), } } +} + +impl VmInterface for Vm { + type TracerDispatcher = TracerDispatcher; + + fn push_transaction(&mut self, tx: Transaction) { + self.push_transaction_inner(tx, 0, true); + } + + fn inspect( + &mut self, + tracer: Self::TracerDispatcher, + execution_mode: VmExecutionMode, + ) -> VmExecutionResultAndLogs { + self.inspect_inner(tracer, None, execution_mode) + } fn get_bootloader_memory(&self) -> BootloaderMemory { self.bootloader_state.bootloader_memory() From 210d8ca43abc53945ee23355b907167d2de2b29c Mon Sep 17 00:00:00 2001 From: Marcos Nicolau Date: Mon, 26 Aug 2024 11:17:37 -0300 Subject: [PATCH 20/32] Remove result tracer and moved logic into vm run having the result handling in a tracer made the everything very obfuscating --- .../src/tracers/call_tracer/era_vm/mod.rs | 1 - .../src/versions/era_vm/tracers/manager.rs | 75 +-------- .../src/versions/era_vm/tracers/mod.rs | 1 - .../versions/era_vm/tracers/result_tracer.rs | 85 ----------- core/lib/multivm/src/versions/era_vm/vm.rs | 143 ++++++++++++++++-- 5 files changed, 134 insertions(+), 171 deletions(-) delete mode 100644 core/lib/multivm/src/versions/era_vm/tracers/result_tracer.rs diff --git a/core/lib/multivm/src/tracers/call_tracer/era_vm/mod.rs b/core/lib/multivm/src/tracers/call_tracer/era_vm/mod.rs index 5a7262c47512..5f9d386dd2d0 100644 --- a/core/lib/multivm/src/tracers/call_tracer/era_vm/mod.rs +++ b/core/lib/multivm/src/tracers/call_tracer/era_vm/mod.rs @@ -8,7 +8,6 @@ use zksync_types::{ }; use super::CallTracer; - use crate::{ era_vm::tracers::traits::{Tracer, VmTracer}, interface::VmRevertReason, diff --git a/core/lib/multivm/src/versions/era_vm/tracers/manager.rs b/core/lib/multivm/src/versions/era_vm/tracers/manager.rs index a4c60667862f..344492f88111 100644 --- a/core/lib/multivm/src/versions/era_vm/tracers/manager.rs +++ b/core/lib/multivm/src/versions/era_vm/tracers/manager.rs @@ -6,7 +6,7 @@ use zksync_state::{ReadStorage, StoragePtr}; use super::{ circuits_tracer::CircuitsTracer, dispatcher::TracerDispatcher, pubdata_tracer::PubdataTracer, - refunds_tracer::RefundsTracer, result_tracer::ResultTracer, traits::VmTracer, + refunds_tracer::RefundsTracer, traits::VmTracer, }; use crate::{ era_vm::{bootloader_state::utils::apply_l2_block, hook::Hook, vm::Vm}, @@ -20,16 +20,14 @@ use crate::{ pub struct VmTracerManager { execution_mode: VmExecutionMode, pub dispatcher: TracerDispatcher, - // this tracer collects the vm results for every transaction - // when the vm stops, the result would be available here - pub result_tracer: ResultTracer, // This tracer is designed specifically for calculating refunds and saves the results to `VmResultAndLogs`. + // it is marked as optional, because tipically we want to track refunds when we are in OneTx mode. pub refund_tracer: Option, // The pubdata tracer is responsible for inserting the pubdata packing information into the bootloader - // memory at the end of the batch. Its separation from the custom tracer - // ensures static dispatch, enhancing performance by avoiding dynamic dispatch overhe + // memory at the end of the batch. pub pubdata_tracer: PubdataTracer, // This tracers keeps track of opcodes calls and collects circuits statistics + // used later by the prover pub circuits_tracer: CircuitsTracer, storage: StoragePtr, } @@ -47,54 +45,10 @@ impl VmTracerManager { dispatcher, refund_tracer, circuits_tracer: CircuitsTracer::new(), - result_tracer: ResultTracer::new(), pubdata_tracer: pubdata_tracer.unwrap_or(PubdataTracer::new(execution_mode)), storage, } } - - fn set_final_batch_info(&self, vm: &mut Vm) { - // set fictive l2 block - let txs_index = vm.bootloader_state.free_tx_index(); - let l2_block = vm.bootloader_state.insert_fictive_l2_block(); - let mut memory = vec![]; - apply_l2_block(&mut memory, l2_block, txs_index); - vm.write_to_bootloader_heap(memory); - } - - fn handle_execution_output( - &mut self, - vm: &mut Vm, - output: ExecutionOutput, - ) -> TracerExecutionStatus { - match output { - ExecutionOutput::SuspendedOnHook { - hook, - pc_to_resume_from, - } => { - vm.suspended_at = pc_to_resume_from; - vm.inner.execution.current_frame_mut().unwrap().pc = vm.suspended_at as u64; - let hook = Hook::from_u32(hook); - self.bootloader_hook_call(vm, hook.clone(), &vm.get_hook_params()); - match hook { - Hook::TxHasEnded => { - if let VmExecutionMode::OneTx = self.execution_mode { - TracerExecutionStatus::Stop(TracerExecutionStopReason::Finish) - } else { - TracerExecutionStatus::Continue - } - } - Hook::FinalBatchInfo => { - self.set_final_batch_info(vm); - TracerExecutionStatus::Continue - } - _ => TracerExecutionStatus::Continue, - } - } - // any other output means the vm has finished executing - _ => TracerExecutionStatus::Stop(TracerExecutionStopReason::Finish), - } - } } impl Tracer for VmTracerManager { @@ -103,7 +57,6 @@ impl Tracer for VmTracerManager { self.dispatcher.before_decoding(execution, state); // Individual tracers - self.result_tracer.before_decoding(execution, state); if let Some(refunds_tracer) = &mut self.refund_tracer { refunds_tracer.before_decoding(execution, state); } @@ -116,7 +69,6 @@ impl Tracer for VmTracerManager { self.dispatcher.after_decoding(opcode, execution, state); // Individual tracers - self.result_tracer.after_decoding(opcode, execution, state); if let Some(refunds_tracer) = &mut self.refund_tracer { refunds_tracer.after_decoding(opcode, execution, state); } @@ -135,8 +87,6 @@ impl Tracer for VmTracerManager { self.dispatcher.before_execution(opcode, execution, state); // Individual tracers - self.result_tracer - .before_execution(opcode, execution, state); if let Some(refunds_tracer) = &mut self.refund_tracer { refunds_tracer.before_execution(opcode, execution, state); } @@ -151,7 +101,6 @@ impl Tracer for VmTracerManager { self.dispatcher.after_execution(opcode, execution, state); // Individual tracers - self.result_tracer.after_execution(opcode, execution, state); if let Some(refunds_tracer) = &mut self.refund_tracer { refunds_tracer.after_execution(opcode, execution, state); } @@ -168,8 +117,6 @@ impl VmTracer for VmTracerManager { self.dispatcher.before_bootloader_execution(state); // Individual tracers - self.result_tracer.before_bootloader_execution(state); - if let Some(refunds_tracer) = &mut self.refund_tracer { refunds_tracer.before_bootloader_execution(state); } @@ -182,7 +129,6 @@ impl VmTracer for VmTracerManager { self.dispatcher.after_bootloader_execution(state); // Individual tracers - self.result_tracer.after_bootloader_execution(state); if let Some(refunds_tracer) = &mut self.refund_tracer { refunds_tracer.after_bootloader_execution(state); } @@ -201,8 +147,6 @@ impl VmTracer for VmTracerManager { .bootloader_hook_call(state, hook.clone(), hook_params); // Individual tracers - self.result_tracer - .bootloader_hook_call(state, hook.clone(), hook_params); if let Some(refunds_tracer) = &mut self.refund_tracer { refunds_tracer.bootloader_hook_call(state, hook.clone(), hook_params); } @@ -220,10 +164,6 @@ impl VmTracer for VmTracerManager { let mut result = self.dispatcher.after_vm_run(vm, output.clone()); // Individual tracers - result = self - .result_tracer - .after_vm_run(vm, output.clone()) - .stricter(&result); if let Some(refunds_tracer) = &mut self.refund_tracer { result = refunds_tracer .after_vm_run(vm, output.clone()) @@ -233,11 +173,8 @@ impl VmTracer for VmTracerManager { .pubdata_tracer .after_vm_run(vm, output.clone()) .stricter(&result); - result = self - .circuits_tracer + self.circuits_tracer .after_vm_run(vm, output.clone()) - .stricter(&result); - - self.handle_execution_output(vm, output).stricter(&result) + .stricter(&result) } } diff --git a/core/lib/multivm/src/versions/era_vm/tracers/mod.rs b/core/lib/multivm/src/versions/era_vm/tracers/mod.rs index 6676649e3073..abfe75e675e7 100644 --- a/core/lib/multivm/src/versions/era_vm/tracers/mod.rs +++ b/core/lib/multivm/src/versions/era_vm/tracers/mod.rs @@ -3,5 +3,4 @@ pub mod dispatcher; pub mod manager; pub mod pubdata_tracer; pub mod refunds_tracer; -pub mod result_tracer; pub mod traits; diff --git a/core/lib/multivm/src/versions/era_vm/tracers/result_tracer.rs b/core/lib/multivm/src/versions/era_vm/tracers/result_tracer.rs deleted file mode 100644 index 859b085f5bd9..000000000000 --- a/core/lib/multivm/src/versions/era_vm/tracers/result_tracer.rs +++ /dev/null @@ -1,85 +0,0 @@ -use era_vm::{value::FatPointer, vm::ExecutionOutput}; -use zksync_state::ReadStorage; - -use super::traits::{ExecutionResult, Tracer, VmTracer}; -use crate::{ - era_vm::hook::Hook, - interface::{tracer::TracerExecutionStatus, Halt, TxRevertReason, VmRevertReason}, -}; - -pub struct ResultTracer { - pub result: Option, -} - -impl ResultTracer { - pub fn new() -> Self { - Self { result: None } - } -} - -impl Tracer for ResultTracer {} - -impl VmTracer for ResultTracer { - fn after_vm_run( - &mut self, - vm: &mut super::traits::Vm, - output: era_vm::vm::ExecutionOutput, - ) -> TracerExecutionStatus { - let result = match output { - ExecutionOutput::Ok(output) => Some(ExecutionResult::Success { output }), - ExecutionOutput::Revert(output) => match TxRevertReason::parse_error(&output) { - TxRevertReason::TxReverted(output) => Some(ExecutionResult::Revert { output }), - TxRevertReason::Halt(reason) => Some(ExecutionResult::Halt { reason }), - }, - ExecutionOutput::Panic => Some(ExecutionResult::Halt { - reason: if vm.inner.execution.gas_left().unwrap() == 0 { - Halt::BootloaderOutOfGas - } else { - Halt::VMPanic - }, - }), - ExecutionOutput::SuspendedOnHook { .. } => None, - }; - - // if the result is none, it means the execution has been suspended - // and we don't want to remove the previous value - if result.is_some() { - self.result = result; - } - - TracerExecutionStatus::Continue - } - - fn bootloader_hook_call( - &mut self, - vm: &mut super::traits::Vm, - hook: Hook, - hook_params: &[zksync_types::U256; 3], - ) { - if let Hook::PostResult = hook { - let result = hook_params[0]; - let value = hook_params[1]; - let pointer = FatPointer::decode(value); - assert_eq!(pointer.offset, 0); - - let return_data = vm - .inner - .execution - .heaps - .get(pointer.page) - .unwrap() - .read_unaligned_from_pointer(&pointer) - .unwrap(); - - self.result = Some(if result.is_zero() { - ExecutionResult::Revert { - output: VmRevertReason::from(return_data.as_slice()), - } - } else { - ExecutionResult::Success { - output: return_data, - } - }); - }; - } -} diff --git a/core/lib/multivm/src/versions/era_vm/vm.rs b/core/lib/multivm/src/versions/era_vm/vm.rs index c268fb21f3ff..6bcaa3228c67 100644 --- a/core/lib/multivm/src/versions/era_vm/vm.rs +++ b/core/lib/multivm/src/versions/era_vm/vm.rs @@ -1,6 +1,9 @@ use std::{cell::RefCell, collections::HashMap, rc::Rc}; -use era_vm::{rollbacks::Rollbackable, store::StorageKey as EraStorageKey, EraVM, Execution}; +use era_vm::{ + rollbacks::Rollbackable, store::StorageKey as EraStorageKey, value::FatPointer, + vm::ExecutionOutput, EraVM, Execution, +}; use itertools::Itertools; use zksync_state::{ReadStorage, StoragePtr}; use zksync_types::{ @@ -22,8 +25,9 @@ use zksync_utils::{ }; use super::{ - bootloader_state::BootloaderState, + bootloader_state::{utils::apply_l2_block, BootloaderState}, event::merge_events, + hook::Hook, initial_bootloader_memory::bootloader_initial_memory, logs::IntoSystemLog, snapshot::VmSnapshot, @@ -34,10 +38,14 @@ use super::{ }; use crate::{ era_vm::{bytecode::compress_bytecodes, transaction_data::TransactionData}, - interface::{tracer::TracerExecutionStatus, VmFactory, VmInterface, VmInterfaceHistoryEnabled}, + interface::{ + tracer::{TracerExecutionStatus, TracerExecutionStopReason}, + Halt, TxRevertReason, VmFactory, VmInterface, VmInterfaceHistoryEnabled, VmRevertReason, + }, vm_latest::{ constants::{ - get_vm_hook_position, get_vm_hook_start_position_latest, VM_HOOK_PARAMS_COUNT, + get_result_success_first_slot, get_vm_hook_position, get_vm_hook_start_position_latest, + VM_HOOK_PARAMS_COUNT, }, BootloaderMemory, CurrentExecutionState, ExecutionResult, L1BatchEnv, L2BlockEnv, SystemEnv, VmExecutionLogs, VmExecutionMode, VmExecutionResultAndLogs, @@ -144,18 +152,126 @@ impl VmFactory for Vm { } impl Vm { - pub fn run(&mut self, tracer: &mut impl VmTracer) { + pub fn run( + &mut self, + execution_mode: VmExecutionMode, + tracer: &mut impl VmTracer, + ) -> ExecutionResult { tracer.before_bootloader_execution(self); - loop { + let mut last_tx_result: Option = None; + let result = loop { let output = self.inner.run_program_with_custom_bytecode(Some(tracer)); + let status = tracer.after_vm_run(self, output.clone()); + let (hook, hook_params) = match output { + ExecutionOutput::Ok(output) => break ExecutionResult::Success { output }, + ExecutionOutput::Revert(output) => match TxRevertReason::parse_error(&output) { + TxRevertReason::TxReverted(output) => break ExecutionResult::Revert { output }, + TxRevertReason::Halt(reason) => break ExecutionResult::Halt { reason }, + }, + ExecutionOutput::Panic => { + break ExecutionResult::Halt { + reason: if self.inner.execution.gas_left().unwrap() == 0 { + Halt::BootloaderOutOfGas + } else { + Halt::VMPanic + }, + } + } + ExecutionOutput::SuspendedOnHook { + hook, + pc_to_resume_from, + } => { + self.suspended_at = pc_to_resume_from; + self.inner.execution.current_frame_mut().unwrap().pc = self.suspended_at as u64; + (Hook::from_u32(hook), self.get_hook_params()) + } + }; + + tracer.bootloader_hook_call(self, hook.clone(), &self.get_hook_params()); + + match hook { + Hook::PostResult => { + let result = hook_params[0]; + let value = hook_params[1]; + let pointer = FatPointer::decode(value); + assert_eq!(pointer.offset, 0); + + let return_data = self + .inner + .execution + .heaps + .get(pointer.page) + .unwrap() + .read_unaligned_from_pointer(&pointer) + .unwrap(); + + last_tx_result = Some(if result.is_zero() { + ExecutionResult::Revert { + output: VmRevertReason::from(return_data.as_slice()), + } + } else { + ExecutionResult::Success { + output: return_data, + } + }); + } + Hook::TxHasEnded => { + if let VmExecutionMode::OneTx = execution_mode { + break last_tx_result + .expect("There should always be a result if we got this hook"); + } + } + Hook::FinalBatchInfo => { + // set fictive l2 block + let txs_index = self.bootloader_state.free_tx_index(); + let l2_block = self.bootloader_state.insert_fictive_l2_block(); + let mut memory = vec![]; + apply_l2_block(&mut memory, l2_block, txs_index); + self.write_to_bootloader_heap(memory); + } + _ => {} + } - let status = tracer.after_vm_run(self, output); - - if let TracerExecutionStatus::Stop(_) = status { - break; + if let TracerExecutionStatus::Stop(reason) = status { + match reason { + TracerExecutionStopReason::Abort(halt) => { + break ExecutionResult::Halt { reason: halt } + } + TracerExecutionStopReason::Finish => { + if self.inner.execution.gas_left().unwrap() == 0 { + break ExecutionResult::Halt { + reason: Halt::BootloaderOutOfGas, + }; + } + if last_tx_result.is_some() { + break last_tx_result.unwrap(); + } + let has_failed = + self.tx_has_failed(self.bootloader_state.current_tx() as u32); + if has_failed { + break ExecutionResult::Revert { + output: crate::interface::VmRevertReason::General { + msg: "Transaction reverted with empty reason. Possibly out of gas".to_string(), + data: vec![], + }, + }; + } else { + break ExecutionResult::Success { output: vec![] }; + } + } + } } - } + }; tracer.after_bootloader_execution(self); + result + } + + fn tx_has_failed(&self, tx_id: u32) -> bool { + let mem_slot = get_result_success_first_slot( + crate::vm_latest::MultiVMSubversion::IncreasedBootloaderMemory, + ) + tx_id; + let mem_value = self.read_heap_word(mem_slot as usize); + mem_value == U256::zero() } pub(crate) fn insert_bytecodes<'a>(&mut self, bytecodes: impl IntoIterator) { @@ -281,10 +397,7 @@ impl Vm { let ergs_before = self.inner.execution.gas_left().unwrap(); let monotonic_counter_before = self.inner.statistics.monotonic_counter; - self.run(&mut tracer); - // it is actually safe to unwrap here, since we always expect a result - // the reason we use an option is because we really can't set an initial value in the result tracer - let result = tracer.result_tracer.result.unwrap(); + let result = self.run(execution_mode, &mut tracer); let ergs_after = self.inner.execution.gas_left().unwrap(); let ignore_world_diff = matches!(execution_mode, VmExecutionMode::OneTx) From 36832395a7849cc08ea79564a7aabdcdc4fbb135 Mon Sep 17 00:00:00 2001 From: Marcos Nicolau Date: Mon, 26 Aug 2024 11:23:28 -0300 Subject: [PATCH 21/32] General refactor --- .../src/versions/era_vm/tracers/manager.rs | 15 +++------------ core/lib/multivm/src/versions/era_vm/vm.rs | 16 +++++----------- 2 files changed, 8 insertions(+), 23 deletions(-) diff --git a/core/lib/multivm/src/versions/era_vm/tracers/manager.rs b/core/lib/multivm/src/versions/era_vm/tracers/manager.rs index 344492f88111..c1a20e42705d 100644 --- a/core/lib/multivm/src/versions/era_vm/tracers/manager.rs +++ b/core/lib/multivm/src/versions/era_vm/tracers/manager.rs @@ -2,23 +2,18 @@ use era_vm::{ execution::Execution, opcode::Opcode, state::VMState, tracers::tracer::Tracer, vm::ExecutionOutput, }; -use zksync_state::{ReadStorage, StoragePtr}; +use zksync_state::ReadStorage; use super::{ circuits_tracer::CircuitsTracer, dispatcher::TracerDispatcher, pubdata_tracer::PubdataTracer, refunds_tracer::RefundsTracer, traits::VmTracer, }; -use crate::{ - era_vm::{bootloader_state::utils::apply_l2_block, hook::Hook, vm::Vm}, - interface::tracer::{TracerExecutionStatus, TracerExecutionStopReason}, - vm_1_4_1::VmExecutionMode, -}; +use crate::{era_vm::vm::Vm, interface::tracer::TracerExecutionStatus, vm_1_4_1::VmExecutionMode}; // this tracer manager is the one that gets called when running the vm // all the logic of hooks and results parsing is managed from here // the most important tracers are: `result_tracer`, `refund_tracer`, `pubdata_tracer`, and `circuits_tracer` pub struct VmTracerManager { - execution_mode: VmExecutionMode, pub dispatcher: TracerDispatcher, // This tracer is designed specifically for calculating refunds and saves the results to `VmResultAndLogs`. // it is marked as optional, because tipically we want to track refunds when we are in OneTx mode. @@ -29,24 +24,20 @@ pub struct VmTracerManager { // This tracers keeps track of opcodes calls and collects circuits statistics // used later by the prover pub circuits_tracer: CircuitsTracer, - storage: StoragePtr, } -impl VmTracerManager { +impl VmTracerManager { pub fn new( execution_mode: VmExecutionMode, - storage: StoragePtr, dispatcher: TracerDispatcher, refund_tracer: Option, pubdata_tracer: Option, ) -> Self { Self { - execution_mode, dispatcher, refund_tracer, circuits_tracer: CircuitsTracer::new(), pubdata_tracer: pubdata_tracer.unwrap_or(PubdataTracer::new(execution_mode)), - storage, } } } diff --git a/core/lib/multivm/src/versions/era_vm/vm.rs b/core/lib/multivm/src/versions/era_vm/vm.rs index 6bcaa3228c67..658fc8273851 100644 --- a/core/lib/multivm/src/versions/era_vm/vm.rs +++ b/core/lib/multivm/src/versions/era_vm/vm.rs @@ -4,7 +4,6 @@ use era_vm::{ rollbacks::Rollbackable, store::StorageKey as EraStorageKey, value::FatPointer, vm::ExecutionOutput, EraVM, Execution, }; -use itertools::Itertools; use zksync_state::{ReadStorage, StoragePtr}; use zksync_types::{ event::extract_l2tol1logs_from_l1_messenger, @@ -12,12 +11,12 @@ use zksync_types::{ l2_to_l1_log::UserL2ToL1Log, utils::key_for_eth_balance, writes::{ - compression::compress_with_best_strategy, StateDiffRecord, BYTES_PER_DERIVED_KEY, + compression::compress_with_best_strategy, BYTES_PER_DERIVED_KEY, BYTES_PER_ENUMERATION_INDEX, }, AccountTreeId, StorageKey, StorageLog, StorageLogKind, StorageLogWithPreviousValue, - Transaction, BOOTLOADER_ADDRESS, H160, KNOWN_CODES_STORAGE_ADDRESS, L1_MESSENGER_ADDRESS, - L2_BASE_TOKEN_ADDRESS, U256, + Transaction, BOOTLOADER_ADDRESS, H160, KNOWN_CODES_STORAGE_ADDRESS, L2_BASE_TOKEN_ADDRESS, + U256, }; use zksync_utils::{ bytecode::{hash_bytecode, CompressedBytecodeInfo}, @@ -385,13 +384,8 @@ impl Vm { } else { None }; - let mut tracer = VmTracerManager::new( - execution_mode, - self.storage.clone(), - tracer, - refund_tracer, - custom_pubdata_tracer, - ); + let mut tracer = + VmTracerManager::new(execution_mode, tracer, refund_tracer, custom_pubdata_tracer); let snapshot = self.inner.state.snapshot(); let ergs_before = self.inner.execution.gas_left().unwrap(); From 611dc845b4e01c3e14586c91b2169770c8667d7e Mon Sep 17 00:00:00 2001 From: Marcos Nicolau Date: Mon, 26 Aug 2024 18:30:25 -0300 Subject: [PATCH 22/32] Update vm new execute for tracers --- .../versions/era_vm/tracers/pubdata_tracer.rs | 20 ++++++++++++++++--- core/lib/multivm/src/versions/era_vm/vm.rs | 4 +++- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/core/lib/multivm/src/versions/era_vm/tracers/pubdata_tracer.rs b/core/lib/multivm/src/versions/era_vm/tracers/pubdata_tracer.rs index 1bb0997b4895..ac1098256a57 100644 --- a/core/lib/multivm/src/versions/era_vm/tracers/pubdata_tracer.rs +++ b/core/lib/multivm/src/versions/era_vm/tracers/pubdata_tracer.rs @@ -17,12 +17,14 @@ use crate::{ event::merge_events, hook::Hook, }, + interface::tracer::{TracerExecutionStatus, TracerExecutionStopReason}, vm_1_4_1::VmExecutionMode, }; pub struct PubdataTracer { execution_mode: VmExecutionMode, pubdata_before_run: i32, + should_stop: bool, pub pubdata_published: u32, // this field is to enforce a custom storage diff when setting the pubdata to the bootloader // this is meant to be used for testing purposes only. @@ -36,6 +38,7 @@ impl PubdataTracer { pubdata_before_run: 0, pubdata_published: 0, enforced_storage_diff: None, + should_stop: false, } } @@ -49,7 +52,7 @@ impl PubdataTracer { } } - fn get_storage_diff(&mut self, vm: &Vm) -> Vec { + fn get_storage_diff(&mut self, vm: &mut Vm) -> Vec { vm.inner .state .get_storage_changes() @@ -101,6 +104,17 @@ impl VmTracer for PubdataTracer { self.pubdata_published = (vm.inner.state.pubdata() - self.pubdata_before_run).max(0) as u32; } + fn after_vm_run( + &mut self, + _vm: &mut Vm, + _output: era_vm::vm::ExecutionOutput, + ) -> TracerExecutionStatus { + if self.should_stop { + return TracerExecutionStatus::Stop(TracerExecutionStopReason::Finish); + } + TracerExecutionStatus::Continue + } + fn bootloader_hook_call( &mut self, vm: &mut Vm, @@ -109,13 +123,13 @@ impl VmTracer for PubdataTracer { ) { if let Hook::PubdataRequested = hook { if !matches!(self.execution_mode, VmExecutionMode::Batch) { - unreachable!("We do not provide the pubdata when executing the block tip or a single transaction"); + self.should_stop = true; }; let state_diffs = if let Some(diff) = &self.enforced_storage_diff { diff.clone() } else { - self.get_storage_diff(&vm) + self.get_storage_diff(vm) }; let events = merge_events(vm.inner.state.events(), vm.batch_env.number); diff --git a/core/lib/multivm/src/versions/era_vm/vm.rs b/core/lib/multivm/src/versions/era_vm/vm.rs index 658fc8273851..19f065088f27 100644 --- a/core/lib/multivm/src/versions/era_vm/vm.rs +++ b/core/lib/multivm/src/versions/era_vm/vm.rs @@ -159,7 +159,9 @@ impl Vm { tracer.before_bootloader_execution(self); let mut last_tx_result: Option = None; let result = loop { - let output = self.inner.run_program_with_custom_bytecode(Some(tracer)); + let output = self + .inner + .run_program_with_custom_bytecode_and_tracer(tracer); let status = tracer.after_vm_run(self, output.clone()); let (hook, hook_params) = match output { ExecutionOutput::Ok(output) => break ExecutionResult::Success { output }, From 5312aad8c1034e564439ce6cb6145a96c7070f9b Mon Sep 17 00:00:00 2001 From: Marcos Nicolau Date: Tue, 27 Aug 2024 11:51:01 -0300 Subject: [PATCH 23/32] Fix opcode imports in call_tracer --- core/lib/multivm/src/tracers/call_tracer/era_vm/mod.rs | 9 ++++++--- .../src/versions/era_vm/tracers/circuits_tracer.rs | 3 +-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/core/lib/multivm/src/tracers/call_tracer/era_vm/mod.rs b/core/lib/multivm/src/tracers/call_tracer/era_vm/mod.rs index 5f9d386dd2d0..cfb07b63f53a 100644 --- a/core/lib/multivm/src/tracers/call_tracer/era_vm/mod.rs +++ b/core/lib/multivm/src/tracers/call_tracer/era_vm/mod.rs @@ -1,5 +1,8 @@ -use era_vm::{value::FatPointer, Execution, Opcode}; -use zkevm_opcode_defs::{Opcode as Variant, RetOpcode}; +use era_vm::{ + opcode::{RetOpcode, Variant}, + value::FatPointer, + Execution, Opcode, +}; use zksync_state::ReadStorage; use zksync_types::{ vm_trace::{Call, CallType}, @@ -18,7 +21,7 @@ impl Tracer for CallTracer { &mut self, opcode: &Opcode, execution: &mut era_vm::Execution, - state: &mut era_vm::state::VMState, + _state: &mut era_vm::state::VMState, ) { match opcode.variant { Variant::NearCall(_) => { diff --git a/core/lib/multivm/src/versions/era_vm/tracers/circuits_tracer.rs b/core/lib/multivm/src/versions/era_vm/tracers/circuits_tracer.rs index 8ef06818ba4a..32ed7dde1e88 100644 --- a/core/lib/multivm/src/versions/era_vm/tracers/circuits_tracer.rs +++ b/core/lib/multivm/src/versions/era_vm/tracers/circuits_tracer.rs @@ -1,9 +1,8 @@ use circuit_sequencer_api_1_5_0::{geometry_config::get_geometry_config, toolset::GeometryConfig}; use era_vm::{ - opcode::{Opcode, Variant}, + opcode::{LogOpcode, Opcode, UMAOpcode, Variant}, statistics::VmStatistics, }; -use zkevm_opcode_defs::{LogOpcode, UMAOpcode}; use zksync_state::ReadStorage; use zksync_types::circuit::CircuitStatistic; From 172ccaed5ca09a1097476dc5bbb86cbd860918d0 Mon Sep 17 00:00:00 2001 From: Marcos Nicolau Date: Wed, 28 Aug 2024 10:47:59 -0300 Subject: [PATCH 24/32] Add optional debug tracer --- .../versions/era_vm/tracers/debug_tracer.rs | 58 +++++++++++++++++++ .../src/versions/era_vm/tracers/manager.rs | 33 ++++++++++- .../src/versions/era_vm/tracers/mod.rs | 1 + 3 files changed, 90 insertions(+), 2 deletions(-) create mode 100644 core/lib/multivm/src/versions/era_vm/tracers/debug_tracer.rs diff --git a/core/lib/multivm/src/versions/era_vm/tracers/debug_tracer.rs b/core/lib/multivm/src/versions/era_vm/tracers/debug_tracer.rs new file mode 100644 index 000000000000..5585745e05a2 --- /dev/null +++ b/core/lib/multivm/src/versions/era_vm/tracers/debug_tracer.rs @@ -0,0 +1,58 @@ +use zksync_state::ReadStorage; +use zksync_types::U256; +use zksync_utils::u256_to_h256; + +use super::traits::{Tracer, VmTracer}; +use crate::era_vm::hook::Hook; + +pub struct DebugTracer {} + +impl Tracer for DebugTracer {} + +impl VmTracer for DebugTracer { + fn bootloader_hook_call( + &mut self, + _vm: &mut super::traits::Vm, + hook: crate::era_vm::hook::Hook, + hook_params: &[U256; 3], + ) { + match hook { + Hook::DebugLog => { + let msg = u256_to_h256(hook_params[0]).as_bytes().to_vec(); + let data = u256_to_h256(hook_params[1]).as_bytes().to_vec(); + + let msg = String::from_utf8(msg).expect("Invalid debug message"); + let data = U256::from_big_endian(&data); + + // For long data, it is better to use hex-encoding for greater readability + let data_str = if data > U256::from(u64::max_value()) { + let mut bytes = [0u8; 32]; + data.to_big_endian(&mut bytes); + format!("0x{}", hex::encode(bytes)) + } else { + data.to_string() + }; + + println!("======== BOOTLOADER DEBUG LOG ========"); + println!("MSG: {:?}", msg); + println!("DATA: {}", data_str); + } + Hook::AccountValidationEntered => { + // println!("ACCOUNT VALIDATION ENTERED"); + } + Hook::ValidationStepEnded => { + // println!("VALIDATION STEP ENDED"); + } + Hook::AccountValidationExited => { + // println!("ACCOUNT VALIDATION EXITED"); + } + Hook::DebugReturnData => { + // println!("DEBUG RETURN DATA"); + } + Hook::NearCallCatch => { + // println!("NOTIFY ABOUT NEAR CALL CATCH"); + } + _ => {} + }; + } +} diff --git a/core/lib/multivm/src/versions/era_vm/tracers/manager.rs b/core/lib/multivm/src/versions/era_vm/tracers/manager.rs index c1a20e42705d..5ffc6ea58bd6 100644 --- a/core/lib/multivm/src/versions/era_vm/tracers/manager.rs +++ b/core/lib/multivm/src/versions/era_vm/tracers/manager.rs @@ -5,8 +5,8 @@ use era_vm::{ use zksync_state::ReadStorage; use super::{ - circuits_tracer::CircuitsTracer, dispatcher::TracerDispatcher, pubdata_tracer::PubdataTracer, - refunds_tracer::RefundsTracer, traits::VmTracer, + circuits_tracer::CircuitsTracer, debug_tracer::DebugTracer, dispatcher::TracerDispatcher, + pubdata_tracer::PubdataTracer, refunds_tracer::RefundsTracer, traits::VmTracer, }; use crate::{era_vm::vm::Vm, interface::tracer::TracerExecutionStatus, vm_1_4_1::VmExecutionMode}; @@ -24,6 +24,8 @@ pub struct VmTracerManager { // This tracers keeps track of opcodes calls and collects circuits statistics // used later by the prover pub circuits_tracer: CircuitsTracer, + // Tracer used for debugging purposes + pub debug_tracer: Option, } impl VmTracerManager { @@ -38,6 +40,7 @@ impl VmTracerManager { refund_tracer, circuits_tracer: CircuitsTracer::new(), pubdata_tracer: pubdata_tracer.unwrap_or(PubdataTracer::new(execution_mode)), + debug_tracer: None, // or Some(DebugTracer) to enable debugger } } } @@ -51,6 +54,9 @@ impl Tracer for VmTracerManager { if let Some(refunds_tracer) = &mut self.refund_tracer { refunds_tracer.before_decoding(execution, state); } + if let Some(debug_tracer) = &mut self.debug_tracer { + debug_tracer.before_decoding(execution, state); + } self.pubdata_tracer.before_decoding(execution, state); self.circuits_tracer.before_decoding(execution, state); } @@ -63,6 +69,9 @@ impl Tracer for VmTracerManager { if let Some(refunds_tracer) = &mut self.refund_tracer { refunds_tracer.after_decoding(opcode, execution, state); } + if let Some(debug_tracer) = &mut self.debug_tracer { + debug_tracer.after_decoding(opcode, execution, state); + } self.pubdata_tracer.after_decoding(opcode, execution, state); self.circuits_tracer .after_decoding(opcode, execution, state); @@ -81,6 +90,9 @@ impl Tracer for VmTracerManager { if let Some(refunds_tracer) = &mut self.refund_tracer { refunds_tracer.before_execution(opcode, execution, state); } + if let Some(debug_tracer) = &mut self.debug_tracer { + debug_tracer.before_execution(opcode, execution, state); + } self.pubdata_tracer .before_execution(opcode, execution, state); self.circuits_tracer @@ -95,6 +107,9 @@ impl Tracer for VmTracerManager { if let Some(refunds_tracer) = &mut self.refund_tracer { refunds_tracer.after_execution(opcode, execution, state); } + if let Some(debug_tracer) = &mut self.debug_tracer { + debug_tracer.after_execution(opcode, execution, state); + } self.pubdata_tracer .after_execution(opcode, execution, state); self.circuits_tracer @@ -111,6 +126,9 @@ impl VmTracer for VmTracerManager { if let Some(refunds_tracer) = &mut self.refund_tracer { refunds_tracer.before_bootloader_execution(state); } + if let Some(debug_tracer) = &mut self.debug_tracer { + debug_tracer.before_bootloader_execution(state); + } self.pubdata_tracer.before_bootloader_execution(state); self.circuits_tracer.before_bootloader_execution(state); } @@ -123,6 +141,9 @@ impl VmTracer for VmTracerManager { if let Some(refunds_tracer) = &mut self.refund_tracer { refunds_tracer.after_bootloader_execution(state); } + if let Some(debug_tracer) = &mut self.debug_tracer { + debug_tracer.after_bootloader_execution(state); + } self.pubdata_tracer.after_bootloader_execution(state); self.circuits_tracer.after_bootloader_execution(state); } @@ -141,6 +162,9 @@ impl VmTracer for VmTracerManager { if let Some(refunds_tracer) = &mut self.refund_tracer { refunds_tracer.bootloader_hook_call(state, hook.clone(), hook_params); } + if let Some(debug_tracer) = &mut self.debug_tracer { + debug_tracer.bootloader_hook_call(state, hook.clone(), hook_params); + } self.pubdata_tracer .bootloader_hook_call(state, hook.clone(), hook_params); self.circuits_tracer @@ -160,6 +184,11 @@ impl VmTracer for VmTracerManager { .after_vm_run(vm, output.clone()) .stricter(&result); } + if let Some(debug_tracer) = &mut self.debug_tracer { + result = debug_tracer + .after_vm_run(vm, output.clone()) + .stricter(&result); + } result = self .pubdata_tracer .after_vm_run(vm, output.clone()) diff --git a/core/lib/multivm/src/versions/era_vm/tracers/mod.rs b/core/lib/multivm/src/versions/era_vm/tracers/mod.rs index abfe75e675e7..ff4c310e3293 100644 --- a/core/lib/multivm/src/versions/era_vm/tracers/mod.rs +++ b/core/lib/multivm/src/versions/era_vm/tracers/mod.rs @@ -1,4 +1,5 @@ pub mod circuits_tracer; +pub mod debug_tracer; pub mod dispatcher; pub mod manager; pub mod pubdata_tracer; From ec31b9e2c1639f7480a800ac0505df62b551e521 Mon Sep 17 00:00:00 2001 From: Marcos Nicolau <76252340+MarcosNicolau@users.noreply.github.com> Date: Wed, 28 Aug 2024 14:59:38 -0300 Subject: [PATCH 25/32] Remove ignore flag in call_tracer test --- core/lib/multivm/src/versions/era_vm/tests/call_tracer.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/core/lib/multivm/src/versions/era_vm/tests/call_tracer.rs b/core/lib/multivm/src/versions/era_vm/tests/call_tracer.rs index 9317faddbf5c..9d752a3ba116 100644 --- a/core/lib/multivm/src/versions/era_vm/tests/call_tracer.rs +++ b/core/lib/multivm/src/versions/era_vm/tests/call_tracer.rs @@ -18,7 +18,6 @@ use crate::{ // This test is ultra slow, so it's ignored by default. #[test] -#[ignore] fn test_max_depth() { let contarct = read_max_depth_contract(); let address = Address::random(); From de0eb520a68e4f9caa82fdc238f1e0ea5d6b823c Mon Sep 17 00:00:00 2001 From: Marcos Nicolau Date: Fri, 30 Aug 2024 16:27:12 -0300 Subject: [PATCH 26/32] Add support of zk server with lambda vm --- .../src/versions/era_vm/tests/code_oracle.rs | 2 +- .../versions/era_vm/tests/tester/vm_tester.rs | 10 ++-- core/lib/multivm/src/versions/era_vm/vm.rs | 51 ++++++++++++++----- core/lib/multivm/src/vm_instance.rs | 15 ++++-- 4 files changed, 56 insertions(+), 22 deletions(-) diff --git a/core/lib/multivm/src/versions/era_vm/tests/code_oracle.rs b/core/lib/multivm/src/versions/era_vm/tests/code_oracle.rs index 41f60a1c78a9..a0e3e6bc0345 100644 --- a/core/lib/multivm/src/versions/era_vm/tests/code_oracle.rs +++ b/core/lib/multivm/src/versions/era_vm/tests/code_oracle.rs @@ -211,7 +211,7 @@ fn refunds_in_code_oracle() { .vm .inner .state - .decommit(h256_to_u256(normal_zkevm_bytecode_hash)); + .decommit(h256_to_u256(normal_zkevm_bytecode_hash), &mut vm.vm.world); assert!(!was_decommited); } diff --git a/core/lib/multivm/src/versions/era_vm/tests/tester/vm_tester.rs b/core/lib/multivm/src/versions/era_vm/tests/tester/vm_tester.rs index 59fb18bfcab1..21387c5a36fe 100644 --- a/core/lib/multivm/src/versions/era_vm/tests/tester/vm_tester.rs +++ b/core/lib/multivm/src/versions/era_vm/tests/tester/vm_tester.rs @@ -54,11 +54,11 @@ impl VmTester { } pub(crate) fn reset_with_empty_storage(&mut self) { self.storage = Rc::new(RefCell::new(get_empty_storage())); - let world_storage = Rc::new(RefCell::new(World::new( - self.storage.clone(), - self.vm.program_cache.clone(), - ))); - self.vm.inner.state.storage = world_storage; + // let world_storage = Rc::new(RefCell::new(World::new( + // self.storage.clone(), + // self.vm.program_cache.clone(), + // ))); + // self.vm.inner.state.storage = world_storage; self.vm.inner.state.reset(); self.reset_state(false); } diff --git a/core/lib/multivm/src/versions/era_vm/vm.rs b/core/lib/multivm/src/versions/era_vm/vm.rs index 1ebd1a2e0baf..1910996d0c68 100644 --- a/core/lib/multivm/src/versions/era_vm/vm.rs +++ b/core/lib/multivm/src/versions/era_vm/vm.rs @@ -11,6 +11,7 @@ use zksync_types::{ extract_l2tol1logs_from_l1_messenger, extract_long_l2_to_l1_messages, L1_MESSENGER_BYTECODE_PUBLICATION_EVENT_SIGNATURE, }, + get_known_code_key, l1::is_l1_tx_type, l2_to_l1_log::UserL2ToL1Log, utils::key_for_eth_balance, @@ -19,7 +20,7 @@ use zksync_types::{ BYTES_PER_ENUMERATION_INDEX, }, AccountTreeId, StorageKey, StorageLog, StorageLogKind, StorageLogWithPreviousValue, - Transaction, BOOTLOADER_ADDRESS, H160, KNOWN_CODES_STORAGE_ADDRESS, L1_MESSENGER_ADDRESS, + Transaction, BOOTLOADER_ADDRESS, H160, H256, KNOWN_CODES_STORAGE_ADDRESS, L1_MESSENGER_ADDRESS, L2_BASE_TOKEN_ADDRESS, U256, }; use zksync_utils::{ @@ -42,7 +43,8 @@ use super::{ use crate::{ era_vm::{bytecode::compress_bytecodes, transaction_data::TransactionData}, interface::{ - Halt, TxRevertReason, VmFactory, VmInterface, VmInterfaceHistoryEnabled, VmRevertReason, + BytecodeCompressionError, Halt, TxRevertReason, VmFactory, VmInterface, + VmInterfaceHistoryEnabled, VmRevertReason, }, vm_latest::{ constants::{ @@ -59,6 +61,7 @@ pub struct Vm { pub(crate) inner: EraVM, pub suspended_at: u16, pub gas_for_account_validation: u32, + pub world: World, pub bootloader_state: BootloaderState, pub(crate) storage: StoragePtr, @@ -74,9 +77,10 @@ pub struct Vm { } /// Encapsulates creating VM instance based on the provided environment. -impl VmFactory for Vm { +impl VmFactory for Vm { /// Creates a new VM instance. fn new(batch_env: L1BatchEnv, system_env: SystemEnv, storage: StoragePtr) -> Self { + todo!(); let bootloader_code = system_env .base_system_smart_contracts .bootloader @@ -114,13 +118,13 @@ impl VmFactory for Vm { .code .clone(), ); - let world_storage = World::new(storage.clone(), pre_contract_storage.clone()); - let mut vm = EraVM::new(vm_execution, Rc::new(RefCell::new(world_storage))); + let world = World::new(storage.clone(), pre_contract_storage.clone()); + let mut vm = EraVM::new(vm_execution); let bootloader_memory = bootloader_initial_memory(&batch_env); // The bootloader shouldn't pay for growing memory and it writes results // to the end of its heap, so it makes sense to preallocate it in its entirety. - const BOOTLOADER_MAX_MEMORY_SIZE: u32 = 59000000; + const BOOTLOADER_MAX_MEMORY_SIZE: u32 = u32::MAX; vm.execution .heaps .get_mut(era_vm::execution::FIRST_HEAP) @@ -146,6 +150,7 @@ impl VmFactory for Vm { batch_env, system_env, snapshot: None, + world, }; mv.write_to_bootloader_heap(bootloader_memory); @@ -153,7 +158,7 @@ impl VmFactory for Vm { } } -impl Vm { +impl Vm { pub fn run( &mut self, execution_mode: VmExecutionMode, @@ -167,7 +172,7 @@ impl Vm { let mut last_tx_result = None; loop { - let (result, _blob_tracer) = self.inner.run_program_with_custom_bytecode(); + let result = self.inner.run_program_with_custom_bytecode(&mut self.world); let result = match result { ExecutionOutput::Ok(output) => { @@ -348,6 +353,7 @@ impl Vm { apply_pubdata_to_memory(&mut memory_to_apply, pubdata_input); self.write_to_bootloader_heap(memory_to_apply); } + _ => {} } } } @@ -420,7 +426,7 @@ impl Vm { fn get_storage_diff(&mut self) -> Vec { self.inner .state - .get_storage_changes() + .get_storage_changes(&mut self.world) .iter() .filter_map(|(storage_key, initial_value, value)| { let address = storage_key.address; @@ -493,9 +499,20 @@ impl Vm { self.write_to_bootloader_heap(memory); } + + fn has_unpublished_bytecodes(&mut self) -> bool { + self.bootloader_state + .get_last_tx_compressed_bytecodes() + .iter() + .any(|info| { + let hash_bytecode = hash_bytecode(&info.original); + let code_key = get_known_code_key(&hash_bytecode); + self.storage.borrow_mut().read_value(&code_key) != H256::zero() + }) + } } -impl VmInterface for Vm { +impl VmInterface for Vm { type TracerDispatcher = (); fn push_transaction(&mut self, tx: Transaction) { @@ -542,7 +559,7 @@ impl VmInterface for Vm { let storage_logs: Vec = self .inner .state - .get_storage_changes_from_snapshot(snapshot.storage_changes) + .get_storage_changes_from_snapshot(snapshot.storage_changes, &mut self.world) .iter() .map(|(storage_key, previos_value, value, is_initial)| { let key = StorageKey::new( @@ -648,7 +665,15 @@ impl VmInterface for Vm { Result<(), crate::interface::BytecodeCompressionError>, VmExecutionResultAndLogs, ) { - todo!() + self.push_transaction_inner(tx, 0, with_compression); + let result = self.inspect((), VmExecutionMode::OneTx); + + let compression_result = if self.has_unpublished_bytecodes() { + Err(BytecodeCompressionError::BytecodeCompressionFailed) + } else { + Ok(()) + }; + (compression_result, result) } fn record_vm_memory_metrics(&self) -> crate::vm_1_4_1::VmMemoryMetrics { @@ -660,7 +685,7 @@ impl VmInterface for Vm { } } -impl VmInterfaceHistoryEnabled for Vm { +impl VmInterfaceHistoryEnabled for Vm { fn make_snapshot(&mut self) { assert!( self.snapshot.is_none(), diff --git a/core/lib/multivm/src/vm_instance.rs b/core/lib/multivm/src/vm_instance.rs index c8a7ce837991..b20f82f0d1e9 100644 --- a/core/lib/multivm/src/vm_instance.rs +++ b/core/lib/multivm/src/vm_instance.rs @@ -15,7 +15,8 @@ use crate::{ pub type ShadowedFastVm = ShadowVm, H>>; -#[derive(Debug)] +// TODO READ ADD THE DEBUG +// #[derive(Debug)] pub enum VmInstance { VmM5(crate::vm_m5::Vm, H>), VmM6(crate::vm_m6::Vm, H>), @@ -27,6 +28,7 @@ pub enum VmInstance { Vm1_4_2(crate::vm_1_4_2::Vm, H>), Vm1_5_0(crate::vm_latest::Vm, H>), VmFast(crate::vm_fast::Vm>), + LambdaVm(crate::era_vm::vm::Vm>), ShadowedVmFast(ShadowedFastVm), } @@ -43,6 +45,7 @@ macro_rules! dispatch_vm { VmInstance::Vm1_4_2(vm) => vm.$function($($params)*), VmInstance::Vm1_5_0(vm) => vm.$function($($params)*), VmInstance::VmFast(vm) => vm.$function($($params)*), + VmInstance::LambdaVm(vm) => vm.$function($($params)*), VmInstance::ShadowedVmFast(vm) => vm.$function($($params)*), } }; @@ -260,8 +263,14 @@ impl VmInstance { VmVersion::Vm1_5_0IncreasedBootloaderMemory => match mode { FastVmMode::Old => Self::new(l1_batch_env, system_env, storage_view), FastVmMode::New => { - let storage = ImmutableStorageView::new(storage_view); - Self::VmFast(crate::vm_fast::Vm::new(l1_batch_env, system_env, storage)) + // let storage = ImmutableStorageView::new(storage_view); + // Self::VmFast(crate::vm_fast::Vm::new(l1_batch_env, system_env, storage)) + + Self::LambdaVm(crate::era_vm::vm::Vm::new( + l1_batch_env, + system_env, + storage_view, + )) } FastVmMode::Shadow => { Self::ShadowedVmFast(ShadowVm::new(l1_batch_env, system_env, storage_view)) From a31ae3abeac8586d58632c1a0e86e848c65debde Mon Sep 17 00:00:00 2001 From: Marcos Nicolau Date: Fri, 30 Aug 2024 17:38:07 -0300 Subject: [PATCH 27/32] Add finish_batch and sort storage_logs --- core/lib/multivm/src/versions/era_vm/vm.rs | 35 ++++++++++++++++++++-- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/core/lib/multivm/src/versions/era_vm/vm.rs b/core/lib/multivm/src/versions/era_vm/vm.rs index 1910996d0c68..24358faf7f29 100644 --- a/core/lib/multivm/src/versions/era_vm/vm.rs +++ b/core/lib/multivm/src/versions/era_vm/vm.rs @@ -51,8 +51,8 @@ use crate::{ get_vm_hook_position, get_vm_hook_start_position_latest, OPERATOR_REFUNDS_OFFSET, TX_GAS_LIMIT_OFFSET, VM_HOOK_PARAMS_COUNT, }, - BootloaderMemory, CurrentExecutionState, ExecutionResult, L1BatchEnv, L2BlockEnv, Refunds, - SystemEnv, VmExecutionLogs, VmExecutionMode, VmExecutionResultAndLogs, + BootloaderMemory, CurrentExecutionState, ExecutionResult, FinishedL1Batch, L1BatchEnv, + L2BlockEnv, Refunds, SystemEnv, VmExecutionLogs, VmExecutionMode, VmExecutionResultAndLogs, VmExecutionStatistics, }, }; @@ -80,7 +80,6 @@ pub struct Vm { impl VmFactory for Vm { /// Creates a new VM instance. fn new(batch_env: L1BatchEnv, system_env: SystemEnv, storage: StoragePtr) -> Self { - todo!(); let bootloader_code = system_env .base_system_smart_contracts .bootloader @@ -580,6 +579,13 @@ impl VmInterface for Vm { previous_value: u256_to_h256(previos_value.unwrap_or_default()), } }) + .sorted_by(|a, b| { + a.log + .key + .address() + .cmp(&b.log.key.address()) + .then_with(|| a.log.key.key().cmp(&b.log.key.key())) + }) .collect(); VmExecutionLogs { @@ -683,6 +689,29 @@ impl VmInterface for Vm { fn gas_remaining(&self) -> u32 { self.inner.execution.current_frame().unwrap().gas_left.0 } + + fn finish_batch(&mut self) -> FinishedL1Batch { + let result = self.execute(VmExecutionMode::Batch); + let execution_state = self.get_current_execution_state(); + let bootloader_memory = self.get_bootloader_memory(); + FinishedL1Batch { + block_tip_execution_result: result, + final_execution_state: execution_state, + final_bootloader_memory: Some(bootloader_memory), + pubdata_input: Some( + self.bootloader_state + .get_pubdata_information() + .clone() + .build_pubdata(false), + ), + state_diffs: Some( + self.bootloader_state + .get_pubdata_information() + .state_diffs + .to_vec(), + ), + } + } } impl VmInterfaceHistoryEnabled for Vm { From 602af240aead3d877913e2779e4d4ab7c06dc5b5 Mon Sep 17 00:00:00 2001 From: Marcos Nicolau Date: Fri, 30 Aug 2024 17:39:15 -0300 Subject: [PATCH 28/32] Update vm_runner config to run our vm --- etc/env/base/vm_runner.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/etc/env/base/vm_runner.toml b/etc/env/base/vm_runner.toml index 8e6171d79366..53016ed548c4 100644 --- a/etc/env/base/vm_runner.toml +++ b/etc/env/base/vm_runner.toml @@ -18,10 +18,10 @@ first_processed_batch = 0 [experimental_vm] # Mode in which to run the new fast VM in the state keeper. Don't set to "new" / "shadow" in production yet! -state_keeper_fast_vm_mode = "old" # default value +state_keeper_fast_vm_mode = "new" # default value [experimental_vm.playground] # Path to the directory that contains RocksDB with protective reads writer cache. db_path = "./db/main/vm_playground" # Mode in which to run the new fast VM -fast_vm_mode = "shadow" +fast_vm_mode = "new" From 9773fe84564d9391caddca0cc74178f5b5dff3aa Mon Sep 17 00:00:00 2001 From: Gianbelinche <39842759+gianbelinche@users.noreply.github.com> Date: Mon, 2 Sep 2024 12:22:42 -0300 Subject: [PATCH 29/32] Finish merge --- core/lib/multivm/src/versions/era_vm/vm.rs | 54 +++----------------- core/tests/vm-benchmark/benches/criterion.rs | 9 ++-- 2 files changed, 14 insertions(+), 49 deletions(-) diff --git a/core/lib/multivm/src/versions/era_vm/vm.rs b/core/lib/multivm/src/versions/era_vm/vm.rs index 753ffa8b5b34..f949eac952f3 100644 --- a/core/lib/multivm/src/versions/era_vm/vm.rs +++ b/core/lib/multivm/src/versions/era_vm/vm.rs @@ -4,6 +4,7 @@ use era_vm::{ rollbacks::Rollbackable, store::StorageKey as EraStorageKey, value::FatPointer, vm::ExecutionOutput, EraVM, Execution, }; +use itertools::Itertools; use zksync_state::{ReadStorage, StoragePtr}; use zksync_types::{ event::{ @@ -19,8 +20,8 @@ use zksync_types::{ BYTES_PER_ENUMERATION_INDEX, }, AccountTreeId, StorageKey, StorageLog, StorageLogKind, StorageLogWithPreviousValue, - Transaction, BOOTLOADER_ADDRESS, H160, KNOWN_CODES_STORAGE_ADDRESS, L2_BASE_TOKEN_ADDRESS, - U256, + Transaction, BOOTLOADER_ADDRESS, H160, H256, KNOWN_CODES_STORAGE_ADDRESS, + L2_BASE_TOKEN_ADDRESS, U256, }; use zksync_utils::{ bytecode::{hash_bytecode, CompressedBytecodeInfo}, @@ -42,9 +43,9 @@ use super::{ use crate::{ era_vm::{bytecode::compress_bytecodes, transaction_data::TransactionData}, interface::{ + tracer::{TracerExecutionStatus, TracerExecutionStopReason}, BytecodeCompressionError, Halt, TxRevertReason, VmFactory, VmInterface, VmInterfaceHistoryEnabled, VmRevertReason, - tracer::{TracerExecutionStatus, TracerExecutionStopReason} }, vm_latest::{ constants::{ @@ -168,7 +169,7 @@ impl Vm { let result = loop { let output = self .inner - .run_program_with_custom_bytecode_and_tracer(tracer,&mut self.world); + .run_program_with_custom_bytecode_and_tracer(tracer, &mut self.world); let status = tracer.after_vm_run(self, output.clone()); let (hook, hook_params) = match output { ExecutionOutput::Ok(output) => break ExecutionResult::Success { output }, @@ -267,8 +268,8 @@ impl Vm { break ExecutionResult::Success { output: vec![] }; } } + _ => {} } - _ => {} } }; tracer.after_bootloader_execution(self); @@ -339,46 +340,6 @@ impl Vm { } } - fn get_storage_diff(&mut self) -> Vec { - self.inner - .state - .get_storage_changes(&mut self.world) - .iter() - .filter_map(|(storage_key, initial_value, value)| { - let address = storage_key.address; - - if address == L1_MESSENGER_ADDRESS { - return None; - } - - let key = storage_key.key; - - let diff = StateDiffRecord { - key, - address, - derived_key: - zk_evm_1_5_0::aux_structures::LogQuery::derive_final_address_for_params( - &address, &key, - ), - enumeration_index: self - .storage - .borrow_mut() - .get_enumeration_index(&StorageKey::new( - AccountTreeId::new(address), - u256_to_h256(key), - )) - .unwrap_or_default(), - initial_value: initial_value.unwrap_or_default(), - final_value: value.clone(), - }; - - Some(diff) - }) - // the compressor expects the storage diff to be sorted - .sorted_by(|a, b| a.address.cmp(&b.address).then_with(|| a.key.cmp(&b.key))) - .collect() - } - pub fn push_transaction_inner(&mut self, tx: Transaction, refund: u64, with_compression: bool) { let tx: TransactionData = tx.into(); let overhead = tx.overhead_gas(); @@ -428,6 +389,7 @@ impl Vm { } } +impl Vm { pub fn inspect_inner( &mut self, tracer: TracerDispatcher, @@ -613,7 +575,7 @@ impl VmInterface for Vm { VmExecutionResultAndLogs, ) { self.push_transaction_inner(tx, 0, with_compression); - let result = self.inspect((), VmExecutionMode::OneTx); + let result = self.inspect(tracer, VmExecutionMode::OneTx); let compression_result = if self.has_unpublished_bytecodes() { Err(BytecodeCompressionError::BytecodeCompressionFailed) diff --git a/core/tests/vm-benchmark/benches/criterion.rs b/core/tests/vm-benchmark/benches/criterion.rs index c1b660823b84..5021ec62563c 100644 --- a/core/tests/vm-benchmark/benches/criterion.rs +++ b/core/tests/vm-benchmark/benches/criterion.rs @@ -7,8 +7,8 @@ use criterion::{ use zksync_types::Transaction; use zksync_vm_benchmark_harness::{ cut_to_allowed_bytecode_size, get_deploy_tx, get_heavy_load_test_tx, get_load_test_deploy_tx, - get_load_test_tx, get_realistic_load_test_tx, BenchmarkingVm, BenchmarkingVmFactory, Fast, Lambda, - Legacy, LoadTestParams, + get_load_test_tx, get_realistic_load_test_tx, BenchmarkingVm, BenchmarkingVmFactory, Fast, + Lambda, Legacy, LoadTestParams, }; const SAMPLE_SIZE: usize = 20; @@ -20,7 +20,10 @@ fn benches_in_folder(c: &mut Criter .sample_size(SAMPLE_SIZE) .measurement_time(Duration::from_secs(10)); - let benches = format!("{}/core/tests/vm-benchmark/deployment_benchmarks", ZKSYNC_HOME); + let benches = format!( + "{}/core/tests/vm-benchmark/deployment_benchmarks", + ZKSYNC_HOME + ); for path in std::fs::read_dir(&benches).unwrap() { let path = path.unwrap().path(); From 593c7ea90f1fb9e5e3e12c5349413eee3e4d88dd Mon Sep 17 00:00:00 2001 From: Gianbelinche <39842759+gianbelinche@users.noreply.github.com> Date: Mon, 2 Sep 2024 16:53:33 -0300 Subject: [PATCH 30/32] Make server compile --- core/lib/multivm/src/glue/tracers/mod.rs | 21 ++++++++++++++++-- .../multivm/src/tracers/multivm_dispatcher.rs | 12 ++++++++-- .../tracers/storage_invocation/era_vm/mod.rs | 22 +++++++++++++++++++ .../src/tracers/storage_invocation/mod.rs | 1 + .../src/tracers/validator/era_vm/mod.rs | 22 +++++++++++++++++++ core/lib/multivm/src/tracers/validator/mod.rs | 1 + .../src/versions/era_vm/tracers/manager.rs | 2 +- .../versions/era_vm/tracers/pubdata_tracer.rs | 4 ++-- .../versions/era_vm/tracers/refunds_tracer.rs | 2 +- core/lib/multivm/src/versions/era_vm/vm.rs | 4 ++-- core/lib/multivm/src/vm_instance.rs | 6 +++-- 11 files changed, 85 insertions(+), 12 deletions(-) create mode 100644 core/lib/multivm/src/tracers/storage_invocation/era_vm/mod.rs create mode 100644 core/lib/multivm/src/tracers/validator/era_vm/mod.rs diff --git a/core/lib/multivm/src/glue/tracers/mod.rs b/core/lib/multivm/src/glue/tracers/mod.rs index 7aa792ef1f71..6bae150dec58 100644 --- a/core/lib/multivm/src/glue/tracers/mod.rs +++ b/core/lib/multivm/src/glue/tracers/mod.rs @@ -30,7 +30,7 @@ //! - Add this trait as a trait bound for `T` in `MultiVMTracer` implementation. //! - Implement the trait for `T` with a bound to `VmTracer` for a specific version. -use zksync_state::WriteStorage; +use zksync_state::{ImmutableStorageView, WriteStorage}; use crate::{tracers::old_tracers::OldTracers, HistoryMode}; @@ -44,6 +44,7 @@ pub trait MultiVMTracer: + IntoVm1_4_1IntegrationTracer + IntoVm1_4_2IntegrationTracer + IntoOldVmTracer + + IntoEraVmTracer { fn into_tracer_pointer(self) -> MultiVmTracerPointer where @@ -57,6 +58,10 @@ pub trait IntoLatestTracer { fn latest(&self) -> crate::vm_latest::TracerPointer; } +pub trait IntoEraVmTracer { + fn era_vm(&self) -> Box>; +} + pub trait IntoVmVirtualBlocksTracer { fn vm_virtual_blocks( &self, @@ -106,6 +111,17 @@ where } } +impl IntoEraVmTracer for T +where + S: WriteStorage, + H: HistoryMode, + T: crate::era_vm::tracers::traits::VmTracer + Clone + 'static, +{ + fn era_vm(&self) -> Box> { + Box::new(self.clone()) + } +} + impl IntoVmVirtualBlocksTracer for T where S: WriteStorage, @@ -180,6 +196,7 @@ where + IntoVmBoojumIntegrationTracer + IntoVm1_4_1IntegrationTracer + IntoVm1_4_2IntegrationTracer - + IntoOldVmTracer, + + IntoOldVmTracer + + IntoEraVmTracer, { } diff --git a/core/lib/multivm/src/tracers/multivm_dispatcher.rs b/core/lib/multivm/src/tracers/multivm_dispatcher.rs index 5b0d36b5e793..53cdab4d4737 100644 --- a/core/lib/multivm/src/tracers/multivm_dispatcher.rs +++ b/core/lib/multivm/src/tracers/multivm_dispatcher.rs @@ -1,6 +1,6 @@ -use zksync_state::WriteStorage; +use zksync_state::{ImmutableStorageView, StorageView, WriteStorage}; -use crate::{tracers::old_tracers, HistoryMode, MultiVmTracerPointer}; +use crate::{tracers::old_tracers, HistoryMode, MultiVMTracer, MultiVmTracerPointer}; /// Tracer dispatcher is a tracer that can dispatch calls to multiple tracers. pub struct TracerDispatcher { @@ -37,6 +37,14 @@ impl From> } } +impl From> + for crate::era_vm::tracers::dispatcher::TracerDispatcher +{ + fn from(value: TracerDispatcher) -> Self { + Self::new(value.tracers.into_iter().map(|x| x.era_vm()).collect()) + } +} + impl From> for crate::vm_boojum_integration::TracerDispatcher { diff --git a/core/lib/multivm/src/tracers/storage_invocation/era_vm/mod.rs b/core/lib/multivm/src/tracers/storage_invocation/era_vm/mod.rs new file mode 100644 index 000000000000..e15c53bc4061 --- /dev/null +++ b/core/lib/multivm/src/tracers/storage_invocation/era_vm/mod.rs @@ -0,0 +1,22 @@ +use era_vm::{ + opcode::{RetOpcode, Variant}, + value::FatPointer, + Execution, Opcode, +}; +use zksync_state::ReadStorage; +use zksync_types::{ + vm_trace::{Call, CallType}, + zk_evm_types::FarCallOpcode, + CONTRACT_DEPLOYER_ADDRESS, U256, +}; + +use super::StorageInvocations; +use crate::{ + era_vm::tracers::traits::{Tracer, VmTracer}, + interface::VmRevertReason, +}; + +//TODO: Implement the Tracer trait for StorageInvocations +impl Tracer for StorageInvocations {} + +impl VmTracer for StorageInvocations {} diff --git a/core/lib/multivm/src/tracers/storage_invocation/mod.rs b/core/lib/multivm/src/tracers/storage_invocation/mod.rs index 8c9677ba8d7c..49ca28e2f961 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 era_vm; pub mod vm_1_4_1; pub mod vm_1_4_2; pub mod vm_boojum_integration; diff --git a/core/lib/multivm/src/tracers/validator/era_vm/mod.rs b/core/lib/multivm/src/tracers/validator/era_vm/mod.rs new file mode 100644 index 000000000000..806fb641ee48 --- /dev/null +++ b/core/lib/multivm/src/tracers/validator/era_vm/mod.rs @@ -0,0 +1,22 @@ +use era_vm::{ + opcode::{RetOpcode, Variant}, + value::FatPointer, + Execution, Opcode, +}; +use zksync_state::ReadStorage; +use zksync_types::{ + vm_trace::{Call, CallType}, + zk_evm_types::FarCallOpcode, + CONTRACT_DEPLOYER_ADDRESS, U256, +}; + +use super::ValidationTracer; +use crate::{ + era_vm::tracers::traits::{Tracer, VmTracer}, + interface::VmRevertReason, +}; + +//TODO: Implement the Tracer trait for ValidationTracer +impl Tracer for ValidationTracer {} + +impl VmTracer for ValidationTracer {} diff --git a/core/lib/multivm/src/tracers/validator/mod.rs b/core/lib/multivm/src/tracers/validator/mod.rs index 635915f95278..daf93c3fb8f9 100644 --- a/core/lib/multivm/src/tracers/validator/mod.rs +++ b/core/lib/multivm/src/tracers/validator/mod.rs @@ -18,6 +18,7 @@ use crate::{ tracers::validator::types::{NewTrustedValidationItems, ValidationTracerMode}, }; +mod era_vm; mod types; mod vm_1_4_1; mod vm_1_4_2; diff --git a/core/lib/multivm/src/versions/era_vm/tracers/manager.rs b/core/lib/multivm/src/versions/era_vm/tracers/manager.rs index 5ffc6ea58bd6..c08cc5d4b18a 100644 --- a/core/lib/multivm/src/versions/era_vm/tracers/manager.rs +++ b/core/lib/multivm/src/versions/era_vm/tracers/manager.rs @@ -117,7 +117,7 @@ impl Tracer for VmTracerManager { } } -impl VmTracer for VmTracerManager { +impl VmTracer for VmTracerManager { fn before_bootloader_execution(&mut self, state: &mut Vm) { // Call the dispatcher to handle all the tracers added to it self.dispatcher.before_bootloader_execution(state); diff --git a/core/lib/multivm/src/versions/era_vm/tracers/pubdata_tracer.rs b/core/lib/multivm/src/versions/era_vm/tracers/pubdata_tracer.rs index ac1098256a57..f907dcfb16db 100644 --- a/core/lib/multivm/src/versions/era_vm/tracers/pubdata_tracer.rs +++ b/core/lib/multivm/src/versions/era_vm/tracers/pubdata_tracer.rs @@ -55,7 +55,7 @@ impl PubdataTracer { fn get_storage_diff(&mut self, vm: &mut Vm) -> Vec { vm.inner .state - .get_storage_changes() + .get_storage_changes(&mut vm.world) .iter() .filter_map(|(storage_key, initial_value, value)| { let address = storage_key.address; @@ -95,7 +95,7 @@ impl PubdataTracer { impl Tracer for PubdataTracer {} -impl VmTracer for PubdataTracer { +impl VmTracer for PubdataTracer { fn before_bootloader_execution(&mut self, vm: &mut super::traits::Vm) { self.pubdata_before_run = vm.inner.state.pubdata(); } diff --git a/core/lib/multivm/src/versions/era_vm/tracers/refunds_tracer.rs b/core/lib/multivm/src/versions/era_vm/tracers/refunds_tracer.rs index ee87e1ce7b90..c436b7342d4d 100644 --- a/core/lib/multivm/src/versions/era_vm/tracers/refunds_tracer.rs +++ b/core/lib/multivm/src/versions/era_vm/tracers/refunds_tracer.rs @@ -40,7 +40,7 @@ impl Into for RefundsTracer { impl Tracer for RefundsTracer {} -impl VmTracer for RefundsTracer { +impl VmTracer for RefundsTracer { fn before_bootloader_execution(&mut self, vm: &mut super::traits::Vm) { self.pubdata_before = vm.inner.state.pubdata() as u32; } diff --git a/core/lib/multivm/src/versions/era_vm/vm.rs b/core/lib/multivm/src/versions/era_vm/vm.rs index f949eac952f3..56283b71e7a3 100644 --- a/core/lib/multivm/src/versions/era_vm/vm.rs +++ b/core/lib/multivm/src/versions/era_vm/vm.rs @@ -389,7 +389,7 @@ impl Vm { } } -impl Vm { +impl Vm { pub fn inspect_inner( &mut self, tracer: TracerDispatcher, @@ -502,7 +502,7 @@ impl Vm { } } -impl VmInterface for Vm { +impl VmInterface for Vm { type TracerDispatcher = TracerDispatcher; fn push_transaction(&mut self, tx: Transaction) { diff --git a/core/lib/multivm/src/vm_instance.rs b/core/lib/multivm/src/vm_instance.rs index b20f82f0d1e9..8fb96be01263 100644 --- a/core/lib/multivm/src/vm_instance.rs +++ b/core/lib/multivm/src/vm_instance.rs @@ -1,3 +1,5 @@ +use std::{cell::RefCell, rc::Rc}; + use zksync_state::{ImmutableStorageView, ReadStorage, StoragePtr, StorageView}; use zksync_types::vm::{FastVmMode, VmVersion}; use zksync_utils::bytecode::CompressedBytecodeInfo; @@ -263,8 +265,8 @@ impl VmInstance { VmVersion::Vm1_5_0IncreasedBootloaderMemory => match mode { FastVmMode::Old => Self::new(l1_batch_env, system_env, storage_view), FastVmMode::New => { - // let storage = ImmutableStorageView::new(storage_view); - // Self::VmFast(crate::vm_fast::Vm::new(l1_batch_env, system_env, storage)) + //let storage = ImmutableStorageView::new(storage_view); + //Self::VmFast(crate::vm_fast::Vm::new(l1_batch_env, system_env, storage)) Self::LambdaVm(crate::era_vm::vm::Vm::new( l1_batch_env, From 0e0c2577f8e5efb57f7bff3fd9bc9657c7cfbb81 Mon Sep 17 00:00:00 2001 From: Gianbelinche <39842759+gianbelinche@users.noreply.github.com> Date: Mon, 2 Sep 2024 17:46:38 -0300 Subject: [PATCH 31/32] Increase timeout --- core/tests/ts-integration/package.json | 2 +- core/tests/ts-integration/tests/api/web3.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/tests/ts-integration/package.json b/core/tests/ts-integration/package.json index 03bd84bb3f48..9fd4d32cbd43 100644 --- a/core/tests/ts-integration/package.json +++ b/core/tests/ts-integration/package.json @@ -4,7 +4,7 @@ "license": "MIT", "private": true, "scripts": { - "test": "zk f jest --forceExit --testTimeout 60000", + "test": "zk f jest --forceExit --testTimeout 600000", "long-running-test": "zk f jest", "fee-test": "RUN_FEE_TEST=1 zk f jest -- fees.test.ts", "api-test": "zk f jest -- api/web3.test.ts api/debug.test.ts", diff --git a/core/tests/ts-integration/tests/api/web3.test.ts b/core/tests/ts-integration/tests/api/web3.test.ts index 569321d548ce..9a22c89cd43a 100644 --- a/core/tests/ts-integration/tests/api/web3.test.ts +++ b/core/tests/ts-integration/tests/api/web3.test.ts @@ -1232,7 +1232,7 @@ describe('web3 API compatibility tests', () => { * @param iterationStep The number of times this function has been called. */ async function tryWait(iterationStep: number) { - const maxWaitTimeMs = 100_000; // 100 seconds + const maxWaitTimeMs = 1_000_000; // 1000 seconds const maxRetries = maxWaitTimeMs / alice.provider.pollingInterval; await zksync.utils.sleep(alice.provider.pollingInterval); if (iterationStep >= maxRetries) { From 87dd05ca77fca5b343c5935b5b033816d9674b4a Mon Sep 17 00:00:00 2001 From: Gianbelinche <39842759+gianbelinche@users.noreply.github.com> Date: Tue, 3 Sep 2024 16:07:06 -0300 Subject: [PATCH 32/32] Adress PR comments --- core/lib/multivm/src/glue/tracers/mod.rs | 4 +-- .../src/tracers/call_tracer/era_vm/mod.rs | 28 ++++++++----------- .../multivm/src/tracers/multivm_dispatcher.rs | 2 +- .../versions/era_vm/tracers/pubdata_tracer.rs | 1 - 4 files changed, 15 insertions(+), 20 deletions(-) diff --git a/core/lib/multivm/src/glue/tracers/mod.rs b/core/lib/multivm/src/glue/tracers/mod.rs index 6bae150dec58..432d569979b8 100644 --- a/core/lib/multivm/src/glue/tracers/mod.rs +++ b/core/lib/multivm/src/glue/tracers/mod.rs @@ -59,7 +59,7 @@ pub trait IntoLatestTracer { } pub trait IntoEraVmTracer { - fn era_vm(&self) -> Box>; + fn into_era_vm(&self) -> Box>; } pub trait IntoVmVirtualBlocksTracer { @@ -117,7 +117,7 @@ where H: HistoryMode, T: crate::era_vm::tracers::traits::VmTracer + Clone + 'static, { - fn era_vm(&self) -> Box> { + fn into_era_vm(&self) -> Box> { Box::new(self.clone()) } } diff --git a/core/lib/multivm/src/tracers/call_tracer/era_vm/mod.rs b/core/lib/multivm/src/tracers/call_tracer/era_vm/mod.rs index cfb07b63f53a..31d06610a4ec 100644 --- a/core/lib/multivm/src/tracers/call_tracer/era_vm/mod.rs +++ b/core/lib/multivm/src/tracers/call_tracer/era_vm/mod.rs @@ -36,12 +36,12 @@ impl Tracer for CallTracer { .map(|call| call.frame.gas_left.0.saturating_add(current_ergs)) .unwrap_or(current_ergs) as u64; - // we need to to this cast because `Call` uses another library + // we need to do this cast because `Call` uses another library let far_call_variant = match far_call as u8 { 0 => FarCallOpcode::Normal, 1 => FarCallOpcode::Delegate, 2 => FarCallOpcode::Mimic, - _ => unreachable!(), + _ => FarCallOpcode::Normal, // Should never happen }; let mut current_call = Call { @@ -94,7 +94,7 @@ impl CallTracer { CallType::Call(far_call) } } else { - unreachable!() + return; }; let calldata = if current.heap_id == 0 || current.frame.gas_left.0 == 0 { vec![] @@ -127,22 +127,18 @@ impl CallTracer { let fat_data_pointer = execution.get_register(1); // 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::decode(fat_data_pointer.value); - if fat_data_pointer.len == 0 && fat_data_pointer.offset == 0 { - Some( - execution + let output = match fat_data_pointer.is_pointer { + true => { + let fat_data_pointer = FatPointer::decode(fat_data_pointer.value); + match (fat_data_pointer.len, fat_data_pointer.offset) { + (0, 0) => execution .heaps .get(fat_data_pointer.page) - .unwrap() - .read_unaligned_from_pointer(&fat_data_pointer) - .unwrap(), - ) - } else { - None + .and_then(|ptr| ptr.read_unaligned_from_pointer(&fat_data_pointer).ok()), + _ => None, + } } - } else { - None + _ => None, }; match ret_opcode { diff --git a/core/lib/multivm/src/tracers/multivm_dispatcher.rs b/core/lib/multivm/src/tracers/multivm_dispatcher.rs index 53cdab4d4737..e0126f18ed8c 100644 --- a/core/lib/multivm/src/tracers/multivm_dispatcher.rs +++ b/core/lib/multivm/src/tracers/multivm_dispatcher.rs @@ -41,7 +41,7 @@ impl From> for crate::era_vm::tracers::dispatcher::TracerDispatcher { fn from(value: TracerDispatcher) -> Self { - Self::new(value.tracers.into_iter().map(|x| x.era_vm()).collect()) + Self::new(value.tracers.into_iter().map(|x| x.into_era_vm()).collect()) } } diff --git a/core/lib/multivm/src/versions/era_vm/tracers/pubdata_tracer.rs b/core/lib/multivm/src/versions/era_vm/tracers/pubdata_tracer.rs index f907dcfb16db..0041a10da526 100644 --- a/core/lib/multivm/src/versions/era_vm/tracers/pubdata_tracer.rs +++ b/core/lib/multivm/src/versions/era_vm/tracers/pubdata_tracer.rs @@ -75,7 +75,6 @@ impl PubdataTracer { ), enumeration_index: vm .storage - .borrow_mut() .get_enumeration_index(&StorageKey::new( AccountTreeId::new(address), u256_to_h256(key),