Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat(vm): Introduce new way of returning from the tracer #2569 #116

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions core/lib/vm/src/errors/halt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ pub enum Halt {
UnexpectedVMBehavior(String),
// Bootloader is out of gas.
BootloaderOutOfGas,
// Validation step is out of gas
ValidationOutOfGas,
// Transaction has a too big gas limit and will not be executed by the server.
TooBigGasLimit,
// The bootloader did not have enough gas to start the transaction in the first place
Expand All @@ -37,6 +39,7 @@ pub enum Halt {
// Failed to publish information about the batch and the L2 block onto L1
FailedToAppendTransactionToL2Block(String),
VMPanic,
TracerCustom(String),
}

impl Display for Halt {
Expand Down Expand Up @@ -102,6 +105,12 @@ impl Display for Halt {
reason
)
}
Halt::TracerCustom(reason) => {
write!(f, "Tracer aborted execution: {}", reason)
}
Halt::ValidationOutOfGas => {
write!(f, "Validation run out of gas")
}
}
}
}
10 changes: 6 additions & 4 deletions core/lib/vm/src/implementation/execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ use crate::old_vm::{
utils::{vm_may_have_ended_inner, VmExecutionResult},
};
use crate::tracers::{
traits::{BoxedTracer, ExecutionEndTracer, ExecutionProcessing, VmTracer},
traits::{
BoxedTracer, ExecutionEndTracer, ExecutionProcessing, TracerExecutionStatus, VmTracer,
},
DefaultExecutionTracer, RefundsTracer,
};
use crate::types::{inputs::VmExecutionMode, outputs::VmExecutionResultAndLogs};
Expand Down Expand Up @@ -104,11 +106,11 @@ impl<S: WriteStorage, H: HistoryMode> Vm<S, H> {
break VmExecutionStopReason::VmFinished;
}

if tracer.should_stop_execution() {
break VmExecutionStopReason::TracerRequestedStop;
if let TracerExecutionStatus::Stop(reason) = tracer.should_stop_execution() {
break VmExecutionStopReason::TracerRequestedStop(reason);
}
};
tracer.after_vm_execution(&mut self.state, &self.bootloader_state, result);
tracer.after_vm_execution(&mut self.state, &self.bootloader_state, result.clone());
result
}

Expand Down
42 changes: 30 additions & 12 deletions core/lib/vm/src/tracers/default_tracers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,17 @@ use crate::bootloader_state::BootloaderState;
use crate::constants::BOOTLOADER_HEAP_PAGE;
use crate::old_vm::history_recorder::HistoryMode;
use crate::old_vm::memory::SimpleMemory;
use crate::tracers::traits::{DynTracer, ExecutionEndTracer, ExecutionProcessing, VmTracer};
use crate::tracers::traits::{
DynTracer, ExecutionEndTracer, ExecutionProcessing, TracerExecutionStatus,
TracerExecutionStopReason, VmTracer,
};
use crate::tracers::utils::{
computational_gas_price, gas_spent_on_bytecodes_and_long_messages_this_opcode,
print_debug_if_needed, VmHook,
};
use crate::tracers::ResultTracer;
use crate::types::internals::ZkSyncVmState;
use crate::{VmExecutionMode, VmExecutionStopReason};
use crate::{Halt, VmExecutionMode, VmExecutionStopReason};

