Skip to content

Commit

Permalink
feat: impl hardhat_mine
Browse files Browse the repository at this point in the history
  • Loading branch information
grw-ms committed Sep 14, 2023
1 parent 296e295 commit 5922a6c
Show file tree
Hide file tree
Showing 3 changed files with 239 additions and 88 deletions.
99 changes: 19 additions & 80 deletions src/evm.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,9 @@
use std::sync::{Arc, RwLock};

use crate::{fork::ForkSource, node::InMemoryNodeInner};
use crate::{fork::ForkSource, node::InMemoryNodeInner, utils::mine_empty_blocks};
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
Expand Down Expand Up @@ -134,72 +124,8 @@ impl<S: Send + Sync + 'static + ForkSource + std::fmt::Debug> EvmNamespaceT
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::<S>::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(),
)
}
log::info!("👷 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;

mine_empty_blocks(&mut inner, 1, 1000);
println!("👷 Mined block #{}", inner.current_miniblock);
Ok("0x0".to_string())
}
Err(_) => Err(into_jsrpc_error(Web3Error::InternalError)),
Expand Down Expand Up @@ -526,19 +452,32 @@ mod tests {
let evm = EvmNamespaceImpl::new(node.get_inner());

let start_block = node
.get_block_by_number(zksync_types::api::BlockNumber::Latest, true)
.get_block_by_number(zksync_types::api::BlockNumber::Latest, false)
.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)
.get_block_by_number(zksync_types::api::BlockNumber::Latest, false)
.await
.unwrap()
.expect("block exists");

assert_eq!(start_block.number + 1, current_block.number);
assert_eq!(start_block.timestamp + 1000, current_block.timestamp);

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, false)
.await
.unwrap()
.expect("block exists");

assert_eq!(start_block.number + 2, current_block.number);
assert_eq!(start_block.timestamp + 2000, current_block.timestamp);
}
}
118 changes: 116 additions & 2 deletions src/hardhat.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use std::sync::{Arc, RwLock};

use crate::{fork::ForkSource, node::InMemoryNodeInner};
use crate::{fork::ForkSource, node::InMemoryNodeInner, utils::mine_empty_blocks};
use jsonrpc_core::{BoxFuture, Result};
use jsonrpc_derive::rpc;
use zksync_basic_types::{Address, U256};
use zksync_basic_types::{Address, U256, U64};
use zksync_core::api_server::web3::backend_jsonrpc::error::into_jsrpc_error;
use zksync_state::ReadStorage;
use zksync_types::{
Expand Down Expand Up @@ -52,6 +52,25 @@ pub trait HardhatNamespaceT {
/// A `BoxFuture` containing a `Result` with a `bool` representing the success of the operation.
#[rpc(name = "hardhat_setNonce")]
fn set_nonce(&self, address: Address, balance: U256) -> BoxFuture<Result<bool>>;

/// Sometimes you may want to advance the latest block number of the network by a large number of blocks.
/// One way to do this would be to call the evm_mine RPC method multiple times, but this is too slow if you want to mine thousands of blocks.
/// The hardhat_mine method can mine any number of blocks at once, in constant time. (It exhibits the same performance no matter how many blocks are mined.)
///
/// # Arguments
///
/// * `num_blocks` - The number of blocks to mine, defaults to 1
/// * `interval` - The interval between the timestamps of each block, in seconds, and it also defaults to 1
///
/// # Returns
///
/// A `BoxFuture` containing a `Result` with a `bool` representing the success of the operation.
#[rpc(name = "hardhat_mine")]
fn hardhat_mine(
&self,
num_blocks: Option<U64>,
interval: Option<U64>,
) -> BoxFuture<Result<bool>>;
}

impl<S: Send + Sync + 'static + ForkSource + std::fmt::Debug> HardhatNamespaceT
Expand Down Expand Up @@ -126,6 +145,33 @@ impl<S: Send + Sync + 'static + ForkSource + std::fmt::Debug> HardhatNamespaceT
}
})
}

fn hardhat_mine(
&self,
num_blocks: Option<U64>,
interval: Option<U64>,
) -> BoxFuture<Result<bool>> {
let inner = Arc::clone(&self.node);
Box::pin(async move {
match inner.write() {
Ok(mut inner) => {
let num_blocks = num_blocks.unwrap_or(U64::from(1));
let interval_ms = interval
.unwrap_or(U64::from(1))
.saturating_mul(1_000.into());
if num_blocks.is_zero() {
return Err(jsonrpc_core::Error::invalid_params(
"Number of blocks must be greater than 0".to_string(),
));
}
mine_empty_blocks(&mut inner, num_blocks.as_u64(), interval_ms.as_u64());
println!("👷 Mined {} blocks", num_blocks);
Ok(true)
}
Err(_) => Err(into_jsrpc_error(Web3Error::InternalError)),
}
})
}
}

#[cfg(test)]
Expand Down Expand Up @@ -173,4 +219,72 @@ mod tests {
let result = hardhat.set_nonce(address, U256::from(1336)).await;
assert!(result.is_err());
}

#[tokio::test]
async fn test_hardhat_mine_default() {
let node = InMemoryNode::<HttpForkSource>::default();
let hardhat = HardhatNamespaceImpl::new(node.get_inner());

let start_block = node
.get_block_by_number(zksync_types::api::BlockNumber::Latest, false)
.await
.unwrap()
.expect("block exists");
println!("start_block: {:?}", start_block);

// test with defaults
let result = hardhat
.hardhat_mine(None, None)
.await
.expect("hardhat_mine");
assert_eq!(result, true);

println!("mined default");
let current_block = node
.get_block_by_number(zksync_types::api::BlockNumber::Latest, false)
.await
.unwrap()
.expect("block exists");
println!("current_block: {:?}", current_block);

assert_eq!(start_block.number + 1, current_block.number);
assert_eq!(start_block.timestamp + 1000, current_block.timestamp);
let result = hardhat
.hardhat_mine(None, None)
.await
.expect("hardhat_mine");
assert_eq!(result, true);

println!("mined default");
let current_block = node
.get_block_by_number(zksync_types::api::BlockNumber::Latest, false)
.await
.unwrap()
.expect("block exists");
println!("current_block: {:?}", current_block);

assert_eq!(start_block.number + 2, current_block.number);
assert_eq!(start_block.timestamp + 2000, current_block.timestamp);
}

#[tokio::test]
async fn test_hardhat_mine_custom() {
let node = InMemoryNode::<HttpForkSource>::default();
let hardhat = HardhatNamespaceImpl::new(node.get_inner());

let start_block = node
.get_block_by_number(zksync_types::api::BlockNumber::Latest, false)
.await
.unwrap()
.expect("block exists");
println!("start_block: {:?}", start_block);

// test with custom values
let result = hardhat
.hardhat_mine(Some(U64::from(5)), Some(U64::from(10)))
.await
.expect("hardhat_mine");
assert_eq!(result, true);
println!("mined twice");
}
}
Loading

0 comments on commit 5922a6c

Please sign in to comment.