diff --git a/.github/workflows/ci-core-lint-reusable.yml b/.github/workflows/ci-core-lint-reusable.yml index f9107c2133bd..527d9db4c09e 100644 --- a/.github/workflows/ci-core-lint-reusable.yml +++ b/.github/workflows/ci-core-lint-reusable.yml @@ -82,7 +82,6 @@ jobs: ci_run zkstack dev lint -t js --check ci_run zkstack dev lint -t ts --check ci_run zkstack dev lint -t rs --check - ci_run zkstack dev lint -t rust-toolchain ci_run zkstack dev lint -t autocompletion - name: Check Database diff --git a/core/lib/types/src/api/mod.rs b/core/lib/types/src/api/mod.rs index a8a4aadc2cb5..ebf739df8989 100644 --- a/core/lib/types/src/api/mod.rs +++ b/core/lib/types/src/api/mod.rs @@ -1112,6 +1112,7 @@ pub struct GatewayMigrationStatus { pub latest_notification: Option, pub state: GatewayMigrationState, pub settlement_layer: Option, + pub wait_for_batches_to_be_committed: bool, } #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] diff --git a/core/node/api_server/src/web3/namespaces/unstable/mod.rs b/core/node/api_server/src/web3/namespaces/unstable/mod.rs index 005e57797cc2..2d8c1d534dac 100644 --- a/core/node/api_server/src/web3/namespaces/unstable/mod.rs +++ b/core/node/api_server/src/web3/namespaces/unstable/mod.rs @@ -8,11 +8,13 @@ use zksync_dal::{Connection, Core, CoreDal, DalError}; use zksync_mini_merkle_tree::MiniMerkleTree; use zksync_multivm::{interface::VmEvent, zk_evm_latest::ethereum_types::U64}; use zksync_types::{ + aggregated_operations::L1BatchAggregatedActionType, api, api::{ ChainAggProof, DataAvailabilityDetails, GatewayMigrationStatus, L1ToL2TxsStatus, TeeProof, TransactionDetailedResult, TransactionExecutionInfo, }, + eth_sender::EthTxFinalityStatus, server_notification::GatewayMigrationState, tee_types::TeeType, web3, @@ -248,6 +250,27 @@ impl UnstableNamespace { .await .map_err(DalError::generalize)?; + let all_batches_with_interop_roots_committed = match connection + .interop_root_dal() + .get_latest_processed_interop_root_l1_batch_number() + .await + .map_err(DalError::generalize)? + { + None => true, + Some(latest_processed_l1_batch_number) => { + match connection + .eth_sender_dal() + .get_last_sent_successfully_eth_tx_by_batch_and_op( + L1BatchNumber::from(latest_processed_l1_batch_number), + L1BatchAggregatedActionType::Commit, + ) + .await + { + Some(tx) => tx.eth_tx_finality_status == EthTxFinalityStatus::Finalized, + None => false, + } + } + }; let state = GatewayMigrationState::from_sl_and_notification( self.state.api_config.settlement_layer, latest_notification, @@ -257,6 +280,7 @@ impl UnstableNamespace { latest_notification, state, settlement_layer: self.state.api_config.settlement_layer, + wait_for_batches_to_be_committed: !all_batches_with_interop_roots_committed, }) } diff --git a/core/node/eth_sender/src/eth_tx_aggregator.rs b/core/node/eth_sender/src/eth_tx_aggregator.rs index fcbc476f22ef..58048eaf1f43 100644 --- a/core/node/eth_sender/src/eth_tx_aggregator.rs +++ b/core/node/eth_sender/src/eth_tx_aggregator.rs @@ -819,18 +819,17 @@ impl EthTxAggregator { op_restrictions.commit_restriction = reason; op_restrictions.precommit_restriction = reason; // For the migration from gateway to L1, we need to wait for all blocks to be executed - if let None | Some(SettlementLayer::L1(_)) = self.settlement_layer { + if matches!(self.settlement_layer, None | Some(SettlementLayer::L1(_))) { op_restrictions.prove_restriction = reason; op_restrictions.execute_restriction = reason; - } else { - // For the migration from gateway to L1, we need we need to ensure all batches containing interop roots get committed and executed. - if !self - .is_waiting_for_batches_with_interop_roots_to_be_committed(storage) - .await? - { - op_restrictions.commit_restriction = None; - op_restrictions.precommit_restriction = None; - } + } else if self + .is_waiting_for_batches_with_interop_roots_to_be_committed(storage) + .await? + { + // For the migration from gateway to L1, we need to ensure all batches containing interop roots + // get committed and executed. Once this happens, we can re-enable commit & precommit. + op_restrictions.commit_restriction = None; + op_restrictions.precommit_restriction = None; } } diff --git a/core/tests/gateway-migration-test/tests/migration.test.ts b/core/tests/gateway-migration-test/tests/migration.test.ts index e9e27b23715f..5ebeff1c4f90 100644 --- a/core/tests/gateway-migration-test/tests/migration.test.ts +++ b/core/tests/gateway-migration-test/tests/migration.test.ts @@ -170,9 +170,23 @@ describe('Migration From/To gateway test', function () { `zkstack chain gateway migrate-to-gateway --chain ${fileConfig.chain} --gateway-chain-name ${gatewayChain}` ); } else { - await utils.spawn( - `zkstack chain gateway migrate-from-gateway --chain ${fileConfig.chain} --gateway-chain-name ${gatewayChain}` - ); + let migrationSucceeded = false; + for (let i = 0; i < 60; i++) { + try { + await utils.spawn( + `zkstack chain gateway migrate-from-gateway --chain ${fileConfig.chain} --gateway-chain-name ${gatewayChain}` + ); + migrationSucceeded = true; + break; + } catch (e) { + console.log(`Migration attempt ${i} failed with error: ${e}`); + await utils.sleep(2); + } + } + + if (!migrationSucceeded) { + throw new Error('Migration from gateway did not succeed after 60 attempts'); + } } await mainNodeSpawner.mainNode?.waitForShutdown(); // Node is already killed, so we simply start the new server diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/gateway/gateway_common.rs b/zkstack_cli/crates/zkstack/src/commands/chain/gateway/gateway_common.rs index 29bfdc878b24..227f2e6b3f21 100644 --- a/zkstack_cli/crates/zkstack/src/commands/chain/gateway/gateway_common.rs +++ b/zkstack_cli/crates/zkstack/src/commands/chain/gateway/gateway_common.rs @@ -52,6 +52,7 @@ impl MigrationDirection { #[derive(Debug, Eq, PartialEq, Copy, Clone)] pub enum NotificationReceivedState { + NotAllBatchesCommitted, NotAllBatchesExecuted(U256, U256), UnconfirmedTxs(usize), } @@ -71,6 +72,9 @@ impl std::fmt::Display for NotificationReceivedState { "There are some unconfirmed transactions: {unconfirmed_txs}" ) } + NotificationReceivedState::NotAllBatchesCommitted => { + write!(f, "Not all batches have been committed yet") + } } } } @@ -309,6 +313,12 @@ pub(crate) async fn get_gateway_migration_state( ), )); } + + if gateway_migration_status.wait_for_batches_to_be_committed { + return Ok(GatewayMigrationProgressState::NotificationReceived( + NotificationReceivedState::NotAllBatchesCommitted, + )); + } } let unconfirmed_txs = zk_client.get_unconfirmed_txs_count().await?; diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/gateway/migrate_from_gateway.rs b/zkstack_cli/crates/zkstack/src/commands/chain/gateway/migrate_from_gateway.rs index e72746c7d0a0..f6a718a66544 100644 --- a/zkstack_cli/crates/zkstack/src/commands/chain/gateway/migrate_from_gateway.rs +++ b/zkstack_cli/crates/zkstack/src/commands/chain/gateway/migrate_from_gateway.rs @@ -38,7 +38,10 @@ use crate::{ admin_call_builder::AdminCallBuilder, gateway::{ constants::DEFAULT_MAX_L1_GAS_PRICE_FOR_PRIORITY_TXS, - gateway_common::extract_and_wait_for_priority_ops, + gateway_common::{ + extract_and_wait_for_priority_ops, get_gateway_migration_state, + GatewayMigrationProgressState, MigrationDirection, + }, }, init::get_l1_da_validator, utils::send_tx, @@ -88,6 +91,32 @@ pub async fn run(args: MigrateFromGatewayArgs, shell: &Shell) -> anyhow::Result< .ctm .diamond_cut_data; + let gateway_general_config = gateway_chain_config.get_general_config().await?; + let gw_rpc_url = gateway_general_config.l2_http_url()?; + + let state = get_gateway_migration_state( + l1_url.clone(), + chain_contracts_config + .ecosystem_contracts + .bridgehub_proxy_addr, + chain_config.chain_id.as_u64(), + chain_config + .get_general_config() + .await? + .l2_http_url() + .context("L2 RPC URL must be provided for cross checking")?, + gw_rpc_url.clone(), + MigrationDirection::FromGateway, + ) + .await?; + + if state != GatewayMigrationProgressState::ServerReady { + anyhow::bail!( + "Chain is not ready for starting the migration from Gateway. Current state: {:?}", + state + ); + } + let start_migrate_from_gateway_call = start_migrate_chain_from_gateway( shell, &args.forge_args, @@ -111,8 +140,6 @@ pub async fn run(args: MigrateFromGatewayArgs, shell: &Shell) -> anyhow::Result< let (calldata, value) = AdminCallBuilder::new(start_migrate_from_gateway_call.calls).compile_full_calldata(); - let general_config = gateway_chain_config.get_general_config().await?; - let gw_rpc_url = general_config.l2_http_url()?; let gateway_provider = get_ethers_provider(&gw_rpc_url)?; let gateway_zk_client = get_zk_client(&gw_rpc_url, chain_config.chain_id.as_u64())?; diff --git a/zkstack_cli/crates/zkstack/src/commands/dev/commands/lint.rs b/zkstack_cli/crates/zkstack/src/commands/dev/commands/lint.rs index c3f195e18362..316b25181e75 100644 --- a/zkstack_cli/crates/zkstack/src/commands/dev/commands/lint.rs +++ b/zkstack_cli/crates/zkstack/src/commands/dev/commands/lint.rs @@ -41,8 +41,8 @@ pub fn run(shell: &Shell, args: LintArgs) -> anyhow::Result<()> { Target::Js, Target::Ts, Target::Contracts, - Target::RustToolchain, Target::Autocompletion, + Target::RustToolchain, ] } else { args.targets.clone() diff --git a/zkstack_cli/rust-toolchain b/zkstack_cli/rust-toolchain index 81febe965884..9e75344344a7 100644 --- a/zkstack_cli/rust-toolchain +++ b/zkstack_cli/rust-toolchain @@ -1,2 +1,2 @@ [toolchain] -channel = "nightly-2025-03-19" +channel = "nightly-2025-09-19"