use std::collections::HashMap;
use std::pin::Pin;
use futures::Future;
use vm::{ExecutionResult, VmExecutionResultAndLogs};
use vm::{HistoryDisabled, Vm};
use zksync_basic_types::{U256, U64};
use zksync_state::StorageView;
use zksync_state::WriteStorage;
use zksync_types::api::{BlockNumber, DebugCall, DebugCallType};
use zksync_types::l2::L2Tx;
use zksync_types::vm_trace::Call;
use zksync_types::CONTRACT_DEPLOYER_ADDRESS;
use zksync_utils::u256_to_h256;
use zksync_web3_decl::error::Web3Error;
use crate::node::create_empty_block;
use crate::{fork::ForkSource, node::InMemoryNodeInner};
use vm::utils::fee::derive_base_fee_and_gas_per_pubdata;
use zksync_utils::{bytecode::hash_bytecode, bytes_to_be_words};
pub(crate) trait IntoBoxedFuture: Sized + Send + 'static {
fn into_boxed_future(self) -> Pin<Box<dyn Future<Output = Self> + Send>> {
Box::pin(async { self })
}
}
impl<T, U> IntoBoxedFuture for Result<T, U>
where
T: Send + 'static,
U: Send + 'static,
{
}
pub fn adjust_l1_gas_price_for_tx(
l1_gas_price: u64,
fair_l2_gas_price: u64,
tx_gas_per_pubdata_limit: U256,
) -> u64 {
let (_, current_pubdata_price) =
derive_base_fee_and_gas_per_pubdata(l1_gas_price, fair_l2_gas_price);
if U256::from(current_pubdata_price) <= tx_gas_per_pubdata_limit {
l1_gas_price
} else {
let l1_gas_price = U256::from(fair_l2_gas_price)
* (tx_gas_per_pubdata_limit - U256::from(1u32))
/ U256::from(17);
l1_gas_price.as_u64()
}
}
pub fn to_human_size(input: U256) -> String {
let input = format!("{:?}", input);
let tmp: Vec<_> = input
.chars()
.rev()
.enumerate()
.flat_map(|(index, val)| {
if index > 0 && index % 3 == 0 {
vec!['_', val]
} else {
vec![val]
}
})
.collect();
tmp.iter().rev().collect()
}
pub fn bytecode_to_factory_dep(bytecode: Vec<u8>) -> (U256, Vec<U256>) {
let bytecode_hash = hash_bytecode(&bytecode);
let bytecode_hash = U256::from_big_endian(bytecode_hash.as_bytes());
let bytecode_words = bytes_to_be_words(bytecode);
(bytecode_hash, bytecode_words)
}
pub fn mine_empty_blocks<S: std::fmt::Debug + ForkSource>(
node: &mut InMemoryNodeInner<S>,
num_blocks: u64,
interval_ms: u64,
) {
for i in 0..num_blocks {
let (keys, bytecodes, block_ctx) = {
let storage = StorageView::new(&node.fork_storage).to_rc_ptr();
let bootloader_code = node.system_contracts.contracts_for_l2_call();
let (batch_env, mut block_ctx) = node.create_l1_batch_env(storage.clone());
if i != 0 {
block_ctx.timestamp = node.current_timestamp.saturating_add(interval_ms);
}
let system_env =
node.create_system_env(bootloader_code.clone(), vm::TxExecutionMode::VerifyExecute);
let mut vm = Vm::new(batch_env, system_env, storage.clone(), HistoryDisabled);
vm.execute(vm::VmExecutionMode::Bootloader);
let bytecodes: HashMap<U256, Vec<U256>> = vm
.get_last_tx_compressed_bytecodes()
.iter()
.map(|b| bytecode_to_factory_dep(b.original.clone()))
.collect();
let modified_keys = storage.borrow().modified_storage_keys().clone();
(modified_keys, bytecodes, block_ctx)
};
for (key, value) in keys.iter() {
node.fork_storage.set_value(*key, *value);
}
for (hash, code) in bytecodes.iter() {
node.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(),
)
}
let block = create_empty_block(block_ctx.miniblock, block_ctx.timestamp, block_ctx.batch);
node.block_hashes.insert(block.number.as_u64(), block.hash);
node.blocks.insert(block.hash, block);
node.current_batch = block_ctx.batch;
node.current_miniblock = block_ctx.miniblock;
node.current_timestamp = block_ctx.timestamp;
}
}
pub fn to_real_block_number(block_number: BlockNumber, latest_block_number: U64) -> U64 {
match block_number {
BlockNumber::Finalized
| BlockNumber::Pending
| BlockNumber::Committed
| BlockNumber::Latest => latest_block_number,
BlockNumber::Earliest => U64::zero(),
BlockNumber::Number(n) => n,
}
}
pub fn not_implemented<T: Send + 'static>(
method_name: &str,
) -> jsonrpc_core::BoxFuture<Result<T, jsonrpc_core::Error>> {
log::warn!("Method {} is not implemented", method_name);
Err(jsonrpc_core::Error {
data: None,
code: jsonrpc_core::ErrorCode::MethodNotFound,
message: format!("Method {} is not implemented", method_name),
})
.into_boxed_future()
}
pub fn create_debug_output(
l2_tx: &L2Tx,
result: &VmExecutionResultAndLogs,
traces: Vec<Call>,
) -> Result<DebugCall, Web3Error> {
let calltype = if l2_tx.recipient_account() == CONTRACT_DEPLOYER_ADDRESS {
DebugCallType::Create
} else {
DebugCallType::Call
};
match &result.result {
ExecutionResult::Success { output } => Ok(DebugCall {
gas_used: result.statistics.gas_used.into(),
output: output.clone().into(),
r#type: calltype,
from: l2_tx.initiator_account(),
to: l2_tx.recipient_account(),
gas: l2_tx.common_data.fee.gas_limit,
value: l2_tx.execute.value,
input: l2_tx.execute.calldata().into(),
error: None,
revert_reason: None,
calls: traces.into_iter().map(Into::into).collect(),
}),
ExecutionResult::Revert { output } => Ok(DebugCall {
gas_used: result.statistics.gas_used.into(),
output: Default::default(),
r#type: calltype,
from: l2_tx.initiator_account(),
to: l2_tx.recipient_account(),
gas: l2_tx.common_data.fee.gas_limit,
value: l2_tx.execute.value,
input: l2_tx.execute.calldata().into(),
error: None,
revert_reason: Some(output.to_string()),
calls: traces.into_iter().map(Into::into).collect(),
}),
ExecutionResult::Halt { reason } => Err(Web3Error::SubmitTransactionError(
reason.to_string(),
vec![],
)),
}
}
#[cfg(test)]
mod tests {
use zksync_basic_types::{H256, U256};
use crate::{http_fork_source::HttpForkSource, node::InMemoryNode, testing};
use super::*;
#[test]
fn test_human_sizes() {
assert_eq!("123", to_human_size(U256::from(123u64)));
assert_eq!("1_234", to_human_size(U256::from(1234u64)));
assert_eq!("12_345", to_human_size(U256::from(12345u64)));
assert_eq!("0", to_human_size(U256::from(0)));
assert_eq!("1", to_human_size(U256::from(1)));
assert_eq!("250_000_000", to_human_size(U256::from(250000000u64)));
}
#[test]
fn test_to_real_block_number_finalized() {
let actual = to_real_block_number(BlockNumber::Finalized, U64::from(10));
assert_eq!(U64::from(10), actual);
}
#[test]
fn test_to_real_block_number_pending() {
let actual = to_real_block_number(BlockNumber::Pending, U64::from(10));
assert_eq!(U64::from(10), actual);
}
#[test]
fn test_to_real_block_number_committed() {
let actual = to_real_block_number(BlockNumber::Committed, U64::from(10));
assert_eq!(U64::from(10), actual);
}
#[test]
fn test_to_real_block_number_latest() {
let actual = to_real_block_number(BlockNumber::Latest, U64::from(10));
assert_eq!(U64::from(10), actual);
}
#[test]
fn test_to_real_block_number_earliest() {
let actual = to_real_block_number(BlockNumber::Earliest, U64::from(10));
assert_eq!(U64::zero(), actual);
}
#[test]
fn test_to_real_block_number_number() {
let actual = to_real_block_number(BlockNumber::Number(U64::from(5)), U64::from(10));
assert_eq!(U64::from(5), actual);
}
#[test]
fn test_mine_empty_blocks_mines_the_first_block_immediately() {
let node = InMemoryNode::<HttpForkSource>::default();
let inner = node.get_inner();
let starting_block = {
let reader = inner.read().expect("failed acquiring reader");
reader
.block_hashes
.get(&reader.current_miniblock)
.and_then(|hash| reader.blocks.get(hash))
.expect("failed finding block")
.clone()
};
assert_eq!(U64::from(0), starting_block.number);
assert_eq!(Some(U64::from(0)), starting_block.l1_batch_number);
assert_eq!(U256::from(1000), starting_block.timestamp);
{
let mut writer = inner.write().expect("failed acquiring write lock");
mine_empty_blocks(&mut writer, 1, 1000);
}
let reader = inner.read().expect("failed acquiring reader");
let mined_block = reader
.block_hashes
.get(&1)
.and_then(|hash| reader.blocks.get(hash))
.expect("failed finding block");
assert_eq!(U64::from(1), mined_block.number);
assert_eq!(Some(U64::from(1)), mined_block.l1_batch_number);
assert_eq!(U256::from(1001), mined_block.timestamp);
}
#[test]
fn test_mine_empty_blocks_mines_2_blocks_with_interval() {
let node = InMemoryNode::<HttpForkSource>::default();
let inner = node.get_inner();
let starting_block = {
let reader = inner.read().expect("failed acquiring reader");
reader
.block_hashes
.get(&reader.current_miniblock)
.and_then(|hash| reader.blocks.get(hash))
.expect("failed finding block")
.clone()
};
assert_eq!(U64::from(0), starting_block.number);
assert_eq!(Some(U64::from(0)), starting_block.l1_batch_number);
assert_eq!(U256::from(1000), starting_block.timestamp);
{
let mut writer = inner.write().expect("failed acquiring write lock");
mine_empty_blocks(&mut writer, 2, 1000);
}
let reader = inner.read().expect("failed acquiring reader");
let mined_block_1 = reader
.block_hashes
.get(&1)
.and_then(|hash| reader.blocks.get(hash))
.expect("failed finding block 1");
assert_eq!(U64::from(1), mined_block_1.number);
assert_eq!(Some(U64::from(1)), mined_block_1.l1_batch_number);
assert_eq!(U256::from(1001), mined_block_1.timestamp);
let mined_block_2 = reader
.block_hashes
.get(&2)
.and_then(|hash| reader.blocks.get(hash))
.expect("failed finding block 2");
assert_eq!(U64::from(2), mined_block_2.number);
assert_eq!(Some(U64::from(2)), mined_block_2.l1_batch_number);
assert_eq!(U256::from(2001), mined_block_2.timestamp);
}
#[test]
fn test_mine_empty_blocks_mines_2_blocks_with_interval_and_next_block_immediately() {
let node = InMemoryNode::<HttpForkSource>::default();
let inner = node.get_inner();
let starting_block = {
let reader = inner.read().expect("failed acquiring reader");
reader
.block_hashes
.get(&reader.current_miniblock)
.and_then(|hash| reader.blocks.get(hash))
.expect("failed finding block")
.clone()
};
assert_eq!(U64::from(0), starting_block.number);
assert_eq!(Some(U64::from(0)), starting_block.l1_batch_number);
assert_eq!(U256::from(1000), starting_block.timestamp);
{
let mut writer = inner.write().expect("failed acquiring write lock");
mine_empty_blocks(&mut writer, 2, 1000);
}
{
let reader = inner.read().expect("failed acquiring reader");
let mined_block_1 = reader
.block_hashes
.get(&1)
.and_then(|hash| reader.blocks.get(hash))
.expect("failed finding block 1");
assert_eq!(U64::from(1), mined_block_1.number);
assert_eq!(Some(U64::from(1)), mined_block_1.l1_batch_number);
assert_eq!(U256::from(1001), mined_block_1.timestamp);
let mined_block_2 = reader
.block_hashes
.get(&2)
.and_then(|hash| reader.blocks.get(hash))
.expect("failed finding block 2");
assert_eq!(U64::from(2), mined_block_2.number);
assert_eq!(Some(U64::from(2)), mined_block_2.l1_batch_number);
assert_eq!(U256::from(2001), mined_block_2.timestamp);
}
{
testing::apply_tx(&node, H256::repeat_byte(0x1));
let reader = inner.read().expect("failed acquiring reader");
let tx_block_3 = reader
.block_hashes
.get(&3)
.and_then(|hash| reader.blocks.get(hash))
.expect("failed finding block 2");
assert_eq!(U64::from(3), tx_block_3.number);
assert_eq!(Some(U64::from(3)), tx_block_3.l1_batch_number);
assert_eq!(U256::from(2002), tx_block_3.timestamp);
}
}
}