Skip to content

Commit

Permalink
add --no-mine mode (#445)
Browse files Browse the repository at this point in the history
  • Loading branch information
itegulov authored Nov 28, 2024
1 parent 2e9c7c8 commit bf55eb9
Show file tree
Hide file tree
Showing 7 changed files with 69 additions and 21 deletions.
2 changes: 1 addition & 1 deletion e2e-tests-rust/Cargo.lock

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

2 changes: 1 addition & 1 deletion e2e-tests-rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ fs2 = "0.4.3"
tokio = { version = "1", features = ["time", "rt", "process"] }

[dev-dependencies]
alloy-zksync = { git = "https://github.com/itegulov/alloy-zksync.git", rev = "b433d2d64e0f7d939891064b00d8a7991a427682" }
alloy-zksync = { git = "https://github.com/itegulov/alloy-zksync.git", rev = "e0e54a5ac9e24c1c32c7a8783bbf3e34dde2e218" }
alloy = { version = "0.6", features = ["full", "rlp", "serde", "sol-types"] }

[workspace] # ignore higher-level workspace
55 changes: 39 additions & 16 deletions e2e-tests-rust/tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,39 @@ use alloy::providers::{PendingTransaction, PendingTransactionError, Provider, Wa
use alloy::transports::http::{reqwest, Http};
use alloy_zksync::network::transaction_request::TransactionRequest;
use alloy_zksync::network::Zksync;
use alloy_zksync::node_bindings::EraTestNode;
use alloy_zksync::provider::{zksync_provider, ProviderBuilderExt};
use era_test_node_e2e_tests::utils::LockedPort;
use std::time::Duration;

fn init(
block_time: u64,
port: u16,
) -> impl Provider<Http<reqwest::Client>, Zksync> + WalletProvider<Zksync> + Clone {
zksync_provider()
async fn init(
f: impl FnOnce(EraTestNode) -> EraTestNode,
) -> anyhow::Result<impl Provider<Http<reqwest::Client>, Zksync> + WalletProvider<Zksync> + Clone> {
let locked_port = LockedPort::acquire_unused().await?;
let provider = zksync_provider()
.with_recommended_fillers()
.on_era_test_node_with_wallet_and_config(|node| {
node.path(
std::env::var("ERA_TEST_NODE_BINARY_PATH")
.unwrap_or("../target/release/era_test_node".to_string()),
)
.port(port)
.block_time(block_time)
})
f(node
.path(
std::env::var("ERA_TEST_NODE_BINARY_PATH")
.unwrap_or("../target/release/era_test_node".to_string()),
)
.port(locked_port.port))
});

// Wait for era-test-node to get up and be able to respond
provider.get_accounts().await?;
// Explicitly unlock the port to showcase why we waited above
drop(locked_port);

Ok(provider)
}

#[tokio::test]
async fn interval_sealing_finalization() -> anyhow::Result<()> {
// Test that we can submit a transaction and wait for it to finalize when era-test-node is
// operating in interval sealing mode.
let locked_port = LockedPort::acquire_unused().await?;
let provider = init(1, locked_port.port);
let provider = init(|node| node.block_time(1)).await?;

let tx = TransactionRequest::default()
.with_to(address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045"))
Expand All @@ -44,8 +52,7 @@ async fn interval_sealing_multiple_txs() -> anyhow::Result<()> {
// Test that we can submit two transactions and wait for them to finalize in the same block when
// era-test-node is operating in interval sealing mode. 3 seconds should be long enough for
// the entire flow to execute before the first block is produced.
let locked_port = LockedPort::acquire_unused().await?;
let provider = init(3, locked_port.port);
let provider = init(|node| node.block_time(3)).await?;
const RICH_WALLET0: Address = address!("f39Fd6e51aad88F6F4ce6aB8827279cffFb92266");
const RICH_WALLET1: Address = address!("70997970C51812dc3A010C7d01b50e0d17dc79C8");
const TARGET: Address = address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045");
Expand Down Expand Up @@ -95,3 +102,19 @@ async fn interval_sealing_multiple_txs() -> anyhow::Result<()> {

Ok(())
}

#[tokio::test]
async fn no_sealing_timeout() -> anyhow::Result<()> {
// Test that we can submit a transaction and timeout while waiting for it to finalize when
// era-test-node is operating in no sealing mode.
let provider = init(|node| node.no_mine()).await?;

let tx = TransactionRequest::default()
.with_to(address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045"))
.with_value(U256::from(100));
let pending_tx = provider.send_transaction(tx).await?.register().await?;
let finalization_result = tokio::time::timeout(Duration::from_secs(3), pending_tx).await;
assert!(finalization_result.is_err());

Ok(())
}
7 changes: 6 additions & 1 deletion src/config/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,10 @@ pub struct Cli {
/// If unset, node seals a new block as soon as there is at least one transaction.
#[arg(short, long, value_name = "SECONDS", value_parser = duration_from_secs_f64, help_heading = "Block Sealing")]
pub block_time: Option<Duration>,

/// Disable auto and interval mining, and mine on demand instead.
#[arg(long, visible_alias = "no-mine", conflicts_with = "block_time")]
pub no_mining: bool,
}

#[derive(Debug, Subcommand, Clone)]
Expand Down Expand Up @@ -357,7 +361,8 @@ impl Cli {
} else {
None
})
.with_block_time(self.block_time);
.with_block_time(self.block_time)
.with_no_mining(self.no_mining);

if self.emulate_evm && self.dev_system_contracts != Some(SystemContractsOptions::Local) {
return Err(eyre::eyre!(
Expand Down
11 changes: 11 additions & 0 deletions src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@ pub struct TestNodeConfig {
pub block_time: Option<Duration>,
/// Maximum number of transactions per block
pub max_transactions: usize,
/// Disable automatic sealing mode and use `BlockSealer::Noop` instead
pub no_mining: bool,
}

impl Default for TestNodeConfig {
Expand Down Expand Up @@ -172,6 +174,8 @@ impl Default for TestNodeConfig {

// Block sealing configuration default
block_time: None,
no_mining: false,

max_transactions: 1000,
}
}
Expand Down Expand Up @@ -796,6 +800,13 @@ impl TestNodeConfig {
self.block_time = block_time;
self
}

/// If set to `true` auto sealing will be disabled
#[must_use]
pub fn with_no_mining(mut self, no_mining: bool) -> Self {
self.no_mining = no_mining;
self
}
}

/// Account Generator
Expand Down
4 changes: 3 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,9 @@ async fn main() -> anyhow::Result<()> {
}))
.await;

let block_sealer = if let Some(block_time) = config.block_time {
let block_sealer = if config.no_mining {
BlockSealer::noop()
} else if let Some(block_time) = config.block_time {
BlockSealer::fixed_time(config.max_transactions, block_time)
} else {
BlockSealer::immediate(config.max_transactions)
Expand Down
9 changes: 8 additions & 1 deletion src/node/sealer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,22 @@ use std::task::{Context, Poll};
use std::time::Duration;
use tokio::time::{Interval, MissedTickBehavior};

/// Mode of operations for the `BlockSealer`
/// Represents different modes of block sealing available on the node
#[derive(Debug)]
pub enum BlockSealer {
/// Never seals blocks.
Noop,
/// Seals a block as soon as there is at least one transaction.
Immediate(ImmediateBlockSealer),
/// Seals a new block every `interval` tick
FixedTime(FixedTimeBlockSealer),
}

impl BlockSealer {
pub fn noop() -> Self {
Self::Noop
}

pub fn immediate(max_transactions: usize) -> Self {
Self::Immediate(ImmediateBlockSealer { max_transactions })
}
Expand All @@ -23,6 +29,7 @@ impl BlockSealer {

pub fn poll(&mut self, pool: &TxPool, cx: &mut Context<'_>) -> Poll<TxBatch> {
match self {
BlockSealer::Noop => Poll::Pending,
BlockSealer::Immediate(immediate) => immediate.poll(pool),
BlockSealer::FixedTime(fixed) => fixed.poll(pool, cx),
}
Expand Down

0 comments on commit bf55eb9

Please sign in to comment.