1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
/*!
Builder construct that spawn new chains with some common parameters.
*/
use eyre::eyre;
use std::str::FromStr;
use alloc::sync::Arc;
use ibc_relayer::config::compat_mode::CompatMode;
use tokio::runtime::Runtime;
use crate::chain::driver::ChainDriver;
use crate::error::Error;
use crate::types::config::TestConfig;
use crate::util::random::random_unused_tcp_port;
use super::chain_type::ChainType;
/**
Used for holding common configuration needed to create new `ChainDriver`s.
Currently this is hardcoded to support only a single version of Gaia chain.
We may want to turn this into a trait in the future to support different
chain implementations.
*/
#[derive(Debug)]
pub struct ChainBuilder {
/**
The CLI executable used for the chain commands. Defaults to `gaiad`.
TODO: Have a mutable list of command paths so that the `ChainBuilder`
may return [`ChainDriver`]s bound to different chain commands
for testing with multiple chain implementations.
*/
pub command_paths: Vec<String>,
/**
The filesystem path to store the data files used by the chain.
*/
pub base_store_dir: String,
pub account_prefixes: Vec<String>,
pub native_tokens: Vec<String>,
pub compat_modes: Option<Vec<String>>,
pub runtime: Arc<Runtime>,
}
impl ChainBuilder {
/**
Create a new `ChainBuilder`.
*/
pub fn new(
command_paths: Vec<String>,
base_store_dir: &str,
account_prefixes: Vec<String>,
native_tokens: Vec<String>,
compat_modes: Option<Vec<String>>,
runtime: Arc<Runtime>,
) -> Self {
Self {
command_paths,
base_store_dir: base_store_dir.to_string(),
account_prefixes,
native_tokens,
compat_modes,
runtime,
}
}
/**
Create a `ChainBuilder` based on the provided [`TestConfig`].
*/
pub fn new_with_config(config: &TestConfig, runtime: Arc<Runtime>) -> Self {
Self::new(
config.chain_command_paths.clone(),
&format!("{}", config.chain_store_dir.display()),
config.account_prefixes.clone(),
config.native_tokens.clone(),
config.compat_modes.clone(),
runtime,
)
}
/**
Create a new [`ChainDriver`] with the chain ID containing the
given prefix.
Note that this only configures the [`ChainDriver`] without
the actual chain being intitialized or spawned.
The `ChainBuilder` will configure the [`ChainDriver`] with random
unused ports, and add a random suffix to the chain ID.
For example, calling this with a prefix `"alpha"` will return
a [`ChainDriver`] configured with a chain ID like
`"ibc-alpha-f5a2a988"`.
*/
pub fn new_chain(
&self,
prefix: &str,
use_random_id: bool,
chain_number: usize,
) -> Result<ChainDriver, Error> {
// If there are more spawned chains than given chain binaries, take the N-th position modulo
// the number of chain binaries given. Same for account prefix.
let chain_number = chain_number % self.command_paths.len();
let account_number = chain_number % self.account_prefixes.len();
let native_token_number = chain_number % self.native_tokens.len();
let compat_mode = if let Some(modes) = &self.compat_modes {
let mode_str = &modes[chain_number % modes.len()];
Some(CompatMode::from_str(mode_str).map_err(|e| {
Error::generic(eyre!(
"Invalid CompatMode environment variable `{mode_str}`: {e}"
))
})?)
} else {
None
};
let chain_type = ChainType::from_str(&self.command_paths[chain_number])?;
let chain_id = chain_type.chain_id(prefix, use_random_id);
let rpc_port = random_unused_tcp_port();
let grpc_port = random_unused_tcp_port();
let grpc_web_port = random_unused_tcp_port();
let p2p_port = random_unused_tcp_port();
let pprof_port = random_unused_tcp_port();
let home_path = format!("{}/{}", self.base_store_dir, chain_id);
let driver = ChainDriver::create(
chain_type,
self.command_paths[chain_number].clone(),
chain_id,
home_path,
self.account_prefixes[account_number].clone(),
rpc_port,
grpc_port,
grpc_web_port,
p2p_port,
pprof_port,
self.runtime.clone(),
self.native_tokens[native_token_number].clone(),
compat_mode,
)?;
Ok(driver)
}
}