Skip to content

Commit

Permalink
feat: use anvil for sequencer tests (#427)
Browse files Browse the repository at this point in the history
  • Loading branch information
atanmarko committed Jan 9, 2024
1 parent 1e91086 commit 5b0257b
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 101 deletions.
1 change: 0 additions & 1 deletion Cargo.lock

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

14 changes: 7 additions & 7 deletions crates/topos-sequencer-subnet-client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ pub use topos_core::uci::{
Address, Certificate, CertificateId, ReceiptsRootHash, StateRoot, SubnetId, TxRootHash,
CERTIFICATE_ID_LENGTH, SUBNET_ID_LENGTH,
};
use tracing::{error, info};
use tracing::{error, info, warn};

const PUSH_CERTIFICATE_GAS_LIMIT: u64 = 1000000;

Expand Down Expand Up @@ -171,9 +171,9 @@ impl SubnetClientListener {
Ok(events) => events,
Err(Error::EventDecodingError(e)) => {
// FIXME: Happens in block before subnet contract is deployed, seems like bug in ethers
error!(
"Error decoding events from block {}: {e} \nTopos smart contracts may not be \
deployed?",
warn!(
"Error decoding events from block {}: {e}. Topos smart contracts may not be \
deployed before the parsed block?",
block_number
);
Vec::new()
Expand Down Expand Up @@ -220,9 +220,9 @@ impl SubnetClientListener {
Ok(events) => events,
Err(Error::EventDecodingError(e)) => {
// FIXME: Happens in block before subnet contract is deployed, seems like bug in ethers
error!(
"Error decoding events from block {}: {e} \nTopos smart contracts may not \
be deployed?",
warn!(
"Error decoding events from block {}: {e}. Topos smart contracts may not \
be deployed before the parsed block?",
block_number
);
Vec::new()
Expand Down
1 change: 0 additions & 1 deletion crates/topos-sequencer-subnet-runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ secp256k1.workspace = true
serial_test.workspace = true
tiny-keccak.workspace = true
ethers.workspace = true
dockertest = "0.3.1"
fs_extra = "1.3"


Expand Down
142 changes: 50 additions & 92 deletions crates/topos-sequencer-subnet-runtime/tests/subnet_contract.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
#![allow(unknown_lints)]
use crate::common::abi;
use dockertest::{
Composition, DockerTest, Image, LogAction, LogOptions, LogPolicy, LogSource, PullPolicy, Source,
};
use ethers::{
abi::{ethabi::ethereum_types::U256, Address},
core::types::Filter,
Expand All @@ -14,6 +11,7 @@ use ethers::{
use rstest::*;
use serial_test::serial;
use std::collections::HashSet;
use std::process::{Child, Command};
use std::sync::Arc;
use test_log::test;
use tokio::sync::{oneshot, Mutex};
Expand All @@ -38,27 +36,22 @@ const STANDALONE_SUBNET_WITH_LONG_BLOCKS_BLOCK_TIME: u64 = 12;
const SUBNET_RPC_PORT: u32 = 8545;
// Account 0x4AAb25B4fAd0Beaac466050f3A7142A502f4Cf0a
const TEST_SECRET_ETHEREUM_KEY: &str =
"d7e2e00b43c12cf17239d4755ed744df6ca70a933fc7c8bbb7da1342a5ff2e38";
const TEST_ETHEREUM_ACCOUNT: &str = "0x4AAb25B4fAd0Beaac466050f3A7142A502f4Cf0a";
const POLYGON_EDGE_CONTAINER: &str = "ghcr.io/topos-protocol/polygon-edge";
const POLYGON_EDGE_CONTAINER_TAG: &str = "develop";
"ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80";
const TEST_ETHEREUM_ACCOUNT: &str = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266";
const SUBNET_STARTUP_DELAY: u64 = 5; // seconds left for subnet startup
const TEST_SUBNET_ID: &str = "6464646464646464646464646464646464646464646464646464646464646464";
const TOKEN_SYMBOL: &str = "TKX";

// Accounts pre-filled in STANDALONE_SUBNET_WITH_LONG_BLOCKS
// Account Alith 0xf24FF3a9CF04c71Dbc94D0b566f7A27B94566cac
const TEST_ACCOUNT_ALITH_KEY: &str =
"5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133";
const TEST_ACCOUNT_ALITH_ACCOUNT: &str = "0xf24FF3a9CF04c71Dbc94D0b566f7A27B94566cac";
// Account Balathar 0x3Cd0A705a2DC65e5b1E1205896BaA2be8A07c6e0
"59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d";
const TEST_ACCOUNT_ALITH_ACCOUNT: &str = "0x70997970C51812dc3A010C7d01b50e0d17dc79C8";
const TEST_ACCOUNT_BALATHAR_KEY: &str =
"8075991ce870b93a8870eca0c0f91913d12f47948ca0fd25b49c6fa7cdbeee8b";
const TEST_ACCOUNT_BALATHAR_ACCOUNT: &str = "0x3Cd0A705a2DC65e5b1E1205896BaA2be8A07c6e0";
// Account Cezar 0x5283ac54A7B9669F6415168DC7a5FcEe05019E45
"5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a";
const TEST_ACCOUNT_BALATHAR_ACCOUNT: &str = "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC";
const TEST_ACCOUNT_CEZAR_KEY: &str =
"11eddfae7abe45531b3f18342c8062969323a7131d3043f1a33c40df74803cc7";
const TEST_ACCOUNT_CEZAR_ACCOUNT: &str = "0x5283ac54A7B9669F6415168DC7a5FcEe05019E45";
"7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6";
const TEST_ACCOUNT_CEZAR_ACCOUNT: &str = "0x90F79bf6EB2c4f870365E785982E1f101E93b906";

const PREV_CERTIFICATE_ID_1: CertificateId = CERTIFICATE_ID_4;
const PREV_CERTIFICATE_ID_2: CertificateId = CERTIFICATE_ID_5;
Expand All @@ -68,85 +61,40 @@ const CERTIFICATE_ID_3: CertificateId = CERTIFICATE_ID_8;
const DEFAULT_GAS: u64 = 5_000_000;

fn spawn_subnet_node(
stop_subnet_receiver: tokio::sync::oneshot::Receiver<()>,
subnet_ready_sender: tokio::sync::oneshot::Sender<()>,
port: u32,
block_time: u64, // Block time in seconds
) -> Result<tokio::task::JoinHandle<()>, Box<dyn std::error::Error>> {
let subnet_test_type = STANDALONE_SUBNET;
let subnet_startup_delay = SUBNET_STARTUP_DELAY * (block_time / 2);
let block_time_str = block_time.to_string() + "s";
let handle = tokio::task::spawn_blocking(move || {
let source = Source::DockerHub;
let img = Image::with_repository(POLYGON_EDGE_CONTAINER)
.source(Source::Local)
.tag(POLYGON_EDGE_CONTAINER_TAG)
.pull_policy(PullPolicy::IfNotPresent);
let mut polygon_edge_node = Composition::with_image(img);
polygon_edge_node.env("BLOCK_TIME", block_time_str.clone());

// Define docker options
polygon_edge_node.port_map(SUBNET_RPC_PORT, port);

let mut polygon_edge_node_docker = DockerTest::new().with_default_source(source);

// Setup command for polygon edge binary
let cmd: Vec<String> = vec![subnet_test_type.to_string()];
polygon_edge_node_docker.add_composition(
polygon_edge_node
.with_log_options(Some(LogOptions {
action: LogAction::Forward,
policy: LogPolicy::OnError,
source: LogSource::StdErr,
}))
.with_cmd(cmd),
);
polygon_edge_node_docker.run(|ops| async move {
let container = ops.handle(POLYGON_EDGE_CONTAINER);
info!(
"Running container with id: {} name: {} ...",
container.id(),
container.name(),
);
// TODO: use polling of network block number or some other means to learn when subnet node has started
tokio::time::sleep(tokio::time::Duration::from_secs(subnet_startup_delay)).await;
subnet_ready_sender
.send(())
.expect("subnet ready channel available");

info!("Waiting for signal to close...");
stop_subnet_receiver.await.unwrap();
info!("Container id={} execution finished", container.id());
})
});

Ok(handle)
) -> std::io::Result<Child> {
// Ignore output, too verbose
let child = Command::new("anvil")
.args([
"--block-time",
&block_time.to_string(),
"--port",
&port.to_string(),
])
.stdout(std::process::Stdio::null())
.spawn();

child
}

#[allow(dead_code)]
struct Context {
pub i_topos_core: abi::IToposCoreClient,
pub i_topos_messaging: abi::IToposMessagingClient,
pub i_erc20_messaging: abi::IERC20MessagingClient,
pub subnet_node_handle: Option<tokio::task::JoinHandle<()>>,
pub subnet_stop_sender: Option<tokio::sync::oneshot::Sender<()>>,
pub subnet_node_handle: Option<std::process::Child>,
pub port: u32,
}

impl Context {
pub async fn shutdown(mut self) -> Result<(), Box<dyn std::error::Error>> {
// Send shutdown message to subnet node
self.subnet_stop_sender
.take()
.expect("valid subnet stop channel")
.send(())
.expect("invalid subnet stop channel");

// Wait for the subnet node to close
self.subnet_node_handle
.take()
.expect("Valid subnet node handle")
.await?;
.kill()
.expect("Could not kill anvil subprocess");
Ok(())
}

Expand All @@ -155,13 +103,15 @@ impl Context {
}

pub fn jsonrpc_ws(&self) -> String {
format!("ws://127.0.0.1:{}/ws", self.port)
format!("ws://127.0.0.1:{}", self.port)
}
}

impl Drop for Context {
fn drop(&mut self) {
// TODO: cleanup if necessary
if let Some(mut child) = self.subnet_node_handle.take() {
child.kill().expect("Could not kill anvil subprocess");
}
}
}

Expand Down Expand Up @@ -397,6 +347,12 @@ async fn deploy_test_token(
.event::<abi::ierc20_messaging::TokenDeployedFilter>()
.from_block(0);
let events = events.query().await?;
if events.is_empty() {
panic!(
"Missing TokenDeployed event. Token contract is not deployed to test subnet. Could \
not execute test"
);
}
let token_address = events[0].token_address;
info!("Token contract deployed to {}", token_address.to_string());

Expand Down Expand Up @@ -474,23 +430,26 @@ async fn context_running_subnet_node(
#[default(8545)] port: u32,
#[default(STANDALONE_SUBNET_BLOCK_TIME)] block_time: u64,
) -> Context {
let (subnet_stop_sender, subnet_stop_receiver) = oneshot::channel::<()>();
let (subnet_ready_sender, subnet_ready_receiver) = oneshot::channel::<()>();
info!(
"Starting subnet node on port {}, block time: {}s",
port, block_time
);

let subnet_node_handle =
match spawn_subnet_node(subnet_stop_receiver, subnet_ready_sender, port, block_time) {
Ok(subnet_node_handle) => subnet_node_handle,
Err(e) => {
panic!("Failed to start the polygon edge subnet node as part of test context: {e}");
let subnet_node_handle = match spawn_subnet_node(port, block_time) {
Ok(subnet_node_handle) => subnet_node_handle,
Err(e) => {
if e.kind() == std::io::ErrorKind::NotFound {
panic!(
"Could not find Anvil binary. Please install and add to path Foundry tools \
including Anvil"
);
} else {
panic!("Failed to start the Anvil subnet node as part of test context: {e}");
}
};
subnet_ready_receiver
.await
.expect("subnet ready channel error");
}
};
// Wait a bit for anvil subprocess to spin itself up
tokio::time::sleep(tokio::time::Duration::from_secs(3)).await;
info!("Subnet node started...");

// Deploy contracts
Expand All @@ -504,7 +463,6 @@ async fn context_running_subnet_node(
i_topos_messaging,
i_erc20_messaging,
subnet_node_handle: Some(subnet_node_handle),
subnet_stop_sender: Some(subnet_stop_sender),
port,
}
}
Expand Down Expand Up @@ -580,7 +538,7 @@ async fn test_create_runtime() -> Result<(), Box<dyn std::error::Error>> {
SubnetRuntimeProxyConfig {
subnet_id: SOURCE_SUBNET_ID_1,
http_endpoint: format!("http://localhost:{SUBNET_RPC_PORT}"),
ws_endpoint: format!("ws://localhost:{SUBNET_RPC_PORT}/ws"),
ws_endpoint: format!("ws://localhost:{SUBNET_RPC_PORT}"),
subnet_contract_address: "0x0000000000000000000000000000000000000000".to_string(),
verifier: 0,
source_head_certificate_id: None,
Expand Down

0 comments on commit 5b0257b

Please sign in to comment.