foundry_test_utils/
revive.rs1use alloy_rpc_client::ClientBuilder;
2use eyre::Result;
3use std::{process, thread::sleep, time::Duration};
4use subxt::{OnlineClient, PolkadotConfig};
5use tempfile::TempDir;
6
7const NODE_BINARY: &str = "substrate-node";
8const RPC_PROXY_BINARY: &str = "eth-rpc";
9const MAX_ATTEMPTS: u32 = 15;
10const RPC_URL: &str = "http://127.0.0.1:8545";
11const RETRY_DELAY: Duration = Duration::from_secs(1);
12
13const WALLETS: [(&str, &str); 1] = [(
14 "0xf24FF3a9CF04c71Dbc94D0b566f7A27B94566cac",
15 "0x5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133",
16)];
17
18#[allow(dead_code)]
20struct ContractsNodeProcess {
21 node: process::Child,
22 tmp_dir: tempfile::TempDir,
23}
24
25impl Drop for ContractsNodeProcess {
26 fn drop(&mut self) {
27 self.kill()
28 }
29}
30
31impl ContractsNodeProcess {
32 async fn start() -> Result<Self> {
33 let tmp_dir = TempDir::with_prefix("cargo-contract.cli.test.node")?;
34
35 let mut node = process::Command::new(NODE_BINARY)
36 .env("RUST_LOG", "error")
37 .arg("--dev")
38 .arg(format!("--base-path={}", tmp_dir.path().to_string_lossy()))
39 .arg("--no-prometheus")
40 .spawn()?;
41 let mut attempts = 1;
43 loop {
44 sleep(RETRY_DELAY);
45 tracing::debug!(
46 "Connecting to contracts enabled node, attempt {}/{}",
47 attempts,
48 MAX_ATTEMPTS
49 );
50 match OnlineClient::<PolkadotConfig>::new().await {
51 Result::Ok(_) => return Ok(Self { node, tmp_dir }),
52 Err(err) => {
53 if attempts < MAX_ATTEMPTS {
54 attempts += 1;
55 continue;
56 }
57 let err = eyre::eyre!(
58 "Failed to connect to node rpc after {} attempts: {}",
59 attempts,
60 err
61 );
62 tracing::error!("{}", err);
63 node.kill()?;
64 return Err(err);
65 }
66 }
67 }
68 }
69
70 fn kill(&mut self) {
71 tracing::debug!("Killing contracts node process {}", self.node.id());
72 if let Err(err) = self.node.kill() {
73 tracing::error!("Error killing contracts node process {}: {}", self.node.id(), err)
74 }
75 }
76}
77
78struct RpcProxyProcess(process::Child);
80
81impl Drop for RpcProxyProcess {
82 fn drop(&mut self) {
83 self.kill()
84 }
85}
86
87impl RpcProxyProcess {
88 async fn start() -> Result<Self> {
89 let mut rpc_proxy = process::Command::new(RPC_PROXY_BINARY)
90 .env("RUST_LOG", "error")
91 .arg("--dev")
92 .arg("--no-prometheus")
93 .spawn()?;
94
95 let client = ClientBuilder::default().connect(RPC_URL).await?;
96
97 let mut attempts = 1;
98 loop {
99 sleep(RETRY_DELAY);
100 match client.request_noparams::<String>("eth_chainId").await {
101 Result::Ok(_) => {
102 return Ok(Self(rpc_proxy));
103 }
104 Err(err) => {
105 if attempts < MAX_ATTEMPTS {
106 attempts += 1;
107 continue;
108 }
109
110 let err = eyre::eyre!(
111 "Failed to connect to RPC proxy after {} attempts: {}",
112 MAX_ATTEMPTS,
113 err
114 );
115 tracing::error!("{}", err);
116 rpc_proxy.kill()?;
117 return Err(err);
118 }
119 }
120 }
121 }
122
123 fn kill(&mut self) {
124 tracing::debug!("Killing RPC proxy process {}", self.0.id());
125 if let Err(err) = self.0.kill() {
126 tracing::error!("Error killing RPC proxy process {}: {}", self.0.id(), err)
127 }
128 }
129}
130
131#[allow(dead_code)]
146pub struct PolkadotNode {
147 node: ContractsNodeProcess,
148 rpc_proxy: RpcProxyProcess,
149}
150
151impl PolkadotNode {
152 pub async fn start() -> Result<Self> {
153 let node = ContractsNodeProcess::start().await?;
154 let rpc_proxy = RpcProxyProcess::start().await?;
155 Ok(Self { node, rpc_proxy })
156 }
157
158 pub fn http_endpoint() -> &'static str {
159 RPC_URL
160 }
161
162 pub fn dev_accounts() -> impl Iterator<Item = (&'static str, &'static str)> {
163 WALLETS.iter().copied()
164 }
165}