diff --git a/integration-tests/README.md b/integration-tests/README.md index 878b1d468..c14113225 100644 --- a/integration-tests/README.md +++ b/integration-tests/README.md @@ -111,15 +111,24 @@ Now, you can inspect each container's logs according to your needs using `docker We have a CLI tool that can instantiate a short-lived development environment that has everything except for the leader node set up. You can then seamlessly plug in your own leader node instance that you have set up manually (the tool gives you a CLI command to use as a starting point, but you can attach debugger, enable extra logs etc). Try it out now (sets up 3 signer nodes): +For fastauth: + ```bash $ export RUST_LOG=info $ cd integration-tests/fastauth $ cargo run -- setup-env 3 ``` +For chain signatures: +```bash +$ export RUST_LOG=info +$ cd integration-tests/chain-signatures +$ cargo run -- setup-env --nodes 3 --threshold 2 +``` + ### I'm getting "Error: error trying to connect: No such file or directory (os error 2)" -It's a known issue on MacOS. Try executiong the following command: +It's a known issue on MacOS. Try executing the following command: ```bash sudo ln -s $HOME/.docker/run/docker.sock /var/run/docker.sock diff --git a/integration-tests/chain-signatures/Cargo.lock b/integration-tests/chain-signatures/Cargo.lock index e42caa2c5..ebac5c37f 100644 --- a/integration-tests/chain-signatures/Cargo.lock +++ b/integration-tests/chain-signatures/Cargo.lock @@ -3978,6 +3978,7 @@ dependencies = [ "backon", "bollard", "cait-sith", + "clap", "crypto-shared", "ecdsa 0.16.9", "elliptic-curve 0.13.8", diff --git a/integration-tests/chain-signatures/Cargo.toml b/integration-tests/chain-signatures/Cargo.toml index 0034e3e69..8b35380e6 100644 --- a/integration-tests/chain-signatures/Cargo.toml +++ b/integration-tests/chain-signatures/Cargo.toml @@ -46,6 +46,7 @@ crypto-shared = { path = "../../chain-signatures/crypto-shared" } mpc-contract = { path = "../../chain-signatures/contract" } mpc-keys = { path = "../../chain-signatures/keys" } mpc-recovery-node = { path = "../../chain-signatures/node" } +clap = { version = "4.5.4", features = ["derive"] } lazy_static = "1.4.0" [dev-dependencies] diff --git a/integration-tests/chain-signatures/src/lib.rs b/integration-tests/chain-signatures/src/lib.rs index 63f834e45..7d403ad4d 100644 --- a/integration-tests/chain-signatures/src/lib.rs +++ b/integration-tests/chain-signatures/src/lib.rs @@ -27,7 +27,7 @@ use testcontainers::{Container, GenericImage}; const NETWORK: &str = "mpc_it_network"; -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct MultichainConfig { pub nodes: usize, pub threshold: usize, @@ -109,10 +109,29 @@ impl Nodes<'_> { } pub fn near_accounts(&self) -> Vec { - self.near_acc_sk() - .iter() - .map(|(account_id, account_sk)| { - Account::from_secret_key(account_id.clone(), account_sk.clone(), &self.ctx().worker) + let acc_sk = self.near_acc_sk(); + let mut account_ids = Vec::new(); + match self { + Nodes::Local { nodes, .. } => { + for node in nodes { + account_ids.push(node.account_id.clone()); + } + } + Nodes::Docker { nodes, .. } => { + for node in nodes { + account_ids.push(node.account_id.clone()); + } + } + }; + + account_ids + .into_iter() + .map(|account_id| { + Account::from_secret_key( + account_id.clone(), + acc_sk.get(&account_id).unwrap().clone(), + &self.ctx().worker, + ) }) .collect() } diff --git a/integration-tests/chain-signatures/src/main.rs b/integration-tests/chain-signatures/src/main.rs new file mode 100644 index 000000000..ac2c09793 --- /dev/null +++ b/integration-tests/chain-signatures/src/main.rs @@ -0,0 +1,79 @@ +use clap::Parser; +use integration_tests_chain_signatures::containers::DockerClient; +use integration_tests_chain_signatures::{run, setup, utils, MultichainConfig}; +use tokio::signal; +use tracing_subscriber::EnvFilter; + +#[derive(Parser, Debug)] +enum Cli { + /// Spin up dependent services and mpc nodes + SetupEnv { + #[arg(short, long, default_value_t = 3)] + nodes: usize, + #[arg(short, long, default_value_t = 2)] + threshold: usize, + }, + /// Spin up dependent services but not mpc nodes + DepServices, +} + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + let subscriber = tracing_subscriber::fmt() + .with_thread_ids(true) + .with_env_filter(EnvFilter::from_default_env()); + subscriber.init(); + let docker_client = DockerClient::default(); + + match Cli::parse() { + Cli::SetupEnv { nodes, threshold } => { + println!( + "Setting up an environment with {} nodes, {} threshold ...", + nodes, threshold + ); + let config = MultichainConfig { + nodes, + threshold, + ..Default::default() + }; + println!("Full config: {:?}", config); + let nodes = run(config.clone(), &docker_client).await?; + let ctx = nodes.ctx(); + let urls: Vec<_> = (0..config.nodes).map(|i| nodes.url(i)).collect(); + let near_acc_sks = nodes.near_acc_sk(); + let near_accounts = nodes.near_accounts(); + let sk_local_path = nodes.ctx().storage_options.sk_share_local_path.clone(); + + println!("\nEnvironment is ready:"); + println!(" docker-network: {}", ctx.docker_network); + println!(" release: {}", ctx.release); + + println!("\nExternal services:"); + println!(" datastore: {}", ctx.datastore.local_address); + println!(" lake_indexer: {}", ctx.lake_indexer.rpc_host_address); + + println!("\nNodes:"); + for i in 0..urls.len() { + println!(" Node {}", i); + println!(" Url: {}", urls[i]); + let account_id = near_accounts[i].id(); + println!(" Account: {}", account_id); + let sk = near_acc_sks.get(account_id).unwrap(); + println!(" Secret Key: {}", sk); + let pk = sk.public_key(); + println!(" Public Key: {}", pk); + } + + signal::ctrl_c().await.expect("Failed to listen for event"); + println!("Received Ctrl-C"); + utils::clear_local_sk_shares(sk_local_path).await?; + println!("Clean up finished"); + } + Cli::DepServices => { + println!("Settting up dependency services"); + let _ctx = setup(&docker_client).await?; + } + } + + Ok(()) +} diff --git a/integration-tests/chain-signatures/src/utils.rs b/integration-tests/chain-signatures/src/utils.rs index 36099ccbb..ca9d19a08 100644 --- a/integration-tests/chain-signatures/src/utils.rs +++ b/integration-tests/chain-signatures/src/utils.rs @@ -2,6 +2,7 @@ use anyhow::Context; use hyper::{Body, Client, Method, Request, StatusCode, Uri}; use near_workspaces::result::ExecutionFinalResult; use near_workspaces::{Account, AccountId}; +use std::fs; pub async fn vote_join( accounts: Vec, @@ -99,3 +100,22 @@ pub async fn ping_until_ok(addr: &str, timeout: u64) -> anyhow::Result<()> { .await?; Ok(()) } + +pub async fn clear_local_sk_shares(sk_local_path: Option) -> anyhow::Result<()> { + if let Some(sk_share_local_path) = sk_local_path { + let pattern = format!("{sk_share_local_path}*"); + for entry in glob::glob(&pattern).expect("Failed to read glob pattern") { + match entry { + Ok(path) => { + if path.is_file() { + if let Err(e) = fs::remove_file(&path) { + eprintln!("Failed to delete file {:?}: {}", path.display(), e); + } + } + } + Err(e) => eprintln!("{:?}", e), + } + } + } + Ok(()) +} diff --git a/integration-tests/chain-signatures/tests/lib.rs b/integration-tests/chain-signatures/tests/lib.rs index 5a2b0d929..5269c8bce 100644 --- a/integration-tests/chain-signatures/tests/lib.rs +++ b/integration-tests/chain-signatures/tests/lib.rs @@ -5,15 +5,13 @@ use crate::actions::wait_for; use anyhow::anyhow; use futures::future::BoxFuture; -use glob::glob; use integration_tests_chain_signatures::containers::DockerClient; use integration_tests_chain_signatures::utils::{vote_join, vote_leave}; -use integration_tests_chain_signatures::{run, MultichainConfig, Nodes}; +use integration_tests_chain_signatures::{run, utils, MultichainConfig, Nodes}; use near_jsonrpc_client::JsonRpcClient; use near_workspaces::{Account, AccountId}; -use std::fs; use std::str::FromStr; pub struct MultichainTestContext<'a> { @@ -150,26 +148,7 @@ where cfg, }) .await; - clear_local_sk_shares(sk_local_path).await?; + utils::clear_local_sk_shares(sk_local_path).await?; result } - -pub async fn clear_local_sk_shares(sk_local_path: Option) -> anyhow::Result<()> { - if let Some(sk_share_local_path) = sk_local_path { - let pattern = format!("{sk_share_local_path}*"); - for entry in glob(&pattern).expect("Failed to read glob pattern") { - match entry { - Ok(path) => { - if path.is_file() { - if let Err(e) = fs::remove_file(&path) { - eprintln!("Failed to delete file {:?}: {}", path.display(), e); - } - } - } - Err(e) => eprintln!("{:?}", e), - } - } - } - Ok(()) -}