use std::sync::Arc;
use once_cell::sync::OnceCell;
use vm::{
constants::BOOTLOADER_HEAP_PAGE, BootloaderState, DynTracer, ExecutionEndTracer,
ExecutionProcessing, HistoryMode, SimpleMemory, VmExecutionResultAndLogs,
VmExecutionStopReason, VmTracer, ZkSyncVmState,
};
use zksync_basic_types::U256;
use zksync_state::WriteStorage;
const DEBUG_START_SENTINEL: u64 = 1337;
const MAX_MEMORY_BYTES: usize = usize::pow(2, 24);
const MAX_TRANSACTIONS: usize = 1024;
const RESULTS_BYTES_OFFSET: usize = MAX_MEMORY_BYTES - MAX_TRANSACTIONS * 32;
const VM_HOOKS_PARAMS: usize = 2;
const VM_HOOKS_START: usize = RESULTS_BYTES_OFFSET - (VM_HOOKS_PARAMS + 1) * 32;
const DEBUG_SLOTS: usize = 32;
const DEBUG_START_BYTE: usize = VM_HOOKS_START - DEBUG_SLOTS * 32;
const DEBUG_START_SLOT: usize = DEBUG_START_BYTE / 32;
#[derive(Debug, Clone)]
pub struct BootloaderDebug {
pub total_gas_limit_from_user: U256,
pub reserved_gas: U256,
pub gas_per_pubdata: U256,
pub gas_limit_after_intrinsic: U256,
pub gas_after_validation: U256,
pub gas_spent_on_execution: U256,
pub gas_spent_on_bytecode_preparation: U256,
pub refund_computed: U256,
pub refund_by_operator: U256,
pub intrinsic_overhead: U256,
pub total_overhead_for_block: U256,
pub required_overhead: U256,
pub operator_overhead: U256,
pub overhead_for_circuits: U256,
pub overhead_for_length: U256,
pub overhead_for_slot: U256,
}
pub struct BootloaderDebugTracer {
pub result: Arc<OnceCell<eyre::Result<BootloaderDebug, String>>>,
}
impl<S, H: HistoryMode> DynTracer<S, H> for BootloaderDebugTracer {}
impl<H: HistoryMode> ExecutionEndTracer<H> for BootloaderDebugTracer {}
impl<S: WriteStorage, H: HistoryMode> ExecutionProcessing<S, H> for BootloaderDebugTracer {
fn after_vm_execution(
&mut self,
state: &mut ZkSyncVmState<S, H>,
_bootloader_state: &BootloaderState,
_stop_reason: VmExecutionStopReason,
) {
self.result
.set(BootloaderDebug::load_from_memory(&state.memory))
.unwrap();
}
}
fn load_debug_slot<H: HistoryMode>(memory: &SimpleMemory<H>, slot: usize) -> U256 {
memory
.read_slot(BOOTLOADER_HEAP_PAGE as usize, DEBUG_START_SLOT + slot)
.value
}
impl<S: WriteStorage, H: HistoryMode> VmTracer<S, H> for BootloaderDebugTracer {
fn save_results(&mut self, _result: &mut VmExecutionResultAndLogs) {}
}
impl BootloaderDebug {
pub fn load_from_memory<H: HistoryMode>(
memory: &SimpleMemory<H>,
) -> eyre::Result<Self, String> {
if load_debug_slot(memory, 0) != U256::from(DEBUG_START_SENTINEL) {
Err(
"Debug slot has wrong value. Probably bootloader slot mapping has changed."
.to_owned(),
)
} else {
Ok(BootloaderDebug {
total_gas_limit_from_user: load_debug_slot(memory, 1),
reserved_gas: load_debug_slot(memory, 2),
gas_per_pubdata: load_debug_slot(memory, 3),
gas_limit_after_intrinsic: load_debug_slot(memory, 4),
gas_after_validation: load_debug_slot(memory, 5),
gas_spent_on_execution: load_debug_slot(memory, 6),
gas_spent_on_bytecode_preparation: load_debug_slot(memory, 7),
refund_computed: load_debug_slot(memory, 8),
refund_by_operator: load_debug_slot(memory, 9),
intrinsic_overhead: load_debug_slot(memory, 10),
operator_overhead: load_debug_slot(memory, 11),
required_overhead: load_debug_slot(memory, 12),
total_overhead_for_block: load_debug_slot(memory, 13),
overhead_for_circuits: load_debug_slot(memory, 14),
overhead_for_length: load_debug_slot(memory, 15),
overhead_for_slot: load_debug_slot(memory, 16),
})
}
}
}