/// Default tracer for the VM. It manages the other tracers execution and stop the vm when needed.
pub(crate) struct DefaultExecutionTracer<S, H: HistoryMode> {
Expand Down Expand Up @@ -141,17 +144,32 @@ impl<S, H: HistoryMode> Tracer for DefaultExecutionTracer<S, H> {
}

impl<S: WriteStorage, H: HistoryMode> ExecutionEndTracer<H> for DefaultExecutionTracer<S, H> {
fn should_stop_execution(&self) -> bool {
let mut should_stop = match self.execution_mode {
VmExecutionMode::OneTx => self.tx_has_been_processed(),
VmExecutionMode::Batch => false,
VmExecutionMode::Bootloader => self.ret_from_the_bootloader == Some(RetOpcode::Ok),
fn should_stop_execution(&self) -> TracerExecutionStatus {
match self.execution_mode {
VmExecutionMode::OneTx => {
if self.tx_has_been_processed() {
return TracerExecutionStatus::Stop(TracerExecutionStopReason::Finish);
}
}
VmExecutionMode::Bootloader => {
if self.ret_from_the_bootloader == Some(RetOpcode::Ok) {
return TracerExecutionStatus::Stop(TracerExecutionStopReason::Finish);
}
}
VmExecutionMode::Batch => {}
};
should_stop = should_stop || self.validation_run_out_of_gas();
if self.validation_run_out_of_gas() {
return TracerExecutionStatus::Stop(TracerExecutionStopReason::Abort(
Halt::ValidationOutOfGas,
));
}
for tracer in self.custom_tracers.iter() {
should_stop = should_stop || tracer.should_stop_execution();
let reason = tracer.should_stop_execution();
if TracerExecutionStatus::Continue != reason {
return reason;
}
}
should_stop
TracerExecutionStatus::Continue
}
}

Expand Down Expand Up @@ -244,9 +262,9 @@ impl<S: WriteStorage, H: HistoryMode> ExecutionProcessing<S, H> for DefaultExecu
stop_reason: VmExecutionStopReason,
) {
self.result_tracer
.after_vm_execution(state, bootloader_state, stop_reason);
.after_vm_execution(state, bootloader_state, stop_reason.clone());
for processor in self.custom_tracers.iter_mut() {
processor.after_vm_execution(state, bootloader_state, stop_reason);
processor.after_vm_execution(state, bootloader_state, stop_reason.clone());
}
}
}
Expand Down
13 changes: 11 additions & 2 deletions core/lib/vm/src/tracers/result_tracer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ use crate::types::{
};

use crate::constants::{BOOTLOADER_HEAP_PAGE, RESULT_SUCCESS_FIRST_SLOT};
use crate::tracers::traits::TracerExecutionStopReason;
use crate::{Halt, TxRevertReason};
use crate::{VmExecutionMode, VmExecutionStopReason};

Expand Down Expand Up @@ -120,9 +121,11 @@ impl<S: WriteStorage, H: HistoryMode> ExecutionProcessing<S, H> for ResultTracer
// One of the tracers above has requested to stop the execution.
// If it was the correct stop we already have the result,
// otherwise it can be out of gas error
VmExecutionStopReason::TracerRequestedStop => {
VmExecutionStopReason::TracerRequestedStop(reason) => {
match self.execution_mode {
VmExecutionMode::OneTx => self.vm_stopped_execution(state, bootloader_state),
VmExecutionMode::OneTx => {
self.vm_stopped_execution(state, bootloader_state, reason)
}
VmExecutionMode::Batch => self.vm_finished_execution(state),
VmExecutionMode::Bootloader => self.vm_finished_execution(state),
};
Expand Down Expand Up @@ -188,7 +191,13 @@ impl ResultTracer {
&mut self,
state: &ZkSyncVmState<S, H>,
bootloader_state: &BootloaderState,
reason: TracerExecutionStopReason,
) {
if let TracerExecutionStopReason::Abort(halt) = reason {
self.result = Some(Result::Halt { reason: halt });
return;
}

if self.bootloader_out_of_gas {
self.result = Some(Result::Halt {
reason: Halt::BootloaderOutOfGas,
Expand Down
15 changes: 12 additions & 3 deletions core/lib/vm/src/tracers/storage_invocations.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
use crate::bootloader_state::BootloaderState;
use crate::old_vm::history_recorder::HistoryMode;
use crate::tracers::traits::{DynTracer, ExecutionEndTracer, ExecutionProcessing, VmTracer};
use crate::tracers::traits::{
DynTracer, ExecutionEndTracer, ExecutionProcessing, TracerExecutionStatus,
TracerExecutionStopReason, VmTracer,
};
use crate::types::internals::ZkSyncVmState;
use crate::Halt;
use zksync_state::WriteStorage;

#[derive(Debug, Default, Clone)]
Expand All @@ -21,8 +25,13 @@ impl StorageInvocations {
impl<S, H: HistoryMode> DynTracer<S, H> for StorageInvocations {}

impl<H: HistoryMode> ExecutionEndTracer<H> for StorageInvocations {
fn should_stop_execution(&self) -> bool {
self.current >= self.limit
fn should_stop_execution(&self) -> TracerExecutionStatus {
if self.current >= self.limit {
return TracerExecutionStatus::Stop(TracerExecutionStopReason::Abort(
Halt::TracerCustom("Storage invocations limit reached".to_string()),
));
}
TracerExecutionStatus::Continue
}
}

Expand Down
30 changes: 21 additions & 9 deletions core/lib/vm/src/tracers/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::old_vm::history_recorder::HistoryMode;
use crate::old_vm::memory::SimpleMemory;
use crate::types::internals::ZkSyncVmState;
use crate::types::outputs::VmExecutionResultAndLogs;
use crate::VmExecutionStopReason;
use crate::{Halt, VmExecutionStopReason};

/// Run tracer for collecting data during the vm execution cycles
pub trait ExecutionProcessing<S: WriteStorage, H: HistoryMode>:
Expand All @@ -31,14 +31,6 @@ pub trait ExecutionProcessing<S: WriteStorage, H: HistoryMode>:
}
}

/// Stop the vm execution if the tracer conditions are met
pub trait ExecutionEndTracer<H: HistoryMode> {
// Returns whether the vm execution should stop.
fn should_stop_execution(&self) -> bool {
false
}
}

/// Version of zk_evm::Tracer suitable for dynamic dispatch.
pub trait DynTracer<S, H: HistoryMode> {
fn before_decoding(&mut self, _state: VmLocalStateData<'_>, _memory: &SimpleMemory<H>) {}
Expand Down Expand Up @@ -83,3 +75,23 @@ impl<S: WriteStorage, H: HistoryMode, T: VmTracer<S, H> + 'static> BoxedTracer<S
Box::new(self)
}
}

#[derive(Debug, Clone, PartialEq)]
pub enum TracerExecutionStopReason {
Finish,
Abort(Halt),
}

#[derive(Debug, Clone, PartialEq)]
pub enum TracerExecutionStatus {
Continue,
Stop(TracerExecutionStopReason),
}

/// Stop the vm execution if the tracer conditions are met
pub trait ExecutionEndTracer<H: HistoryMode> {
// Returns whether the vm execution should stop.
fn should_stop_execution(&self) -> TracerExecutionStatus {
TracerExecutionStatus::Continue
}
}
5 changes: 3 additions & 2 deletions core/lib/vm/src/tracers/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use crate::constants::{
use crate::old_vm::history_recorder::HistoryMode;
use crate::old_vm::memory::SimpleMemory;
use crate::old_vm::utils::{aux_heap_page_from_base, heap_page_from_base};
use crate::tracers::traits::TracerExecutionStopReason;

#[derive(Clone, Debug, Copy)]
pub(crate) enum VmHook {
Expand Down Expand Up @@ -217,8 +218,8 @@ pub(crate) fn get_vm_hook_params<H: HistoryMode>(memory: &SimpleMemory<H>) -> Ve
)
}

#[derive(Debug, Clone, Copy, PartialEq)]
#[derive(Debug, Clone, PartialEq)]
pub enum VmExecutionStopReason {
VmFinished,
TracerRequestedStop,
TracerRequestedStop(TracerExecutionStopReason),
}
19 changes: 15 additions & 4 deletions core/lib/vm/src/tracers/validation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ use zksync_utils::{

use crate::old_vm::history_recorder::HistoryMode;
use crate::old_vm::memory::SimpleMemory;
use crate::tracers::traits::{DynTracer, ExecutionEndTracer, ExecutionProcessing, VmTracer};
use crate::tracers::traits::{
DynTracer, ExecutionEndTracer, ExecutionProcessing, TracerExecutionStatus,
TracerExecutionStopReason, VmTracer,
};
use crate::tracers::utils::{
computational_gas_price, get_calldata_page_via_abi, print_debug_if_needed, VmHook,
};
Expand All @@ -38,7 +41,7 @@ pub use params::ValidationTracerParams;
use types::NewTrustedValidationItems;
use types::ValidationTracerMode;

use crate::VmExecutionResultAndLogs;
use crate::{Halt, VmExecutionResultAndLogs};

/// Tracer that is used to ensure that the validation adheres to all the rules
/// to prevent DDoS attacks on the server.
Expand Down Expand Up @@ -341,8 +344,16 @@ impl<S: WriteStorage, H: HistoryMode> DynTracer<S, H> for ValidationTracer<H> {
}

impl<H: HistoryMode> ExecutionEndTracer<H> for ValidationTracer<H> {
fn should_stop_execution(&self) -> bool {
self.should_stop_execution || self.result.get().is_some()
fn should_stop_execution(&self) -> TracerExecutionStatus {
if self.should_stop_execution {
return TracerExecutionStatus::Stop(TracerExecutionStopReason::Finish);
}
if let Some(result) = self.result.get() {
return TracerExecutionStatus::Stop(TracerExecutionStopReason::Abort(
Halt::TracerCustom(format!("Validation error: {:#?}", result)),
));
}
TracerExecutionStatus::Continue
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ impl From<Halt> for SandboxExecutionError {
Halt::FailedToAppendTransactionToL2Block(reason) => {
SandboxExecutionError::Revert(reason, vec![])
}
Halt::TracerCustom(reason) => SandboxExecutionError::Revert(reason, vec![]),
Halt::ValidationOutOfGas => Self::AccountValidationFailed(
"The validation of the transaction ran out of gas".to_string(),
),
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,8 @@ impl TxSharedArgs {
]);

let result = match (result.result, validation_result.get()) {
(ExecutionResult::Halt { reason }, _) => Err(ValidationError::FailedTx(reason)),
(_, Some(err)) => Err(ValidationError::ViolatedRule(err.clone())),
(ExecutionResult::Halt { reason }, _) => Err(ValidationError::FailedTx(reason)),
(_, None) => Ok(()),
};

Expand Down