diff --git a/apps/src/bin/namada-node/cli.rs b/apps/src/bin/namada-node/cli.rs index b9048ea312..c13de5a2e4 100644 --- a/apps/src/bin/namada-node/cli.rs +++ b/apps/src/bin/namada-node/cli.rs @@ -8,12 +8,15 @@ use namada_apps::node::ledger; pub fn main() -> Result<()> { let (cmd, mut ctx) = cli::namada_node_cli()?; if let Some(mode) = ctx.global_args.mode.clone() { - ctx.config.ledger.tendermint.tendermint_mode = mode; + if let Some(chain) = ctx.chain.as_mut() { + chain.config.ledger.tendermint.tendermint_mode = mode; + } } match cmd { cmds::NamadaNode::Ledger(sub) => match sub { cmds::Ledger::Run(cmds::LedgerRun(args)) => { - let wasm_dir = ctx.wasm_dir(); + let chain_ctx = ctx.take_chain_or_exit(); + let wasm_dir = chain_ctx.wasm_dir(); // Sleep until start time if needed if let Some(time) = args.0 { @@ -31,10 +34,11 @@ pub fn main() -> Result<()> { } } } - ledger::run(ctx.config.ledger, wasm_dir); + ledger::run(chain_ctx.config.ledger, wasm_dir); } cmds::Ledger::Reset(_) => { - ledger::reset(ctx.config.ledger) + let chain_ctx = ctx.take_chain_or_exit(); + ledger::reset(chain_ctx.config.ledger) .wrap_err("Failed to reset Namada node")?; } }, @@ -44,18 +48,18 @@ pub fn main() -> Result<()> { // In here, we just need to overwrite the default chain ID, in // case it's been already set to a different value if let Some(chain_id) = ctx.global_args.chain_id.as_ref() { - ctx.global_config.default_chain_id = chain_id.clone(); + ctx.global_config.default_chain_id = Some(chain_id.clone()); ctx.global_config .write(&ctx.global_args.base_dir) .unwrap_or_else(|err| { - eprintln!("Error writing global config: {}", err); + eprintln!("Error writing global config: {err}"); cli::safe_exit(1) }); + tracing::debug!( + "Generated config and set default chain ID to \ + {chain_id}" + ); } - tracing::debug!( - "Generated config and set default chain ID to {}", - &ctx.global_config.default_chain_id - ); } }, } diff --git a/apps/src/bin/namada-wallet/cli.rs b/apps/src/bin/namada-wallet/cli.rs index 82a994b0ac..01c3d18c0a 100644 --- a/apps/src/bin/namada-wallet/cli.rs +++ b/apps/src/bin/namada-wallet/cli.rs @@ -12,7 +12,8 @@ use namada::types::masp::{MaspValue, PaymentAddress}; use namada_apps::cli; use namada_apps::cli::{args, cmds, Context}; use namada_apps::client::tx::find_valid_diversifier; -use namada_apps::wallet::{DecryptionError, FindKeyError}; +use namada_apps::client::utils::PRE_GENESIS_DIR; +use namada_apps::wallet::{DecryptionError, FindKeyError, Wallet}; use rand_core::OsRng; pub fn main() -> Result<()> { @@ -35,7 +36,9 @@ pub fn main() -> Result<()> { cmds::WalletAddress::Find(cmds::AddressOrAliasFind(args)) => { address_or_alias_find(ctx, args) } - cmds::WalletAddress::List(cmds::AddressList) => address_list(ctx), + cmds::WalletAddress::List(cmds::AddressList(args)) => { + address_list(ctx, args) + } cmds::WalletAddress::Add(cmds::AddressAdd(args)) => { address_add(ctx, args) } @@ -50,8 +53,8 @@ pub fn main() -> Result<()> { cmds::WalletMasp::AddAddrKey(cmds::MaspAddAddrKey(args)) => { address_key_add(ctx, args) } - cmds::WalletMasp::ListPayAddrs(cmds::MaspListPayAddrs) => { - payment_addresses_list(ctx) + cmds::WalletMasp::ListPayAddrs(cmds::MaspListPayAddrs(args)) => { + payment_addresses_list(ctx, args) } cmds::WalletMasp::ListKeys(cmds::MaspListKeys(args)) => { spending_keys_list(ctx, args) @@ -70,9 +73,10 @@ fn address_key_find( args::AddrKeyFind { alias, unsafe_show_secret, + is_pre_genesis, }: args::AddrKeyFind, ) { - let mut wallet = ctx.wallet; + let mut wallet = load_wallet(ctx, is_pre_genesis); let alias = alias.to_lowercase(); if let Ok(viewing_key) = wallet.find_viewing_key(&alias) { // Check if alias is a viewing key @@ -105,9 +109,10 @@ fn spending_keys_list( args::MaspKeysList { decrypt, unsafe_show_secret, + is_pre_genesis, }: args::MaspKeysList, ) { - let wallet = ctx.wallet; + let wallet = load_wallet(ctx, is_pre_genesis); let known_view_keys = wallet.get_viewing_keys(); let known_spend_keys = wallet.get_spending_keys(); if known_view_keys.is_empty() { @@ -171,8 +176,11 @@ fn spending_keys_list( } /// List payment addresses. -fn payment_addresses_list(ctx: Context) { - let wallet = ctx.wallet; +fn payment_addresses_list( + ctx: Context, + args::MaspListPayAddrs { is_pre_genesis }: args::MaspListPayAddrs, +) { + let wallet = load_wallet(ctx, is_pre_genesis); let known_addresses = wallet.get_payment_addrs(); if known_addresses.is_empty() { println!( @@ -194,10 +202,11 @@ fn spending_key_gen( ctx: Context, args::MaspSpendKeyGen { alias, + is_pre_genesis, unsafe_dont_encrypt, }: args::MaspSpendKeyGen, ) { - let mut wallet = ctx.wallet; + let mut wallet = load_wallet(ctx, is_pre_genesis); let alias = alias.to_lowercase(); let (alias, _key) = wallet.gen_spending_key(alias, unsafe_dont_encrypt); wallet.save().unwrap_or_else(|err| eprintln!("{}", err)); @@ -209,23 +218,28 @@ fn spending_key_gen( /// Generate a shielded payment address from the given key. fn payment_address_gen( - mut ctx: Context, + ctx: Context, args::MaspPayAddrGen { alias, viewing_key, pin, + is_pre_genesis, }: args::MaspPayAddrGen, ) { + let mut wallet = load_wallet(ctx, is_pre_genesis); let alias = alias.to_lowercase(); - let viewing_key = - ExtendedFullViewingKey::from(ctx.get_cached(&viewing_key)) - .fvk - .vk; + let viewing_key = wallet + .find_viewing_key(&viewing_key.raw) + .map(Clone::clone) + .unwrap_or_else(|_| { + eprintln!("Unknown viewing key {}", viewing_key.raw); + cli::safe_exit(1) + }); + let viewing_key = ExtendedFullViewingKey::from(viewing_key).fvk.vk; let (div, _g_d) = find_valid_diversifier(&mut OsRng); let payment_addr = viewing_key .to_payment_address(div) .expect("a PaymentAddress"); - let mut wallet = ctx.wallet; let alias = wallet .insert_payment_addr( alias, @@ -244,18 +258,19 @@ fn payment_address_gen( /// Add a viewing key, spending key, or payment address to wallet. fn address_key_add( - mut ctx: Context, + ctx: Context, args::MaspAddrKeyAdd { alias, value, + is_pre_genesis, unsafe_dont_encrypt, }: args::MaspAddrKeyAdd, ) { let alias = alias.to_lowercase(); + let mut wallet = load_wallet(ctx, is_pre_genesis); let (alias, typ) = match value { MaspValue::FullViewingKey(viewing_key) => { - let alias = ctx - .wallet + let alias = wallet .insert_viewing_key(alias, viewing_key) .unwrap_or_else(|| { eprintln!("Viewing key not added"); @@ -264,8 +279,7 @@ fn address_key_add( (alias, "viewing key") } MaspValue::ExtendedSpendingKey(spending_key) => { - let alias = ctx - .wallet + let alias = wallet .encrypt_insert_spending_key( alias, spending_key, @@ -278,8 +292,7 @@ fn address_key_add( (alias, "spending key") } MaspValue::PaymentAddress(payment_addr) => { - let alias = ctx - .wallet + let alias = wallet .insert_payment_addr(alias, payment_addr) .unwrap_or_else(|| { eprintln!("Payment address not added"); @@ -288,7 +301,7 @@ fn address_key_add( (alias, "payment address") } }; - ctx.wallet.save().unwrap_or_else(|err| eprintln!("{}", err)); + wallet.save().unwrap_or_else(|err| eprintln!("{}", err)); println!( "Successfully added a {} with the following alias to wallet: {}", typ, alias, @@ -302,10 +315,11 @@ fn key_and_address_gen( args::KeyAndAddressGen { scheme, alias, + is_pre_genesis, unsafe_dont_encrypt, }: args::KeyAndAddressGen, ) { - let mut wallet = ctx.wallet; + let mut wallet = load_wallet(ctx, is_pre_genesis); let (alias, _key) = wallet.gen_key(scheme, alias, unsafe_dont_encrypt); wallet.save().unwrap_or_else(|err| eprintln!("{}", err)); println!( @@ -321,10 +335,11 @@ fn key_find( public_key, alias, value, + is_pre_genesis, unsafe_show_secret, }: args::KeyFind, ) { - let mut wallet = ctx.wallet; + let mut wallet = load_wallet(ctx, is_pre_genesis); let found_keypair = match public_key { Some(pk) => wallet.find_key_by_pk(&pk), None => { @@ -361,10 +376,11 @@ fn key_list( ctx: Context, args::KeyList { decrypt, + is_pre_genesis, unsafe_show_secret, }: args::KeyList, ) { - let wallet = ctx.wallet; + let wallet = load_wallet(ctx, is_pre_genesis); let known_keys = wallet.get_keys(); if known_keys.is_empty() { println!( @@ -406,8 +422,14 @@ fn key_list( } /// Export a keypair to a file. -fn key_export(ctx: Context, args::KeyExport { alias }: args::KeyExport) { - let mut wallet = ctx.wallet; +fn key_export( + ctx: Context, + args::KeyExport { + alias, + is_pre_genesis, + }: args::KeyExport, +) { + let mut wallet = load_wallet(ctx, is_pre_genesis); wallet .find_key(alias.to_lowercase()) .map(|keypair| { @@ -427,8 +449,11 @@ fn key_export(ctx: Context, args::KeyExport { alias }: args::KeyExport) { } /// List all known addresses. -fn address_list(ctx: Context) { - let wallet = ctx.wallet; +fn address_list( + ctx: Context, + args::AddressList { is_pre_genesis }: args::AddressList, +) { + let wallet = load_wallet(ctx, is_pre_genesis); let known_addresses = wallet.get_addresses(); if known_addresses.is_empty() { println!( @@ -447,50 +472,71 @@ fn address_list(ctx: Context) { } /// Find address (alias) by its alias (address). -fn address_or_alias_find(ctx: Context, args: args::AddressOrAliasFind) { - let wallet = ctx.wallet; - if args.address.is_some() && args.alias.is_some() { +fn address_or_alias_find( + ctx: Context, + args::AddressOrAliasFind { + alias, + address, + is_pre_genesis, + }: args::AddressOrAliasFind, +) { + let wallet = load_wallet(ctx, is_pre_genesis); + if address.is_some() && alias.is_some() { panic!( "This should not be happening: clap should emit its own error \ message." ); - } else if args.alias.is_some() { - if let Some(address) = wallet.find_address(args.alias.as_ref().unwrap()) - { + } else if alias.is_some() { + if let Some(address) = wallet.find_address(alias.as_ref().unwrap()) { println!("Found address {}", address.to_pretty_string()); } else { println!( "No address with alias {} found. Use the command `address \ list` to see all the known addresses.", - args.alias.unwrap().to_lowercase() + alias.unwrap().to_lowercase() ); } - } else if args.address.is_some() { - if let Some(alias) = wallet.find_alias(args.address.as_ref().unwrap()) { + } else if address.is_some() { + if let Some(alias) = wallet.find_alias(address.as_ref().unwrap()) { println!("Found alias {}", alias); } else { println!( "No alias with address {} found. Use the command `address \ list` to see all the known addresses.", - args.address.unwrap() + address.unwrap() ); } } } /// Add an address to the wallet. -fn address_add(ctx: Context, args: args::AddressAdd) { - let mut wallet = ctx.wallet; - if wallet - .add_address(args.alias.clone().to_lowercase(), args.address) - .is_none() - { +fn address_add( + ctx: Context, + args::AddressAdd { + alias, + address, + is_pre_genesis, + }: args::AddressAdd, +) { + let mut wallet = load_wallet(ctx, is_pre_genesis); + if wallet.add_address(alias.to_lowercase(), address).is_none() { eprintln!("Address not added"); cli::safe_exit(1); } wallet.save().unwrap_or_else(|err| eprintln!("{}", err)); println!( "Successfully added a key and an address with alias: \"{}\"", - args.alias.to_lowercase() + alias.to_lowercase() ); } + +/// Load wallet for chain when `ctx.chain.is_some()` or pre-genesis wallet when +/// `is_pre_genesis || ctx.chain.is_none()`. +fn load_wallet(ctx: Context, is_pre_genesis: bool) -> Wallet { + if is_pre_genesis || ctx.chain.is_none() { + let wallet_path = ctx.global_args.base_dir.join(PRE_GENESIS_DIR); + Wallet::load_or_new(&wallet_path) + } else { + ctx.take_chain_or_exit().wallet + } +} diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index 08d6dfe722..24d54048be 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -553,7 +553,7 @@ pub mod cmds { /// List all known payment addresses #[derive(Clone, Debug)] - pub struct MaspListPayAddrs; + pub struct MaspListPayAddrs(pub args::MaspListPayAddrs); impl SubCmd for MaspListPayAddrs { const CMD: &'static str = "list-addrs"; @@ -561,12 +561,13 @@ pub mod cmds { fn parse(matches: &ArgMatches) -> Option { matches .subcommand_matches(Self::CMD) - .map(|_matches| MaspListPayAddrs) + .map(|matches| Self(args::MaspListPayAddrs::parse(matches))) } fn def() -> App { App::new(Self::CMD) .about("Lists all payment addresses in the wallet") + .add_args::() } } @@ -715,7 +716,7 @@ pub mod cmds { /// List known addresses #[derive(Clone, Debug)] - pub struct AddressList; + pub struct AddressList(pub args::AddressList); impl SubCmd for AddressList { const CMD: &'static str = "list"; @@ -723,11 +724,13 @@ pub mod cmds { fn parse(matches: &ArgMatches) -> Option { matches .subcommand_matches(Self::CMD) - .map(|_matches| AddressList) + .map(|matches| Self(args::AddressList::parse(matches))) } fn def() -> App { - App::new(Self::CMD).about("List all known addresses.") + App::new(Self::CMD) + .about("List all known addresses.") + .add_args::() } } @@ -1603,6 +1606,7 @@ pub mod args { "port-id", DefaultFn(|| PortId::from_str("transfer").unwrap()), ); + const PRE_GENESIS: ArgFlag = flag("pre-genesis"); const PROPOSAL_OFFLINE: ArgFlag = flag("offline"); const PROTOCOL_KEY: ArgOpt = arg_opt("protocol-key"); const PRE_GENESIS_PATH: ArgOpt = arg_opt("pre-genesis-path"); @@ -1793,7 +1797,7 @@ pub mod args { impl TxTransfer { pub fn parse_from_context( &self, - ctx: &mut Context, + ctx: &mut ChainContext, ) -> ParsedTxTransferArgs { ParsedTxTransferArgs { tx: self.tx.parse_from_context(ctx), @@ -2756,7 +2760,10 @@ pub mod args { } impl Tx { - pub fn parse_from_context(&self, ctx: &mut Context) -> ParsedTxArgs { + pub fn parse_from_context( + &self, + ctx: &mut ChainContext, + ) -> ParsedTxArgs { ParsedTxArgs { dry_run: self.dry_run, force: self.force, @@ -2880,6 +2887,7 @@ pub mod args { pub alias: String, /// Any MASP value pub value: MaspValue, + pub is_pre_genesis: bool, /// Don't encrypt the keypair pub unsafe_dont_encrypt: bool, } @@ -2888,10 +2896,12 @@ pub mod args { fn parse(matches: &ArgMatches) -> Self { let alias = ALIAS.parse(matches); let value = MASP_VALUE.parse(matches); + let is_pre_genesis = PRE_GENESIS.parse(matches); let unsafe_dont_encrypt = UNSAFE_DONT_ENCRYPT.parse(matches); Self { alias, value, + is_pre_genesis, unsafe_dont_encrypt, } } @@ -2907,6 +2917,10 @@ pub mod args { .def() .about("A spending key, viewing key, or payment address."), ) + .arg(PRE_GENESIS.def().about( + "Use pre-genesis wallet, instead of for the current chain, if \ + any.", + )) .arg(UNSAFE_DONT_ENCRYPT.def().about( "UNSAFE: Do not encrypt the keypair. Do not use this for keys \ used in a live network.", @@ -2919,6 +2933,7 @@ pub mod args { pub struct MaspSpendKeyGen { /// Key alias pub alias: String, + pub is_pre_genesis: bool, /// Don't encrypt the keypair pub unsafe_dont_encrypt: bool, } @@ -2926,9 +2941,11 @@ pub mod args { impl Args for MaspSpendKeyGen { fn parse(matches: &ArgMatches) -> Self { let alias = ALIAS.parse(matches); + let is_pre_genesis = PRE_GENESIS.parse(matches); let unsafe_dont_encrypt = UNSAFE_DONT_ENCRYPT.parse(matches); Self { alias, + is_pre_genesis, unsafe_dont_encrypt, } } @@ -2939,6 +2956,10 @@ pub mod args { .def() .about("An alias to be associated with the spending key."), ) + .arg(PRE_GENESIS.def().about( + "Use pre-genesis wallet, instead of for the current chain, if \ + any.", + )) .arg(UNSAFE_DONT_ENCRYPT.def().about( "UNSAFE: Do not encrypt the keypair. Do not use this for keys \ used in a live network.", @@ -2955,6 +2976,7 @@ pub mod args { pub viewing_key: WalletViewingKey, /// Pin pub pin: bool, + pub is_pre_genesis: bool, } impl Args for MaspPayAddrGen { @@ -2962,10 +2984,12 @@ pub mod args { let alias = ALIAS.parse(matches); let viewing_key = VIEWING_KEY.parse(matches); let pin = PIN.parse(matches); + let is_pre_genesis = PRE_GENESIS.parse(matches); Self { alias, viewing_key, pin, + is_pre_genesis, } } @@ -2980,6 +3004,10 @@ pub mod args { "Require that the single transaction to this address be \ pinned.", )) + .arg(PRE_GENESIS.def().about( + "Use pre-genesis wallet, instead of for the current chain, if \ + any.", + )) } } @@ -2990,6 +3018,8 @@ pub mod args { pub scheme: SchemeType, /// Key alias pub alias: Option, + /// Generate a key for pre-genesis, instead of a current chain + pub is_pre_genesis: bool, /// Don't encrypt the keypair pub unsafe_dont_encrypt: bool, } @@ -2998,10 +3028,12 @@ pub mod args { fn parse(matches: &ArgMatches) -> Self { let scheme = SCHEME.parse(matches); let alias = ALIAS_OPT.parse(matches); + let is_pre_genesis = PRE_GENESIS.parse(matches); let unsafe_dont_encrypt = UNSAFE_DONT_ENCRYPT.parse(matches); Self { scheme, alias, + is_pre_genesis, unsafe_dont_encrypt, } } @@ -3016,6 +3048,10 @@ pub mod args { "The key and address alias. If none provided, the alias will \ be the public key hash.", )) + .arg(PRE_GENESIS.def().about( + "Generate a key for pre-genesis, instead of for the current \ + chain, if any.", + )) .arg(UNSAFE_DONT_ENCRYPT.def().about( "UNSAFE: Do not encrypt the keypair. Do not use this for keys \ used in a live network.", @@ -3029,6 +3065,7 @@ pub mod args { pub public_key: Option, pub alias: Option, pub value: Option, + pub is_pre_genesis: bool, pub unsafe_show_secret: bool, } @@ -3037,12 +3074,14 @@ pub mod args { let public_key = RAW_PUBLIC_KEY_OPT.parse(matches); let alias = ALIAS_OPT.parse(matches); let value = VALUE.parse(matches); + let is_pre_genesis = PRE_GENESIS.parse(matches); let unsafe_show_secret = UNSAFE_SHOW_SECRET.parse(matches); Self { public_key, alias, value, + is_pre_genesis, unsafe_show_secret, } } @@ -3065,6 +3104,10 @@ pub mod args { "A public key or alias associated with the keypair.", ), ) + .arg(PRE_GENESIS.def().about( + "Use pre-genesis wallet, instead of for the current chain, if \ + any.", + )) .arg( UNSAFE_SHOW_SECRET .def() @@ -3078,15 +3121,18 @@ pub mod args { pub struct AddrKeyFind { pub alias: String, pub unsafe_show_secret: bool, + pub is_pre_genesis: bool, } impl Args for AddrKeyFind { fn parse(matches: &ArgMatches) -> Self { let alias = ALIAS.parse(matches); let unsafe_show_secret = UNSAFE_SHOW_SECRET.parse(matches); + let is_pre_genesis = PRE_GENESIS.parse(matches); Self { alias, unsafe_show_secret, + is_pre_genesis, } } @@ -3097,6 +3143,10 @@ pub mod args { .def() .about("UNSAFE: Print the spending key values."), ) + .arg(PRE_GENESIS.def().about( + "Use pre-genesis wallet, instead of for the current \ + chain, if any.", + )) } } @@ -3104,21 +3154,28 @@ pub mod args { #[derive(Clone, Debug)] pub struct MaspKeysList { pub decrypt: bool, + pub is_pre_genesis: bool, pub unsafe_show_secret: bool, } impl Args for MaspKeysList { fn parse(matches: &ArgMatches) -> Self { let decrypt = DECRYPT.parse(matches); + let is_pre_genesis = PRE_GENESIS.parse(matches); let unsafe_show_secret = UNSAFE_SHOW_SECRET.parse(matches); Self { decrypt, + is_pre_genesis, unsafe_show_secret, } } fn def(app: App) -> App { app.arg(DECRYPT.def().about("Decrypt keys that are encrypted.")) + .arg(PRE_GENESIS.def().about( + "Use pre-genesis wallet, instead of for the current \ + chain, if any.", + )) .arg( UNSAFE_SHOW_SECRET .def() @@ -3127,25 +3184,52 @@ pub mod args { } } + /// Wallet list shielded payment addresses arguments + #[derive(Clone, Debug)] + pub struct MaspListPayAddrs { + pub is_pre_genesis: bool, + } + + impl Args for MaspListPayAddrs { + fn parse(matches: &ArgMatches) -> Self { + let is_pre_genesis = PRE_GENESIS.parse(matches); + Self { is_pre_genesis } + } + + fn def(app: App) -> App { + app.arg(PRE_GENESIS.def().about( + "Use pre-genesis wallet, instead of for the current chain, if \ + any.", + )) + } + } + /// Wallet list keys arguments #[derive(Clone, Debug)] pub struct KeyList { pub decrypt: bool, + pub is_pre_genesis: bool, pub unsafe_show_secret: bool, } impl Args for KeyList { fn parse(matches: &ArgMatches) -> Self { let decrypt = DECRYPT.parse(matches); + let is_pre_genesis = PRE_GENESIS.parse(matches); let unsafe_show_secret = UNSAFE_SHOW_SECRET.parse(matches); Self { decrypt, + is_pre_genesis, unsafe_show_secret, } } fn def(app: App) -> App { app.arg(DECRYPT.def().about("Decrypt keys that are encrypted.")) + .arg(PRE_GENESIS.def().about( + "Use pre-genesis wallet, instead of for the current \ + chain, if any.", + )) .arg( UNSAFE_SHOW_SECRET .def() @@ -3158,13 +3242,17 @@ pub mod args { #[derive(Clone, Debug)] pub struct KeyExport { pub alias: String, + pub is_pre_genesis: bool, } impl Args for KeyExport { fn parse(matches: &ArgMatches) -> Self { let alias = ALIAS.parse(matches); - - Self { alias } + let is_pre_genesis = PRE_GENESIS.parse(matches); + Self { + alias, + is_pre_genesis, + } } fn def(app: App) -> App { @@ -3173,6 +3261,10 @@ pub mod args { .def() .about("The alias of the key you wish to export."), ) + .arg(PRE_GENESIS.def().about( + "Use pre-genesis wallet, instead of for the current chain, if \ + any.", + )) } } @@ -3181,13 +3273,19 @@ pub mod args { pub struct AddressOrAliasFind { pub alias: Option, pub address: Option
, + pub is_pre_genesis: bool, } impl Args for AddressOrAliasFind { fn parse(matches: &ArgMatches) -> Self { let alias = ALIAS_OPT.parse(matches); let address = RAW_ADDRESS_OPT.parse(matches); - Self { alias, address } + let is_pre_genesis = PRE_GENESIS.parse(matches); + Self { + alias, + address, + is_pre_genesis, + } } fn def(app: App) -> App { @@ -3201,6 +3299,10 @@ pub mod args { .def() .about("The bech32m encoded address string."), ) + .arg(PRE_GENESIS.def().about( + "Use pre-genesis wallet, instead of for the current chain, if \ + any.", + )) .group( ArgGroup::new("find_flags") .args(&[ALIAS_OPT.name, RAW_ADDRESS_OPT.name]) @@ -3209,18 +3311,44 @@ pub mod args { } } + /// List wallet address + #[derive(Clone, Debug)] + pub struct AddressList { + pub is_pre_genesis: bool, + } + + impl Args for AddressList { + fn parse(matches: &ArgMatches) -> Self { + let is_pre_genesis = PRE_GENESIS.parse(matches); + Self { is_pre_genesis } + } + + fn def(app: App) -> App { + app.arg(PRE_GENESIS.def().about( + "Use pre-genesis wallet, instead of for the current chain, if \ + any.", + )) + } + } + /// Wallet address add arguments #[derive(Clone, Debug)] pub struct AddressAdd { pub alias: String, pub address: Address, + pub is_pre_genesis: bool, } impl Args for AddressAdd { fn parse(matches: &ArgMatches) -> Self { let alias = ALIAS.parse(matches); let address = RAW_ADDRESS.parse(matches); - Self { alias, address } + let is_pre_genesis = PRE_GENESIS.parse(matches); + Self { + alias, + address, + is_pre_genesis, + } } fn def(app: App) -> App { @@ -3234,6 +3362,10 @@ pub mod args { .def() .about("The bech32m encoded address string."), ) + .arg(PRE_GENESIS.def().about( + "Use pre-genesis wallet, instead of for the current chain, if \ + any.", + )) } } diff --git a/apps/src/lib/cli/context.rs b/apps/src/lib/cli/context.rs index e61fda9dfc..c9a67fd83d 100644 --- a/apps/src/lib/cli/context.rs +++ b/apps/src/lib/cli/context.rs @@ -12,6 +12,7 @@ use namada::types::key::*; use namada::types::masp::*; use super::args; +use crate::cli::utils; use crate::client::tx::ShieldedContext; use crate::config::genesis::genesis_config; use crate::config::global::GlobalConfig; @@ -65,10 +66,17 @@ pub type WalletBalanceOwner = FromContext; pub struct Context { /// Global arguments pub global_args: args::Global, - /// The wallet - pub wallet: Wallet, /// The global configuration pub global_config: GlobalConfig, + /// Chain-specific context, if any chain is configured in `global_config` + pub chain: Option, +} + +/// Command execution context with chain-specific data +#[derive(Debug)] +pub struct ChainContext { + /// The wallet + pub wallet: Wallet, /// The ledger configuration for a specific chain ID pub config: Config, /// The context fr shielded operations @@ -80,49 +88,92 @@ pub struct Context { impl Context { pub fn new(global_args: args::Global) -> Result { let global_config = read_or_try_new_global_config(&global_args); - tracing::info!("Chain ID: {}", global_config.default_chain_id); - - let mut config = Config::load( - &global_args.base_dir, - &global_config.default_chain_id, - global_args.mode.clone(), - ); - - let chain_dir = global_args - .base_dir - .join(global_config.default_chain_id.as_str()); - let genesis_file_path = global_args - .base_dir - .join(format!("{}.toml", global_config.default_chain_id.as_str())); - let genesis = genesis_config::read_genesis_config(&genesis_file_path); - let native_token = genesis.native_token; - let default_genesis = - genesis_config::open_genesis_config(genesis_file_path)?; - let wallet = - Wallet::load_or_new_from_genesis(&chain_dir, default_genesis); - - // If the WASM dir specified, put it in the config - match global_args.wasm_dir.as_ref() { - Some(wasm_dir) => { - config.wasm_dir = wasm_dir.clone(); - } - None => { - if let Ok(wasm_dir) = env::var(ENV_VAR_WASM_DIR) { - let wasm_dir: PathBuf = wasm_dir.into(); - config.wasm_dir = wasm_dir; + + let chain = match global_config.default_chain_id.as_ref() { + Some(default_chain_id) => { + tracing::info!("Default chain ID: {default_chain_id}"); + let mut config = Config::load( + &global_args.base_dir, + default_chain_id, + global_args.mode.clone(), + ); + let chain_dir = + global_args.base_dir.join(default_chain_id.as_str()); + let genesis_file_path = global_args + .base_dir + .join(format!("{}.toml", default_chain_id.as_str())); + let genesis = + genesis_config::read_genesis_config(&genesis_file_path); + let native_token = genesis.native_token; + let default_genesis = + genesis_config::open_genesis_config(genesis_file_path)?; + let wallet = Wallet::load_or_new_from_genesis( + &chain_dir, + default_genesis, + ); + + // If the WASM dir specified, put it in the config + match global_args.wasm_dir.as_ref() { + Some(wasm_dir) => { + config.wasm_dir = wasm_dir.clone(); + } + None => { + if let Ok(wasm_dir) = env::var(ENV_VAR_WASM_DIR) { + let wasm_dir: PathBuf = wasm_dir.into(); + config.wasm_dir = wasm_dir; + } + } } + Some(ChainContext { + wallet, + config, + shielded: ShieldedContext::new(chain_dir), + native_token, + }) } - } + None => None, + }; + Ok(Self { global_args, - wallet, global_config, - config, - shielded: ShieldedContext::new(chain_dir), - native_token, + chain, }) } + /// Try to take the chain context, or exit the process with an error if no + /// chain is configured. + pub fn take_chain_or_exit(self) -> ChainContext { + self.chain + .unwrap_or_else(|| safe_exit_on_missing_chain_context()) + } + + /// Try to borrow chain context, or exit the process with an error if no + /// chain is configured. + pub fn borrow_chain_or_exit(&self) -> &ChainContext { + self.chain + .as_ref() + .unwrap_or_else(|| safe_exit_on_missing_chain_context()) + } + + /// Try to borrow mutably chain context, or exit the process with an error + /// if no chain is configured. + pub fn borrow_mut_chain_or_exit(&mut self) -> &mut ChainContext { + self.chain + .as_mut() + .unwrap_or_else(|| safe_exit_on_missing_chain_context()) + } +} + +fn safe_exit_on_missing_chain_context() -> ! { + eprintln!( + "No chain is configured. You may need to run `namada client utils \ + join-network` command." + ); + utils::safe_exit(1) +} + +impl ChainContext { /// Parse and/or look-up the value from the context. pub fn get(&self, from_context: &FromContext) -> T where @@ -165,22 +216,9 @@ impl Context { /// Get the wasm directory configured for the chain. /// - /// Note that in "dev" build, this may be the root `wasm` dir. + /// TODO(Tomas): Note that in "dev" build, this may be the root `wasm` dir. pub fn wasm_dir(&self) -> PathBuf { - let wasm_dir = - self.config.ledger.chain_dir().join(&self.config.wasm_dir); - - // In dev-mode with dev chain (the default), load wasm directly from the - // root wasm dir instead of the chain dir - #[cfg(feature = "dev")] - let wasm_dir = - if self.global_config.default_chain_id == ChainId::default() { - "wasm".into() - } else { - wasm_dir - }; - - wasm_dir + self.config.ledger.chain_dir().join(&self.config.wasm_dir) } /// Read the given WASM file from the WASM directory or an absolute path. @@ -224,7 +262,7 @@ pub fn read_or_try_new_global_config( /// Argument that can be given raw or found in the [`Context`]. #[derive(Debug, Clone)] pub struct FromContext { - raw: String, + pub raw: String, phantom: PhantomData, } @@ -282,8 +320,8 @@ impl FromContext where T: ArgFromContext, { - /// Parse and/or look-up the value from the context. - fn arg_from_ctx(&self, ctx: &Context) -> Result { + /// Parse and/or look-up the value from the chain context. + fn arg_from_ctx(&self, ctx: &ChainContext) -> Result { T::arg_from_ctx(ctx, &self.raw) } } @@ -292,32 +330,32 @@ impl FromContext where T: ArgFromMutContext, { - /// Parse and/or look-up the value from the mutable context. - fn arg_from_mut_ctx(&self, ctx: &mut Context) -> Result { + /// Parse and/or look-up the value from the mutable chain context. + fn arg_from_mut_ctx(&self, ctx: &mut ChainContext) -> Result { T::arg_from_mut_ctx(ctx, &self.raw) } } -/// CLI argument that found via the [`Context`]. +/// CLI argument that found via the [`ChainContext`]. pub trait ArgFromContext: Sized { fn arg_from_ctx( - ctx: &Context, + ctx: &ChainContext, raw: impl AsRef, ) -> Result; } -/// CLI argument that found via the [`Context`] and cached (as in case of an -/// encrypted keypair that has been decrypted), hence using mutable context. +/// CLI argument that found via the [`ChainContext`] and cached (as in case of +/// an encrypted keypair that has been decrypted), hence using mutable context. pub trait ArgFromMutContext: Sized { fn arg_from_mut_ctx( - ctx: &mut Context, + ctx: &mut ChainContext, raw: impl AsRef, ) -> Result; } impl ArgFromContext for Address { fn arg_from_ctx( - ctx: &Context, + ctx: &ChainContext, raw: impl AsRef, ) -> Result { let raw = raw.as_ref(); @@ -335,7 +373,7 @@ impl ArgFromContext for Address { impl ArgFromMutContext for common::SecretKey { fn arg_from_mut_ctx( - ctx: &mut Context, + ctx: &mut ChainContext, raw: impl AsRef, ) -> Result { let raw = raw.as_ref(); @@ -351,7 +389,7 @@ impl ArgFromMutContext for common::SecretKey { impl ArgFromMutContext for common::PublicKey { fn arg_from_mut_ctx( - ctx: &mut Context, + ctx: &mut ChainContext, raw: impl AsRef, ) -> Result { let raw = raw.as_ref(); @@ -376,7 +414,7 @@ impl ArgFromMutContext for common::PublicKey { impl ArgFromMutContext for ExtendedSpendingKey { fn arg_from_mut_ctx( - ctx: &mut Context, + ctx: &mut ChainContext, raw: impl AsRef, ) -> Result { let raw = raw.as_ref(); @@ -392,7 +430,7 @@ impl ArgFromMutContext for ExtendedSpendingKey { impl ArgFromMutContext for ExtendedViewingKey { fn arg_from_mut_ctx( - ctx: &mut Context, + ctx: &mut ChainContext, raw: impl AsRef, ) -> Result { let raw = raw.as_ref(); @@ -409,7 +447,7 @@ impl ArgFromMutContext for ExtendedViewingKey { impl ArgFromContext for PaymentAddress { fn arg_from_ctx( - ctx: &Context, + ctx: &ChainContext, raw: impl AsRef, ) -> Result { let raw = raw.as_ref(); @@ -426,7 +464,7 @@ impl ArgFromContext for PaymentAddress { impl ArgFromMutContext for TransferSource { fn arg_from_mut_ctx( - ctx: &mut Context, + ctx: &mut ChainContext, raw: impl AsRef, ) -> Result { let raw = raw.as_ref(); @@ -442,7 +480,7 @@ impl ArgFromMutContext for TransferSource { impl ArgFromContext for TransferTarget { fn arg_from_ctx( - ctx: &Context, + ctx: &ChainContext, raw: impl AsRef, ) -> Result { let raw = raw.as_ref(); @@ -457,7 +495,7 @@ impl ArgFromContext for TransferTarget { impl ArgFromMutContext for BalanceOwner { fn arg_from_mut_ctx( - ctx: &mut Context, + ctx: &mut ChainContext, raw: impl AsRef, ) -> Result { let raw = raw.as_ref(); diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index ab64ce948d..2e60f273fa 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -56,6 +56,7 @@ use namada::types::{address, storage, token}; use rust_decimal::Decimal; use tokio::time::{Duration, Instant}; +use crate::cli::context::ChainContext; use crate::cli::{self, args, Context}; use crate::client::tendermint_rpc_types::TxResponse; use crate::client::tx::{ @@ -154,7 +155,7 @@ pub async fn query_results(args: args::Query) -> Vec { /// transactions crediting/debiting the given owner. If token is specified, then /// restrict set to only transactions involving the given token. pub async fn query_tx_deltas( - ctx: &mut Context, + ctx: &mut ChainContext, ledger_address: TendermintAddress, query_owner: &Option, query_token: &Option
, @@ -266,11 +267,12 @@ pub async fn query_tx_deltas( /// Query the specified accepted transfers from the ledger pub async fn query_transfers(mut ctx: Context, args: args::QueryTransfers) { - let query_token = args.token.as_ref().map(|x| ctx.get(x)); - let query_owner = args.owner.as_ref().map(|x| ctx.get_cached(x)); + let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let query_token = args.token.as_ref().map(|x| chain_ctx.get(x)); + let query_owner = args.owner.as_ref().map(|x| chain_ctx.get_cached(x)); // Obtain the effects of all shielded and transparent transactions let transfers = query_tx_deltas( - &mut ctx, + chain_ctx, args.query.ledger_address.clone(), &query_owner, &query_token, @@ -278,7 +280,7 @@ pub async fn query_transfers(mut ctx: Context, args: args::QueryTransfers) { .await; // To facilitate lookups of human-readable token names let tokens = tokens(); - let vks = ctx.wallet.get_viewing_keys(); + let vks = chain_ctx.wallet.get_viewing_keys(); // To enable ExtendedFullViewingKeys to be displayed instead of ViewingKeys let fvk_map: HashMap<_, _> = vks .values() @@ -303,7 +305,7 @@ pub async fn query_transfers(mut ctx: Context, args: args::QueryTransfers) { for (acc, amt) in tx_delta { // Realize the rewards that would have been attained upon the // transaction's reception - let amt = ctx + let amt = chain_ctx .shielded .compute_exchanged_amount( client.clone(), @@ -313,8 +315,10 @@ pub async fn query_transfers(mut ctx: Context, args: args::QueryTransfers) { ) .await .0; - let dec = - ctx.shielded.decode_amount(client.clone(), amt, epoch).await; + let dec = chain_ctx + .shielded + .decode_amount(client.clone(), amt, epoch) + .await; shielded_accounts.insert(acc, dec); } // Check if this transfer pertains to the supplied token @@ -436,32 +440,33 @@ pub async fn query_raw_bytes(_ctx: Context, args: args::QueryRawBytes) { /// Query token balance(s) pub async fn query_balance(mut ctx: Context, args: args::QueryBalance) { + let chain_ctx = ctx.borrow_mut_chain_or_exit(); // Query the balances of shielded or transparent account types depending on // the CLI arguments - match args.owner.as_ref().map(|x| ctx.get_cached(x)) { + match args.owner.as_ref().map(|x| chain_ctx.get_cached(x)) { Some(BalanceOwner::FullViewingKey(_viewing_key)) => { - query_shielded_balance(&mut ctx, args).await + query_shielded_balance(chain_ctx, args).await } Some(BalanceOwner::Address(_owner)) => { - query_transparent_balance(&mut ctx, args).await + query_transparent_balance(chain_ctx, args).await } Some(BalanceOwner::PaymentAddress(_owner)) => { - query_pinned_balance(&mut ctx, args).await + query_pinned_balance(chain_ctx, args).await } None => { // Print pinned balance - query_pinned_balance(&mut ctx, args.clone()).await; + query_pinned_balance(chain_ctx, args.clone()).await; // Print shielded balance - query_shielded_balance(&mut ctx, args.clone()).await; + query_shielded_balance(chain_ctx, args.clone()).await; // Then print transparent balance - query_transparent_balance(&mut ctx, args).await; + query_transparent_balance(chain_ctx, args).await; } }; } /// Query token balance(s) pub async fn query_transparent_balance( - ctx: &mut Context, + ctx: &mut ChainContext, args: args::QueryBalance, ) { let client = HttpClient::new(args.query.ledger_address).unwrap(); @@ -541,7 +546,10 @@ pub async fn query_transparent_balance( } /// Query the token pinned balance(s) -pub async fn query_pinned_balance(ctx: &mut Context, args: args::QueryBalance) { +pub async fn query_pinned_balance( + ctx: &mut ChainContext, + args: args::QueryBalance, +) { // Map addresses to token names let tokens = address::tokens(); let owners = if let Some(pa) = args @@ -679,7 +687,7 @@ pub async fn query_pinned_balance(ctx: &mut Context, args: args::QueryBalance) { } fn print_balances( - ctx: &Context, + ctx: &ChainContext, balances: impl Iterator, token: &Address, target: Option<&Address>, @@ -871,7 +879,7 @@ pub fn value_by_address( /// Query token shielded balance(s) pub async fn query_shielded_balance( - ctx: &mut Context, + ctx: &mut ChainContext, args: args::QueryBalance, ) { // Used to control whether balances for all keys or a specific key are @@ -1370,12 +1378,13 @@ pub async fn query_protocol_parameters( /// Query PoS bond(s) pub async fn query_bonds(ctx: Context, args: args::QueryBonds) { + let chain_ctx = ctx.borrow_chain_or_exit(); let epoch = query_epoch(args.query.clone()).await; let client = HttpClient::new(args.query.ledger_address).unwrap(); match (args.owner, args.validator) { (Some(owner), Some(validator)) => { - let source = ctx.get(&owner); - let validator = ctx.get(&validator); + let source = chain_ctx.get(&owner); + let validator = chain_ctx.get(&validator); // Find owner's delegations to the given validator let bond_id = pos::BondId { source, validator }; let bond_key = pos::bond_key(&bond_id); @@ -1431,7 +1440,7 @@ pub async fn query_bonds(ctx: Context, args: args::QueryBonds) { } } (None, Some(validator)) => { - let validator = ctx.get(&validator); + let validator = chain_ctx.get(&validator); // Find validator's self-bonds let bond_id = pos::BondId { source: validator.clone(), @@ -1478,7 +1487,7 @@ pub async fn query_bonds(ctx: Context, args: args::QueryBonds) { } } (Some(owner), None) => { - let owner = ctx.get(&owner); + let owner = chain_ctx.get(&owner); // Find owner's bonds to any validator let bonds_prefix = pos::bonds_for_source_prefix(&owner); let bonds = @@ -1717,6 +1726,7 @@ pub async fn query_bonds(ctx: Context, args: args::QueryBonds) { /// Query PoS bonded stake pub async fn query_bonded_stake(ctx: Context, args: args::QueryBondedStake) { + let chain_ctx = ctx.borrow_chain_or_exit(); let epoch = match args.epoch { Some(epoch) => epoch, None => query_epoch(args.query.clone()).await, @@ -1735,7 +1745,7 @@ pub async fn query_bonded_stake(ctx: Context, args: args::QueryBondedStake) { match args.validator { Some(validator) => { - let validator = ctx.get(&validator); + let validator = chain_ctx.get(&validator); // Find bonded stake for the given validator let validator_deltas_key = pos::validator_deltas_key(&validator); let validator_deltas = query_storage_value::( @@ -1820,12 +1830,13 @@ pub async fn query_commission_rate( ctx: Context, args: args::QueryCommissionRate, ) { + let chain_ctx = ctx.borrow_chain_or_exit(); let epoch = match args.epoch { Some(epoch) => epoch, None => query_epoch(args.query.clone()).await, }; let client = HttpClient::new(args.query.ledger_address.clone()).unwrap(); - let validator = ctx.get(&args.validator); + let validator = chain_ctx.get(&args.validator); let is_validator = is_validator(&validator, args.query.ledger_address).await; @@ -1873,10 +1884,11 @@ pub async fn query_commission_rate( /// Query PoS slashes pub async fn query_slashes(ctx: Context, args: args::QuerySlashes) { + let chain_ctx = ctx.borrow_chain_or_exit(); let client = HttpClient::new(args.query.ledger_address).unwrap(); match args.validator { Some(validator) => { - let validator = ctx.get(&validator); + let validator = chain_ctx.get(&validator); // Find slashes for the given validator let slashes_key = pos::validator_slashes_key(&validator); let slashes = @@ -2183,8 +2195,9 @@ fn process_unbonds_query( /// Query for all conversions. pub async fn query_conversions(ctx: Context, args: args::QueryConversions) { + let chain_ctx = ctx.borrow_chain_or_exit(); // The chosen token type of the conversions - let target_token = args.token.as_ref().map(|x| ctx.get(x)); + let target_token = args.token.as_ref().map(|x| chain_ctx.get(x)); // To facilitate human readable token addresses let tokens = address::tokens(); let client = HttpClient::new(args.query.ledger_address).unwrap(); @@ -2931,7 +2944,7 @@ pub async fn get_governance_parameters(client: &HttpClient) -> GovParams { /// Try to find an alias for a given address from the wallet. If not found, /// formats the address into a string. -fn lookup_alias(ctx: &Context, addr: &Address) -> String { +fn lookup_alias(ctx: &ChainContext, addr: &Address) -> String { match ctx.wallet.find_alias(addr) { Some(alias) => format!("{}", alias), None => format!("{}", addr), diff --git a/apps/src/lib/client/signing.rs b/apps/src/lib/client/signing.rs index 0f893a0190..695c859042 100644 --- a/apps/src/lib/client/signing.rs +++ b/apps/src/lib/client/signing.rs @@ -12,8 +12,8 @@ use namada::types::token::Amount; use namada::types::transaction::{hash_tx, Fee, WrapperTx, MIN_FEE}; use super::rpc; -use crate::cli::context::{WalletAddress, WalletKeypair}; -use crate::cli::{self, args, Context}; +use crate::cli::context::{ChainContext, WalletAddress, WalletKeypair}; +use crate::cli::{self, args}; use crate::client::tendermint_rpc_types::TxBroadcastData; use crate::facade::tendermint_config::net::Address as TendermintAddress; use crate::facade::tendermint_rpc::HttpClient; @@ -90,7 +90,7 @@ pub enum TxSigningKey { /// possible. If no explicit signer given, use the `default`. If no `default` /// is given, panics. pub async fn tx_signer( - ctx: &mut Context, + ctx: &mut ChainContext, args: &args::Tx, mut default: TxSigningKey, ) -> common::SecretKey { @@ -145,13 +145,13 @@ pub async fn tx_signer( /// /// If it is a dry run, it is not put in a wrapper, but returned as is. pub async fn sign_tx( - mut ctx: Context, + ctx: &mut ChainContext, tx: Tx, args: &args::Tx, default: TxSigningKey, #[cfg(not(feature = "mainnet"))] requires_pow: bool, -) -> (Context, TxBroadcastData) { - let keypair = tx_signer(&mut ctx, args, default).await; +) -> TxBroadcastData { + let keypair = tx_signer(ctx, args, default).await; let tx = tx.sign(&keypair); let epoch = rpc::query_epoch(args::Query { @@ -162,7 +162,7 @@ pub async fn sign_tx( TxBroadcastData::DryRun(tx) } else { sign_wrapper( - &ctx, + ctx, args, epoch, tx, @@ -172,14 +172,14 @@ pub async fn sign_tx( ) .await }; - (ctx, broadcast_data) + broadcast_data } /// Create a wrapper tx from a normal tx. Get the hash of the /// wrapper and its payload which is needed for monitoring its /// progress on chain. pub async fn sign_wrapper( - ctx: &Context, + ctx: &ChainContext, args: &args::Tx, epoch: Epoch, tx: Tx, diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index 9ae1d0562f..4969db8616 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -66,7 +66,7 @@ use tokio::time::{Duration, Instant}; use super::rpc; use super::types::ShieldedTransferContext; -use crate::cli::context::WalletAddress; +use crate::cli::context::{ChainContext, WalletAddress}; use crate::cli::{args, safe_exit, Context}; use crate::client::rpc::{query_conversion, query_storage_value}; use crate::client::signing::{find_keypair, sign_tx, tx_signer, TxSigningKey}; @@ -101,14 +101,15 @@ const ENV_VAR_NAMADA_EVENTS_MAX_WAIT_TIME_SECONDS: &str = /// and `/applied` ABCI query endpoints. const DEFAULT_NAMADA_EVENTS_MAX_WAIT_TIME_SECONDS: u64 = 60; -pub async fn submit_custom(ctx: Context, args: args::TxCustom) { - let tx_code = ctx.read_wasm(args.code_path); +pub async fn submit_custom(mut ctx: Context, args: args::TxCustom) { + let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let tx_code = chain_ctx.read_wasm(args.code_path); let data = args.data_path.map(|data_path| { std::fs::read(data_path).expect("Expected a file at given data path") }); let tx = Tx::new(tx_code, data); - let (ctx, initialized_accounts) = process_tx( - ctx, + let initialized_accounts = process_tx( + chain_ctx, &args.tx, tx, TxSigningKey::None, @@ -116,11 +117,12 @@ pub async fn submit_custom(ctx: Context, args: args::TxCustom) { false, ) .await; - save_initialized_accounts(ctx, &args.tx, initialized_accounts).await; + save_initialized_accounts(chain_ctx, &args.tx, initialized_accounts).await; } -pub async fn submit_update_vp(ctx: Context, args: args::TxUpdateVp) { - let addr = ctx.get(&args.addr); +pub async fn submit_update_vp(mut ctx: Context, args: args::TxUpdateVp) { + let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let addr = chain_ctx.get(&args.addr); // Check that the address is established and exists on chain match &addr { @@ -155,7 +157,7 @@ pub async fn submit_update_vp(ctx: Context, args: args::TxUpdateVp) { } } - let vp_code = ctx.read_wasm(args.vp_code_path); + let vp_code = chain_ctx.read_wasm(args.vp_code_path); // Validate the VP code if let Err(err) = vm::validate_untrusted_wasm(&vp_code) { eprintln!("Validity predicate code validation failed with {}", err); @@ -164,14 +166,14 @@ pub async fn submit_update_vp(ctx: Context, args: args::TxUpdateVp) { } } - let tx_code = ctx.read_wasm(TX_UPDATE_VP_WASM); + let tx_code = chain_ctx.read_wasm(TX_UPDATE_VP_WASM); let data = UpdateVp { addr, vp_code }; let data = data.try_to_vec().expect("Encoding tx data shouldn't fail"); let tx = Tx::new(tx_code, Some(data)); process_tx( - ctx, + chain_ctx, &args.tx, tx, TxSigningKey::WalletAddress(args.addr), @@ -182,11 +184,12 @@ pub async fn submit_update_vp(ctx: Context, args: args::TxUpdateVp) { } pub async fn submit_init_account(mut ctx: Context, args: args::TxInitAccount) { - let public_key = ctx.get_cached(&args.public_key); + let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let public_key = chain_ctx.get_cached(&args.public_key); let vp_code = args .vp_code_path - .map(|path| ctx.read_wasm(path)) - .unwrap_or_else(|| ctx.read_wasm(VP_USER_WASM)); + .map(|path| chain_ctx.read_wasm(path)) + .unwrap_or_else(|| chain_ctx.read_wasm(VP_USER_WASM)); // Validate the VP code if let Err(err) = vm::validate_untrusted_wasm(&vp_code) { eprintln!("Validity predicate code validation failed with {}", err); @@ -195,7 +198,7 @@ pub async fn submit_init_account(mut ctx: Context, args: args::TxInitAccount) { } } - let tx_code = ctx.read_wasm(TX_INIT_ACCOUNT_WASM); + let tx_code = chain_ctx.read_wasm(TX_INIT_ACCOUNT_WASM); let data = InitAccount { public_key, vp_code, @@ -203,8 +206,8 @@ pub async fn submit_init_account(mut ctx: Context, args: args::TxInitAccount) { let data = data.try_to_vec().expect("Encoding tx data shouldn't fail"); let tx = Tx::new(tx_code, Some(data)); - let (ctx, initialized_accounts) = process_tx( - ctx, + let initialized_accounts = process_tx( + chain_ctx, &args.tx, tx, TxSigningKey::WalletAddress(args.source), @@ -212,7 +215,7 @@ pub async fn submit_init_account(mut ctx: Context, args: args::TxInitAccount) { false, ) .await; - save_initialized_accounts(ctx, &args.tx, initialized_accounts).await; + save_initialized_accounts(chain_ctx, &args.tx, initialized_accounts).await; } pub async fn submit_init_validator( @@ -230,6 +233,7 @@ pub async fn submit_init_validator( unsafe_dont_encrypt, }: args::TxInitValidator, ) { + let chain_ctx = ctx.borrow_mut_chain_or_exit(); let alias = tx_args .initialized_account_alias .as_ref() @@ -238,19 +242,21 @@ pub async fn submit_init_validator( let validator_key_alias = format!("{}-key", alias); let consensus_key_alias = format!("{}-consensus-key", alias); - let account_key = ctx.get_opt_cached(&account_key).unwrap_or_else(|| { - println!("Generating validator account key..."); - ctx.wallet - .gen_key( - scheme, - Some(validator_key_alias.clone()), - unsafe_dont_encrypt, - ) - .1 - .ref_to() - }); + let account_key = + chain_ctx.get_opt_cached(&account_key).unwrap_or_else(|| { + println!("Generating validator account key..."); + chain_ctx + .wallet + .gen_key( + scheme, + Some(validator_key_alias.clone()), + unsafe_dont_encrypt, + ) + .1 + .ref_to() + }); - let consensus_key = ctx + let consensus_key = chain_ctx .get_opt_cached(&consensus_key) .map(|key| match key { common::SecretKey::Ed25519(_) => key, @@ -261,7 +267,8 @@ pub async fn submit_init_validator( }) .unwrap_or_else(|| { println!("Generating consensus key..."); - ctx.wallet + chain_ctx + .wallet .gen_key( // Note that TM only allows ed25519 for consensus key SchemeType::Ed25519, @@ -271,14 +278,16 @@ pub async fn submit_init_validator( .1 }); - let protocol_key = ctx.get_opt_cached(&protocol_key); + let protocol_key = chain_ctx.get_opt_cached(&protocol_key); if protocol_key.is_none() { println!("Generating protocol signing key..."); } // Generate the validator keys - let validator_keys = - ctx.wallet.gen_validator_keys(protocol_key, scheme).unwrap(); + let validator_keys = chain_ctx + .wallet + .gen_validator_keys(protocol_key, scheme) + .unwrap(); let protocol_key = validator_keys.get_protocol_keypair().ref_to(); let dkg_key = validator_keys .dkg_keypair @@ -286,11 +295,14 @@ pub async fn submit_init_validator( .expect("DKG sessions keys should have been created") .public(); - ctx.wallet.save().unwrap_or_else(|err| eprintln!("{}", err)); + chain_ctx + .wallet + .save() + .unwrap_or_else(|err| eprintln!("{}", err)); let validator_vp_code = validator_vp_code_path - .map(|path| ctx.read_wasm(path)) - .unwrap_or_else(|| ctx.read_wasm(VP_USER_WASM)); + .map(|path| chain_ctx.read_wasm(path)) + .unwrap_or_else(|| chain_ctx.read_wasm(VP_USER_WASM)); // Validate the commission rate data if commission_rate > Decimal::ONE || commission_rate < Decimal::ZERO { @@ -323,7 +335,7 @@ pub async fn submit_init_validator( safe_exit(1) } } - let tx_code = ctx.read_wasm(TX_INIT_VALIDATOR_WASM); + let tx_code = chain_ctx.read_wasm(TX_INIT_VALIDATOR_WASM); let data = InitValidator { account_key, @@ -336,8 +348,8 @@ pub async fn submit_init_validator( }; let data = data.try_to_vec().expect("Encoding tx data shouldn't fail"); let tx = Tx::new(tx_code, Some(data)); - let (mut ctx, initialized_accounts) = process_tx( - ctx, + let initialized_accounts = process_tx( + chain_ctx, &tx_args, tx, TxSigningKey::WalletAddress(source), @@ -374,7 +386,7 @@ pub async fn submit_init_validator( } else { validator_address_alias }; - if let Some(new_alias) = ctx.wallet.add_address( + if let Some(new_alias) = chain_ctx.wallet.add_address( validator_address_alias.clone(), validator_address.clone(), ) { @@ -392,11 +404,15 @@ pub async fn submit_init_validator( } }; // add validator address and keys to the wallet - ctx.wallet + chain_ctx + .wallet .add_validator_data(validator_address, validator_keys); - ctx.wallet.save().unwrap_or_else(|err| eprintln!("{}", err)); + chain_ctx + .wallet + .save() + .unwrap_or_else(|err| eprintln!("{}", err)); - let tendermint_home = ctx.config.ledger.tendermint_dir(); + let tendermint_home = chain_ctx.config.ledger.tendermint_dir(); tendermint_node::write_validator_key(&tendermint_home, &consensus_key); tendermint_node::write_validator_state(tendermint_home); @@ -1503,7 +1519,8 @@ where } pub async fn submit_transfer(mut ctx: Context, args: args::TxTransfer) { - let parsed_args = args.parse_from_context(&mut ctx); + let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let parsed_args = args.parse_from_context(chain_ctx); let source = parsed_args.source.effective_address(); let target = parsed_args.target.effective_address(); // Check that the source address exists on chain @@ -1590,7 +1607,7 @@ pub async fn submit_transfer(mut ctx: Context, args: args::TxTransfer) { ( TxSigningKey::SecretKey(masp_tx_key()), 0.into(), - ctx.native_token.clone(), + chain_ctx.native_token.clone(), ) } else if source == masp_addr { ( @@ -1607,12 +1624,12 @@ pub async fn submit_transfer(mut ctx: Context, args: args::TxTransfer) { }; // If our chosen signer is the MASP sentinel key, then our shielded inputs // will need to cover the gas fees. - let chosen_signer = tx_signer(&mut ctx, &args.tx, default_signer.clone()) + let chosen_signer = tx_signer(chain_ctx, &args.tx, default_signer.clone()) .await .ref_to(); let shielded_gas = masp_tx_key().ref_to() == chosen_signer; // Determine whether to pin this transaction to a storage key - let key = match ctx.get(&args.target) { + let key = match chain_ctx.get(&args.target) { TransferTarget::PaymentAddress(pa) if pa.is_pinned() => Some(pa.hash()), _ => None, }; @@ -1642,16 +1659,20 @@ pub async fn submit_transfer(mut ctx: Context, args: args::TxTransfer) { let spending_keys: Vec<_> = spending_key.into_iter().collect(); // Load the current shielded context given the spending key we // possess - let _ = ctx.shielded.load(); - ctx.shielded + let _ = chain_ctx.shielded.load(); + chain_ctx + .shielded .fetch(&args.tx.ledger_address, &spending_keys, &[]) .await; // Save the update state so that future fetches can be // short-circuited - let _ = ctx.shielded.save(); - let stx_result = - gen_shielded_transfer(&mut ctx, &parsed_args, shielded_gas) - .await; + let _ = chain_ctx.shielded.save(); + let stx_result = gen_shielded_transfer( + chain_ctx, + &parsed_args, + shielded_gas, + ) + .await; match stx_result { Ok(stx) => stx.map(|x| x.0), Err(builder::Error::ChangeIsNegative(_)) => { @@ -1676,12 +1697,12 @@ pub async fn submit_transfer(mut ctx: Context, args: args::TxTransfer) { let data = transfer .try_to_vec() .expect("Encoding tx data shouldn't fail"); - let tx_code = ctx.read_wasm(TX_TRANSFER_WASM); + let tx_code = chain_ctx.read_wasm(TX_TRANSFER_WASM); let tx = Tx::new(tx_code, Some(data)); let signing_address = TxSigningKey::WalletAddress(args.source.to_address()); process_tx( - ctx, + chain_ctx, &args.tx, tx, signing_address, @@ -1691,8 +1712,9 @@ pub async fn submit_transfer(mut ctx: Context, args: args::TxTransfer) { .await; } -pub async fn submit_ibc_transfer(ctx: Context, args: args::TxIbcTransfer) { - let source = ctx.get(&args.source); +pub async fn submit_ibc_transfer(mut ctx: Context, args: args::TxIbcTransfer) { + let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let source = chain_ctx.get(&args.source); // Check that the source address exists on chain let source_exists = rpc::known_address(&source, args.tx.ledger_address.clone()).await; @@ -1705,7 +1727,7 @@ pub async fn submit_ibc_transfer(ctx: Context, args: args::TxIbcTransfer) { // We cannot check the receiver - let token = ctx.get(&args.token); + let token = chain_ctx.get(&args.token); // Check that the token address exists on chain let token_exists = rpc::known_address(&token, args.tx.ledger_address.clone()).await; @@ -1753,7 +1775,7 @@ pub async fn submit_ibc_transfer(ctx: Context, args: args::TxIbcTransfer) { } } } - let tx_code = ctx.read_wasm(TX_IBC_WASM); + let tx_code = chain_ctx.read_wasm(TX_IBC_WASM); let denom = match sub_prefix { // To parse IbcToken address, remove the address prefix @@ -1799,7 +1821,7 @@ pub async fn submit_ibc_transfer(ctx: Context, args: args::TxIbcTransfer) { let tx = Tx::new(tx_code, Some(data)); process_tx( - ctx, + chain_ctx, &args.tx, tx, TxSigningKey::WalletAddress(args.source), @@ -1810,6 +1832,7 @@ pub async fn submit_ibc_transfer(ctx: Context, args: args::TxIbcTransfer) { } pub async fn submit_init_proposal(mut ctx: Context, args: args::InitProposal) { + let chain_ctx = ctx.borrow_mut_chain_or_exit(); let file = File::open(&args.proposal_data).expect("File must exist."); let proposal: Proposal = serde_json::from_reader(file).expect("JSON was not well-formatted"); @@ -1878,9 +1901,9 @@ pub async fn submit_init_proposal(mut ctx: Context, args: args::InitProposal) { } if args.offline { - let signer = ctx.get(&signer); + let signer = chain_ctx.get(&signer); let signing_key = find_keypair( - &mut ctx.wallet, + &mut chain_ctx.wallet, &signer, args.tx.ledger_address.clone(), ) @@ -1916,7 +1939,7 @@ pub async fn submit_init_proposal(mut ctx: Context, args: args::InitProposal) { let balance = rpc::get_token_balance( &client, - &ctx.native_token, + &chain_ctx.native_token, &proposal.author, ) .await @@ -1941,11 +1964,11 @@ pub async fn submit_init_proposal(mut ctx: Context, args: args::InitProposal) { let data = init_proposal_data .try_to_vec() .expect("Encoding proposal data shouldn't fail"); - let tx_code = ctx.read_wasm(TX_INIT_PROPOSAL); + let tx_code = chain_ctx.read_wasm(TX_INIT_PROPOSAL); let tx = Tx::new(tx_code, Some(data)); process_tx( - ctx, + chain_ctx, &args.tx, tx, TxSigningKey::WalletAddress(signer), @@ -1957,6 +1980,7 @@ pub async fn submit_init_proposal(mut ctx: Context, args: args::InitProposal) { } pub async fn submit_vote_proposal(mut ctx: Context, args: args::VoteProposal) { + let chain_ctx = ctx.borrow_mut_chain_or_exit(); let signer = if let Some(addr) = &args.tx.signer { addr } else { @@ -1965,7 +1989,7 @@ pub async fn submit_vote_proposal(mut ctx: Context, args: args::VoteProposal) { }; if args.offline { - let signer = ctx.get(signer); + let signer = chain_ctx.get(signer); let proposal_file_path = args.proposal_data.expect("Proposal file should exist."); let file = File::open(&proposal_file_path).expect("File must exist."); @@ -1984,7 +2008,7 @@ pub async fn submit_vote_proposal(mut ctx: Context, args: args::VoteProposal) { } let signing_key = find_keypair( - &mut ctx.wallet, + &mut chain_ctx.wallet, &signer, args.tx.ledger_address.clone(), ) @@ -2020,7 +2044,7 @@ pub async fn submit_vote_proposal(mut ctx: Context, args: args::VoteProposal) { }) .await; - let voter_address = ctx.get(signer); + let voter_address = chain_ctx.get(signer); let proposal_id = args.proposal_id.unwrap(); let proposal_start_epoch_key = gov_storage::get_voting_start_epoch_key(proposal_id); @@ -2081,11 +2105,11 @@ pub async fn submit_vote_proposal(mut ctx: Context, args: args::VoteProposal) { let data = tx_data .try_to_vec() .expect("Encoding proposal data shouldn't fail"); - let tx_code = ctx.read_wasm(TX_VOTE_PROPOSAL); + let tx_code = chain_ctx.read_wasm(TX_VOTE_PROPOSAL); let tx = Tx::new(tx_code, Some(data)); process_tx( - ctx, + chain_ctx, &args.tx, tx, TxSigningKey::WalletAddress(signer.clone()), @@ -2108,19 +2132,20 @@ pub async fn submit_vote_proposal(mut ctx: Context, args: args::VoteProposal) { } pub async fn submit_reveal_pk(mut ctx: Context, args: args::RevealPk) { + let chain_ctx = ctx.borrow_mut_chain_or_exit(); let args::RevealPk { tx: args, public_key, } = args; - let public_key = ctx.get_cached(&public_key); - if !reveal_pk_if_needed(&mut ctx, &public_key, &args).await { + let public_key = chain_ctx.get_cached(&public_key); + if !reveal_pk_if_needed(chain_ctx, &public_key, &args).await { let addr: Address = (&public_key).into(); println!("PK for {addr} is already revealed, nothing to do."); } } pub async fn reveal_pk_if_needed( - ctx: &mut Context, + ctx: &mut ChainContext, public_key: &common::PublicKey, args: &args::Tx, ) -> bool { @@ -2144,7 +2169,7 @@ pub async fn has_revealed_pk( } pub async fn submit_reveal_pk_aux( - ctx: &mut Context, + ctx: &mut ChainContext, public_key: &common::PublicKey, args: &args::Tx, ) { @@ -2293,8 +2318,9 @@ async fn filter_delegations( delegations.into_iter().flatten().collect() } -pub async fn submit_bond(ctx: Context, args: args::Bond) { - let validator = ctx.get(&args.validator); +pub async fn submit_bond(mut ctx: Context, args: args::Bond) { + let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let validator = chain_ctx.get(&args.validator); // Check that the validator address exists on chain let is_validator = rpc::is_validator(&validator, args.tx.ledger_address.clone()).await; @@ -2307,7 +2333,7 @@ pub async fn submit_bond(ctx: Context, args: args::Bond) { safe_exit(1) } } - let source = ctx.get_opt(&args.source); + let source = chain_ctx.get_opt(&args.source); // Check that the source address exists on chain if let Some(source) = &source { let source_exists = @@ -2322,7 +2348,7 @@ pub async fn submit_bond(ctx: Context, args: args::Bond) { // Check bond's source (source for delegation or validator for self-bonds) // balance let bond_source = source.as_ref().unwrap_or(&validator); - let balance_key = token::balance_key(&ctx.native_token, bond_source); + let balance_key = token::balance_key(&chain_ctx.native_token, bond_source); let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); match rpc::query_storage_value::(&client, &balance_key).await { @@ -2346,7 +2372,7 @@ pub async fn submit_bond(ctx: Context, args: args::Bond) { } } } - let tx_code = ctx.read_wasm(TX_BOND_WASM); + let tx_code = chain_ctx.read_wasm(TX_BOND_WASM); let bond = pos::Bond { validator, amount: args.amount, @@ -2357,7 +2383,7 @@ pub async fn submit_bond(ctx: Context, args: args::Bond) { let tx = Tx::new(tx_code, Some(data)); let default_signer = args.source.unwrap_or(args.validator); process_tx( - ctx, + chain_ctx, &args.tx, tx, TxSigningKey::WalletAddress(default_signer), @@ -2367,8 +2393,9 @@ pub async fn submit_bond(ctx: Context, args: args::Bond) { .await; } -pub async fn submit_unbond(ctx: Context, args: args::Unbond) { - let validator = ctx.get(&args.validator); +pub async fn submit_unbond(mut ctx: Context, args: args::Unbond) { + let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let validator = chain_ctx.get(&args.validator); // Check that the validator address exists on chain let is_validator = rpc::is_validator(&validator, args.tx.ledger_address.clone()).await; @@ -2382,8 +2409,8 @@ pub async fn submit_unbond(ctx: Context, args: args::Unbond) { } } - let source = ctx.get_opt(&args.source); - let tx_code = ctx.read_wasm(TX_UNBOND_WASM); + let source = chain_ctx.get_opt(&args.source); + let tx_code = chain_ctx.read_wasm(TX_UNBOND_WASM); // Check the source's current bond amount let bond_source = source.clone().unwrap_or_else(|| validator.clone()); @@ -2432,7 +2459,7 @@ pub async fn submit_unbond(ctx: Context, args: args::Unbond) { let tx = Tx::new(tx_code, Some(data)); let default_signer = args.source.unwrap_or(args.validator); process_tx( - ctx, + chain_ctx, &args.tx, tx, TxSigningKey::WalletAddress(default_signer), @@ -2442,13 +2469,14 @@ pub async fn submit_unbond(ctx: Context, args: args::Unbond) { .await; } -pub async fn submit_withdraw(ctx: Context, args: args::Withdraw) { +pub async fn submit_withdraw(mut ctx: Context, args: args::Withdraw) { + let chain_ctx = ctx.borrow_mut_chain_or_exit(); let epoch = rpc::query_epoch(args::Query { ledger_address: args.tx.ledger_address.clone(), }) .await; - let validator = ctx.get(&args.validator); + let validator = chain_ctx.get(&args.validator); // Check that the validator address exists on chain let is_validator = rpc::is_validator(&validator, args.tx.ledger_address.clone()).await; @@ -2462,8 +2490,8 @@ pub async fn submit_withdraw(ctx: Context, args: args::Withdraw) { } } - let source = ctx.get_opt(&args.source); - let tx_code = ctx.read_wasm(TX_WITHDRAW_WASM); + let source = chain_ctx.get_opt(&args.source); + let tx_code = chain_ctx.read_wasm(TX_WITHDRAW_WASM); // Check the source's current unbond amount let bond_source = source.clone().unwrap_or_else(|| validator.clone()); @@ -2507,7 +2535,7 @@ pub async fn submit_withdraw(ctx: Context, args: args::Withdraw) { let tx = Tx::new(tx_code, Some(data)); let default_signer = args.source.unwrap_or(args.validator); process_tx( - ctx, + chain_ctx, &args.tx, tx, TxSigningKey::WalletAddress(default_signer), @@ -2518,18 +2546,19 @@ pub async fn submit_withdraw(ctx: Context, args: args::Withdraw) { } pub async fn submit_validator_commission_change( - ctx: Context, + mut ctx: Context, args: args::TxCommissionRateChange, ) { + let chain_ctx = ctx.borrow_mut_chain_or_exit(); let epoch = rpc::query_epoch(args::Query { ledger_address: args.tx.ledger_address.clone(), }) .await; - let tx_code = ctx.read_wasm(TX_CHANGE_COMMISSION_WASM); + let tx_code = chain_ctx.read_wasm(TX_CHANGE_COMMISSION_WASM); let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); - let validator = ctx.get(&args.validator); + let validator = chain_ctx.get(&args.validator); if rpc::is_validator(&validator, args.tx.ledger_address.clone()).await { if args.rate < Decimal::ZERO || args.rate > Decimal::ONE { eprintln!("Invalid new commission rate, received {}", args.rate); @@ -2583,7 +2612,7 @@ pub async fn submit_validator_commission_change( } let data = pos::CommissionChange { - validator: ctx.get(&args.validator), + validator: chain_ctx.get(&args.validator), new_rate: args.rate, }; let data = data.try_to_vec().expect("Encoding tx data shouldn't fail"); @@ -2591,7 +2620,7 @@ pub async fn submit_validator_commission_change( let tx = Tx::new(tx_code, Some(data)); let default_signer = args.validator; process_tx( - ctx, + chain_ctx, &args.tx, tx, TxSigningKey::WalletAddress(default_signer), @@ -2604,13 +2633,13 @@ pub async fn submit_validator_commission_change( /// Submit transaction and wait for result. Returns a list of addresses /// initialized in the transaction if any. In dry run, this is always empty. async fn process_tx( - ctx: Context, + ctx: &mut ChainContext, args: &args::Tx, tx: Tx, default_signer: TxSigningKey, #[cfg(not(feature = "mainnet"))] requires_pow: bool, -) -> (Context, Vec
) { - let (ctx, to_broadcast) = sign_tx( +) -> Vec
{ + let to_broadcast = sign_tx( ctx, tx, args, @@ -2632,7 +2661,7 @@ async fn process_tx( if args.dry_run { if let TxBroadcastData::DryRun(tx) = to_broadcast { rpc::dry_run_tx(&args.ledger_address, tx.to_bytes()).await; - (ctx, vec![]) + vec![] } else { panic!( "Expected a dry-run transaction, received a wrapper \ @@ -2650,8 +2679,8 @@ async fn process_tx( // Return result based on executed operation, otherwise deal with // the encountered errors uniformly match result { - Right(Ok(result)) => (ctx, result.initialized_accounts), - Left(Ok(_)) => (ctx, Vec::default()), + Right(Ok(result)) => result.initialized_accounts, + Left(Ok(_)) => Vec::default(), Right(Err(err)) => { eprintln!( "Encountered error while broadcasting transaction: {}", @@ -2672,7 +2701,7 @@ async fn process_tx( /// Save accounts initialized from a tx into the wallet, if any. async fn save_initialized_accounts( - mut ctx: Context, + ctx: &mut ChainContext, args: &args::Tx, initialized_accounts: Vec
, ) { diff --git a/apps/src/lib/client/types.rs b/apps/src/lib/client/types.rs index 1ed41e9c80..b451a09b8b 100644 --- a/apps/src/lib/client/types.rs +++ b/apps/src/lib/client/types.rs @@ -10,7 +10,8 @@ use namada::types::transaction::GasLimit; use namada::types::{key, token}; use super::rpc; -use crate::cli::{args, Context}; +use crate::cli::args; +use crate::cli::context::ChainContext; use crate::client::tx::Conversions; use crate::facade::tendermint_config::net::Address as TendermintAddress; @@ -71,7 +72,7 @@ pub trait ShieldedTransferContext { } #[async_trait(?Send)] -impl ShieldedTransferContext for Context { +impl ShieldedTransferContext for ChainContext { async fn collect_unspent_notes( &mut self, ledger_address: TendermintAddress, diff --git a/apps/src/lib/config/global.rs b/apps/src/lib/config/global.rs index f1d8e5d483..6c9bae0e4a 100644 --- a/apps/src/lib/config/global.rs +++ b/apps/src/lib/config/global.rs @@ -26,29 +26,30 @@ pub enum Error { pub type Result = std::result::Result; -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Default, Serialize, Deserialize)] pub struct GlobalConfig { /// The default chain ID - pub default_chain_id: ChainId, + pub default_chain_id: Option, // NOTE: There will be sub-chains in here in future } impl GlobalConfig { pub fn new(default_chain_id: ChainId) -> Self { - Self { default_chain_id } + Self { + default_chain_id: Some(default_chain_id), + } } /// Try to read the global config from a file. pub fn read(base_dir: impl AsRef) -> Result { let file_path = Self::file_path(base_dir.as_ref()); let file_name = file_path.to_str().expect("Expected UTF-8 file path"); - if !file_path.exists() { - return Err(Error::FileNotFound(file_name.to_string())); - }; let mut config = config::Config::new(); - config - .merge(config::File::with_name(file_name)) - .map_err(Error::ReadError)?; + if file_path.exists() { + config + .merge(config::File::with_name(file_name)) + .map_err(Error::ReadError)?; + }; config.try_into().map_err(Error::DeserializationError) }