From 7acd57b5af850081b7ddf03d61d4c43ec1428673 Mon Sep 17 00:00:00 2001 From: Andrew Kirillov Date: Sat, 25 Nov 2023 07:32:38 -0800 Subject: [PATCH] arbitrum-client: add integration test setup --- Cargo.lock | 5 ++ arbitrum-client/Cargo.toml | 12 +++ arbitrum-client/integration/constants.rs | 17 +++++ arbitrum-client/integration/helpers.rs | 18 +++++ arbitrum-client/integration/main.rs | 95 ++++++++++++++++++++++++ 5 files changed, 147 insertions(+) create mode 100644 arbitrum-client/integration/constants.rs create mode 100644 arbitrum-client/integration/helpers.rs create mode 100644 arbitrum-client/integration/main.rs diff --git a/Cargo.lock b/Cargo.lock index c7a70b5d2..3ea6f8bd4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -351,9 +351,12 @@ dependencies = [ "ark-ec", "ark-ff", "circuit-types", + "clap 4.4.8", "common", "constants", "ethers", + "eyre", + "json", "lazy_static", "num-bigint", "num-traits", @@ -361,6 +364,8 @@ dependencies = [ "renegade-crypto", "serde", "serde_with 3.4.0", + "test-helpers", + "util", ] [[package]] diff --git a/arbitrum-client/Cargo.toml b/arbitrum-client/Cargo.toml index 3a4f5eb53..0982acbdf 100644 --- a/arbitrum-client/Cargo.toml +++ b/arbitrum-client/Cargo.toml @@ -3,6 +3,11 @@ name = "arbitrum-client" version = "0.1.0" edition = "2021" +[[test]] +name = "integration" +path = "integration/main.rs" +harness = false + [dependencies] ethers = "2" ark-bn254 = "0.4.0" @@ -24,3 +29,10 @@ renegade-crypto = { path = "../renegade-crypto" } serde = { workspace = true } serde_with = "3.4" postcard = { version = "1", features = ["alloc"] } + +[dev-dependencies] +clap = { version = "4.0", features = ["derive"] } +eyre = { workspace = true } +test-helpers = { path = "../test-helpers" } +util = { path = "../util" } +json = "0.12" diff --git a/arbitrum-client/integration/constants.rs b/arbitrum-client/integration/constants.rs new file mode 100644 index 000000000..f21828178 --- /dev/null +++ b/arbitrum-client/integration/constants.rs @@ -0,0 +1,17 @@ +//! Constants used in the Arbitrum client integration tests + +/// The default hostport that the Nitro devnet L2 node runs on +pub(crate) const DEFAULT_DEVNET_HOSTPORT: &str = "http://localhost:8547"; + +/// The default private key that the Nitro devnet is seeded with +pub(crate) const DEFAULT_DEVNET_PKEY: &str = + "0xb6b15c8cb491557369f3c7d2c287b053eb229daa9c22138887752191c9520659"; + +/// The deployments key in the `deployments.json` file +pub(crate) const DEPLOYMENTS_KEY: &str = "deployments"; + +/// The darkpool contract key in the `deployments.json` file +pub(crate) const DARKPOOL_CONTRACT_KEY: &str = "darkpool_contract"; + +/// The darkpool proxy contract key in the `deployments.json` file +pub(crate) const DARKPOOL_PROXY_CONTRACT_KEY: &str = "darkpool_proxy_contract"; diff --git a/arbitrum-client/integration/helpers.rs b/arbitrum-client/integration/helpers.rs new file mode 100644 index 000000000..79c0198f2 --- /dev/null +++ b/arbitrum-client/integration/helpers.rs @@ -0,0 +1,18 @@ +//! Helper functions for Arbitrum client integration tests + +use std::{fs::File, io::Read}; +use eyre::{Result, eyre}; + +use crate::constants::DEPLOYMENTS_KEY; + +/// Parse a `deployments.json` file to get the address of a deployed contract +pub fn parse_addr_from_deployments_file(file_path: &str, contract_key: &str) -> Result { + let mut file_contents = String::new(); + File::open(file_path)?.read_to_string(&mut file_contents)?; + + let parsed_json = json::parse(&file_contents)?; + parsed_json[DEPLOYMENTS_KEY][contract_key] + .as_str() + .map(|s| s.to_string()) + .ok_or_else(|| eyre!("Could not parse darkpool address from deployments file")) +} diff --git a/arbitrum-client/integration/main.rs b/arbitrum-client/integration/main.rs new file mode 100644 index 000000000..4eff92c01 --- /dev/null +++ b/arbitrum-client/integration/main.rs @@ -0,0 +1,95 @@ +#![deny(unsafe_code)] +#![deny(missing_docs)] +#![deny(clippy::missing_docs_in_private_items)] +#![allow(incomplete_features)] +#![feature(generic_const_exprs)] + +mod constants; +mod helpers; + +use arbitrum_client::{ + client::{ArbitrumClient, ArbitrumClientConfig}, + constants::Chain, +}; +use clap::Parser; +use constants::{DARKPOOL_CONTRACT_KEY, DARKPOOL_PROXY_CONTRACT_KEY}; +use helpers::parse_addr_from_deployments_file; +use test_helpers::integration_test_main; +use util::{logging::LevelFilter, runtime::block_on_result}; + +use crate::constants::{DEFAULT_DEVNET_HOSTPORT, DEFAULT_DEVNET_PKEY}; + +#[derive(Debug, Clone, Parser)] +#[command(author, version, about, long_about=None)] +struct CliArgs { + /// The private key to use for signing transactions in the integration test + /// + /// Defaults to the private key that the Nitro devnet is pre-seeded with + #[arg(short = 'p', long, default_value = DEFAULT_DEVNET_PKEY)] + private_key: String, + + /// The location of a `deployments.json` file that contains the addresses of + /// the deployed contracts + #[arg(short, long)] + deployments_path: String, + + // TODO: Add a flag for the contract artifacts to allow for building/deploying + // during test setup + /// The url of the Arbitrum RPC endpoint to use for the integration test + #[arg(long, default_value = DEFAULT_DEVNET_HOSTPORT)] + rpc_url: String, + + /// The test to run + #[arg(short, long, value_parser)] + test: Option, + + /// The verbosity level of the test harness + #[arg(short, long)] + verbose: bool, +} + +/// The arguments provided to every integration test +#[derive(Clone)] +struct IntegrationTestArgs { + /// The Arbitrum client that resolves to a locally running devnet node + client: ArbitrumClient, +} + +impl From for IntegrationTestArgs { + fn from(test_args: CliArgs) -> Self { + // Pull the contract address either from the CLI or a shared volume at the CLI + // specified path + + let darkpool_addr = parse_addr_from_deployments_file( + &test_args.deployments_path, + DARKPOOL_PROXY_CONTRACT_KEY, + ) + .unwrap(); + let event_source = + parse_addr_from_deployments_file(&test_args.deployments_path, DARKPOOL_CONTRACT_KEY) + .unwrap(); + + // Build a client that references the darkpool + // We block on the client creation so that we can match the (synchronous) + // function signature of `From`, which is assumed to be implemented in + // the integration test harness + let client = block_on_result(ArbitrumClient::new(ArbitrumClientConfig { + chain: Chain::Devnet, + darkpool_addr, + event_source, + arb_priv_key: test_args.private_key, + rpc_url: test_args.rpc_url, + })) + .unwrap(); + + Self { client } + } +} + +/// Setup code for the integration tests +fn setup_integration_tests(_test_args: &CliArgs) { + // Configure logging + util::logging::setup_system_logger(LevelFilter::INFO); +} + +integration_test_main!(CliArgs, IntegrationTestArgs, setup_integration_tests);