From 434d22038ee8f31f8f8a5e902b3a357e76c461bb Mon Sep 17 00:00:00 2001 From: grw-ms Date: Thu, 14 Sep 2023 02:30:51 -0700 Subject: [PATCH] feat: impl evm_mine --- src/evm.rs | 120 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/node.rs | 4 +- 2 files changed, 122 insertions(+), 2 deletions(-) diff --git a/src/evm.rs b/src/evm.rs index 8eede93b..a68b4afc 100644 --- a/src/evm.rs +++ b/src/evm.rs @@ -3,7 +3,17 @@ use std::sync::{Arc, RwLock}; use crate::{fork::ForkSource, node::InMemoryNodeInner}; use jsonrpc_core::{BoxFuture, Result}; use jsonrpc_derive::rpc; +use vm::{ + utils::BLOCK_GAS_LIMIT, + vm_with_bootloader::{init_vm_inner, BlockContextMode, BootloaderJobType, TxExecutionMode}, + HistoryEnabled, OracleTools, +}; +use zksync_basic_types::H256; use zksync_core::api_server::web3::backend_jsonrpc::error::into_jsrpc_error; +use zksync_state::StorageView; +use zksync_state::WriteStorage; +use zksync_types::api::Block; +use zksync_utils::u256_to_h256; use zksync_web3_decl::error::Web3Error; /// Implementation of EvmNamespace @@ -30,6 +40,15 @@ pub trait EvmNamespaceT { #[rpc(name = "evm_increaseTime")] fn increase_time(&self, time_delta_seconds: u64) -> BoxFuture>; + /// Force a single block to be mined. + /// + /// Will mine an empty block (containing zero transactions) + /// + /// # Returns + /// The string "0x0". + #[rpc(name = "evm_mine")] + fn evm_mine(&self) -> BoxFuture>; + /// Set the current timestamp for the node. The timestamp must be in future. /// /// # Parameters @@ -109,11 +128,90 @@ impl EvmNamespaceT } }) } + + fn evm_mine(&self) -> BoxFuture> { + let inner = Arc::clone(&self.node); + Box::pin(async move { + match inner.write() { + Ok(mut inner) => { + let (keys, block, bytecodes) = { + let mut storage_view = StorageView::new(&inner.fork_storage); + let mut oracle_tools = OracleTools::new(&mut storage_view, HistoryEnabled); + + let bootloader_code = &inner.system_contracts.baseline_contracts; + let block_context = inner.create_block_context(); + let block_properties = + InMemoryNodeInner::::create_block_properties(bootloader_code); + + let block = Block { + hash: H256::random(), + number: inner.current_miniblock.saturating_add(1).into(), + timestamp: block_context.block_timestamp.into(), + l1_batch_number: Some(block_context.block_number.into()), + ..Default::default() + }; + + // init vm + let mut vm = init_vm_inner( + &mut oracle_tools, + BlockContextMode::NewBlock(block_context.into(), Default::default()), + &block_properties, + BLOCK_GAS_LIMIT, + bootloader_code, + TxExecutionMode::VerifyExecute, + ); + + vm.execute_till_block_end(BootloaderJobType::BlockPostprocessing); + + let bytecodes = vm + .state + .decommittment_processor + .known_bytecodes + .inner() + .clone(); + + let modified_keys = storage_view.modified_storage_keys().clone(); + (modified_keys, block, bytecodes) + }; + + for (key, value) in keys.iter() { + inner.fork_storage.set_value(*key, *value); + } + + // Write all the factory deps. + for (hash, code) in bytecodes.iter() { + inner.fork_storage.store_factory_dep( + u256_to_h256(*hash), + code.iter() + .flat_map(|entry| { + let mut bytes = vec![0u8; 32]; + entry.to_big_endian(&mut bytes); + bytes.to_vec() + }) + .collect(), + ) + } + println!("👷 Mined block #{}", block.number.as_u64()); + + // update node state + inner.block_hashes.insert(block.number.as_u64(), block.hash); + inner.blocks.insert(block.hash, block); + inner.current_timestamp += 1; + inner.current_batch += 1; + inner.current_miniblock += 1; + + Ok("0x0".to_string()) + } + Err(_) => Err(into_jsrpc_error(Web3Error::InternalError)), + } + }) + } } #[cfg(test)] mod tests { use crate::{http_fork_source::HttpForkSource, node::InMemoryNode}; + use zksync_core::api_server::web3::backend_jsonrpc::namespaces::eth::EthNamespaceT; use super::*; @@ -421,4 +519,26 @@ mod tests { ); } } + + #[tokio::test] + async fn test_evm_mine() { + let node = InMemoryNode::::default(); + let evm = EvmNamespaceImpl::new(node.get_inner()); + + let start_block = node + .get_block_by_number(zksync_types::api::BlockNumber::Latest, true) + .await + .unwrap() + .expect("block exists"); + + let result = evm.evm_mine().await.expect("evm_mine"); + assert_eq!(&result, "0x0"); + + let current_block = node + .get_block_by_number(zksync_types::api::BlockNumber::Latest, true) + .await + .unwrap() + .expect("block exists"); + assert_eq!(start_block.number + 1, current_block.number); + } } diff --git a/src/node.rs b/src/node.rs index 187a61a7..222c224f 100644 --- a/src/node.rs +++ b/src/node.rs @@ -253,7 +253,7 @@ type L2TxResult = ( ); impl InMemoryNodeInner { - fn create_block_context(&self) -> BlockContext { + pub fn create_block_context(&self) -> BlockContext { BlockContext { block_number: self.current_batch, block_timestamp: self.current_timestamp, @@ -263,7 +263,7 @@ impl InMemoryNodeInner { } } - fn create_block_properties(contracts: &BaseSystemContracts) -> BlockProperties { + pub fn create_block_properties(contracts: &BaseSystemContracts) -> BlockProperties { BlockProperties { default_aa_code_hash: h256_to_u256(contracts.default_aa.hash), zkporter_is_available: false,