diff --git a/Cargo.lock b/Cargo.lock index 23d90bb1be..c8b894eedf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3092,6 +3092,7 @@ dependencies = [ "regex", "retry", "ripemd", + "rpassword", "secp256k1", "semver 1.0.20", "serde", @@ -3173,6 +3174,7 @@ dependencies = [ "ibc-relayer-types", "reqwest", "serde", + "serde_json", "tokio", "toml 0.7.8", "tracing", @@ -5403,6 +5405,16 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "rpassword" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffc936cf8a7ea60c58f030fd36a612a48f440610214dc54bc36431f9ea0c3efb" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "rustc-demangle" version = "0.1.23" @@ -5750,9 +5762,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.107" +version = "1.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" dependencies = [ "itoa", "ryu", diff --git a/crates/relayer-cli/Cargo.toml b/crates/relayer-cli/Cargo.toml index 2ff1bd1005..be23988a68 100644 --- a/crates/relayer-cli/Cargo.toml +++ b/crates/relayer-cli/Cargo.toml @@ -55,6 +55,7 @@ tokio = { version = "1.0", features = ["full"] } tracing = "0.1.36" tracing-subscriber = { version = "0.3.14", features = ["fmt", "env-filter", "json"]} time = "0.3" + [dependencies.tendermint] version = "0.34.0" features = ["secp256k1"] diff --git a/crates/relayer-cli/src/commands/keys/add.rs b/crates/relayer-cli/src/commands/keys/add.rs index c63559834f..2c26f51afc 100644 --- a/crates/relayer-cli/src/commands/keys/add.rs +++ b/crates/relayer-cli/src/commands/keys/add.rs @@ -10,9 +10,11 @@ use abscissa_core::{Command, Runnable}; use eyre::eyre; use hdpath::StandardHDPath; use ibc_relayer::{ + chain::namada::wallet::CliWalletUtils, config::{ChainConfig, Config}, keyring::{ - AnySigningKeyPair, KeyRing, Secp256k1KeyPair, SigningKeyPair, SigningKeyPairSized, Store, + AnySigningKeyPair, KeyRing, NamadaKeyPair, Secp256k1KeyPair, SigningKeyPair, + SigningKeyPairSized, Store, }, }; use ibc_relayer_types::core::ics24_host::identifier::ChainId; @@ -220,8 +222,42 @@ pub fn add_key( keyring.add_key(key_name, key_pair.clone())?; key_pair.into() } - ChainConfig::Namada(_) => { - return Err(eyre!("Namada key cannot be added")); + ChainConfig::Namada(config) => { + let mut keyring = + KeyRing::new_namada(Store::Test, &config.id, &config.key_store_folder)?; + + check_key_exists(&keyring, key_name, overwrite); + + let path = if file.is_file() { + file.parent().ok_or(eyre!("invalid Namada wallet path"))? + } else { + file + }; + let mut wallet = CliWalletUtils::new(path.to_path_buf()); + wallet + .load() + .map_err(|_| eyre!("error loading Namada wallet"))?; + + let keys = wallet.get_keys(); + let (secret_key, pkh) = keys + .get(key_name) + .ok_or_else(|| eyre!("error loading the key from Namada wallet"))?; + let secret_key = secret_key + .get::(true, None) + .map_err(|_| eyre!("error loading the key from Namada wallet"))?; + let pkh = + pkh.ok_or_else(|| eyre!("error loading the public key hash from Namada wallet"))?; + let address = wallet + .find_address(key_name) + .ok_or_else(|| eyre!("error loading the address from Namada wallet"))?; + let namada_key = NamadaKeyPair { + alias: key_name.to_string(), + address: address.into_owned(), + pkh: pkh.clone(), + secret_key: secret_key.clone(), + }; + keyring.add_key(key_name, namada_key.clone())?; + namada_key.into() } }; @@ -260,7 +296,9 @@ pub fn restore_key( key_pair.into() } ChainConfig::Namada(_) => { - return Err(eyre!("Namada key cannot be restored")); + return Err(eyre!( + "Namada key can't be restored here. Use Namada wallet." + )); } }; diff --git a/crates/relayer-cli/src/commands/keys/delete.rs b/crates/relayer-cli/src/commands/keys/delete.rs index b7be2e6258..aef00748d0 100644 --- a/crates/relayer-cli/src/commands/keys/delete.rs +++ b/crates/relayer-cli/src/commands/keys/delete.rs @@ -123,8 +123,10 @@ pub fn delete_key(config: &ChainConfig, key_name: &str) -> eyre::Result<()> { )?; keyring.remove_key(key_name)?; } - ChainConfig::Namada(_) => { - return Err(eyre!("Namada key cannot be deleted")); + ChainConfig::Namada(config) => { + let mut keyring = + KeyRing::new_namada(Store::Test, &config.id, &config.key_store_folder)?; + keyring.remove_key(key_name)?; } } Ok(()) @@ -144,8 +146,13 @@ pub fn delete_all_keys(config: &ChainConfig) -> eyre::Result<()> { keyring.remove_key(&key_name)?; } } - ChainConfig::Namada(_) => { - return Err(eyre!("Namada key cannot be deleted")); + ChainConfig::Namada(config) => { + let mut keyring = + KeyRing::new_namada(Store::Test, &config.id, &config.key_store_folder)?; + let keys = keyring.keys()?; + for (key_name, _) in keys { + keyring.remove_key(&key_name)?; + } } } Ok(()) diff --git a/crates/relayer-rest/Cargo.toml b/crates/relayer-rest/Cargo.toml index 4eee1d3dc5..e65583a65c 100644 --- a/crates/relayer-rest/Cargo.toml +++ b/crates/relayer-rest/Cargo.toml @@ -25,4 +25,5 @@ tokio = "1.26" [dev-dependencies] reqwest = { version = "0.11.16", features = ["json"], default-features = false } +serde_json = "1" toml = "0.7.3" diff --git a/crates/relayer-rest/tests/mock.rs b/crates/relayer-rest/tests/mock.rs index b18a1b4531..1eb950f7d9 100644 --- a/crates/relayer-rest/tests/mock.rs +++ b/crates/relayer-rest/tests/mock.rs @@ -46,9 +46,12 @@ where let response = reqwest::get(&format!("http://127.0.0.1:{port}{path}")) .await .unwrap() - .json::() + .json() .await .unwrap(); + // Workaround for serde_json deserialization failure + // from_str/from_slice() failed for ChainConfig + let response = serde_json::from_value::(response).unwrap(); assert_eq!(response, expected); diff --git a/crates/relayer/Cargo.toml b/crates/relayer/Cargo.toml index d92a63c6e7..382474e326 100644 --- a/crates/relayer/Cargo.toml +++ b/crates/relayer/Cargo.toml @@ -24,9 +24,6 @@ ibc-proto = { version = "0.38.0", features = ["serde"] } ibc-telemetry = { version = "0.26.0", path = "../telemetry", optional = true } ibc-relayer-types = { version = "0.26.0", path = "../relayer-types", features = ["mocks"] } -namada = { git = "https://github.com/anoma/namada", rev = "7116b6aa916026e97f3f871f291c6ba1c7b427d2" } -namada_sdk = { git = "https://github.com/anoma/namada", rev = "7116b6aa916026e97f3f871f291c6ba1c7b427d2", features = ["std", "async-client"] } - subtle-encoding = "0.5" humantime-serde = "1.1.1" serde = "1.0" @@ -73,6 +70,7 @@ strum = { version = "0.25", features = ["derive"] } tokio-stream = "0.1.14" once_cell = "1.17.1" tracing-subscriber = { version = "0.3.14", features = ["fmt", "env-filter", "json"] } +rpassword = "5.0.1" [dependencies.byte-unit] version = "4.0.19" @@ -111,6 +109,15 @@ default-features = false version = "0.34.0" default-features = false +[dependencies.namada] +git = "https://github.com/anoma/namada" +rev = "7116b6aa916026e97f3f871f291c6ba1c7b427d2" + +[dependencies.namada_sdk] +git = "https://github.com/anoma/namada" +rev = "7116b6aa916026e97f3f871f291c6ba1c7b427d2" +features = ["std", "async-client"] + [dev-dependencies] ibc-relayer-types = { version = "0.26.0", path = "../relayer-types", features = ["mocks"] } serial_test = "2.0.0" diff --git a/crates/relayer/src/chain/namada.rs b/crates/relayer/src/chain/namada.rs index 78413bfbb6..3834df34ee 100644 --- a/crates/relayer/src/chain/namada.rs +++ b/crates/relayer/src/chain/namada.rs @@ -3,7 +3,6 @@ use core::str::FromStr; use std::thread; use core::time::Duration; -use std::path::Path; use ibc_proto::ibc::applications::fee::v1::{ QueryIncentivizedPacketRequest, QueryIncentivizedPacketResponse, @@ -34,8 +33,7 @@ use ibc_relayer_types::events::IbcEvent; use ibc_relayer_types::signer::Signer; use ibc_relayer_types::Height as ICSHeight; use namada::ledger::ibc::storage; -use namada::ledger::parameters::storage as param_storage; -use namada::ledger::parameters::EpochDuration; +use namada::ledger::parameters::{storage as param_storage, EpochDuration}; use namada::ledger::storage::ics23_specs::ibc_proof_specs; use namada::ledger::storage::Sha256Hasher; use namada::proof_of_stake::parameters::OwnedPosParams; @@ -44,16 +42,15 @@ use namada::types::address::{Address, InternalAddress}; use namada::types::storage::{Key, KeySeg, PrefixValue}; use namada::types::token; use namada_sdk::borsh::BorshDeserialize; -use namada_sdk::io::StdIo; +use namada_sdk::io::NullIo; use namada_sdk::masp::fs::FsShieldedUtils; use namada_sdk::masp::ShieldedContext; use namada_sdk::queries::Client as SdkClient; -use namada_sdk::wallet::fs::FsWalletUtils; -use namada_sdk::wallet::Wallet; +use namada_sdk::wallet::Store; +use namada_sdk::wallet::{StoredKeypair, Wallet}; use namada_sdk::{rpc, NamadaImpl}; use tendermint::block::Height as TmHeight; -use tendermint::node; -use tendermint::Time; +use tendermint::{node, Time}; use tendermint_light_client::types::LightBlock as TMLightBlock; use tendermint_rpc::client::CompatMode; use tendermint_rpc::endpoint::broadcast::tx_sync::Response; @@ -64,6 +61,7 @@ use crate::account::Balance; use crate::chain::client::ClientSettings; use crate::chain::cosmos::config::CosmosSdkConfig; use crate::chain::cosmos::types::tx::{TxStatus, TxSyncResult}; +use crate::chain::endpoint::{ChainEndpoint, ChainStatus, HealthCheck}; use crate::chain::handle::Subscription; use crate::chain::requests::*; use crate::chain::tracking::TrackedMsgs; @@ -75,39 +73,41 @@ use crate::denom::DenomTrace; use crate::error::Error; use crate::event::source::{EventSource, TxEventSourceCmd}; use crate::event::IbcEventWithHeight; -use crate::keyring::{KeyRing, Secp256k1KeyPair}; +use crate::keyring::{KeyRing, NamadaKeyPair, SigningKeyPair}; use crate::light_client::tendermint::LightClient as TmLightClient; -use crate::light_client::LightClient; -use crate::light_client::Verified; +use crate::light_client::{LightClient, Verified}; use crate::misbehaviour::MisbehaviourEvidence; -use crate::chain::endpoint::{ChainEndpoint, ChainStatus, HealthCheck}; - -const BASE_WALLET_DIR: &str = "namada_wallet"; - -pub mod query; -pub mod tx; +pub mod key; +mod query; +mod tx; +pub mod wallet; pub struct NamadaChain { - // Reuse CosmosSdkConfig for tendermint's light clients + /// Reuse CosmosSdkConfig for tendermint's light clients config: CosmosSdkConfig, rpc_client: HttpClient, - wallet: Wallet, + /// Wallet for Namada context just reading the added keys + wallet: Wallet, + /// Shielded context for Namada context shielded_ctx: ShieldedContext, + /// Namada native token native_token: Address, light_client: TmLightClient, rt: Arc, - keybase: KeyRing, + keybase: KeyRing, tx_monitor_cmd: Option, } impl NamadaChain { - fn namada_ctx(&mut self) -> NamadaImpl<'_, HttpClient, FsWalletUtils, FsShieldedUtils, StdIo> { + fn namada_ctx( + &mut self, + ) -> NamadaImpl<'_, HttpClient, wallet::NullWalletUtils, FsShieldedUtils, NullIo> { NamadaImpl::native_new( &self.rpc_client, &mut self.wallet, &mut self.shielded_ctx, - &StdIo, + &NullIo, self.native_token.clone(), ) } @@ -185,7 +185,7 @@ impl ChainEndpoint for NamadaChain { type ConsensusState = TmConsensusState; type ClientState = TmClientState; type Time = Time; - type SigningKeyPair = Secp256k1KeyPair; + type SigningKeyPair = NamadaKeyPair; fn id(&self) -> &ChainId { &self.config.id @@ -209,25 +209,25 @@ impl ChainEndpoint for NamadaChain { let node_info = rt.block_on(fetch_node_info(&rpc_client, &config))?; let light_client = TmLightClient::from_cosmos_sdk_config(&config, node_info.id)?; - // not used in Namada, but the trait requires KeyRing - let keybase = KeyRing::new( - config.key_store_type, - "", - &config.id, - &config.key_store_folder, - ) - .map_err(Error::key_base)?; - - // check if the wallet has been set up for this relayer - let wallet_path = Path::new(BASE_WALLET_DIR).join(config.id.to_string()); - let mut wallet = FsWalletUtils::new(wallet_path); - wallet.load().expect("Loading a wallet failed"); - wallet - .find_key(&config.key_name, None) - .map_err(Error::namada_key_pair_not_found)?; + let keybase = + KeyRing::new_namada(config.key_store_type, &config.id, &config.key_store_folder) + .map_err(Error::key_base)?; let shielded_ctx = ShieldedContext::::default(); + let mut store = Store::default(); + let key = keybase + .get_key(&config.key_name) + .map_err(|e| Error::key_not_found(config.key_name.clone(), e))?; + store.insert_address::(key.alias.into(), key.address, true); + store.insert_keypair::( + config.key_name.clone().into(), + StoredKeypair::Raw(key.secret_key), + key.pkh, + true, + ); + let wallet = Wallet::new(wallet::NullWalletUtils, store); + let native_token = rt .block_on(rpc::query_native_token(&rpc_client)) .map_err(Error::namada_sdk)?; @@ -267,10 +267,6 @@ impl ChainEndpoint for NamadaChain { ) })?; - // TODO Namada health check - - // TODO version check - Ok(HealthCheck::Healthy) } @@ -297,21 +293,17 @@ impl ChainEndpoint for NamadaChain { } fn get_key(&mut self) -> Result { - // TODO add Namada key - use crate::keyring::errors::Error as KeyringError; - Err(Error::key_not_found( - self.config().key_name.clone(), - KeyringError::key_not_found(), - )) + self.keybase + .get_key(&self.config.key_name) + .map_err(|e| Error::key_not_found(self.config.key_name.clone(), e)) } fn get_signer(&self) -> Result { - let address = self - .wallet - .find_address(&self.config.key_name) - .ok_or_else(|| Error::namada_address_not_found(self.config.key_name.clone()))?; - - Ok(Signer::from_str(&address.to_string()).unwrap()) + let key = self + .keybase + .get_key(&self.config.key_name) + .map_err(|e| Error::key_not_found(self.config.key_name.clone(), e))?; + Ok(Signer::from_str(&key.account()).expect("The key name shouldn't be empty")) } fn ibc_version(&self) -> Result, Error> { @@ -418,18 +410,16 @@ impl ChainEndpoint for NamadaChain { } fn query_balance(&self, key_name: Option<&str>, denom: Option<&str>) -> Result { - let key_name = key_name.unwrap_or(&self.config.key_name); - let denom = denom.unwrap_or("NAM"); - let token = match self.wallet.find_address(denom) { - Some(addr) => addr.into_owned(), - None => Address::decode(denom) - .map_err(|_| Error::namada_address_not_found(denom.to_string()))?, - }; + // Given key_name and denom should be raw Namada addresses + let default_owner = self.get_signer()?; + let owner = key_name.unwrap_or(default_owner.as_ref()); + let owner = Address::decode(owner) + .map_err(|_| Error::namada_address_not_found(owner.to_string()))?; - let owner = self - .wallet - .find_address(key_name) - .ok_or_else(|| Error::namada_address_not_found(key_name.to_string()))?; + let default_token = self.native_token.to_string(); + let denom = denom.unwrap_or(&default_token); + let token = Address::decode(denom) + .map_err(|_| Error::namada_address_not_found(denom.to_string()))?; let balance_key = token::balance_key(&token, &owner); let (value, _) = self.query(balance_key, QueryHeight::Latest, IncludeProof::No)?; @@ -463,17 +453,16 @@ impl ChainEndpoint for NamadaChain { } fn query_all_balances(&self, key_name: Option<&str>) -> Result, Error> { - let key_name = key_name.unwrap_or(&self.config.key_name); - let key_owner = self - .wallet - .find_address(key_name) - .ok_or_else(|| Error::namada_address_not_found(key_name.to_string()))?; + let default_owner = self.get_signer()?; + let owner = key_name.unwrap_or(default_owner.as_ref()); + let owner = Address::decode(owner) + .map_err(|_| Error::namada_address_not_found(owner.to_string()))?; let mut balances = vec![]; let prefix = Key::from(Address::Internal(InternalAddress::Multitoken).to_db_key()); for PrefixValue { key, value } in self.query_prefix(prefix)? { - if let Some([token, owner]) = token::is_any_token_balance_key(&key) { - if key_owner.as_ref() == owner { + if let Some([token, bal_owner]) = token::is_any_token_balance_key(&key) { + if owner == *bal_owner { let amount = token::Amount::try_from_slice(&value[..]).map_err(Error::borsh_decode)?; let denom_key = token::denom_key(token); @@ -492,14 +481,9 @@ impl ChainEndpoint for NamadaChain { denom: namada_denom, } }; - let alias = self - .wallet - .find_alias(token) - .map(|a| a.to_string()) - .unwrap_or(token.to_string()); let balance = Balance { amount: denominated_amount.to_string(), - denom: alias, + denom: token.to_string(), }; balances.push(balance); } diff --git a/crates/relayer/src/chain/namada/key.rs b/crates/relayer/src/chain/namada/key.rs new file mode 100644 index 0000000000..558cb6a468 --- /dev/null +++ b/crates/relayer/src/chain/namada/key.rs @@ -0,0 +1,49 @@ +use core::any::Any; + +use namada_sdk::core::types::address::Address; +use namada_sdk::core::types::key::common::SecretKey; +use namada_sdk::core::types::key::PublicKeyHash; + +use crate::config::AddressType; +use crate::keyring::errors::Error; +use crate::keyring::{KeyFile, KeyType, SigningKeyPair}; +use hdpath::StandardHDPath; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct NamadaKeyPair { + pub alias: String, + pub address: Address, + pub pkh: PublicKeyHash, + pub secret_key: SecretKey, +} + +impl SigningKeyPair for NamadaKeyPair { + const KEY_TYPE: KeyType = KeyType::Secp256k1; + type KeyFile = KeyFile; + + fn from_key_file(_key_file: Self::KeyFile, _hd_path: &StandardHDPath) -> Result { + unimplemented!("Namada key can't be restored from a KeyFile") + } + + fn from_mnemonic( + _mnemonic: &str, + _hd_path: &StandardHDPath, + _address_type: &AddressType, + _account_prefix: &str, + ) -> Result { + unimplemented!("Namada key can't be restored from a KeyFile") + } + + fn account(&self) -> String { + self.address.to_string() + } + + fn sign(&self, _message: &[u8]) -> Result, Error> { + unimplemented!("don't use this to sign a Namada transaction") + } + + fn as_any(&self) -> &dyn Any { + self + } +} diff --git a/crates/relayer/src/chain/namada/query.rs b/crates/relayer/src/chain/namada/query.rs index 7a0c6ea21d..44d534abb1 100644 --- a/crates/relayer/src/chain/namada/query.rs +++ b/crates/relayer/src/chain/namada/query.rs @@ -7,18 +7,18 @@ use namada::ledger::ibc::storage::{ibc_denom_key_prefix, is_ibc_denom_key}; use namada::types::address::{Address, InternalAddress}; use namada::types::storage::{BlockHeight, Epoch, Key, PrefixValue}; use namada_sdk::borsh::BorshDeserialize; -use namada_sdk::queries::Client as SdkClient; -use namada_sdk::queries::RPC; +use namada_sdk::queries::{Client as SdkClient, RPC}; use namada_sdk::rpc; use tendermint::block::Height as TmHeight; -use crate::chain::requests::{QueryClientEventRequest, QueryHeight, QueryPacketEventDataRequest}; +use crate::chain::endpoint::ChainEndpoint; +use crate::chain::requests::{ + IncludeProof, QueryClientEventRequest, QueryHeight, QueryPacketEventDataRequest, +}; use crate::error::Error; use crate::event::{ibc_event_try_from_abci_event, IbcEventWithHeight}; use super::NamadaChain; -use crate::chain::endpoint::ChainEndpoint; -use crate::chain::requests::IncludeProof; impl NamadaChain { pub fn query( diff --git a/crates/relayer/src/chain/namada/tx.rs b/crates/relayer/src/chain/namada/tx.rs index 62c8291014..7b569e4f62 100644 --- a/crates/relayer/src/chain/namada/tx.rs +++ b/crates/relayer/src/chain/namada/tx.rs @@ -33,29 +33,18 @@ impl NamadaChain { let chain_id = ChainId::from_str(self.config.id.as_str()).expect("invalid chain ID"); - let fee_token = self.config.gas_price.denom.clone(); - let fee_token = self - .wallet - .find_address(&fee_token) - .ok_or_else(|| Error::namada_address_not_found(fee_token))? - .into_owned(); + let fee_token = &self.config.gas_price.denom; + let fee_token = Address::decode(fee_token) + .map_err(|_| Error::namada_address_not_found(fee_token.to_string()))?; // fee let gas_limit_key = parameter_storage::get_fee_unshielding_gas_limit_key(); let (value, _) = self.query(gas_limit_key, QueryHeight::Latest, IncludeProof::No)?; let gas_limit = GasLimit::try_from_slice(&value).map_err(Error::borsh_decode)?; - // the wallet should exist because it's confirmed when the bootstrap - let relayer_key_pair = self - .wallet - .find_key(&self.config.key_name, None) - .expect("The relayer key should exist in the wallet"); - - let relayer_addr = self - .wallet - .find_address(&self.config.key_name) - .expect("The relayer doesn't exist in the wallet") - .into_owned(); + let namada_key = self.get_key()?; + let relayer_key_pair = namada_key.secret_key; + let relayer_addr = namada_key.address; let tx_args = TxArgs { dry_run: false, diff --git a/crates/relayer/src/chain/namada/wallet.rs b/crates/relayer/src/chain/namada/wallet.rs new file mode 100644 index 0000000000..00f59b4e45 --- /dev/null +++ b/crates/relayer/src/chain/namada/wallet.rs @@ -0,0 +1,65 @@ +use std::path::PathBuf; +use std::{env, fs}; + +use namada_sdk::wallet::fs::FsWalletStorage; +use namada_sdk::wallet::{LoadStoreError, Store, Wallet, WalletIo, WalletStorage}; +use namada_sdk::zeroize::Zeroizing; +use signature::rand_core::OsRng; + +/// Wallet utils for Namada context +#[derive(Clone)] +pub(super) struct NullWalletUtils; + +impl WalletIo for NullWalletUtils { + type Rng = OsRng; +} + +// Namada wallet never accesses the storage. It reads the keys added in the bootstrap from the store. +impl WalletStorage for NullWalletUtils { + fn save(&self, _wallet: &Wallet) -> Result<(), LoadStoreError> { + Ok(()) + } + + fn load(&self, _wallet: &mut Wallet) -> Result<(), LoadStoreError> { + Ok(()) + } +} + +/// For Namada wallet for adding a key with a password +#[derive(Clone)] +pub struct CliWalletUtils { + store_dir: PathBuf, +} + +impl CliWalletUtils { + pub fn new(store_dir: PathBuf) -> Wallet { + Wallet::new(Self { store_dir }, Store::default()) + } +} + +impl FsWalletStorage for CliWalletUtils { + fn store_dir(&self) -> &PathBuf { + &self.store_dir + } +} + +impl WalletIo for CliWalletUtils { + type Rng = OsRng; + + fn read_password(_confirm: bool) -> Zeroizing { + match env::var("NAMADA_WALLET_PASSWORD_FILE") { + Ok(path) => Zeroizing::new( + fs::read_to_string(path).expect("Something went wrong reading the file"), + ), + Err(_) => match env::var("NAMADA_WALLET_PASSWORD") { + Ok(password) => Zeroizing::new(password), + Err(_) => { + let prompt = "Enter your decryption password: "; + rpassword::read_password_from_tty(Some(prompt)) + .map(Zeroizing::new) + .expect("Failed reading password from tty.") + } + }, + } + } +} diff --git a/crates/relayer/src/config.rs b/crates/relayer/src/config.rs index 33ef0851b3..d2572e403f 100644 --- a/crates/relayer/src/config.rs +++ b/crates/relayer/src/config.rs @@ -648,8 +648,15 @@ impl ChainConfig { .map(|(key_name, keys)| (key_name, keys.into())) .collect() } - // TODO Namada should use the wallet - ChainConfig::Namada(_) => return Err(keyring::errors::Error::key_not_found()), + ChainConfig::Namada(config) => { + let keyring = + KeyRing::new_namada(Store::Test, &config.id, &config.key_store_folder)?; + keyring + .keys()? + .into_iter() + .map(|(key_name, keys)| (key_name, keys.into())) + .collect() + } }; Ok(keys) } diff --git a/crates/relayer/src/keyring.rs b/crates/relayer/src/keyring.rs index 0ec609f6e7..446ba7b039 100644 --- a/crates/relayer/src/keyring.rs +++ b/crates/relayer/src/keyring.rs @@ -5,6 +5,8 @@ pub use key_type::KeyType; pub use secp256k1_key_pair::Secp256k1KeyPair; pub use signing_key_pair::{SigningKeyPair, SigningKeyPairSized}; +pub use crate::chain::namada::key::NamadaKeyPair; + mod any_signing_key_pair; mod ed25519_key_pair; mod key_type; @@ -286,6 +288,16 @@ impl KeyRing { } } +impl KeyRing { + pub fn new_namada( + store: Store, + chain_id: &ChainId, + ks_folder: &Option, + ) -> Result { + Self::new(store, "", chain_id, ks_folder) + } +} + fn disk_store_path(folder_name: &str, keystore_folder: &Option) -> Result { let ks_folder = match keystore_folder { Some(folder) => folder.to_owned(), diff --git a/crates/relayer/src/keyring/any_signing_key_pair.rs b/crates/relayer/src/keyring/any_signing_key_pair.rs index b9dd880c10..0a96eb99dc 100644 --- a/crates/relayer/src/keyring/any_signing_key_pair.rs +++ b/crates/relayer/src/keyring/any_signing_key_pair.rs @@ -1,12 +1,13 @@ use serde::Serialize; -use super::{Ed25519KeyPair, KeyType, Secp256k1KeyPair, SigningKeyPair}; +use super::{Ed25519KeyPair, KeyType, NamadaKeyPair, Secp256k1KeyPair, SigningKeyPair}; #[derive(Clone, Debug, Serialize)] #[serde(untagged)] pub enum AnySigningKeyPair { Secp256k1(Secp256k1KeyPair), Ed25519(Ed25519KeyPair), + Namada(NamadaKeyPair), } impl AnySigningKeyPair { @@ -14,6 +15,7 @@ impl AnySigningKeyPair { match self { Self::Secp256k1(key_pair) => key_pair.account(), Self::Ed25519(key_pair) => key_pair.account(), + Self::Namada(key_pair) => key_pair.account(), } } @@ -21,6 +23,7 @@ impl AnySigningKeyPair { match self { Self::Secp256k1(_) => Secp256k1KeyPair::KEY_TYPE, Self::Ed25519(_) => Ed25519KeyPair::KEY_TYPE, + Self::Namada(_) => NamadaKeyPair::KEY_TYPE, } } @@ -28,6 +31,7 @@ impl AnySigningKeyPair { match self { Self::Secp256k1(key_pair) => key_pair.as_any(), Self::Ed25519(key_pair) => key_pair.as_any(), + Self::Namada(key_pair) => key_pair.as_any(), } .downcast_ref::() .map(T::clone) @@ -45,3 +49,9 @@ impl From for AnySigningKeyPair { Self::Ed25519(key_pair) } } + +impl From for AnySigningKeyPair { + fn from(key_pair: NamadaKeyPair) -> Self { + Self::Namada(key_pair) + } +} diff --git a/e2e/namada-gaia-simple-transfers b/e2e/namada-gaia-simple-transfers index 9f1950386b..1cca1451e1 100755 --- a/e2e/namada-gaia-simple-transfers +++ b/e2e/namada-gaia-simple-transfers @@ -85,7 +85,7 @@ cargo run --bin hermes -- --config config_for_namada.toml \ --b-port transfer \ --new-client-connection --yes -# Transfer 100 samoleans from Gaia to Namada +echo "~~ Transfer 100 samoleans from Gaia to Namada ~~" namada_receiver=$(${NAMADAW} --base-dir ${base_dir} address find --alias relayer | awk '{print $4}') cargo run --bin hermes -- --config config_for_namada.toml \ tx ft-transfer \ @@ -114,18 +114,18 @@ cargo run --bin hermes -- --config config_for_namada.toml \ --src-port transfer \ --src-channel channel-0 -echo "Balances on Namada" +echo "==== Balances on Namada ====" ${NAMADAC} --base-dir ${base_dir} balance \ --token ${IBC_TOKEN} \ --owner relayer \ --node ${NAMADA_LEDGER_ADDR} -echo "Balances on Gaia" +echo "==== Balances on Gaia ====" gaia_user=$(gaiad --home ${DATA_DIR}/gaia-0 \ keys --keyring-backend="test" show user -a) gaiad query bank balances ${gaia_user} -# transfer back 50 samoleans from Namada to Gaia +echo "~~ Transfer back 50 samoleans from Namada to Gaia ~~" ${NAMADAC} --base-dir ${base_dir} ibc-transfer \ --source relayer \ --receiver ${gaia_user} \ @@ -151,20 +151,20 @@ cargo run --bin hermes -- --config config_for_namada.toml \ --src-port transfer \ --src-channel channel-0 -echo "Balances on Namada" +echo "==== Balances on Namada ====" ${NAMADAC} --base-dir ${base_dir} balance \ --token ${IBC_TOKEN} \ --owner relayer \ --node ${NAMADA_LEDGER_ADDR} -echo "Balances on Gaia" +echo "==== Balances on Gaia ====" gaiad query bank balances ${gaia_user} echo "==== Start Hermes ====" cargo run --bin hermes -- --config config_for_namada.toml \ start > ${HERMES_DIR}/e2e/hermes.log 2>&1 & -# transfer 200 apfel from Namada to Gaia +echo "~~ Transfer 200 apfel from Namada to Gaia ~~" ${NAMADAC} --base-dir ${base_dir} ibc-transfer \ --source relayer \ --receiver ${gaia_user} \ @@ -175,14 +175,14 @@ ${NAMADAC} --base-dir ${base_dir} ibc-transfer \ --node ${NAMADA_LEDGER_ADDR} # wait for relaying -sleep 10 +sleep 15 -echo "Balances on Namada" +echo "==== Balances on Namada ====" ${NAMADAC} --base-dir ${base_dir} balance \ --owner relayer \ --node ${NAMADA_LEDGER_ADDR} -echo "Balances on Gaia" +echo "==== Balances on Gaia ====" gaiad query bank balances ${gaia_user} killall hermes diff --git a/e2e/namada-simple-transfers b/e2e/namada-simple-transfers index 91ca496231..1737d406d1 100755 --- a/e2e/namada-simple-transfers +++ b/e2e/namada-simple-transfers @@ -84,7 +84,7 @@ ${NAMADAC} --base-dir ${base_dir_b} transfer \ receiver_a=$(${NAMADAW} --base-dir ${base_dir_a} address find --alias relayer | awk '{print $4}') receiver_b=$(${NAMADAW} --base-dir ${base_dir_b} address find --alias relayer | awk '{print $4}') -# transfer 100 apfel from chain_a to chain_b +echo "~~ Transfer 100 apfel from chain_a to chain_b ~~" ${NAMADAC} --base-dir ${base_dir_a} ibc-transfer \ --source relayer \ --receiver ${receiver_b} \ @@ -110,13 +110,13 @@ cargo run --bin hermes -- --config config_for_namada.toml \ --src-port transfer \ --src-channel channel-0 -echo "Balances on chain A" +echo "==== Balances on chain A ====" ${NAMADAC} --base-dir ${base_dir_a} balance \ --token apfel \ --owner relayer \ --node ${LEDGER_ADDR_A} -echo "Balances on chain B" +echo "==== Balances on chain B ====" ${NAMADAC} --base-dir ${base_dir_b} balance \ --owner relayer \ --node ${LEDGER_ADDR_B} @@ -127,7 +127,7 @@ received_token=$(${NAMADAC} --base-dir ${base_dir_b} balance \ | sed -e s/://g \ | awk '$1 ~ /transfer\/channel-0/ {print $1}') -# transfer back 50 apfel from chain_b to chain_a +echo "~~ Transfer back 50 apfel from chain_b to chain_a ~~" ${NAMADAC} --base-dir ${base_dir_b} ibc-transfer \ --source relayer \ --receiver ${receiver_a} \ @@ -153,13 +153,13 @@ cargo run --bin hermes -- --config config_for_namada.toml \ --src-port transfer \ --src-channel channel-0 -echo "Balances on chain A" +echo "==== Balances on chain A ====" ${NAMADAC} --base-dir ${base_dir_a} balance \ --token apfel \ --owner relayer \ --node ${LEDGER_ADDR_A} -echo "Balances on chain B" +echo "==== Balances on chain B ====" ${NAMADAC} --base-dir ${base_dir_b} balance \ --token ${received_token} \ --owner relayer \ @@ -169,7 +169,7 @@ echo "==== Start Hermes ====" cargo run --bin hermes -- --config config_for_namada.toml \ start > ${HERMES_DIR}/e2e/hermes.log 2>&1 & -# transfer 200 apfel from chain_a to chain_b +echo "~~ Transfer 200 apfel from chain_a to chain_b ~~" ${NAMADAC} --base-dir ${base_dir_a} ibc-transfer \ --source relayer \ --receiver ${receiver_b} \ @@ -179,7 +179,7 @@ ${NAMADAC} --base-dir ${base_dir_a} ibc-transfer \ --channel-id channel-0 \ --node ${LEDGER_ADDR_A} -# transfer 300 apfel from chain_b to chain_a +echo "~~ Transfer 300 apfel from chain_b to chain_a ~~" ${NAMADAC} --base-dir ${base_dir_b} ibc-transfer \ --source relayer \ --receiver ${receiver_a} \ @@ -190,14 +190,14 @@ ${NAMADAC} --base-dir ${base_dir_b} ibc-transfer \ --node ${LEDGER_ADDR_B} # wait for relaying -sleep 10 +sleep 15 -echo "Balances on chain A" +echo "==== Balances on chain A ====" ${NAMADAC} --base-dir ${base_dir_a} balance \ --owner relayer \ --node ${LEDGER_ADDR_A} -echo "Balances on chain B" +echo "==== Balances on chain B ====" ${NAMADAC} --base-dir ${base_dir_b} balance \ --owner relayer \ --node ${LEDGER_ADDR_B} diff --git a/scripts/setup-namada b/scripts/setup-namada index 2df3708fc7..2d441b665d 100755 --- a/scripts/setup-namada +++ b/scripts/setup-namada @@ -67,7 +67,7 @@ event_source = { mode = 'push', url = 'ws://127.0.0.1:27657/websocket', batch_de account_prefix = '' key_name = 'relayer' store_prefix = 'ibc' -gas_price = { price = 0.001, denom = 'nam' } +gas_price = { price = 0.001, denom = '_FEE_TOKEN_A_' } [[chains]] id = '_CHAIN_ID_B_' @@ -78,7 +78,7 @@ event_source = { mode = 'push', url = 'ws://127.0.0.1:28657/websocket', batch_de account_prefix = '' key_name = 'relayer' store_prefix = 'ibc' -gas_price = { price = 0.001, denom = 'nam' } +gas_price = { price = 0.001, denom = '_FEE_TOKEN_B_' } " function make_genesis() { @@ -138,18 +138,24 @@ function copy_wasm() { function init_relayer_acc() { local suffix=$1 local chain_id=$2 - local ledger_addr=$3 local base_dir=${DATA_DIR}/namada-${suffix} - local wallet_dir=${HERMES_DIR}/namada_wallet/${chain_id} - - #cp ${base_dir}/${chain_id}/setup/other/wallet.toml ${base_dir}/${chain_id}/wallet.toml ${NAMADAW} --base-dir ${base_dir} \ key gen --alias relayer --unsafe-dont-encrypt +} + +function add_relayer_key() { + local suffix=$1 + local chain_id=$2 - mkdir -p ${wallet_dir} - cp ${base_dir}/${chain_id}/wallet.toml ${wallet_dir} + local base_dir=${DATA_DIR}/namada-${suffix} + + cargo run --bin hermes -- --config ${HERMES_DIR}/config_for_namada.toml \ + keys add \ + --chain ${chain_id} \ + --key-file ${base_dir}/${chain_id}/wallet.toml \ + --overwrite } # ==== main ==== @@ -207,16 +213,25 @@ echo "Namada chain B's PID = $!" sleep 5 -# create "relayer" account on each chain -init_relayer_acc "a" ${chain_id_a} ${LEDGER_ADDR_A} -init_relayer_acc "b" ${chain_id_b} ${LEDGER_ADDR_B} +# Create "relayer" account on each chain +init_relayer_acc "a" ${chain_id_a} +init_relayer_acc "b" ${chain_id_b} -# for the relayer +# Get token addresses +nam_addr_a=$(${NAMADAW} --base-dir ${DATA_DIR}/namada-a address find --alias nam | awk '{print $4}') +nam_addr_b=$(${NAMADAW} --base-dir ${DATA_DIR}/namada-b address find --alias nam | awk '{print $4}') + +# Make Hermes config cd ${HERMES_DIR} echo "${HERMES_CONFIG_TEMPLATE}" \ | sed -e "s/_CHAIN_ID_A_/${chain_id_a}/g" -e "s/_CHAIN_ID_B_/${chain_id_b}/g" \ + | sed -e "s/_FEE_TOKEN_A_/${nam_addr_a}/g" -e "s/_FEE_TOKEN_B_/${nam_addr_b}/g" \ > ${HERMES_DIR}/config_for_namada.toml +# Add namada keys to Hermes +add_relayer_key "a" ${chain_id_a} +add_relayer_key "b" ${chain_id_b} + echo "2 Namada chains are running" echo "You can use Hermes with ${HERMES_DIR}/config_for_namada.toml" diff --git a/scripts/setup-namada-single-node b/scripts/setup-namada-single-node index 9d3f160f2c..b981bc6582 100755 --- a/scripts/setup-namada-single-node +++ b/scripts/setup-namada-single-node @@ -62,7 +62,7 @@ event_source = { mode = 'push', url = 'ws://127.0.0.1:27657/websocket', batch_de account_prefix = '' key_name = 'relayer' store_prefix = 'ibc' -gas_price = { price = 0.001, denom = 'nam' } +gas_price = { price = 0.001, denom = '_FEE_TOKEN_' } " function make_genesis() { @@ -119,13 +119,9 @@ function init_relayer_acc() { local chain_id=$1 local base_dir=${DATA_DIR}/namada - local wallet_dir=${HERMES_DIR}/namada_wallet/${chain_id} ${NAMADAW} --base-dir ${base_dir} \ key gen --alias relayer --unsafe-dont-encrypt - - mkdir -p ${wallet_dir} - cp ${base_dir}/${chain_id}/wallet.toml ${wallet_dir} } function fund_relayer_acc() { @@ -137,6 +133,18 @@ function fund_relayer_acc() { --source ${account} --target relayer --amount 10000 --token NAM } +function add_relayer_key() { + local chain_id=$1 + + local base_dir=${DATA_DIR}/namada + + cargo run --bin hermes -- --config ${HERMES_DIR}/config_for_namada.toml \ + keys add \ + --chain ${chain_id} \ + --key-file ${base_dir}/${chain_id}/wallet.toml \ + --overwrite +} + # ==== main ==== mkdir -p ${DATA_DIR} @@ -157,12 +165,16 @@ sleep 10 fund_relayer_acc "albert" ${LEDGER_ADDR} +nam_addr=$(${NAMADAW} --base-dir ${DATA_DIR}/namada address find --alias nam | awk '{print $4}') + # for the relayer cd ${HERMES_DIR} echo "${HERMES_CONFIG_TEMPLATE}" \ - | sed -e "s/_CHAIN_ID_/${chain_id}/g" \ + | sed -e "s/_CHAIN_ID_/${chain_id}/g" -e "s/_FEE_TOKEN_/${nam_addr}/g" \ > ${HERMES_DIR}/config_for_namada.toml +add_relayer_key ${chain_id} + echo "A Namada chain is running" echo "You can use Hermes with ${HERMES_DIR}/config_for_namada.toml" diff --git a/tools/test-framework/src/chain/chain_type.rs b/tools/test-framework/src/chain/chain_type.rs index 0f43a7310c..2d39f8f811 100644 --- a/tools/test-framework/src/chain/chain_type.rs +++ b/tools/test-framework/src/chain/chain_type.rs @@ -7,13 +7,11 @@ use crate::util::random::{random_u32, random_unused_tcp_port}; const COSMOS_HD_PATH: &str = "m/44'/118'/0'/0/0"; const EVMOS_HD_PATH: &str = "m/44'/60'/0'/0/0"; -const NAMADA_HD_PATH: &str = "m/44'/60'/0'/0/0"; #[derive(Clone, Debug)] pub enum ChainType { Cosmos, Evmos, - Namada, } impl ChainType { @@ -21,7 +19,6 @@ impl ChainType { match self { Self::Cosmos => COSMOS_HD_PATH, Self::Evmos => EVMOS_HD_PATH, - Self::Namada => NAMADA_HD_PATH, } } @@ -35,7 +32,6 @@ impl ChainType { } } Self::Evmos => ChainId::from_string(&format!("evmos_9000-{prefix}")), - Self::Namada => ChainId::from_string(prefix), } } @@ -49,7 +45,6 @@ impl ChainType { res.push("--json-rpc.address".to_owned()); res.push(format!("localhost:{json_rpc_port}")); } - Self::Namada => {} } res } @@ -60,7 +55,6 @@ impl ChainType { Self::Evmos => AddressType::Ethermint { pk_type: "/ethermint.crypto.v1.ethsecp256k1.PubKey".to_string(), }, - Self::Namada => AddressType::default(), } } } @@ -75,7 +69,6 @@ impl FromStr for ChainType { name if name.contains("wasmd") => Ok(ChainType::Cosmos), name if name.contains("icad") => Ok(ChainType::Cosmos), name if name.contains("evmosd") => Ok(ChainType::Evmos), - name if name.contains("namada") => Ok(ChainType::Namada), _ => Ok(ChainType::Cosmos), } }