From 5e61bdc75b2baa03004d4d3e801170c094766964 Mon Sep 17 00:00:00 2001 From: Danil Date: Tue, 3 Oct 2023 14:26:33 +0200 Subject: [PATCH] feat(vm): Restore system-constants-generator (#115) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # What ❔ Restore system-constants-generator ## Why ❔ This crate is rarely used, but it is important to keep it alive: it's used sometimes to tune our fee-related constants. Making sure that the code at least compiles is the smallest yet essential part of maintenance. ## Checklist - [ ] 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`. --- Cargo.lock | 15 ++ Cargo.toml | 2 +- .../src/intrinsic_costs.rs | 2 +- .../system-constants-generator/src/main.rs | 21 +- .../system-constants-generator/src/utils.rs | 214 ++++++++++-------- core/lib/vm/src/constants.rs | 4 +- core/lib/vm/src/tracers/traits.rs | 2 +- 7 files changed, 149 insertions(+), 111 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e03ed8eaf61a..0b584dc9a2ea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6208,6 +6208,21 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "system-constants-generator" +version = "0.1.0" +dependencies = [ + "codegen 0.2.0", + "once_cell", + "serde", + "serde_json", + "vm", + "zksync_contracts", + "zksync_state", + "zksync_types", + "zksync_utils", +] + [[package]] name = "tagptr" version = "0.2.0" diff --git a/Cargo.toml b/Cargo.toml index 9a62cde88d73..9211084819da 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ members = [ "core/bin/merkle_tree_consistency_checker", "core/bin/rocksdb_util", "core/bin/storage_logs_dedup_migration", - # "core/bin/system-constants-generator", + "core/bin/system-constants-generator", "core/bin/verification_key_generator_and_server", "core/bin/verified_sources_fetcher", "core/bin/zksync_server", diff --git a/core/bin/system-constants-generator/src/intrinsic_costs.rs b/core/bin/system-constants-generator/src/intrinsic_costs.rs index c663939db6ee..0491be494ab8 100644 --- a/core/bin/system-constants-generator/src/intrinsic_costs.rs +++ b/core/bin/system-constants-generator/src/intrinsic_costs.rs @@ -9,7 +9,7 @@ use crate::utils::{ get_l2_txs, }; use crate::utils::{metrics_from_txs, TransactionGenerator}; -use vm::vm_with_bootloader::BOOTLOADER_TX_ENCODING_SPACE; +use vm::constants::BOOTLOADER_TX_ENCODING_SPACE; use zksync_types::{ethabi::Address, IntrinsicSystemGasConstants, U256}; #[derive(Debug, Clone, Copy, PartialEq)] diff --git a/core/bin/system-constants-generator/src/main.rs b/core/bin/system-constants-generator/src/main.rs index d101647134f8..f076eadd8c8d 100644 --- a/core/bin/system-constants-generator/src/main.rs +++ b/core/bin/system-constants-generator/src/main.rs @@ -1,16 +1,6 @@ use std::fs; use serde::{Deserialize, Serialize}; -use vm::{ - vm_with_bootloader::{BLOCK_OVERHEAD_GAS, BLOCK_OVERHEAD_L1_GAS, BOOTLOADER_TX_ENCODING_SPACE}, - zk_evm::zkevm_opcode_defs::{ - circuit_prices::{ - ECRECOVER_CIRCUIT_COST_IN_ERGS, KECCAK256_CIRCUIT_COST_IN_ERGS, - SHA256_CIRCUIT_COST_IN_ERGS, - }, - system_params::{MAX_PUBDATA_PER_BLOCK, MAX_TX_ERGS_LIMIT}, - }, -}; use zksync_types::{ IntrinsicSystemGasConstants, GUARANTEED_PUBDATA_IN_TX, L1_GAS_PER_PUBDATA_BYTE, MAX_GAS_PER_PUBDATA_BYTE, MAX_NEW_FACTORY_DEPS, MAX_TXS_IN_BLOCK, @@ -21,6 +11,13 @@ mod utils; use codegen::Block; use codegen::Scope; +use vm::constants::{ + BLOCK_OVERHEAD_GAS, BLOCK_OVERHEAD_L1_GAS, BOOTLOADER_TX_ENCODING_SPACE, MAX_PUBDATA_PER_BLOCK, +}; +use zksync_types::zkevm_test_harness::zk_evm::zkevm_opcode_defs::circuit_prices::{ + ECRECOVER_CIRCUIT_COST_IN_ERGS, KECCAK256_CIRCUIT_COST_IN_ERGS, SHA256_CIRCUIT_COST_IN_ERGS, +}; +use zksync_types::zkevm_test_harness::zk_evm::zkevm_opcode_defs::system_params::MAX_TX_ERGS_LIMIT; // Params needed for L1 contracts #[derive(Copy, Clone, Debug, Serialize, Deserialize)] @@ -128,7 +125,7 @@ fn generate_rust_fee_constants(intrinsic_gas_constants: &IntrinsicSystemGasConst scope.import("super", "IntrinsicSystemGasConstants"); scope.raw( - vec![ + [ "// TODO (SMA-1699): Use this method to ensure that the transactions provide enough", "// intrinsic gas on the API level.", ] @@ -193,7 +190,7 @@ fn generate_rust_fee_constants(intrinsic_gas_constants: &IntrinsicSystemGasConst get_intrinsic_constants_fn.push_block(struct_block); } - vec![ + [ "//! THIS FILE IS AUTOGENERATED: DO NOT EDIT MANUALLY!\n".to_string(), "//! The file with constants related to fees most of which need to be computed\n" .to_string(), diff --git a/core/bin/system-constants-generator/src/utils.rs b/core/bin/system-constants-generator/src/utils.rs index 3af5df328f42..afb00b5cda7d 100644 --- a/core/bin/system-constants-generator/src/utils.rs +++ b/core/bin/system-constants-generator/src/utils.rs @@ -1,36 +1,62 @@ use once_cell::sync::Lazy; +use std::cell::RefCell; +use std::rc::Rc; +use vm::constants::{BLOCK_GAS_LIMIT, BOOTLOADER_HEAP_PAGE}; use vm::{ - utils::{create_test_block_params, read_bootloader_test_code, BLOCK_GAS_LIMIT}, - vm_with_bootloader::{ - init_vm_inner, push_raw_transaction_to_bootloader_memory, BlockContextMode, - BootloaderJobType, DerivedBlockContext, TxExecutionMode, - }, - zk_evm::{aux_structures::Timestamp, zkevm_opcode_defs::BOOTLOADER_HEAP_PAGE}, - HistoryEnabled, OracleTools, + BootloaderState, BoxedTracer, DynTracer, ExecutionEndTracer, ExecutionProcessing, + HistoryEnabled, HistoryMode, L1BatchEnv, L2BlockEnv, SystemEnv, TxExecutionMode, Vm, + VmExecutionMode, VmExecutionStopReason, VmTracer, ZkSyncVmState, }; use zksync_contracts::{ - load_sys_contract, read_bootloader_code, read_sys_contract_bytecode, BaseSystemContracts, - ContractLanguage, SystemContractCode, + load_sys_contract, read_bootloader_code, read_sys_contract_bytecode, read_zbin_bytecode, + BaseSystemContracts, ContractLanguage, SystemContractCode, }; use zksync_state::{InMemoryStorage, StorageView, WriteStorage}; +use zksync_types::block::legacy_miniblock_hash; use zksync_types::{ - ethabi::Token, - fee::Fee, - l1::L1Tx, - l2::L2Tx, - tx::{ - tx_execution_info::{TxExecutionStatus, VmExecutionLogs}, - ExecutionMetrics, - }, - utils::storage_key_for_eth_balance, - AccountTreeId, Address, Execute, L1TxCommonData, L2ChainId, Nonce, StorageKey, Transaction, - BOOTLOADER_ADDRESS, H256, SYSTEM_CONTEXT_ADDRESS, SYSTEM_CONTEXT_GAS_PRICE_POSITION, - SYSTEM_CONTEXT_TX_ORIGIN_POSITION, U256, + ethabi::Token, fee::Fee, l1::L1Tx, l2::L2Tx, utils::storage_key_for_eth_balance, AccountTreeId, + Address, Execute, L1BatchNumber, L1TxCommonData, L2ChainId, MiniblockNumber, Nonce, + ProtocolVersionId, StorageKey, Timestamp, Transaction, BOOTLOADER_ADDRESS, H256, + SYSTEM_CONTEXT_ADDRESS, SYSTEM_CONTEXT_GAS_PRICE_POSITION, SYSTEM_CONTEXT_TX_ORIGIN_POSITION, + U256, ZKPORTER_IS_AVAILABLE, }; use zksync_utils::{bytecode::hash_bytecode, bytes_to_be_words, u256_to_h256}; use crate::intrinsic_costs::VmSpentResourcesResult; +/// Tracer for setting the data for bootloader with custom input +/// and receive an output from this custom bootloader +struct SpecialBootloaderTracer { + input: Vec<(usize, U256)>, + output: Rc>, +} + +impl DynTracer for SpecialBootloaderTracer {} + +impl ExecutionEndTracer for SpecialBootloaderTracer {} + +impl ExecutionProcessing for SpecialBootloaderTracer { + fn initialize_tracer(&mut self, state: &mut ZkSyncVmState) { + state.memory.populate_page( + BOOTLOADER_HEAP_PAGE as usize, + self.input.clone(), + Timestamp(0), + ); + } + fn after_vm_execution( + &mut self, + state: &mut ZkSyncVmState, + _bootloader_state: &BootloaderState, + _stop_reason: VmExecutionStopReason, + ) { + let value_recorded_from_test = state.memory.read_slot(BOOTLOADER_HEAP_PAGE as usize, 0); + let mut res = self.output.borrow_mut(); + *res = value_recorded_from_test.value.as_u32(); + } +} + +impl VmTracer for SpecialBootloaderTracer {} + pub static GAS_TEST_SYSTEM_CONTRACTS: Lazy = Lazy::new(|| { let bytecode = read_bootloader_code("gas_test"); let hash = hash_bytecode(&bytecode); @@ -135,19 +161,39 @@ pub(super) fn get_l1_txs(number_of_txs: usize) -> (Vec, Vec Vec { + read_zbin_bytecode(format!( + "etc/system-contracts/bootloader/tests/artifacts/{}.yul/{}.yul.zbin", + test, test + )) +} + +fn default_l1_batch() -> L1BatchEnv { + L1BatchEnv { + previous_batch_hash: None, + number: L1BatchNumber(1), + timestamp: 100, + l1_gas_price: 50_000_000_000, // 50 gwei + fair_l2_gas_price: 250_000_000, // 0.25 gwei + fee_account: Address::random(), + enforced_base_fee: None, + first_l2_block: L2BlockEnv { + number: 1, + timestamp: 100, + prev_block_hash: legacy_miniblock_hash(MiniblockNumber(0)), + max_virtual_blocks_to_create: 100, + }, + } +} + /// Executes the "internal transfer test" of the bootloader -- the test that /// returns the amount of gas needed to perform and internal transfer, assuming no gas price /// per pubdata, i.e. under assumption that the refund will not touch any new slots. pub(super) fn execute_internal_transfer_test() -> u32 { - let (block_context, block_properties) = create_test_block_params(); - let block_context: DerivedBlockContext = block_context.into(); - let raw_storage = InMemoryStorage::with_system_contracts(hash_bytecode); let mut storage_view = StorageView::new(raw_storage); let bootloader_balance_key = storage_key_for_eth_balance(&BOOTLOADER_ADDRESS); storage_view.set_value(bootloader_balance_key, u256_to_h256(U256([0, 0, 1, 0]))); - let mut oracle_tools = OracleTools::new(&mut storage_view, HistoryEnabled); - let bytecode = read_bootloader_test_code("transfer_test"); let hash = hash_bytecode(&bytecode); let bootloader = SystemContractCode { @@ -155,6 +201,8 @@ pub(super) fn execute_internal_transfer_test() -> u32 { hash, }; + let l1_batch = default_l1_batch(); + let bytecode = read_sys_contract_bytecode("", "DefaultAccount", ContractLanguage::Sol); let hash = hash_bytecode(&bytecode); let default_aa = SystemContractCode { @@ -162,19 +210,20 @@ pub(super) fn execute_internal_transfer_test() -> u32 { hash, }; - let base_system_contract = BaseSystemContracts { + let base_system_smart_contracts = BaseSystemContracts { bootloader, default_aa, }; - let mut vm = init_vm_inner( - &mut oracle_tools, - BlockContextMode::NewBlock(block_context, Default::default()), - &block_properties, - BLOCK_GAS_LIMIT, - &base_system_contract, - TxExecutionMode::VerifyExecute, - ); + let system_env = SystemEnv { + zk_porter_available: ZKPORTER_IS_AVAILABLE, + version: ProtocolVersionId::latest(), + base_system_smart_contracts, + gas_limit: BLOCK_GAS_LIMIT, + execution_mode: TxExecutionMode::VerifyExecute, + default_validation_computational_gas_limit: BLOCK_GAS_LIMIT, + chain_id: L2ChainId::default(), + }; let eth_token_sys_contract = load_sys_contract("L2EthToken"); let transfer_from_to = ð_token_sys_contract @@ -197,24 +246,22 @@ pub(super) fn execute_internal_transfer_test() -> u32 { input }; let input: Vec<_> = bytes_to_be_words(input).into_iter().enumerate().collect(); - vm.state - .memory - .populate_page(BOOTLOADER_HEAP_PAGE as usize, input, Timestamp(0)); - - let result = vm.execute_till_block_end(BootloaderJobType::BlockPostprocessing); - assert!( - result.block_tip_result.revert_reason.is_none(), - "The internal call has reverted" - ); - assert!( - result.full_result.revert_reason.is_none(), - "The internal call has reverted" + let tracer_result = Rc::new(RefCell::new(0)); + let tracer = SpecialBootloaderTracer { + input, + output: tracer_result.clone(), + }; + let mut vm = Vm::new( + l1_batch, + system_env, + Rc::new(RefCell::new(storage_view)), + HistoryEnabled, ); + let result = vm.inspect(vec![tracer.into_boxed()], VmExecutionMode::Bootloader); - let value_recorded_from_test = vm.state.memory.read_slot(BOOTLOADER_HEAP_PAGE as usize, 0); - - value_recorded_from_test.value.as_u32() + assert!(!result.result.is_failed(), "The internal call has reverted"); + tracer_result.take() } // Executes an array of transactions in the VM. @@ -226,9 +273,6 @@ pub(super) fn execute_user_txs_in_test_gas_vm( .iter() .fold(U256::zero(), |sum, elem| sum + elem.gas_limit()); - let (block_context, block_properties) = create_test_block_params(); - let block_context: DerivedBlockContext = block_context.into(); - let raw_storage = InMemoryStorage::with_system_contracts(hash_bytecode); let mut storage_view = StorageView::new(raw_storage); @@ -256,61 +300,43 @@ pub(super) fn execute_user_txs_in_test_gas_vm( storage_view.set_value(tx_gas_price_key, u256_to_h256(U256([1, 0, 0, 0]))); } - let mut oracle_tools = OracleTools::new(&mut storage_view, HistoryEnabled); + let l1_batch = default_l1_batch(); + let system_env = SystemEnv { + zk_porter_available: ZKPORTER_IS_AVAILABLE, + version: ProtocolVersionId::latest(), + base_system_smart_contracts: GAS_TEST_SYSTEM_CONTRACTS.clone(), + gas_limit: BLOCK_GAS_LIMIT, + execution_mode: TxExecutionMode::VerifyExecute, + default_validation_computational_gas_limit: BLOCK_GAS_LIMIT, + chain_id: L2ChainId::default(), + }; - let mut vm = init_vm_inner( - &mut oracle_tools, - BlockContextMode::NewBlock(block_context, Default::default()), - &block_properties, - BLOCK_GAS_LIMIT, - &GAS_TEST_SYSTEM_CONTRACTS, - TxExecutionMode::VerifyExecute, + let mut vm = Vm::new( + l1_batch, + system_env, + Rc::new(RefCell::new(storage_view)), + HistoryEnabled, ); - vm.start_next_l2_block(vm.get_current_l2_block_info().dummy_next_block_info()); let mut total_gas_refunded = 0; for tx in txs { - push_raw_transaction_to_bootloader_memory( - &mut vm, - tx.clone().into(), - TxExecutionMode::VerifyExecute, - 0, - None, - ); - let tx_execution_result = vm - .execute_next_tx(u32::MAX, false) - .expect("Bootloader failed while processing transaction"); + vm.push_transaction(tx); + let tx_execution_result = vm.execute(VmExecutionMode::OneTx); - total_gas_refunded += tx_execution_result.gas_refunded; + total_gas_refunded += tx_execution_result.refunds.gas_refunded; if !accept_failure { - assert_eq!( - tx_execution_result.status, - TxExecutionStatus::Success, + assert!( + !tx_execution_result.result.is_failed(), "A transaction has failed" ); } } - let result = vm.execute_till_block_end(BootloaderJobType::BlockPostprocessing); - let execution_logs = VmExecutionLogs { - storage_logs: result.full_result.storage_log_queries, - events: result.full_result.events, - l2_to_l1_logs: result.full_result.l2_to_l1_logs, - total_log_queries_count: result.full_result.total_log_queries, - }; - - let metrics = ExecutionMetrics::new( - &execution_logs, - result.full_result.gas_used as usize, - 0, // The number of contracts deployed is irrelevant for our needs - result.full_result.contracts_used, - result.full_result.cycles_used, - result.full_result.computational_gas_used, - result.full_result.total_log_queries, - ); + let result = vm.execute(VmExecutionMode::Bootloader); + let metrics = result.get_execution_metrics(None); VmSpentResourcesResult { - gas_consumed: vm.gas_consumed(), + gas_consumed: result.statistics.gas_used, total_gas_paid: total_gas_paid_upfront.as_u32() - total_gas_refunded, pubdata_published: metrics.size() as u32, total_pubdata_paid: 0, diff --git a/core/lib/vm/src/constants.rs b/core/lib/vm/src/constants.rs index a51688b851e7..1c1cb3d5017f 100644 --- a/core/lib/vm/src/constants.rs +++ b/core/lib/vm/src/constants.rs @@ -70,8 +70,8 @@ pub(crate) const TX_GAS_LIMIT_OFFSET: usize = 4; const INITIAL_BASE_PAGE: u32 = 8; pub const BOOTLOADER_HEAP_PAGE: u32 = heap_page_from_base(MemoryPage(INITIAL_BASE_PAGE)).0; -pub(crate) const BLOCK_OVERHEAD_GAS: u32 = 1200000; -pub(crate) const BLOCK_OVERHEAD_L1_GAS: u32 = 1000000; +pub const BLOCK_OVERHEAD_GAS: u32 = 1200000; +pub const BLOCK_OVERHEAD_L1_GAS: u32 = 1000000; pub const BLOCK_OVERHEAD_PUBDATA: u32 = BLOCK_OVERHEAD_L1_GAS / L1_GAS_PER_PUBDATA_BYTE; /// VM Hooks are used for communication between bootloader and tracers. diff --git a/core/lib/vm/src/tracers/traits.rs b/core/lib/vm/src/tracers/traits.rs index 6e76a041fabc..33e149066b16 100644 --- a/core/lib/vm/src/tracers/traits.rs +++ b/core/lib/vm/src/tracers/traits.rs @@ -69,7 +69,7 @@ pub trait DynTracer { /// Save the results of the vm execution. pub trait VmTracer: - DynTracer + ExecutionEndTracer + ExecutionProcessing + Send + DynTracer + ExecutionEndTracer + ExecutionProcessing { fn save_results(&mut self, _result: &mut VmExecutionResultAndLogs) {} }