Skip to content
Open
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
eeb42e6
feat(l1_sender): replace monolithic loop with per-transaction Tokio task
Artemka374-claude Apr 1, 2026
8a83129
test(l1_sender): fix resubmission test fee caps for anvil 1.5.1
Artemka374-claude Apr 2, 2026
648ea7e
Merge branch 'feat/l1-sender-task-per-tx' of https://github.com/Artem…
Artemka374 Apr 2, 2026
4509647
return back `process_prepending_passthrough_commands`
Artemka374 Apr 2, 2026
aa15edf
try to reduce unnecessary diff
Artemka374 Apr 2, 2026
6ea964a
temporarily disable prover tests in CI
Artemka374 Apr 2, 2026
f8e8d43
remove channels
Artemka374 Apr 2, 2026
2094ebc
change fee rules for resubmitting
Artemka374 Apr 2, 2026
7842bd7
use backoff from external dependency
Artemka374 Apr 2, 2026
b7ef3cc
remove backoff completely
Artemka374 Apr 2, 2026
3b5a848
try to remove some strange redundant impls
Artemka374 Apr 2, 2026
a84439c
random nl
Artemka374 Apr 2, 2026
8da0853
random NL #2
Artemka374 Apr 2, 2026
8c909ed
remove dead module
Artemka374 Apr 2, 2026
5ec9ca4
use allloy's watcher instead of customly built
Artemka374 Apr 2, 2026
0b85613
inline some other unnecessary functions
Artemka374 Apr 2, 2026
8e09fcc
fmt and other fixes
Artemka374 Apr 3, 2026
02f145e
fmt
Artemka374 Apr 3, 2026
c01a583
always submit at least one tx
Artemka374 Apr 3, 2026
97b63a3
fmt
Artemka374 Apr 3, 2026
141002d
upd tests
Artemka374 Apr 3, 2026
671bae8
fix submission loop
Artemka374 Apr 3, 2026
f995b7b
cap the first tx
Artemka374 Apr 3, 2026
f340c16
fmt
Artemka374 Apr 3, 2026
c7ec214
update metrics a bit
Artemka374 Apr 3, 2026
8b1c498
Merge origin/main into afo/l1-sender-enhancements
Artemka374 Apr 7, 2026
0ee8aa7
estimate blob fee
Artemka374 Apr 7, 2026
7f74188
report configured blob fee
Artemka374 Apr 7, 2026
caf893f
use fallback in case of estimation error
Artemka374 Apr 7, 2026
8b890a0
fmt
Artemka374 Apr 7, 2026
71d7f2c
fix some issues
Artemka374 Apr 7, 2026
7f9cc80
only check blob fee when sending commit txs
Artemka374 Apr 7, 2026
522952b
a few more fixes
Artemka374 Apr 7, 2026
f6c6099
remove tx_resubmissions metric
Artemka374 Apr 7, 2026
a9ac343
add required confirmations
Artemka374 Apr 7, 2026
e6c3969
fix clippy
Artemka374 Apr 7, 2026
17fb2e5
fix tests
Artemka374 Apr 8, 2026
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
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ jobs:
# Build prover integration tests #
####################################
build-prover-tests:
if: false # TODO: re-enable prover tests in CI
runs-on: matterlabs-ci-runner-ultra-performance
steps:

Expand Down Expand Up @@ -205,6 +206,7 @@ jobs:
# Run prover integration tests on GPU machine #
#################################################
prover-tests:
if: false # TODO: re-enable prover tests in CI
runs-on: [matterlabs-ci-hetzner-rtx6000-gpu]
needs: build-prover-tests
steps:
Expand Down
89 changes: 89 additions & 0 deletions integration-tests/tests/node/l1_sender_resubmission.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
use alloy::network::TransactionBuilder;
use alloy::primitives::{Address, U256};
use alloy::providers::Provider;
use alloy::providers::ext::AnvilApi;
use alloy::rpc::types::TransactionRequest;
use smart_config::EtherAmount;
use std::time::Duration;
use zksync_os_integration_tests::assert_traits::{DEFAULT_TIMEOUT, ReceiptAssert};
use zksync_os_integration_tests::provider::ZksyncTestingProvider;
use zksync_os_integration_tests::{CURRENT_TO_L1, Tester, test_multisetup};

