diff --git a/.changelog/unreleased/features/3660-check-pre-genesis-signing.md b/.changelog/unreleased/features/3660-check-pre-genesis-signing.md new file mode 100644 index 0000000000..64f8739727 --- /dev/null +++ b/.changelog/unreleased/features/3660-check-pre-genesis-signing.md @@ -0,0 +1,5 @@ +- The command `namadan utils test-genesis` now accepts `--check-can-sign` + multi-arg that can be used with genesis addresses and/or public keys to + verify that a pre-genesis wallet in the base directory is able to sign + with the keys associated with the addresses or with the keys themselves. + ([\#3660](https://github.com/anoma/namada/pull/3660)) \ No newline at end of file diff --git a/crates/apps/src/bin/namada-node/cli.rs b/crates/apps/src/bin/namada-node/cli.rs index 52db0ee1a1..884380cdd9 100644 --- a/crates/apps/src/bin/namada-node/cli.rs +++ b/crates/apps/src/bin/namada-node/cli.rs @@ -166,9 +166,9 @@ pub fn main() -> Result<()> { std::fs::write(config_path, updated_config).unwrap(); } }, - cli::NamadaNode::Utils(sub, _global_args) => match sub { + cli::NamadaNode::Utils(sub, global_args) => match sub { cmds::NodeUtils::TestGenesis(TestGenesis(args)) => { - node::utils::test_genesis(args) + node::utils::test_genesis(args, global_args) } }, } diff --git a/crates/apps_lib/src/cli.rs b/crates/apps_lib/src/cli.rs index b68ac06c52..2575b1371e 100644 --- a/crates/apps_lib/src/cli.rs +++ b/crates/apps_lib/src/cli.rs @@ -3338,6 +3338,8 @@ pub mod args { scheme is not supplied, it is assumed to be TCP.", 60 ); + pub const CHECK_CAN_SIGN: ArgMulti = + arg_multi("check-can-sign"); pub const CONFIG_RPC_LEDGER_ADDRESS: ArgDefaultFromCtx = arg_default_from_ctx("node", DefaultFn(|| "".to_string())); @@ -8230,13 +8232,19 @@ pub mod args { /// Templates dir pub path: PathBuf, pub wasm_dir: PathBuf, + pub check_can_sign: Vec, } impl Args for TestGenesis { fn parse(matches: &ArgMatches) -> Self { let path = PATH.parse(matches); let wasm_dir = WASM_DIR.parse(matches).unwrap_or_default(); - Self { path, wasm_dir } + let check_can_sign = CHECK_CAN_SIGN.parse(matches); + Self { + path, + wasm_dir, + check_can_sign, + } } fn def(app: App) -> App { @@ -8249,6 +8257,11 @@ pub mod args { "Optional wasm directory to provide as part of verifying \ genesis template files" ))) + .arg(CHECK_CAN_SIGN.def().help(wrap!( + "Check that the pre-genesis wallet is able to sign with the \ + given keys and/or keys associated with the given addresses. \ + A pre-genesis wallet must be present in the base directory." + ))) } } diff --git a/crates/node/src/utils.rs b/crates/node/src/utils.rs index 5e8e4c3964..913e2c718d 100644 --- a/crates/node/src/utils.rs +++ b/crates/node/src/utils.rs @@ -2,13 +2,24 @@ use std::str::FromStr; -use namada_apps_lib::cli::args::TestGenesis; -use namada_apps_lib::config::genesis; +use namada_apps_lib::cli::args::{self, TestGenesis}; +use namada_apps_lib::client::utils::PRE_GENESIS_DIR; +use namada_apps_lib::config::genesis::{self, AddrOrPk}; +use namada_apps_lib::{cli, wallet}; +use namada_sdk::address::{Address, ImplicitAddress}; +use namada_sdk::key::common; +use namada_sdk::wallet::FindKeyError; -pub fn test_genesis(args: TestGenesis) { - use crate::facade::tendermint::Timeout; +use crate::facade::tendermint::Timeout; - let templates = genesis::templates::load_and_validate(&args.path).unwrap(); +pub fn test_genesis(args: TestGenesis, global_args: args::Global) { + let TestGenesis { + path, + wasm_dir, + check_can_sign, + } = args; + + let templates = genesis::templates::load_and_validate(&path).unwrap(); let genesis = genesis::chain::finalize( templates, FromStr::from_str("namada-dryrun").unwrap(), @@ -21,5 +32,103 @@ pub fn test_genesis(args: TestGenesis) { genesis .write_toml_files(&test_dir.path().join(chain_id.to_string())) .unwrap(); - crate::test_genesis_files(config.ledger, genesis, args.wasm_dir); + crate::test_genesis_files(config.ledger, genesis.clone(), wasm_dir); + + if !check_can_sign.is_empty() { + let wallet_path = global_args.base_dir.join(PRE_GENESIS_DIR); + let mut wallet = if wallet::exists(&wallet_path) { + wallet::load(&wallet_path).unwrap() + } else { + panic!( + "Could not find wallet at {}.", + wallet_path.to_string_lossy() + ); + }; + + let mut all_valid = true; + + type WalletRes = Result; + let handle_wallet_result = + |searched: String, result: WalletRes| match result { + Ok(_) => { + println!("Able to sign with {searched}"); + true + } + Err(err) => { + eprintln!("Unable to sign with {searched}. {err}"); + false + } + }; + + for addr_or_pk in check_can_sign { + match &addr_or_pk { + AddrOrPk::PublicKey(pk) => { + if !handle_wallet_result( + pk.to_string(), + wallet.find_key_by_pk(&pk.raw, None), + ) { + all_valid = false; + } + } + AddrOrPk::Address(addr) => { + match &addr { + Address::Established(_) => { + // Find PK(s) of the address in genesis + if let Some(txs) = genesis + .transactions + .established_account + .as_ref() + { + if let Some(tx) = + txs.iter().find(|tx| &tx.address == addr) + { + println!( + "Found a matching genesis established \ + account tx with {} public key(s).", + tx.tx.public_keys.len() + ); + for pk in &tx.tx.public_keys { + if !handle_wallet_result( + format!("{pk} for {addr}"), + wallet + .find_key_by_pk(&pk.raw, None), + ) { + all_valid = false; + } + } + } else { + eprintln!( + "No genesis established account txs \ + with a matching address {addr} found" + ); + all_valid = false; + } + } else { + eprintln!( + "No genesis established account txs \ + found. Cannot check address {addr}." + ); + all_valid = false; + } + } + Address::Implicit(ImplicitAddress(pkh)) => { + if !handle_wallet_result( + addr.to_string(), + wallet.find_key_by_pkh(pkh, None), + ) { + all_valid = false; + } + } + Address::Internal(_) => { + eprintln!("Unexpected internal address {addr}"); + all_valid = false; + } + } + } + } + } + if !all_valid { + cli::safe_exit(1); + } + } } diff --git a/crates/tests/src/e2e/ledger_tests.rs b/crates/tests/src/e2e/ledger_tests.rs index afe27d577e..13900958b1 100644 --- a/crates/tests/src/e2e/ledger_tests.rs +++ b/crates/tests/src/e2e/ledger_tests.rs @@ -20,6 +20,7 @@ use std::time::{Duration, Instant}; use color_eyre::eyre::Result; use color_eyre::owo_colors::OwoColorize; use namada_apps_lib::cli::context::ENV_VAR_CHAIN_ID; +use namada_apps_lib::client::utils::PRE_GENESIS_DIR; use namada_apps_lib::config::utils::convert_tm_addr_to_socket_addr; use namada_apps_lib::config::{self, ethereum_bridge}; use namada_apps_lib::facade::tendermint_config::net::Address as TendermintAddress; @@ -2562,10 +2563,22 @@ fn masp_txs_and_queries() -> Result<()> { #[test] fn test_localnet_genesis() -> Result<()> { let loc = format!("{}:{}", std::file!(), std::line!()); - let dir = setup::TestDir::new(); + let base_dir = setup::TestDir::new(); let working_dir = working_dir(); let genesis_path = wallet::defaults::derive_template_dir(&working_dir); let wasm_dir = working_dir.join(config::DEFAULT_WASM_DIR); + + // Path to the localnet "pre-genesis" wallet + let pre_genesis_wallet = genesis_path + .join("src") + .join(PRE_GENESIS_DIR) + .join("wallet.toml"); + // Copy the pre-genesis wallet into the base-dir + let base_pre_genesis = base_dir.path().join(PRE_GENESIS_DIR); + std::fs::create_dir(&base_pre_genesis).unwrap(); + std::fs::copy(pre_genesis_wallet, base_pre_genesis.join("wallet.toml")) + .unwrap(); + let mut test_genesis_result = setup::run_cmd( Bin::Node, [ @@ -2575,13 +2588,23 @@ fn test_localnet_genesis() -> Result<()> { &genesis_path.to_string_lossy(), "--wasm-dir", &wasm_dir.to_string_lossy(), + "--check-can-sign", + // Albert established addr (from `genesis/localnet/balances.toml`) + "tnam1qxfj3sf6a0meahdu9t6znp05g8zx4dkjtgyn9gfu", + // Daewon implicit addr (from `genesis/localnet/balances.toml`) + "tnam1qpca48f45pdtpcz06rue7k4kfdcjrvrux5cr3pwn", + // Validator account key (from `genesis/localnet/transactions.toml`) + "tpknam1qpg2tsrplvhu3fd7z7tq5ztc2ne3s7e2ahjl2a2cddufrzdyr752g666ytj", ], Some(30), &working_dir, - dir.path(), + &base_dir, loc, )?; test_genesis_result .exp_string("Genesis files were dry-run successfully")?; + test_genesis_result.exp_string("Able to sign with")?; + test_genesis_result.exp_string("Able to sign with")?; + test_genesis_result.exp_string("Able to sign with")?; Ok(()) }