diff --git a/Cargo.lock b/Cargo.lock index 32ee93ab650..bb78ae0cd55 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8512,6 +8512,7 @@ dependencies = [ "tokio", "tracing", "vise", + "zksync_concurrency", "zksync_consensus_roles", "zksync_consensus_storage", "zksync_contracts", @@ -8519,6 +8520,7 @@ dependencies = [ "zksync_protobuf", "zksync_protobuf_build", "zksync_system_constants", + "zksync_test_account", "zksync_types", "zksync_utils", ] @@ -8905,6 +8907,7 @@ dependencies = [ "zksync_consensus_roles", "zksync_consensus_storage", "zksync_consensus_utils", + "zksync_contracts", "zksync_dal", "zksync_l1_contract_interface", "zksync_merkle_tree", @@ -9438,6 +9441,7 @@ version = "0.1.0" dependencies = [ "ethabi", "hex", + "rand 0.8.5", "zksync_contracts", "zksync_eth_signer", "zksync_system_constants", diff --git a/core/lib/basic_types/src/protocol_version.rs b/core/lib/basic_types/src/protocol_version.rs index d8083c0f6a3..f0d12436e3b 100644 --- a/core/lib/basic_types/src/protocol_version.rs +++ b/core/lib/basic_types/src/protocol_version.rs @@ -71,11 +71,11 @@ pub enum ProtocolVersionId { } impl ProtocolVersionId { - pub fn latest() -> Self { + pub const fn latest() -> Self { Self::Version24 } - pub fn next() -> Self { + pub const fn next() -> Self { Self::Version25 } diff --git a/core/lib/dal/Cargo.toml b/core/lib/dal/Cargo.toml index 034f252f7e5..aa1d7097b9b 100644 --- a/core/lib/dal/Cargo.toml +++ b/core/lib/dal/Cargo.toml @@ -49,5 +49,9 @@ strum = { workspace = true, features = ["derive"] } tracing.workspace = true chrono = { workspace = true, features = ["serde"] } +[dev-dependencies] +zksync_test_account.workspace = true +zksync_concurrency.workspace = true + [build-dependencies] zksync_protobuf_build.workspace = true diff --git a/core/lib/dal/src/consensus/mod.rs b/core/lib/dal/src/consensus/mod.rs index 8e1f246b657..fac045ce222 100644 --- a/core/lib/dal/src/consensus/mod.rs +++ b/core/lib/dal/src/consensus/mod.rs @@ -7,6 +7,7 @@ use anyhow::{anyhow, Context as _}; use zksync_consensus_roles::validator; use zksync_protobuf::{required, ProtoFmt, ProtoRepr}; use zksync_types::{ + abi, ethabi, fee::Fee, l1::{OpProcessingType, PriorityQueueType}, l2::TransactionType, @@ -38,38 +39,59 @@ pub struct Payload { impl ProtoFmt for Payload { type Proto = proto::Payload; - fn read(message: &Self::Proto) -> anyhow::Result { - let mut transactions = Vec::with_capacity(message.transactions.len()); - for (i, tx) in message.transactions.iter().enumerate() { - transactions.push(tx.read().with_context(|| format!("transactions[{i}]"))?) + fn read(r: &Self::Proto) -> anyhow::Result { + let protocol_version = required(&r.protocol_version) + .and_then(|x| Ok(ProtocolVersionId::try_from(u16::try_from(*x)?)?)) + .context("protocol_version")?; + let mut transactions = vec![]; + + match protocol_version { + v if v >= ProtocolVersionId::Version25 => { + anyhow::ensure!( + r.transactions.is_empty(), + "transactions should be empty in protocol_version {v}" + ); + for (i, tx) in r.transactions_v25.iter().enumerate() { + transactions.push( + tx.read() + .with_context(|| format!("transactions_v25[{i}]"))?, + ); + } + } + v => { + anyhow::ensure!( + r.transactions_v25.is_empty(), + "transactions_v25 should be empty in protocol_version {v}" + ); + for (i, tx) in r.transactions.iter().enumerate() { + transactions.push(tx.read().with_context(|| format!("transactions[{i}]"))?) + } + } } Ok(Self { - protocol_version: required(&message.protocol_version) - .and_then(|x| Ok(ProtocolVersionId::try_from(u16::try_from(*x)?)?)) - .context("protocol_version")?, - hash: required(&message.hash) + protocol_version, + hash: required(&r.hash) .and_then(|h| parse_h256(h)) .context("hash")?, l1_batch_number: L1BatchNumber( - *required(&message.l1_batch_number).context("l1_batch_number")?, + *required(&r.l1_batch_number).context("l1_batch_number")?, ), - timestamp: *required(&message.timestamp).context("timestamp")?, - l1_gas_price: *required(&message.l1_gas_price).context("l1_gas_price")?, - l2_fair_gas_price: *required(&message.l2_fair_gas_price) - .context("l2_fair_gas_price")?, - fair_pubdata_price: message.fair_pubdata_price, - virtual_blocks: *required(&message.virtual_blocks).context("virtual_blocks")?, - operator_address: required(&message.operator_address) + timestamp: *required(&r.timestamp).context("timestamp")?, + l1_gas_price: *required(&r.l1_gas_price).context("l1_gas_price")?, + l2_fair_gas_price: *required(&r.l2_fair_gas_price).context("l2_fair_gas_price")?, + fair_pubdata_price: r.fair_pubdata_price, + virtual_blocks: *required(&r.virtual_blocks).context("virtual_blocks")?, + operator_address: required(&r.operator_address) .and_then(|a| parse_h160(a)) .context("operator_address")?, transactions, - last_in_batch: *required(&message.last_in_batch).context("last_in_batch")?, + last_in_batch: *required(&r.last_in_batch).context("last_in_batch")?, }) } fn build(&self) -> Self::Proto { - Self::Proto { + let mut x = Self::Proto { protocol_version: Some((self.protocol_version as u16).into()), hash: Some(self.hash.as_bytes().into()), l1_batch_number: Some(self.l1_batch_number.0), @@ -80,13 +102,19 @@ impl ProtoFmt for Payload { virtual_blocks: Some(self.virtual_blocks), operator_address: Some(self.operator_address.as_bytes().into()), // Transactions are stored in execution order, therefore order is deterministic. - transactions: self - .transactions - .iter() - .map(proto::Transaction::build) - .collect(), + transactions: vec![], + transactions_v25: vec![], last_in_batch: Some(self.last_in_batch), + }; + match self.protocol_version { + v if v >= ProtocolVersionId::Version25 => { + x.transactions_v25 = self.transactions.iter().map(ProtoRepr::build).collect(); + } + _ => { + x.transactions = self.transactions.iter().map(ProtoRepr::build).collect(); + } } + x } } @@ -100,6 +128,50 @@ impl Payload { } } +impl ProtoRepr for proto::TransactionV25 { + type Type = Transaction; + + fn read(&self) -> anyhow::Result { + use proto::transaction_v25::T; + let tx = match required(&self.t)? { + T::L1(l1) => abi::Transaction::L1 { + tx: required(&l1.rlp) + .and_then(|x| { + let tokens = ethabi::decode(&[abi::L2CanonicalTransaction::schema()], x) + .context("ethabi::decode()")?; + // Unwrap is safe because `ethabi::decode` does the verification. + let tx = + abi::L2CanonicalTransaction::decode(tokens.into_iter().next().unwrap()) + .context("L2CanonicalTransaction::decode()")?; + Ok(tx) + }) + .context("rlp")? + .into(), + factory_deps: l1.factory_deps.clone(), + eth_block: 0, + }, + T::L2(l2) => abi::Transaction::L2(required(&l2.rlp).context("rlp")?.clone()), + }; + tx.try_into() + } + + fn build(tx: &Self::Type) -> Self { + let tx = abi::Transaction::try_from(tx.clone()).unwrap(); + use proto::transaction_v25::T; + Self { + t: Some(match tx { + abi::Transaction::L1 { + tx, factory_deps, .. + } => T::L1(proto::L1Transaction { + rlp: Some(ethabi::encode(&[tx.encode()])), + factory_deps, + }), + abi::Transaction::L2(tx) => T::L2(proto::L2Transaction { rlp: Some(tx) }), + }), + } + } +} + impl ProtoRepr for proto::Transaction { type Type = Transaction; diff --git a/core/lib/dal/src/consensus/proto/mod.proto b/core/lib/dal/src/consensus/proto/mod.proto index a5364761183..a7b5ea34415 100644 --- a/core/lib/dal/src/consensus/proto/mod.proto +++ b/core/lib/dal/src/consensus/proto/mod.proto @@ -13,10 +13,30 @@ message Payload { optional uint64 fair_pubdata_price = 11; // required since 1.4.1; gwei optional uint32 virtual_blocks = 6; // required optional bytes operator_address = 7; // required; H160 + // Set for protocol_version < 25. repeated Transaction transactions = 8; + // Set for protocol_version >= 25. + repeated TransactionV25 transactions_v25 = 12; optional bool last_in_batch = 10; // required } +message L1Transaction { + optional bytes rlp = 1; // required; RLP encoded L2CanonicalTransaction + repeated bytes factory_deps = 2; +} + +message L2Transaction { + optional bytes rlp = 1; // required; RLP encoded TransactionRequest +} + +message TransactionV25 { + // required + oneof t { + L1Transaction l1 = 1; + L2Transaction l2 = 2; + } +} + message Transaction { reserved 5; reserved "received_timestamp_ms"; diff --git a/core/lib/dal/src/consensus/tests.rs b/core/lib/dal/src/consensus/tests.rs index 694634f11a8..4a69bebdc36 100644 --- a/core/lib/dal/src/consensus/tests.rs +++ b/core/lib/dal/src/consensus/tests.rs @@ -1,21 +1,75 @@ use std::fmt::Debug; +use rand::Rng; +use zksync_concurrency::ctx; use zksync_protobuf::{ repr::{decode, encode}, + testonly::test_encode, ProtoRepr, }; -use zksync_types::{web3::Bytes, Execute, ExecuteTransactionCommon, Transaction}; +use zksync_test_account::Account; +use zksync_types::{ + web3::Bytes, Execute, ExecuteTransactionCommon, L1BatchNumber, ProtocolVersionId, Transaction, +}; + +use super::{proto, Payload}; +use crate::tests::mock_protocol_upgrade_transaction; + +fn execute(rng: &mut impl Rng) -> Execute { + Execute { + contract_address: rng.gen(), + value: rng.gen::().into(), + calldata: (0..10 * 32).map(|_| rng.gen()).collect(), + // TODO: find a way to generate valid random bytecode. + factory_deps: vec![], + } +} -use crate::tests::{mock_l1_execute, mock_l2_transaction, mock_protocol_upgrade_transaction}; +fn l1_transaction(rng: &mut impl Rng) -> Transaction { + Account::random_using(rng).get_l1_tx(execute(rng), rng.gen()) +} + +fn l2_transaction(rng: &mut impl Rng) -> Transaction { + Account::random_using(rng).get_l2_tx_for_execute(execute(rng), None) +} + +fn payload(rng: &mut impl Rng, protocol_version: ProtocolVersionId) -> Payload { + Payload { + protocol_version, + hash: rng.gen(), + l1_batch_number: L1BatchNumber(rng.gen()), + timestamp: rng.gen(), + l1_gas_price: rng.gen(), + l2_fair_gas_price: rng.gen(), + fair_pubdata_price: Some(rng.gen()), + virtual_blocks: rng.gen(), + operator_address: rng.gen(), + transactions: (0..10) + .map(|_| match rng.gen() { + true => l1_transaction(rng), + false => l2_transaction(rng), + }) + .collect(), + last_in_batch: rng.gen(), + } +} /// Tests struct <-> proto struct conversions. #[test] fn test_encoding() { - encode_decode::(mock_l1_execute().into()); - encode_decode::(mock_l2_transaction().into()); - encode_decode::( + let ctx = &ctx::test_root(&ctx::RealClock); + let rng = &mut ctx.rng(); + encode_decode::(l1_transaction(rng)); + encode_decode::(l2_transaction(rng)); + encode_decode::(l1_transaction(rng)); + encode_decode::(l2_transaction(rng)); + encode_decode::( mock_protocol_upgrade_transaction().into(), ); + let p = payload(rng, ProtocolVersionId::Version24); + test_encode(rng, &p); + let p = payload(rng, ProtocolVersionId::Version25); + test_encode(rng, &p); } fn encode_decode(msg: P::Type) diff --git a/core/node/consensus/Cargo.toml b/core/node/consensus/Cargo.toml index b22fde34e7c..5fc95b6c91f 100644 --- a/core/node/consensus/Cargo.toml +++ b/core/node/consensus/Cargo.toml @@ -43,6 +43,7 @@ zksync_node_genesis.workspace = true zksync_node_test_utils.workspace = true zksync_node_api_server.workspace = true zksync_test_account.workspace = true +zksync_contracts.workspace= true tokio.workspace = true test-casing.workspace = true diff --git a/core/node/consensus/src/storage/mod.rs b/core/node/consensus/src/storage/mod.rs index cf45f89ad11..bc8a0b8b840 100644 --- a/core/node/consensus/src/storage/mod.rs +++ b/core/node/consensus/src/storage/mod.rs @@ -18,7 +18,7 @@ use zksync_types::{commitment::L1BatchWithMetadata, L1BatchNumber, L2BlockNumber use super::config; #[cfg(test)] -mod testonly; +pub(crate) mod testonly; /// Context-aware `zksync_dal::ConnectionPool` wrapper. #[derive(Debug, Clone)] diff --git a/core/node/consensus/src/storage/testonly.rs b/core/node/consensus/src/storage/testonly.rs index ccac1f7e45a..f5f30021b7c 100644 --- a/core/node/consensus/src/storage/testonly.rs +++ b/core/node/consensus/src/storage/testonly.rs @@ -3,13 +3,49 @@ use anyhow::Context as _; use zksync_concurrency::{ctx, error::Wrap as _, time}; use zksync_consensus_roles::validator; -use zksync_node_genesis::{insert_genesis_batch, GenesisParams}; +use zksync_contracts::BaseSystemContracts; +use zksync_node_genesis::{insert_genesis_batch, mock_genesis_config, GenesisParams}; use zksync_node_test_utils::{recover, snapshot, Snapshot}; -use zksync_types::{commitment::L1BatchWithMetadata, L1BatchNumber}; +use zksync_types::{ + commitment::L1BatchWithMetadata, protocol_version::ProtocolSemanticVersion, + system_contracts::get_system_smart_contracts, L1BatchNumber, L2BlockNumber, ProtocolVersionId, +}; use super::ConnectionPool; +pub(crate) fn mock_genesis_params(protocol_version: ProtocolVersionId) -> GenesisParams { + let mut cfg = mock_genesis_config(); + cfg.protocol_version = Some(ProtocolSemanticVersion { + minor: protocol_version, + patch: 0.into(), + }); + GenesisParams::from_genesis_config( + cfg, + BaseSystemContracts::load_from_disk(), + get_system_smart_contracts(), + ) + .unwrap() +} + impl ConnectionPool { + pub(crate) async fn test( + from_snapshot: bool, + protocol_version: ProtocolVersionId, + ) -> ConnectionPool { + match from_snapshot { + true => { + ConnectionPool::from_snapshot(Snapshot::make( + L1BatchNumber(23), + L2BlockNumber(87), + &[], + mock_genesis_params(protocol_version), + )) + .await + } + false => ConnectionPool::from_genesis(protocol_version).await, + } + } + /// Waits for the `number` L2 block to have a certificate. pub async fn wait_for_certificate( &self, @@ -60,11 +96,11 @@ impl ConnectionPool { } /// Constructs a new db initialized with genesis state. - pub(crate) async fn from_genesis() -> Self { + pub(crate) async fn from_genesis(protocol_version: ProtocolVersionId) -> Self { let pool = zksync_dal::ConnectionPool::test_pool().await; { let mut storage = pool.connection().await.unwrap(); - insert_genesis_batch(&mut storage, &GenesisParams::mock()) + insert_genesis_batch(&mut storage, &mock_genesis_params(protocol_version)) .await .unwrap(); } diff --git a/core/node/consensus/src/testonly.rs b/core/node/consensus/src/testonly.rs index 5baa1c7b1ee..ce16efed222 100644 --- a/core/node/consensus/src/testonly.rs +++ b/core/node/consensus/src/testonly.rs @@ -54,6 +54,7 @@ use crate::{ /// Fake StateKeeper for tests. pub(super) struct StateKeeper { + protocol_version: ProtocolVersionId, // Batch of the `last_block`. last_batch: L1BatchNumber, last_block: L2BlockNumber, @@ -130,6 +131,16 @@ impl StateKeeper { pool: ConnectionPool, ) -> ctx::Result<(Self, StateKeeperRunner)> { let mut conn = pool.connection(ctx).await.wrap("connection()")?; + // We fetch the last protocol version from storage. + // `protocol_version_id_by_timestamp` does a wrapping conversion to `i64`. + let protocol_version = ctx + .wait( + conn.0 + .protocol_versions_dal() + .protocol_version_id_by_timestamp(i64::MAX.try_into().unwrap()), + ) + .await? + .context("protocol_version_id_by_timestamp()")?; let cursor = ctx .wait(IoCursor::for_fetcher(&mut conn.0)) .await? @@ -164,6 +175,7 @@ impl StateKeeper { let account = Account::random(); Ok(( Self { + protocol_version, last_batch: cursor.l1_batch, last_block: cursor.next_l2_block - 1, last_timestamp: cursor.prev_l2_block_timestamp, @@ -196,7 +208,7 @@ impl StateKeeper { self.batch_sealed = false; SyncAction::OpenBatch { params: L1BatchParams { - protocol_version: ProtocolVersionId::latest(), + protocol_version: self.protocol_version, validation_computational_gas_limit: u32::MAX, operator_address: GenesisParams::mock().config().fee_account, fee_input: BatchFeeInput::L1Pegged(L1PeggedBatchFeeModelInput { diff --git a/core/node/consensus/src/tests.rs b/core/node/consensus/src/tests.rs index 79784f0fbb5..b16c66e478b 100644 --- a/core/node/consensus/src/tests.rs +++ b/core/node/consensus/src/tests.rs @@ -1,6 +1,6 @@ #![allow(unused)] use anyhow::Context as _; -use test_casing::test_casing; +use test_casing::{test_casing, Product}; use tracing::Instrument as _; use zksync_concurrency::{ctx, scope}; use zksync_config::configs::consensus::{ValidatorPublicKey, WeightedValidator}; @@ -12,26 +12,20 @@ use zksync_consensus_roles::{ }; use zksync_dal::CoreDal; use zksync_node_test_utils::Snapshot; -use zksync_types::{L1BatchNumber, L2BlockNumber}; +use zksync_types::{L1BatchNumber, L2BlockNumber, ProtocolVersionId}; use super::*; -async fn new_pool(from_snapshot: bool) -> ConnectionPool { - match from_snapshot { - true => { - ConnectionPool::from_snapshot(Snapshot::make(L1BatchNumber(23), L2BlockNumber(87), &[])) - .await - } - false => ConnectionPool::from_genesis().await, - } -} +const VERSIONS: [ProtocolVersionId; 2] = [ProtocolVersionId::latest(), ProtocolVersionId::next()]; +const FROM_SNAPSHOT: [bool; 2] = [true, false]; +#[test_casing(2, VERSIONS)] #[tokio::test(flavor = "multi_thread")] -async fn test_validator_block_store() { +async fn test_validator_block_store(version: ProtocolVersionId) { zksync_concurrency::testonly::abort_on_panic(); let ctx = &ctx::test_root(&ctx::RealClock); let rng = &mut ctx.rng(); - let pool = new_pool(false).await; + let pool = ConnectionPool::test(false, version).await; // Fill storage with unsigned L2 blocks. // Fetch a suffix of blocks that we will generate (fake) certs for. @@ -91,9 +85,9 @@ async fn test_validator_block_store() { // In the current implementation, consensus certificates are created asynchronously // for the L2 blocks constructed by the StateKeeper. This means that consensus actor // is effectively just back filling the consensus certificates for the L2 blocks in storage. -#[test_casing(2, [false, true])] +#[test_casing(4, Product((FROM_SNAPSHOT,VERSIONS)))] #[tokio::test(flavor = "multi_thread")] -async fn test_validator(from_snapshot: bool) { +async fn test_validator(from_snapshot: bool, version: ProtocolVersionId) { zksync_concurrency::testonly::abort_on_panic(); let ctx = &ctx::test_root(&ctx::AffineClock::new(10.)); let rng = &mut ctx.rng(); @@ -102,7 +96,7 @@ async fn test_validator(from_snapshot: bool) { scope::run!(ctx, |ctx, s| async { tracing::info!("Start state keeper."); - let pool = new_pool(from_snapshot).await; + let pool = ConnectionPool::test(from_snapshot,version).await; let (mut sk, runner) = testonly::StateKeeper::new(ctx, pool.clone()).await?; s.spawn_bg(runner.run(ctx)); @@ -155,8 +149,9 @@ async fn test_validator(from_snapshot: bool) { } // Test running a validator node and 2 full nodes recovered from different snapshots. +#[test_casing(2, VERSIONS)] #[tokio::test(flavor = "multi_thread")] -async fn test_nodes_from_various_snapshots() { +async fn test_nodes_from_various_snapshots(version: ProtocolVersionId) { zksync_concurrency::testonly::abort_on_panic(); let ctx = &ctx::test_root(&ctx::AffineClock::new(10.)); let rng = &mut ctx.rng(); @@ -165,7 +160,7 @@ async fn test_nodes_from_various_snapshots() { scope::run!(ctx, |ctx, s| async { tracing::info!("spawn validator"); - let validator_pool = ConnectionPool::from_genesis().await; + let validator_pool = ConnectionPool::from_genesis(version).await; let (mut validator, runner) = testonly::StateKeeper::new(ctx, validator_pool.clone()).await?; s.spawn_bg(runner.run(ctx).instrument(tracing::info_span!("validator"))); @@ -233,9 +228,9 @@ async fn test_nodes_from_various_snapshots() { // Test running a validator node and a couple of full nodes. // Validator is producing signed blocks and fetchers are expected to fetch // them directly or indirectly. -#[test_casing(2, [false, true])] +#[test_casing(4, Product((FROM_SNAPSHOT,VERSIONS)))] #[tokio::test(flavor = "multi_thread")] -async fn test_full_nodes(from_snapshot: bool) { +async fn test_full_nodes(from_snapshot: bool, version: ProtocolVersionId) { const NODES: usize = 2; zksync_concurrency::testonly::abort_on_panic(); @@ -256,7 +251,7 @@ async fn test_full_nodes(from_snapshot: bool) { // Run validator and fetchers in parallel. scope::run!(ctx, |ctx, s| async { - let validator_pool = new_pool(from_snapshot).await; + let validator_pool = ConnectionPool::test(from_snapshot, version).await; let (mut validator, runner) = testonly::StateKeeper::new(ctx, validator_pool.clone()).await?; s.spawn_bg(async { @@ -272,8 +267,7 @@ async fn test_full_nodes(from_snapshot: bool) { validator.seal_batch().await; validator_pool .wait_for_payload(ctx, validator.last_block()) - .await - .unwrap(); + .await?; tracing::info!("Run validator."); let (cfg, secrets) = testonly::config(&validator_cfgs[0]); @@ -283,7 +277,7 @@ async fn test_full_nodes(from_snapshot: bool) { let mut node_pools = vec![]; for (i, cfg) in node_cfgs.iter().enumerate() { let i = ctx::NoCopy(i); - let pool = new_pool(from_snapshot).await; + let pool = ConnectionPool::test(from_snapshot, version).await; let (node, runner) = testonly::StateKeeper::new(ctx, pool.clone()).await?; node_pools.push(pool.clone()); s.spawn_bg(async { @@ -318,9 +312,9 @@ async fn test_full_nodes(from_snapshot: bool) { } // Test running external node (non-leader) validators. -#[test_casing(2, [false, true])] +#[test_casing(4, Product((FROM_SNAPSHOT,VERSIONS)))] #[tokio::test(flavor = "multi_thread")] -async fn test_en_validators(from_snapshot: bool) { +async fn test_en_validators(from_snapshot: bool, version: ProtocolVersionId) { const NODES: usize = 3; zksync_concurrency::testonly::abort_on_panic(); @@ -331,7 +325,7 @@ async fn test_en_validators(from_snapshot: bool) { // Run all nodes in parallel. scope::run!(ctx, |ctx, s| async { - let main_node_pool = new_pool(from_snapshot).await; + let main_node_pool = ConnectionPool::test(from_snapshot, version).await; let (mut main_node, runner) = testonly::StateKeeper::new(ctx, main_node_pool.clone()).await?; s.spawn_bg(async { @@ -370,7 +364,7 @@ async fn test_en_validators(from_snapshot: bool) { let mut ext_node_pools = vec![]; for (i, cfg) in cfgs[1..].iter().enumerate() { let i = ctx::NoCopy(i); - let pool = new_pool(from_snapshot).await; + let pool = ConnectionPool::test(from_snapshot, version).await; let (ext_node, runner) = testonly::StateKeeper::new(ctx, pool.clone()).await?; ext_node_pools.push(pool.clone()); s.spawn_bg(async { @@ -404,9 +398,9 @@ async fn test_en_validators(from_snapshot: bool) { } // Test fetcher back filling missing certs. -#[test_casing(2, [false, true])] +#[test_casing(4, Product((FROM_SNAPSHOT,VERSIONS)))] #[tokio::test(flavor = "multi_thread")] -async fn test_p2p_fetcher_backfill_certs(from_snapshot: bool) { +async fn test_p2p_fetcher_backfill_certs(from_snapshot: bool, version: ProtocolVersionId) { zksync_concurrency::testonly::abort_on_panic(); let ctx = &ctx::test_root(&ctx::AffineClock::new(10.)); let rng = &mut ctx.rng(); @@ -416,7 +410,7 @@ async fn test_p2p_fetcher_backfill_certs(from_snapshot: bool) { scope::run!(ctx, |ctx, s| async { tracing::info!("Spawn validator."); - let validator_pool = new_pool(from_snapshot).await; + let validator_pool = ConnectionPool::test(from_snapshot, version).await; let (mut validator, runner) = testonly::StateKeeper::new(ctx, validator_pool.clone()).await?; s.spawn_bg(runner.run(ctx)); @@ -426,7 +420,7 @@ async fn test_p2p_fetcher_backfill_certs(from_snapshot: bool) { validator.seal_batch().await; let client = validator.connect(ctx).await?; - let node_pool = new_pool(from_snapshot).await; + let node_pool = ConnectionPool::test(from_snapshot, version).await; tracing::info!("Run p2p fetcher."); scope::run!(ctx, |ctx, s| async { @@ -479,16 +473,16 @@ async fn test_p2p_fetcher_backfill_certs(from_snapshot: bool) { .unwrap(); } -#[test_casing(2, [false, true])] +#[test_casing(4, Product((FROM_SNAPSHOT,VERSIONS)))] #[tokio::test] -async fn test_centralized_fetcher(from_snapshot: bool) { +async fn test_centralized_fetcher(from_snapshot: bool, version: ProtocolVersionId) { zksync_concurrency::testonly::abort_on_panic(); let ctx = &ctx::test_root(&ctx::RealClock); let rng = &mut ctx.rng(); scope::run!(ctx, |ctx, s| async { tracing::info!("Spawn a validator."); - let validator_pool = new_pool(from_snapshot).await; + let validator_pool = ConnectionPool::test(from_snapshot, version).await; let (mut validator, runner) = testonly::StateKeeper::new(ctx, validator_pool.clone()).await?; s.spawn_bg(runner.run(ctx).instrument(tracing::info_span!("validator"))); @@ -498,7 +492,7 @@ async fn test_centralized_fetcher(from_snapshot: bool) { validator.seal_batch().await; tracing::info!("Spawn a node."); - let node_pool = new_pool(from_snapshot).await; + let node_pool = ConnectionPool::test(from_snapshot, version).await; let (node, runner) = testonly::StateKeeper::new(ctx, node_pool.clone()).await?; s.spawn_bg(runner.run(ctx).instrument(tracing::info_span!("fetcher"))); s.spawn_bg(node.run_fetcher(ctx, validator.connect(ctx).await?)); @@ -520,14 +514,15 @@ async fn test_centralized_fetcher(from_snapshot: bool) { /// Tests that generated L1 batch witnesses can be verified successfully. /// TODO: add tests for verification failures. +#[test_casing(2, VERSIONS)] #[tokio::test] -async fn test_batch_witness() { +async fn test_batch_witness(version: ProtocolVersionId) { zksync_concurrency::testonly::abort_on_panic(); let ctx = &ctx::test_root(&ctx::RealClock); let rng = &mut ctx.rng(); scope::run!(ctx, |ctx, s| async { - let pool = ConnectionPool::from_genesis().await; + let pool = ConnectionPool::from_genesis(version).await; let (mut node, runner) = testonly::StateKeeper::new(ctx, pool.clone()).await?; s.spawn_bg(runner.run_real(ctx)); diff --git a/core/node/test_utils/src/lib.rs b/core/node/test_utils/src/lib.rs index 566eab9c3d2..d0dfe367c21 100644 --- a/core/node/test_utils/src/lib.rs +++ b/core/node/test_utils/src/lib.rs @@ -17,6 +17,7 @@ use zksync_types::{ fee::Fee, fee_model::BatchFeeInput, l2::L2Tx, + protocol_version::ProtocolSemanticVersion, snapshots::SnapshotRecoveryStatus, transaction_request::PaymasterParams, tx::{tx_execution_info::TxExecutionStatus, ExecutionMetrics, TransactionExecutionResult}, @@ -163,8 +164,8 @@ impl Snapshot { l1_batch: L1BatchNumber, l2_block: L2BlockNumber, storage_logs: &[StorageLog], + genesis_params: GenesisParams, ) -> Self { - let genesis_params = GenesisParams::mock(); let contracts = genesis_params.base_system_contracts(); let l1_batch = L1BatchHeader::new( l1_batch, @@ -208,7 +209,11 @@ pub async fn prepare_recovery_snapshot( l2_block: L2BlockNumber, storage_logs: &[StorageLog], ) -> SnapshotRecoveryStatus { - recover(storage, Snapshot::make(l1_batch, l2_block, storage_logs)).await + recover( + storage, + Snapshot::make(l1_batch, l2_block, storage_logs, GenesisParams::mock()), + ) + .await } /// Takes a storage snapshot at the last sealed L1 batch. @@ -290,6 +295,10 @@ pub async fn recover( .protocol_versions_dal() .save_protocol_version_with_tx(&ProtocolVersion { base_system_contracts_hashes: snapshot.l1_batch.base_system_contracts_hashes, + version: ProtocolSemanticVersion { + minor: snapshot.l1_batch.protocol_version.unwrap(), + patch: 0.into(), + }, ..ProtocolVersion::default() }) .await diff --git a/core/tests/test_account/Cargo.toml b/core/tests/test_account/Cargo.toml index 0b2e7aa9340..6df10edd7dc 100644 --- a/core/tests/test_account/Cargo.toml +++ b/core/tests/test_account/Cargo.toml @@ -19,3 +19,4 @@ zksync_contracts.workspace = true hex.workspace = true ethabi.workspace = true +rand.workspace = true diff --git a/core/tests/test_account/src/lib.rs b/core/tests/test_account/src/lib.rs index 619caeb1ebd..e259ce209c6 100644 --- a/core/tests/test_account/src/lib.rs +++ b/core/tests/test_account/src/lib.rs @@ -50,6 +50,10 @@ impl Account { Self::new(K256PrivateKey::random()) } + pub fn random_using(rng: &mut impl rand::Rng) -> Self { + Self::new(K256PrivateKey::random_using(rng)) + } + pub fn get_l2_tx_for_execute(&mut self, execute: Execute, fee: Option) -> Transaction { let tx = self.get_l2_tx_for_execute_with_nonce(execute, fee, self.nonce); self.nonce += 1; diff --git a/prover/Cargo.lock b/prover/Cargo.lock index 05d02d745f5..e2a1bf706d0 100644 --- a/prover/Cargo.lock +++ b/prover/Cargo.lock @@ -9690,6 +9690,7 @@ version = "0.1.0" dependencies = [ "ethabi", "hex", + "rand 0.8.5", "zksync_contracts", "zksync_eth_signer", "zksync_system_constants",