use std::fs;
use std::mem::ManuallyDrop;
use std::path::Path;
use std::str::FromStr;
use std::sync::{Arc, Mutex};
use color_eyre::eyre::{Result, eyre};
use namada_apps_lib::cli::args;
use namada_apps_lib::client::utils::PRE_GENESIS_DIR;
use namada_apps_lib::config;
use namada_apps_lib::config::TendermintMode;
use namada_apps_lib::config::genesis::chain::Finalized;
use namada_apps_lib::config::genesis::templates;
use namada_apps_lib::config::genesis::templates::load_and_validate;
use namada_apps_lib::tendermint::Timeout;
use namada_apps_lib::tendermint_proto::google::protobuf::Timestamp;
use namada_apps_lib::wallet::defaults::derive_template_dir;
use namada_apps_lib::wallet::pre_genesis;
use namada_core::chain::ChainIdPrefix;
use namada_core::collections::HashMap;
use namada_node::shell::Shell;
use namada_node::shell::testing::node::{
InnerMockNode, MockNode, MockServicesCfg, MockServicesController,
MockServicesPackage, SalvageableTestDir, mock_services,
};
use namada_node::shell::testing::utils::TestDir;
use namada_sdk::dec::Dec;
use namada_sdk::token;
use namada_sdk::wallet::alias::Alias;
use crate::e2e::setup::copy_wasm_to_chain_dir;
const ENV_VAR_KEEP_TEMP: &str = "NAMADA_INT_KEEP_TEMP";
pub fn setup() -> Result<(MockNode, MockServicesController)> {
initialize_genesis(|genesis| genesis)
}
pub fn initialize_genesis(
mut update_genesis: impl FnMut(
templates::All<templates::Unvalidated>,
) -> templates::All<templates::Unvalidated>,
) -> Result<(MockNode, MockServicesController)> {
let working_dir = std::fs::canonicalize("../..").unwrap();
let keep_temp = match std::env::var(ENV_VAR_KEEP_TEMP) {
Ok(val) => !val.eq_ignore_ascii_case("false"),
_ => false,
};
let test_dir = TestDir::new();
let template_dir = derive_template_dir(&working_dir);
let mut templates = templates::All::read_toml_files(&template_dir)
.expect("Missing genesis files");
for (_, config) in templates.tokens.token.iter_mut() {
config.masp_params = Some(token::ShieldedParams {
max_reward_rate: Dec::from_str("0.1").unwrap(),
kp_gain_nom: Dec::from_str("0.1").unwrap(),
kd_gain_nom: Dec::from_str("0.1").unwrap(),
locked_amount_target: 1_000_000u64,
});
}
let templates = update_genesis(templates);
let genesis_path = test_dir.path().join("int-test-genesis-src");
std::fs::create_dir(&genesis_path)
.expect("Could not create test chain directory.");
templates
.write_toml_files(&genesis_path)
.expect("Could not write genesis files into test chain directory.");
let templates = load_and_validate(&genesis_path)
.expect("Missing or invalid genesis files");
let genesis_time = Default::default();
let chain_id_prefix = ChainIdPrefix::from_str("integration-test").unwrap();
let genesis = config::genesis::chain::finalize(
templates,
chain_id_prefix.clone(),
genesis_time,
Timeout::from_str("30s").unwrap(),
);
let chain_id = &genesis.metadata.chain_id;
let wasm_checksums_path = working_dir.join("wasm/checksums.json");
let global_args = args::Global {
is_pre_genesis: true,
chain_id: Some(chain_id.clone()),
base_dir: test_dir.path().to_path_buf(),
wasm_dir: Some(test_dir.path().join(chain_id.as_str()).join("wasm")),
};
let release_archive_path = namada_apps_lib::client::utils::init_network(
global_args.clone(),
args::InitNetwork {
templates_path: genesis_path,
wasm_checksums_path,
chain_id_prefix,
consensus_timeout_commit: Timeout::from_str("30s").unwrap(),
archive_dir: Some(test_dir.path().to_path_buf()),
genesis_time,
},
);
let mut archive = {
let decoder = flate2::read::GzDecoder::new(
fs::File::open(&release_archive_path).unwrap(),
);
tar::Archive::new(decoder)
};
archive.unpack(&global_args.base_dir).unwrap();
_ = archive;
fs::remove_file(release_archive_path).unwrap();
let eth_bridge_params = genesis.get_eth_bridge_params();
let auto_drive_services = {
eth_bridge_params.is_some()
};
let enable_eth_oracle = {
eth_bridge_params.is_some()
};
let services_cfg = MockServicesCfg {
auto_drive_services,
enable_eth_oracle,
};
finalize_wallet(&template_dir, &global_args, genesis);
create_node(test_dir, global_args, keep_temp, services_cfg)
}
fn finalize_wallet(
template_dir: &Path,
global_args: &args::Global,
genesis: Finalized,
) {
let pre_genesis_path = template_dir.join("src").join(PRE_GENESIS_DIR);
let validator_alias_and_dir =
Some(("validator-0", pre_genesis_path.join("validator-0")));
let validator_alias_and_pre_genesis_wallet =
validator_alias_and_dir.map(|(validator_alias, pre_genesis_dir)| {
(
Alias::from(validator_alias),
pre_genesis::load(&pre_genesis_dir).unwrap_or_else(|err| {
panic!("Error loading validator pre-genesis wallet {err}")
}),
)
});
let pre_genesis_wallet =
namada_apps_lib::wallet::load(&pre_genesis_path).unwrap();
let chain_dir = global_args
.base_dir
.join(global_args.chain_id.as_ref().unwrap().as_str());
let wallet = genesis.derive_wallet(
&chain_dir,
Some(pre_genesis_wallet),
validator_alias_and_pre_genesis_wallet,
);
namada_apps_lib::wallet::save(&wallet).unwrap();
}
fn create_node(
test_dir: TestDir,
global_args: args::Global,
keep_temp: bool,
services_cfg: MockServicesCfg,
) -> Result<(MockNode, MockServicesController)> {
let chain_id = global_args.chain_id.unwrap_or_default();
copy_wasm_to_chain_dir(
&std::fs::canonicalize("../..").unwrap(),
&global_args.base_dir,
&chain_id,
);
let MockServicesPackage {
auto_drive_services,
services,
shell_handlers,
controller,
} = mock_services(services_cfg);
let node = MockNode(Arc::new(InnerMockNode {
shell: Mutex::new(Shell::new(
config::Ledger::new(
global_args.base_dir,
chain_id.clone(),
TendermintMode::Validator,
),
global_args
.wasm_dir
.expect("Wasm path not provided to integration test setup."),
shell_handlers.tx_broadcaster,
shell_handlers.eth_oracle_channels,
None,
None,
50 * 1024 * 1024, 50 * 1024 * 1024, )),
test_dir: SalvageableTestDir {
keep_temp,
test_dir: ManuallyDrop::new(test_dir),
},
services,
tx_result_codes: Mutex::new(vec![]),
tx_results: Mutex::new(vec![]),
blocks: Mutex::new(HashMap::new()),
auto_drive_services,
}));
let init_req =
namada_apps_lib::tendermint::abci::request::InitChain {
time: Timestamp {
seconds: 0,
nanos: 0,
}
.try_into().unwrap(),
chain_id: chain_id.to_string(),
consensus_params:
namada_apps_lib::tendermint::consensus::params::Params {
block: namada_apps_lib::tendermint::block::Size {
max_bytes: 0,
max_gas: 0,
time_iota_ms: 0,
},
evidence:
namada_apps_lib::tendermint::evidence::Params {
max_age_num_blocks: 0,
max_age_duration: namada_apps_lib::tendermint::evidence::Duration(core::time::Duration::MAX),
max_bytes: 0,
},
validator: namada_apps_lib::tendermint::consensus::params::ValidatorParams {
pub_key_types: vec![]
},
version: None,
abci: namada_apps_lib::tendermint::consensus::params::AbciParams {
vote_extensions_enable_height: None,
},
},
validators: vec![],
app_state_bytes: vec![].into(),
initial_height: 0_u32.into(),
};
{
let mut locked = node.shell.lock().unwrap();
locked
.init_chain(init_req, 1)
.map_err(|e| eyre!("Failed to initialize ledger: {:?}", e))?;
locked.state.in_mem_mut().block.height = 1.into();
locked.commit();
}
Ok((node, controller))
}