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: add --no-mine mode #445

Merged
merged 1 commit into from
Nov 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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: 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());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we force block to seal? If so, it would be nice to add it to this test too to make sure that the node doesn't just hang

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can seal a block manually but it won't have the tx in it (see https://github.com/matter-labs/era-test-node/issues/404). I am hoping to address this shortly and will update this test then


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