Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(api): Integrate new VM into API server (no tracers) #3033

Merged
merged 26 commits into from
Oct 25, 2024
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
8d99366
Sketch fast VM integration in oneshot executor
slowli Sep 25, 2024
1d81e88
Move `StorageWithOverrides` to VM interface
slowli Sep 25, 2024
741b1ad
Use `StorageWithOverrides` in oneshot executor
slowli Sep 25, 2024
50738df
Split fast VM modes for different ops
slowli Oct 2, 2024
053d510
Do not panic on divergence in oneshot executor
slowli Oct 8, 2024
d9e175c
Add basic tests for oneshot executor
slowli Oct 8, 2024
86fe4a8
Fix metrics reporting for new VM
slowli Oct 8, 2024
b14f307
Add config option for gas estimation VM mode
slowli Oct 8, 2024
e87471c
Fix `TxHasEnded` hook processing divergence
slowli Oct 8, 2024
2a230ba
Test estimating gas for L1 transaction
slowli Oct 8, 2024
12fdfbc
Enable fast VM for gas estimation in CI load test
slowli Oct 8, 2024
b4a59c9
Refactor `VmSandbox`
slowli Oct 8, 2024
35dcd5b
Optimize gas estimation for "new" load test
slowli Oct 8, 2024
f33bf6e
Update from upstream
slowli Oct 9, 2024
c25405b
Update from upstream
slowli Oct 14, 2024
47979e8
Deduplicate test code
slowli Oct 14, 2024
bb9ad48
Update from upstream
slowli Oct 16, 2024
f312f5f
Override config for integration tests
slowli Oct 18, 2024
6588b01
Update from upstream
slowli Oct 18, 2024
e226ec6
Update from upstream
slowli Oct 18, 2024
3d82560
Merge branch 'main' into aov-pla-1038-integrate-new-vm-into-api-serve…
joonazan Oct 18, 2024
bcef331
Use single VM mode for oneshot executor ops
slowli Oct 21, 2024
79cd205
Update config parameter naming
slowli Oct 21, 2024
02d7594
Update from upstream
slowli Oct 21, 2024
5c11534
Update from upstream
slowli Oct 24, 2024
69d76a5
Increase expected TPS for load test with new VM
slowli Oct 24, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions .github/workflows/ci-core-reusable.yml
Original file line number Diff line number Diff line change
Expand Up @@ -356,12 +356,16 @@ jobs:

- name: Run servers
run: |
# Override config for part of chains to test the default config as well
ci_run zkstack dev config-writer --path etc/env/file_based/overrides/tests/integration.yaml --chain era
ci_run zkstack dev config-writer --path etc/env/file_based/overrides/tests/integration.yaml --chain validium

ci_run zkstack server --ignore-prerequisites --chain era &> ${{ env.SERVER_LOGS_DIR }}/rollup.log &
ci_run zkstack server --ignore-prerequisites --chain validium &> ${{ env.SERVER_LOGS_DIR }}/validium.log &
ci_run zkstack server --ignore-prerequisites --chain custom_token &> ${{ env.SERVER_LOGS_DIR }}/custom_token.log &
ci_run zkstack server --ignore-prerequisites --chain consensus \
--components=api,tree,eth,state_keeper,housekeeper,commitment_generator,vm_runner_protective_reads,vm_runner_bwip,vm_playground,da_dispatcher,consensus \
&> ${{ env.SERVER_LOGS_DIR }}/consensus.log &
--components=api,tree,eth,state_keeper,housekeeper,commitment_generator,vm_runner_protective_reads,vm_runner_bwip,vm_playground,da_dispatcher,consensus \
&> ${{ env.SERVER_LOGS_DIR }}/consensus.log &

ci_run sleep 5

Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

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

8 changes: 6 additions & 2 deletions core/bin/zksync_server/src/node_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,10 +309,12 @@ impl MainNodeBuilder {
latest_values_cache_size: rpc_config.latest_values_cache_size() as u64,
latest_values_max_block_lag: rpc_config.latest_values_max_block_lag(),
};
let vm_config = try_load_config!(self.configs.experimental_vm_config);