/// Exercises the per-transaction resubmission loop:
///
/// 1. Start the node with a very short `transaction_timeout` (5 s) so the timeout fires
/// quickly in the test.
/// 2. Disable L1 auto-mining so the first submitted transaction is never included.
/// 3. Send an L2 transaction to trigger the batcher pipeline (commit → prove → execute).
/// 4. Wait long enough for the timeout to fire at least once and a replacement transaction
/// to be submitted.
/// 5. Re-enable L1 mining.
/// 6. Wait for the L2 block to be finalized on L1, confirming the pipeline recovered.
#[test_multisetup([CURRENT_TO_L1])]
#[test_runtime(flavor = "multi_thread")]
async fn l1_sender_resubmits_after_timeout() -> anyhow::Result<()> {
// A very short timeout to make the test fast. The node will re-evaluate after 5 s.
const TX_TIMEOUT: Duration = Duration::from_secs(5);

let tester = Tester::setup_with_overrides(|config| {
config.l1_sender_config.transaction_timeout = TX_TIMEOUT;
// Raise fee caps well above what Anvil reports so fee-cap gating does not
// prevent transaction submission during the test.
config.l1_sender_config.max_priority_fee_per_gas = EtherAmount(10 * 1_000_000_000); // 10 gwei
config.l1_sender_config.max_fee_per_gas = EtherAmount(500 * 1_000_000_000); // 500 gwei
// Fast block production so we get a batch quickly.
config.sequencer_config.block_time = Duration::from_millis(200);
})
.await?;

// Stop auto-mining so L1 transactions stay pending indefinitely.
tester
.l1_provider()
.anvil_set_auto_mine(false)
.await
.expect("anvil_set_auto_mine(false)");

// Submit an L2 transaction. This will eventually be batched and trigger the commit
// sender to submit an L1 transaction (which will time out because mining is paused).
let receipt = tester
.l2_provider
.send_transaction(
TransactionRequest::default()
.with_to(Address::random())
.with_value(U256::from(1u64)),
)
.await?
.expect_successful_receipt()
.await?;
let l2_block = receipt
.block_number
.expect("receipt must have a block number");

// Give the node time to submit the first L1 transaction and for the timeout to fire
// at least once. Two full timeout periods is enough.
tokio::time::sleep(TX_TIMEOUT * 2).await;
Comment thread
Artemka374 marked this conversation as resolved.
Outdated

// Re-enable auto-mining. Any replacement transactions submitted during the timeout
// window will now be included.
tester
.l1_provider()
.anvil_set_auto_mine(true)
.await
.expect("anvil_set_auto_mine(true)");

// Mine a few blocks to ensure pending transactions are included promptly.
tester
.l1_provider()
.anvil_mine(Some(3u64), None)
.await
.expect("anvil_mine");

// Wait for the block to be finalized (executed) on L1. If resubmission works the
// pipeline should complete within DEFAULT_TIMEOUT from this point.
tester
.l2_zk_provider
.wait_finalized_with_timeout(l2_block, DEFAULT_TIMEOUT)
.await?;

Ok(())
}
1 change: 1 addition & 0 deletions integration-tests/tests/node/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
mod batcher;
mod external_node;
mod l1_sender_resubmission;
mod mempool;
mod rebuild;
mod restart;
22 changes: 21 additions & 1 deletion lib/l1_sender/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::time::Duration;
use zksync_os_operator_signer::SignerConfig;

/// Configuration of L1 sender.
#[derive(Clone, Debug)]
#[derive(Debug)]
pub struct L1SenderConfig<Input> {
/// Operator signer configuration.
/// Depending on the mode, this can be a commit/prove/execute operator.
Expand All @@ -25,8 +25,28 @@ pub struct L1SenderConfig<Input> {
/// How often to poll L1 for new blocks.
pub poll_interval: Duration,

/// Maximum time to wait for a transaction to be included on L1 before attempting
/// resubmission with updated gas fees.
pub transaction_timeout: Duration,

/// Use Fusaka blob transaction format if the timestamp has passed.
pub fusaka_upgrade_timestamp: u64,

pub phantom_data: PhantomData<Input>,
}

impl<Input> Clone for L1SenderConfig<Input> {
fn clone(&self) -> Self {
Self {
operator_signer: self.operator_signer.clone(),
max_fee_per_gas_wei: self.max_fee_per_gas_wei,
max_priority_fee_per_gas_wei: self.max_priority_fee_per_gas_wei,
max_fee_per_blob_gas_wei: self.max_fee_per_blob_gas_wei,
command_limit: self.command_limit,
poll_interval: self.poll_interval,
transaction_timeout: self.transaction_timeout,
fusaka_upgrade_timestamp: self.fusaka_upgrade_timestamp,
phantom_data: PhantomData,
}
}
}
Loading
Loading