Skip to content

Commit

Permalink
feat(vm): Introduce new way of returning from the tracer #2569 (#116)
Browse files Browse the repository at this point in the history
… private at a4d5ca6625210471d9de66d61e7c9a41a336afb8

# What ❔

<!-- What are the changes this PR brings about? -->
<!-- Example: This PR adds a PR template to the repo. -->
<!-- (For bigger PRs adding more context is appreciated) -->

## Why ❔

<!-- Why are these changes done? What goal do they contribute to? What
are the principles behind them? -->
<!-- Example: PR templates ensure PR reviewers, observers, and future
iterators are in context about the evolution of repos. -->

## Checklist

<!-- Check your PR fulfills the following items. -->
<!-- For draft PRs check the boxes as you complete them. -->

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

---------

Signed-off-by: Danil <deniallugo@gmail.com>
  • Loading branch information
Deniallugo authored Oct 3, 2023
1 parent 5e61bdc commit cf44a49
Show file tree
Hide file tree
Showing 10 changed files with 112 additions and 37 deletions.
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

0 comments on commit cf44a49

Please sign in to comment.