Skip to content

Commit

Permalink
chore(vm): move vm utils outside bwip (matter-labs#936)
Browse files Browse the repository at this point in the history
## What ❔

For future projects, we have to have some utils for executing txs
outside BWIP and zksync_core in general.
In this PR, I've moved the necessary functions, as is to external crate.

p.s. I've not touched the code by itself, I just moved it.

## Why ❔

For correct block execution in different places, e.g. debug methods this
code is pretty useful and could be reused without additional
dependencies

## Checklist

<!-- Check your PR fulfills the following items. -->
<!-- For draft PRs check the boxes as you complete them. -->

- [ ] 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`.
- [ ] Spellcheck has been run via `zk spellcheck`.

---------

Signed-off-by: Danil <[email protected]>
  • Loading branch information
Deniallugo authored Jan 24, 2024
1 parent 638a813 commit b31fdc5
Show file tree
Hide file tree
Showing 12 changed files with 218 additions and 132 deletions.
16 changes: 16 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ members = [
"core/lib/utils",
"core/lib/vlog",
"core/lib/multivm",
"core/lib/vm_utils",
"core/lib/web3_decl",

# Test infrastructure
Expand Down
21 changes: 21 additions & 0 deletions core/lib/vm_utils/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[package]
name = "vm_utils"
version = "0.1.0"
edition = "2021"
authors = ["The Matter Labs Team <[email protected]>"]
homepage = "https://zksync.io/"
repository = "https://github.com/matter-labs/zksync-era"
license = "MIT OR Apache-2.0"
keywords = ["blockchain", "zksync"]
categories = ["cryptography"]

[dependencies]
multivm = { path = "../multivm" }
zksync_types = { path = "../types" }
zksync_dal = { path = "../dal" }
zksync_state = { path = "../state" }
tokio = { version = "1" }
anyhow = "1.0"
tracing = "0.1"
zksync_utils = { path = "../utils" }
zksync_contracts = { path = "../contracts" }
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
pub mod storage;

use anyhow::{anyhow, Context};
use multivm::{
interface::{VmInterface, VmInterfaceHistoryEnabled},
Expand All @@ -9,14 +11,14 @@ use zksync_dal::StorageProcessor;
use zksync_state::{PostgresStorage, StoragePtr, StorageView, WriteStorage};
use zksync_types::{L1BatchNumber, L2ChainId, Transaction};

use crate::state_keeper::io::common::load_l1_batch_params;
use crate::storage::load_l1_batch_params;

pub(super) type VmAndStorage<'a> = (
pub type VmAndStorage<'a> = (
VmInstance<StorageView<PostgresStorage<'a>>, HistoryEnabled>,
StoragePtr<StorageView<PostgresStorage<'a>>>,
);

pub(super) fn create_vm(
pub fn create_vm(
rt_handle: Handle,
l1_batch_number: L1BatchNumber,
mut connection: StorageProcessor<'_>,
Expand Down Expand Up @@ -66,7 +68,7 @@ pub(super) fn create_vm(
Ok((vm, storage_view))
}

pub(super) fn execute_tx<S: WriteStorage>(
pub fn execute_tx<S: WriteStorage>(
tx: &Transaction,
vm: &mut VmInstance<S, HistoryEnabled>,
) -> anyhow::Result<()> {
Expand Down
163 changes: 163 additions & 0 deletions core/lib/vm_utils/src/storage.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
use std::time::{Duration, Instant};

use multivm::{
interface::{L1BatchEnv, L2BlockEnv, SystemEnv, TxExecutionMode},
vm_latest::constants::BLOCK_GAS_LIMIT,
};
use zksync_contracts::BaseSystemContracts;
use zksync_dal::StorageProcessor;
use zksync_types::{
fee_model::BatchFeeInput, Address, L1BatchNumber, L2ChainId, MiniblockNumber,
ProtocolVersionId, H256, U256, ZKPORTER_IS_AVAILABLE,
};
use zksync_utils::{h256_to_u256, u256_to_h256};

pub async fn load_l1_batch_params(
storage: &mut StorageProcessor<'_>,
current_l1_batch_number: L1BatchNumber,
fee_account: Address,
validation_computational_gas_limit: u32,
chain_id: L2ChainId,
) -> Option<(SystemEnv, L1BatchEnv)> {
// If miniblock doesn't exist (for instance if it's pending), it means that there is no unsynced state (i.e. no transactions
// were executed after the last sealed batch).
let pending_miniblock_number = {
let (_, last_miniblock_number_included_in_l1_batch) = storage
.blocks_dal()
.get_miniblock_range_of_l1_batch(current_l1_batch_number - 1)
.await
.unwrap()
.unwrap();
last_miniblock_number_included_in_l1_batch + 1
};
let pending_miniblock_header = storage
.blocks_dal()
.get_miniblock_header(pending_miniblock_number)
.await
.unwrap()?;

tracing::info!("Getting previous batch hash");
let (previous_l1_batch_hash, _) =
wait_for_prev_l1_batch_params(storage, current_l1_batch_number).await;

tracing::info!("Getting previous miniblock hash");
let prev_miniblock_hash = storage
.blocks_dal()
.get_miniblock_header(pending_miniblock_number - 1)
.await
.unwrap()
.unwrap()
.hash;

let base_system_contracts = storage
.storage_dal()
.get_base_system_contracts(
pending_miniblock_header
.base_system_contracts_hashes
.bootloader,
pending_miniblock_header
.base_system_contracts_hashes
.default_aa,
)
.await;

tracing::info!("Previous l1_batch_hash: {}", previous_l1_batch_hash);
Some(l1_batch_params(
current_l1_batch_number,
fee_account,
pending_miniblock_header.timestamp,
previous_l1_batch_hash,
pending_miniblock_header.batch_fee_input,
pending_miniblock_number,
prev_miniblock_hash,
base_system_contracts,
validation_computational_gas_limit,
pending_miniblock_header
.protocol_version
.expect("`protocol_version` must be set for pending miniblock"),
pending_miniblock_header.virtual_blocks,
chain_id,
))
}

pub async fn wait_for_prev_l1_batch_params(
storage: &mut StorageProcessor<'_>,
number: L1BatchNumber,
) -> (U256, u64) {
if number == L1BatchNumber(0) {
return (U256::default(), 0);
}
wait_for_l1_batch_params_unchecked(storage, number - 1).await
}

/// # Warning
///
/// If invoked for a `L1BatchNumber` of a non-existent l1 batch, will block current thread indefinitely.
async fn wait_for_l1_batch_params_unchecked(
storage: &mut StorageProcessor<'_>,
number: L1BatchNumber,
) -> (U256, u64) {
// If the state root is not known yet, this duration will be used to back off in the while loops
const SAFE_STATE_ROOT_INTERVAL: Duration = Duration::from_millis(100);

let stage_started_at: Instant = Instant::now();
loop {
let data = storage
.blocks_dal()
.get_l1_batch_state_root_and_timestamp(number)
.await
.unwrap();
if let Some((root_hash, timestamp)) = data {
tracing::trace!(
"Waiting for hash of L1 batch #{number} took {:?}",
stage_started_at.elapsed()
);
return (h256_to_u256(root_hash), timestamp);
}

tokio::time::sleep(SAFE_STATE_ROOT_INTERVAL).await;
}
}

/// Returns the parameters required to initialize the VM for the next L1 batch.
#[allow(clippy::too_many_arguments)]
pub fn l1_batch_params(
current_l1_batch_number: L1BatchNumber,
fee_account: Address,
l1_batch_timestamp: u64,
previous_batch_hash: U256,
fee_input: BatchFeeInput,
first_miniblock_number: MiniblockNumber,
prev_miniblock_hash: H256,
base_system_contracts: BaseSystemContracts,
validation_computational_gas_limit: u32,
protocol_version: ProtocolVersionId,
virtual_blocks: u32,
chain_id: L2ChainId,
) -> (SystemEnv, L1BatchEnv) {
(
SystemEnv {
zk_porter_available: ZKPORTER_IS_AVAILABLE,
version: protocol_version,
base_system_smart_contracts: base_system_contracts,
gas_limit: BLOCK_GAS_LIMIT,
execution_mode: TxExecutionMode::VerifyExecute,
default_validation_computational_gas_limit: validation_computational_gas_limit,
chain_id,
},
L1BatchEnv {
previous_batch_hash: Some(u256_to_h256(previous_batch_hash)),
number: current_l1_batch_number,
timestamp: l1_batch_timestamp,
fee_input,
fee_account,
enforced_base_fee: None,
first_l2_block: L2BlockEnv {
number: first_miniblock_number.0,
timestamp: l1_batch_timestamp,
prev_block_hash: prev_miniblock_hash,
max_virtual_blocks_to_create: virtual_blocks,
},
},
)
}
1 change: 1 addition & 0 deletions core/lib/zksync_core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ categories = ["cryptography"]
[dependencies]
vise = { git = "https://github.com/matter-labs/vise.git", version = "0.1.0", rev = "1c9cc500e92cf9ea052b230e114a6f9cce4fb2c1" }
zksync_state = { path = "../state" }
vm_utils = { path = "../vm_utils" }
zksync_types = { path = "../types" }
zksync_dal = { path = "../dal" }
zksync_config = { path = "../config" }
Expand Down
8 changes: 2 additions & 6 deletions core/lib/zksync_core/src/basic_witness_input_producer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,15 @@ use anyhow::Context;
use async_trait::async_trait;
use multivm::interface::{L2BlockEnv, VmInterface};
use tokio::{runtime::Handle, task::JoinHandle};
use vm_utils::{create_vm, execute_tx};
use zksync_dal::{basic_witness_input_producer_dal::JOB_MAX_ATTEMPT, ConnectionPool};
use zksync_object_store::{ObjectStore, ObjectStoreFactory};
use zksync_queued_job_processor::JobProcessor;
use zksync_types::{witness_block_state::WitnessBlockState, L1BatchNumber, L2ChainId};

use self::{
metrics::METRICS,
vm_interactions::{create_vm, execute_tx},
};
use self::metrics::METRICS;

mod metrics;
mod vm_interactions;

/// Component that extracts all data (from DB) necessary to run a Basic Witness Generator.
/// Does this by rerunning an entire L1Batch and extracting information from both the VM run and DB.
/// This component will upload Witness Inputs to the object store.
Expand Down
48 changes: 1 addition & 47 deletions core/lib/zksync_core/src/state_keeper/extractors.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,8 @@
//! Pure functions that convert data as required by the state keeper.
use std::{
convert::TryFrom,
fmt,
time::{Duration, Instant},
};
use std::{convert::TryFrom, fmt};

use chrono::{DateTime, TimeZone, Utc};
use zksync_dal::StorageProcessor;
use zksync_types::{L1BatchNumber, U256};
use zksync_utils::h256_to_u256;

/// Displays a Unix timestamp (seconds since epoch) in human-readable form. Useful for logging.
pub(super) fn display_timestamp(timestamp: u64) -> impl fmt::Display {
Expand All @@ -34,42 +27,3 @@ pub(super) fn display_timestamp(timestamp: u64) -> impl fmt::Display {
DisplayedTimestamp::Parsed,
)
}

pub(crate) async fn wait_for_prev_l1_batch_params(
storage: &mut StorageProcessor<'_>,
number: L1BatchNumber,
) -> (U256, u64) {
if number == L1BatchNumber(0) {
return (U256::default(), 0);
}
wait_for_l1_batch_params_unchecked(storage, number - 1).await
}

/// # Warning
///
/// If invoked for a `L1BatchNumber` of a non-existent l1 batch, will block current thread indefinitely.
async fn wait_for_l1_batch_params_unchecked(
storage: &mut StorageProcessor<'_>,
number: L1BatchNumber,
) -> (U256, u64) {
// If the state root is not known yet, this duration will be used to back off in the while loops
const SAFE_STATE_ROOT_INTERVAL: Duration = Duration::from_millis(100);

let stage_started_at: Instant = Instant::now();
loop {
let data = storage
.blocks_dal()
.get_l1_batch_state_root_and_timestamp(number)
.await
.unwrap();
if let Some((root_hash, timestamp)) = data {
tracing::trace!(
"Waiting for hash of L1 batch #{number} took {:?}",
stage_started_at.elapsed()
);
return (h256_to_u256(root_hash), timestamp);
}

tokio::time::sleep(SAFE_STATE_ROOT_INTERVAL).await;
}
}
Loading

0 comments on commit b31fdc5

Please sign in to comment.