// On main node we always use master pool sink.
self.node.add_layer(MasterPoolSinkLayer);
self.node.add_layer(TxSenderLayer::new(

let layer = TxSenderLayer::new(
TxSenderConfig::new(
&sk_config,
&rpc_config,
Expand All @@ -323,7 +325,9 @@ impl MainNodeBuilder {
),
postgres_storage_caches_config,
rpc_config.vm_concurrency_limit(),
));
);
let layer = layer.with_vm_mode(vm_config.api_fast_vm_mode);
self.node.add_layer(layer);
Ok(self)
}

Expand Down
5 changes: 5 additions & 0 deletions core/lib/config/src/configs/experimental.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,4 +106,9 @@ pub struct ExperimentalVmConfig {
/// the new VM doesn't produce call traces and can diverge from the old VM!
#[serde(default)]
pub state_keeper_fast_vm_mode: FastVmMode,

/// Fast VM mode to use in the API server. Currently, some operations are not supported by the fast VM (e.g., `debug_traceCall`
/// or transaction validation), so the legacy VM will always be used for them.
#[serde(default)]
pub api_fast_vm_mode: FastVmMode,
}
1 change: 1 addition & 0 deletions core/lib/config/src/testonly.rs
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,7 @@ impl Distribution<configs::ExperimentalVmConfig> for EncodeDist {
configs::ExperimentalVmConfig {
playground: self.sample(rng),
state_keeper_fast_vm_mode: gen_fast_vm_mode(rng),
api_fast_vm_mode: gen_fast_vm_mode(rng),
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions core/lib/env_config/src/vm_runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ mod tests {
let mut lock = MUTEX.lock();
let config = r#"
EXPERIMENTAL_VM_STATE_KEEPER_FAST_VM_MODE=new
EXPERIMENTAL_VM_API_FAST_VM_MODE=shadow
EXPERIMENTAL_VM_PLAYGROUND_FAST_VM_MODE=shadow
EXPERIMENTAL_VM_PLAYGROUND_DB_PATH=/db/vm_playground
EXPERIMENTAL_VM_PLAYGROUND_FIRST_PROCESSED_BATCH=123
Expand All @@ -64,6 +65,7 @@ mod tests {

let config = ExperimentalVmConfig::from_env().unwrap();
assert_eq!(config.state_keeper_fast_vm_mode, FastVmMode::New);
assert_eq!(config.api_fast_vm_mode, FastVmMode::Shadow);
assert_eq!(config.playground.fast_vm_mode, FastVmMode::Shadow);
assert_eq!(config.playground.db_path.unwrap(), "/db/vm_playground");
assert_eq!(config.playground.first_processed_batch, L1BatchNumber(123));
Expand Down
49 changes: 46 additions & 3 deletions core/lib/multivm/src/versions/testonly/l1_tx_execution.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
use assert_matches::assert_matches;
use ethabi::Token;
use zksync_contracts::l1_messenger_contract;
use zksync_system_constants::{BOOTLOADER_ADDRESS, L1_MESSENGER_ADDRESS};
use zksync_test_account::TxType;
use zksync_types::{
get_code_key, get_known_code_key,
l2_to_l1_log::{L2ToL1Log, UserL2ToL1Log},
Execute, ExecuteTransactionCommon, U256,
Address, Execute, ExecuteTransactionCommon, U256,
};
use zksync_utils::{h256_to_u256, u256_to_h256};

use super::{read_test_contract, tester::VmTesterBuilder, TestedVm, BASE_SYSTEM_CONTRACTS};
use super::{
read_test_contract, tester::VmTesterBuilder, ContractToDeploy, TestedVm, BASE_SYSTEM_CONTRACTS,
};
use crate::{
interface::{TxExecutionMode, VmExecutionMode, VmInterfaceExt},
interface::{
ExecutionResult, TxExecutionMode, VmExecutionMode, VmInterfaceExt, VmRevertReason,
},
utils::StorageWritesDeduplicator,
};

Expand Down Expand Up @@ -180,3 +185,41 @@ pub(crate) fn test_l1_tx_execution_high_gas_limit<VM: TestedVm>() {

assert!(res.result.is_failed(), "The transaction should've failed");
}

pub(crate) fn test_l1_tx_execution_gas_estimation_with_low_gas<VM: TestedVm>() {
let counter_contract = read_test_contract();
let counter_address = Address::repeat_byte(0x11);
let mut vm = VmTesterBuilder::new()
.with_empty_in_memory_storage()
.with_base_system_smart_contracts(BASE_SYSTEM_CONTRACTS.clone())
.with_execution_mode(TxExecutionMode::EstimateFee)
.with_custom_contracts(vec![ContractToDeploy::new(
counter_contract,
counter_address,
)])
.with_rich_accounts(1)
.build::<VM>();

let account = &mut vm.rich_accounts[0];
let mut tx = account.get_test_contract_transaction(
counter_address,
false,
None,
false,
TxType::L1 { serial_id: 0 },
);
let ExecuteTransactionCommon::L1(data) = &mut tx.common_data else {
unreachable!();
};
// This gas limit is chosen so that transaction starts getting executed by the bootloader, but then runs out of gas
// before its execution result is posted.
data.gas_limit = 15_000.into();

vm.vm.push_transaction(tx);
let res = vm.vm.execute(VmExecutionMode::OneTx);
assert_matches!(
&res.result,
ExecutionResult::Revert { output: VmRevertReason::General { msg, .. } }
if msg.contains("reverted with empty reason")
);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::{
versions::testonly::l1_tx_execution::{
test_l1_tx_execution, test_l1_tx_execution_high_gas_limit,
test_l1_tx_execution, test_l1_tx_execution_gas_estimation_with_low_gas,
test_l1_tx_execution_high_gas_limit,
},
vm_fast::Vm,
};
Expand All @@ -14,3 +15,8 @@ fn l1_tx_execution() {
fn l1_tx_execution_high_gas_limit() {
test_l1_tx_execution_high_gas_limit::<Vm<_>>();
}

#[test]
fn l1_tx_execution_gas_estimation_with_low_gas() {
test_l1_tx_execution_gas_estimation_with_low_gas::<Vm<_>>();
}
27 changes: 24 additions & 3 deletions core/lib/multivm/src/versions/vm_fast/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ use crate::{
},
vm_latest::{
constants::{
get_vm_hook_params_start_position, get_vm_hook_position, OPERATOR_REFUNDS_OFFSET,
TX_GAS_LIMIT_OFFSET, VM_HOOK_PARAMS_COUNT,
get_result_success_first_slot, get_vm_hook_params_start_position, get_vm_hook_position,
OPERATOR_REFUNDS_OFFSET, TX_GAS_LIMIT_OFFSET, VM_HOOK_PARAMS_COUNT,
},
MultiVMSubversion,
},
Expand Down Expand Up @@ -212,7 +212,22 @@ impl<S: ReadStorage, Tr: Tracer> Vm<S, Tr> {
}
Hook::TxHasEnded => {
if let VmExecutionMode::OneTx = execution_mode {
break (last_tx_result.take().unwrap(), false);
// The bootloader may invoke `TxHasEnded` hook without posting a tx result previously. One case when this can happen
// is estimating gas for L1 transactions, if a transaction runs out of gas during execution.
let tx_result = last_tx_result.take().unwrap_or_else(|| {
let tx_has_failed = self.get_tx_result().is_zero();
if tx_has_failed {
let output = VmRevertReason::General {
msg: "Transaction reverted with empty reason. Possibly out of gas"
.to_string(),
data: vec![],
};
ExecutionResult::Revert { output }
} else {
ExecutionResult::Success { output: vec![] }
slowli marked this conversation as resolved.
Show resolved Hide resolved
}
});
break (tx_result, false);
}
}
Hook::AskOperatorForRefund => {
Expand Down Expand Up @@ -360,6 +375,12 @@ impl<S: ReadStorage, Tr: Tracer> Vm<S, Tr> {
.unwrap()
}

fn get_tx_result(&self) -> U256 {
let tx_idx = self.bootloader_state.current_tx();
let slot = get_result_success_first_slot(VM_VERSION) as usize + tx_idx;
self.read_word_from_bootloader_heap(slot)
}

fn get_debug_log(&self) -> (String, String) {
let hook_params = self.get_hook_params();
let mut msg = u256_to_h256(hook_params[0]).as_bytes().to_vec();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::{
versions::testonly::l1_tx_execution::{
test_l1_tx_execution, test_l1_tx_execution_high_gas_limit,
test_l1_tx_execution, test_l1_tx_execution_gas_estimation_with_low_gas,
test_l1_tx_execution_high_gas_limit,
},
vm_latest::{HistoryEnabled, Vm},
};
Expand All @@ -14,3 +15,8 @@ fn l1_tx_execution() {
fn l1_tx_execution_high_gas_limit() {
test_l1_tx_execution_high_gas_limit::<Vm<_, HistoryEnabled>>();
}

#[test]
fn l1_tx_execution_gas_estimation_with_low_gas() {
test_l1_tx_execution_gas_estimation_with_low_gas::<Vm<_, HistoryEnabled>>();
}
2 changes: 1 addition & 1 deletion core/lib/multivm/src/vm_instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ pub type ShadowedFastVm<S, Tr = ()> = ShadowVm<

/// Fast VM variants.
#[derive(Debug)]
pub enum FastVmInstance<S: ReadStorage, Tr> {
pub enum FastVmInstance<S: ReadStorage, Tr = ()> {
/// Fast VM running in isolation.
Fast(crate::vm_fast::Vm<ImmutableStorageView<S>, Tr>),
/// Fast VM shadowed by the latest legacy VM.
Expand Down
17 changes: 11 additions & 6 deletions core/lib/protobuf_config/src/experimental.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ use zksync_protobuf::{repr::ProtoRepr, required};

use crate::{proto::experimental as proto, read_optional_repr};

fn parse_vm_mode(raw: Option<i32>) -> anyhow::Result<FastVmMode> {
Ok(raw
.map(proto::FastVmMode::try_from)
.transpose()
.context("fast_vm_mode")?
.map_or_else(FastVmMode::default, |mode| mode.parse()))
}

impl ProtoRepr for proto::Db {
type Type = configs::ExperimentalDBConfig;

Expand Down Expand Up @@ -105,12 +113,8 @@ impl ProtoRepr for proto::Vm {
fn read(&self) -> anyhow::Result<Self::Type> {
Ok(Self::Type {
playground: read_optional_repr(&self.playground).unwrap_or_default(),
state_keeper_fast_vm_mode: self
.state_keeper_fast_vm_mode
.map(proto::FastVmMode::try_from)
.transpose()
.context("fast_vm_mode")?
.map_or_else(FastVmMode::default, |mode| mode.parse()),
state_keeper_fast_vm_mode: parse_vm_mode(self.state_keeper_fast_vm_mode)?,
api_fast_vm_mode: parse_vm_mode(self.api_fast_vm_mode)?,
})
}

Expand All @@ -120,6 +124,7 @@ impl ProtoRepr for proto::Vm {
state_keeper_fast_vm_mode: Some(
proto::FastVmMode::new(this.state_keeper_fast_vm_mode).into(),
),
api_fast_vm_mode: Some(proto::FastVmMode::new(this.api_fast_vm_mode).into()),
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,5 @@ message VmPlayground {
message Vm {
optional VmPlayground playground = 1; // optional
optional FastVmMode state_keeper_fast_vm_mode = 2; // optional; if not set, fast VM is not used
optional FastVmMode api_fast_vm_mode = 3; // optional; if not set, fast VM is not used
}
1 change: 1 addition & 0 deletions core/lib/vm_executor/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,4 @@ vise.workspace = true

[dev-dependencies]
assert_matches.workspace = true
test-casing.workspace = true
16 changes: 12 additions & 4 deletions core/lib/vm_executor/src/oneshot/metrics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ pub(super) fn report_vm_memory_metrics(
tx_id: &str,
memory_metrics: &VmMemoryMetrics,
vm_execution_took: Duration,
storage_metrics: &StorageViewStats,
storage_stats: &StorageViewStats,
) {
MEMORY_METRICS.event_sink_size[&SizeType::Inner].observe(memory_metrics.event_sink_inner);
MEMORY_METRICS.event_sink_size[&SizeType::History].observe(memory_metrics.event_sink_history);
Expand All @@ -65,10 +65,18 @@ pub(super) fn report_vm_memory_metrics(

MEMORY_METRICS
.storage_view_cache_size
.observe(storage_metrics.cache_size);
.observe(storage_stats.cache_size);
MEMORY_METRICS
.full
.observe(memory_metrics.full_size() + storage_metrics.cache_size);
.observe(memory_metrics.full_size() + storage_stats.cache_size);

STORAGE_METRICS.observe(&format!("Tx {tx_id}"), vm_execution_took, storage_metrics);
report_vm_storage_metrics(tx_id, vm_execution_took, storage_stats);
}

pub(super) fn report_vm_storage_metrics(
tx_id: &str,
vm_execution_took: Duration,
storage_stats: &StorageViewStats,
) {
STORAGE_METRICS.observe(&format!("Tx {tx_id}"), vm_execution_took, storage_stats);
}
Loading
Loading