Skip to content

[E2E Testing] Refactor testing types #127

Open
@frisitano

Description

@frisitano

Overview

We have re-architected the rollup node manager to be a node addon. As such, we can now access the rollup node manager handle from the node handle. We use this to invoke methods on the rollup node manager. Currently, we are fetching the rollup manager handle explicitly and invoking commands on the handle. It would be preferable if we had a standard suite of methods that we can invoke that abstract the implementation details. Examples of the explicit / manual mechanism seen below:

#[tokio::test]
async fn can_bridge_l1_messages() -> eyre::Result<()> {
reth_tracing::init_test_tracing();
// Create the chain spec for scroll mainnet with Darwin v2 activated and a test genesis.
let chain_spec = (*SCROLL_DEV).clone();
let node_args = ScrollRollupNodeConfig {
test: true,
network_args: ScrollNetworkArgs {
enable_eth_scroll_wire_bridge: true,
enable_scroll_wire: true,
},
optimistic_sync: false,
database_path: Some(PathBuf::from("sqlite::memory:")),
l1_provider_args: L1ProviderArgs::default(),
engine_api_url: None,
sequencer_args: SequencerArgs {
sequencer_enabled: true,
block_time: 0,
max_l1_messages_per_block: 4,
..SequencerArgs::default()
},
beacon_provider_args: BeaconProviderArgs::default(),
l2_provider_args: L2ProviderArgs::default(),
};
let (mut nodes, _tasks, _wallet) = setup_engine(node_args, 1, chain_spec, false).await.unwrap();
let node = nodes.pop().unwrap();
let rnm_handle: RollupManagerHandle = node.inner.add_ons_handle.rollup_manager_handle.clone();
let mut rnm_events = rnm_handle.get_event_listener().await?;
let l1_watcher_tx = node.inner.add_ons_handle.l1_watcher_tx.clone().unwrap();
let l1_message = TxL1Message {
queue_index: 0,
gas_limit: 21000,
sender: Address::random(),
to: Address::random(),
value: U256::from(1),
input: Default::default(),
};
l1_watcher_tx
.send(Arc::new(L1Notification::L1Message {
message: l1_message.clone(),
block_number: 0,
block_timestamp: 1000,
}))
.await?;
if let Some(RollupManagerEvent::L1MessageIndexed(index)) = rnm_events.next().await {
assert_eq!(index, 0);
} else {
panic!("Incorrect index for L1 message");
};
rnm_handle.build_block().await;
if let Some(RollupManagerEvent::BlockSequenced(block)) = rnm_events.next().await {
assert_eq!(block.body.transactions.len(), 1);
assert_eq!(block.body.transactions[0].transaction.l1_message().unwrap(), &l1_message,);
} else {
panic!("Failed to receive block from rollup node");
}
Ok(())
}
#[tokio::test]
async fn can_sequence_and_gossip_blocks() {
reth_tracing::init_test_tracing();
// create 2 nodes
let chain_spec = (*SCROLL_DEV).clone();
let rollup_manager_args = ScrollRollupNodeConfig {
test: true,
network_args: ScrollNetworkArgs {
enable_eth_scroll_wire_bridge: true,
enable_scroll_wire: true,
},
optimistic_sync: false,
database_path: Some(PathBuf::from("sqlite::memory:")),
l1_provider_args: L1ProviderArgs::default(),
engine_api_url: None,
sequencer_args: SequencerArgs {
sequencer_enabled: true,
block_time: 0,
max_l1_messages_per_block: 4,
..SequencerArgs::default()
},
beacon_provider_args: BeaconProviderArgs::default(),
l2_provider_args: L2ProviderArgs::default(),
};
let (nodes, _tasks, wallet) =
setup_engine(rollup_manager_args, 2, chain_spec, false).await.unwrap();
let wallet = Arc::new(Mutex::new(wallet));
// generate rollup node manager event streams for each node
let sequencer_rnm_handle = nodes[0].inner.add_ons_handle.rollup_manager_handle.clone();
let mut sequencer_events = sequencer_rnm_handle.get_event_listener().await.unwrap();
let mut follower_events =
nodes[1].inner.add_ons_handle.rollup_manager_handle.get_event_listener().await.unwrap();
// inject a transaction into the pool of the first node
let tx = generate_tx(wallet).await;
nodes[0].rpc.inject_tx(tx).await.unwrap();
sequencer_rnm_handle.build_block().await;
// wait for the sequencer to build a block
if let Some(RollupManagerEvent::BlockSequenced(block)) = sequencer_events.next().await {
assert_eq!(block.body.transactions.len(), 1);
} else {
panic!("Failed to receive block from rollup node");
}
// assert that the follower node has received the block from the peer
if let Some(RollupManagerEvent::NewBlockReceived(block_with_peer)) =
follower_events.next().await
{
assert_eq!(block_with_peer.block.body.transactions.len(), 1);
} else {
panic!("Failed to receive block from rollup node");
}
// assert that the block was successfully imported by the follower node
if let Some(RollupManagerEvent::BlockImported(block)) = follower_events.next().await {
assert_eq!(block.body.transactions.len(), 1);
} else {
panic!("Failed to receive block from rollup node");
}
}

Node Test Design

There are two approaches we can take for the node here:

  • Introduce a RollupManagerTest trait and implement it on NodeTestContext.
  • Wrap NodeTestContext in ScrollNodeTestContext which includes a RollupManagerTestContext field which own a rollup node manager handle and implements useful methods for testing. For convenience, we can implement deref on ScrollNodeTestContext to get access to methods from NodeTestContext for free.

I'm slightly in favor of approach 2.

Node Network Design

The setup_engine function returns a Vec<NodeTestContext>. We can consider introducing a wrapper around this ScrollNetworkTestContext, which includes helper methods to invoke actions on nodes in the network and also to assert expected outcomes on the rest of the nodes.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    Status

    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions