From 5b148cb0d3bdbb8273b6fbe6e2ea06b6fd290aaf Mon Sep 17 00:00:00 2001 From: Murisi Tarusenga Date: Mon, 9 Oct 2023 15:25:56 +0200 Subject: [PATCH] SDK can now query for the native token address from the network. Also added null IO implementation. --- apps/src/bin/namada-client/main.rs | 2 +- apps/src/bin/namada-relayer/main.rs | 3 +- apps/src/bin/namada-wallet/main.rs | 2 +- apps/src/lib/cli/api.rs | 4 +- apps/src/lib/cli/client.rs | 7 ++- apps/src/lib/cli/context.rs | 8 ++- apps/src/lib/cli/relayer.rs | 4 +- apps/src/lib/cli/wallet.rs | 2 +- .../lib/node/ledger/shell/testing/client.rs | 6 +- benches/lib.rs | 3 +- shared/src/ledger/mod.rs | 35 ++++++++--- shared/src/ledger/queries/shell.rs | 12 ++++ shared/src/sdk/rpc.rs | 7 +++ shared/src/types/io.rs | 60 ++++++++++++++++--- 14 files changed, 122 insertions(+), 33 deletions(-) diff --git a/apps/src/bin/namada-client/main.rs b/apps/src/bin/namada-client/main.rs index 167674f65e..770dcf5367 100644 --- a/apps/src/bin/namada-client/main.rs +++ b/apps/src/bin/namada-client/main.rs @@ -13,7 +13,7 @@ async fn main() -> Result<()> { let _log_guard = logging::init_from_env_or(LevelFilter::INFO)?; // run the CLI - CliApi::::handle_client_command::( + CliApi::handle_client_command::( None, cli::namada_client_cli()?, &CliIo, diff --git a/apps/src/bin/namada-relayer/main.rs b/apps/src/bin/namada-relayer/main.rs index ef5e05f913..f9d98a2a4e 100644 --- a/apps/src/bin/namada-relayer/main.rs +++ b/apps/src/bin/namada-relayer/main.rs @@ -14,6 +14,5 @@ async fn main() -> Result<()> { let cmd = cli::namada_relayer_cli()?; // run the CLI - CliApi::::handle_relayer_command::(None, cmd, &CliIo) - .await + CliApi::handle_relayer_command::(None, cmd, &CliIo).await } diff --git a/apps/src/bin/namada-wallet/main.rs b/apps/src/bin/namada-wallet/main.rs index 987e9d2699..30d4a64156 100644 --- a/apps/src/bin/namada-wallet/main.rs +++ b/apps/src/bin/namada-wallet/main.rs @@ -6,5 +6,5 @@ pub fn main() -> Result<()> { color_eyre::install()?; let (cmd, ctx) = cli::namada_wallet_cli()?; // run the CLI - CliApi::::handle_wallet_command(cmd, ctx, &CliIo) + CliApi::handle_wallet_command(cmd, ctx, &CliIo) } diff --git a/apps/src/lib/cli/api.rs b/apps/src/lib/cli/api.rs index 052a834f55..1b6851f3a9 100644 --- a/apps/src/lib/cli/api.rs +++ b/apps/src/lib/cli/api.rs @@ -1,5 +1,3 @@ -use std::marker::PhantomData; - use namada::sdk::queries::Client; use namada::sdk::rpc::wait_until_node_is_synched; use namada::tendermint_rpc::HttpClient; @@ -32,4 +30,4 @@ pub struct CliIo; #[async_trait::async_trait(?Send)] impl Io for CliIo {} -pub struct CliApi(PhantomData); +pub struct CliApi; diff --git a/apps/src/lib/cli/client.rs b/apps/src/lib/cli/client.rs index ac1ca1e34d..a342e9ef25 100644 --- a/apps/src/lib/cli/client.rs +++ b/apps/src/lib/cli/client.rs @@ -15,8 +15,8 @@ fn error() -> Report { eyre!("Fatal error") } -impl CliApi { - pub async fn handle_client_command( +impl CliApi { + pub async fn handle_client_command( client: Option, cmd: cli::NamadaClient, io: &IO, @@ -139,11 +139,12 @@ impl CliApi { .await .proceed_or_else(error)?; let args = args.to_sdk(&mut ctx); - let namada = NamadaImpl::new( + let namada = NamadaImpl::native_new( &client, &mut ctx.wallet, &mut ctx.shielded, io, + ctx.native_token, ); tx::submit_init_validator( &namada, diff --git a/apps/src/lib/cli/context.rs b/apps/src/lib/cli/context.rs index 4772ef98b9..f6c3399baf 100644 --- a/apps/src/lib/cli/context.rs +++ b/apps/src/lib/cli/context.rs @@ -161,7 +161,13 @@ impl Context { C: namada::ledger::queries::Client + Sync, IO: Io, { - NamadaImpl::new(client, &mut self.wallet, &mut self.shielded, io) + NamadaImpl::native_new( + client, + &mut self.wallet, + &mut self.shielded, + io, + self.native_token.clone(), + ) } /// Parse and/or look-up the value from the context. diff --git a/apps/src/lib/cli/relayer.rs b/apps/src/lib/cli/relayer.rs index d94fd5a09d..aadf2d3bda 100644 --- a/apps/src/lib/cli/relayer.rs +++ b/apps/src/lib/cli/relayer.rs @@ -15,11 +15,11 @@ fn error() -> Report { eyre!("Fatal error") } -impl CliApi { +impl CliApi { pub async fn handle_relayer_command( client: Option, cmd: cli::NamadaRelayer, - io: &IO, + io: &impl Io, ) -> Result<()> where C: CliClient, diff --git a/apps/src/lib/cli/wallet.rs b/apps/src/lib/cli/wallet.rs index 5dc223cd64..6247145b84 100644 --- a/apps/src/lib/cli/wallet.rs +++ b/apps/src/lib/cli/wallet.rs @@ -25,7 +25,7 @@ use crate::cli::args::CliToSdk; use crate::cli::{args, cmds, Context}; use crate::wallet::{read_and_confirm_encryption_password, CliWalletUtils}; -impl CliApi { +impl CliApi { pub fn handle_wallet_command( cmd: cmds::NamadaWallet, mut ctx: Context, diff --git a/apps/src/lib/node/ledger/shell/testing/client.rs b/apps/src/lib/node/ledger/shell/testing/client.rs index 504bdc7e5b..7649156b8e 100644 --- a/apps/src/lib/node/ledger/shell/testing/client.rs +++ b/apps/src/lib/node/ledger/shell/testing/client.rs @@ -47,7 +47,7 @@ pub fn run( NamadaClient::WithoutContext(sub_cmd, global) } }; - rt.block_on(CliApi::::handle_client_command( + rt.block_on(CliApi::handle_client_command( Some(node), cmd, &TestingIo, @@ -61,7 +61,7 @@ pub fn run( let cmd = cmds::NamadaWallet::parse(&matches) .expect("Could not parse wallet command"); - CliApi::::handle_wallet_command(cmd, ctx, &TestingIo) + CliApi::handle_wallet_command(cmd, ctx, &TestingIo) } Bin::Relayer => { args.insert(0, "relayer"); @@ -83,7 +83,7 @@ pub fn run( NamadaRelayer::ValidatorSet(sub_cmd) } }; - rt.block_on(CliApi::::handle_relayer_command( + rt.block_on(CliApi::handle_relayer_command( Some(node), cmd, &TestingIo, diff --git a/benches/lib.rs b/benches/lib.rs index b5036d5f66..f0cba69475 100644 --- a/benches/lib.rs +++ b/benches/lib.rs @@ -810,11 +810,12 @@ impl BenchShieldedCtx { &[], )) .unwrap(); - let namada = NamadaImpl::new( + let namada = NamadaImpl::native_new( &self.shell, &mut self.wallet, &mut self.shielded, &StdIo, + self.shell.wl_storage.storage.native_token.clone(), ); let shielded = async_runtime .block_on( diff --git a/shared/src/ledger/mod.rs b/shared/src/ledger/mod.rs index 5536f46b63..89478ea0ed 100644 --- a/shared/src/ledger/mod.rs +++ b/shared/src/ledger/mod.rs @@ -28,6 +28,7 @@ use crate::ibc::core::ics24_host::identifier::{ChannelId, PortId}; use crate::proto::Tx; use crate::sdk::args::{self, InputAmount, SdkTypes}; use crate::sdk::masp::{ShieldedContext, ShieldedUtils}; +use crate::sdk::rpc::query_native_token; use crate::sdk::signing::{self, SigningTxData}; use crate::sdk::tx::{ self, ProcessTxResponse, TX_BOND_WASM, TX_BRIDGE_POOL_WASM, @@ -405,13 +406,12 @@ where pub shielded: RwLock<&'a mut ShieldedContext>, /// Captures the input/output streams used by this object pub io: &'a I, + /// The address of the native token + native_token: Address, /// The default builder for a Tx prototype: args::Tx, } -/// The Namada token -pub const NAM: &str = "atest1v4ehgw36x3prswzxggunzv6pxqmnvdj9xvcyzvpsggeyvs3cg9qnywf589qnwvfsg5erg3fkl09rg5"; - impl<'a, C, U, V, I> NamadaImpl<'a, C, U, V, I> where C: crate::ledger::queries::Client + Sync, @@ -419,18 +419,20 @@ where V: ShieldedUtils, I: Io, { - /// Construct a new Namada context - pub fn new( + /// Construct a new Namada context with the given native token address + pub fn native_new( client: &'a C, wallet: &'a mut Wallet, shielded: &'a mut ShieldedContext, io: &'a I, + native_token: Address, ) -> Self { - Self { + NamadaImpl { client, wallet: RwLock::new(wallet), shielded: RwLock::new(shielded), io, + native_token: native_token.clone(), prototype: args::Tx { dry_run: false, dry_run_wrapper: false, @@ -443,7 +445,7 @@ where wallet_alias_force: false, fee_amount: None, wrapper_fee_payer: None, - fee_token: Address::from_str(NAM).unwrap(), + fee_token: native_token, fee_unshield: None, gas_limit: GasLimit::from(20_000), expiration: None, @@ -457,6 +459,23 @@ where }, } } + + /// Construct a new Namada context looking up the native token address + pub async fn new( + client: &'a C, + wallet: &'a mut Wallet, + shielded: &'a mut ShieldedContext, + io: &'a I, + ) -> crate::sdk::error::Result> { + let native_token = query_native_token(client).await?; + Ok(NamadaImpl::native_new( + client, + wallet, + shielded, + io, + native_token, + )) + } } #[async_trait::async_trait(?Send)] @@ -478,7 +497,7 @@ where } async fn native_token(&self) -> Address { - Address::from_str(NAM).unwrap() + self.native_token.clone() } fn io(&self) -> &'a Self::Io { diff --git a/shared/src/ledger/queries/shell.rs b/shared/src/ledger/queries/shell.rs index a766846916..a9f272839f 100644 --- a/shared/src/ledger/queries/shell.rs +++ b/shared/src/ledger/queries/shell.rs @@ -43,6 +43,9 @@ router! {SHELL, // Epoch of the last committed block ( "epoch" ) -> Epoch = epoch, + // The address of the native token + ( "native_token" ) -> Address = native_token, + // Epoch of the input block height ( "epoch_at_height" / [height: BlockHeight]) -> Option = epoch_at_height, @@ -288,6 +291,15 @@ where Ok(data) } +fn native_token(ctx: RequestCtx<'_, D, H>) -> storage_api::Result
+where + D: 'static + DB + for<'iter> DBIter<'iter> + Sync, + H: 'static + StorageHasher + Sync, +{ + let data = ctx.wl_storage.storage.native_token.clone(); + Ok(data) +} + fn epoch_at_height( ctx: RequestCtx<'_, D, H>, height: BlockHeight, diff --git a/shared/src/sdk/rpc.rs b/shared/src/sdk/rpc.rs index eb8ebd8a11..e7da6dcbae 100644 --- a/shared/src/sdk/rpc.rs +++ b/shared/src/sdk/rpc.rs @@ -103,6 +103,13 @@ pub async fn query_epoch( convert_response::(RPC.shell().epoch(client).await) } +/// Query the address of the native token +pub async fn query_native_token( + client: &C, +) -> Result { + convert_response::(RPC.shell().native_token(client).await) +} + /// Query the epoch of the given block height, if it exists. /// Will return none if the input block height is greater than /// the latest committed block height. diff --git a/shared/src/types/io.rs b/shared/src/types/io.rs index f100ca8433..248f6f91d9 100644 --- a/shared/src/types/io.rs +++ b/shared/src/types/io.rs @@ -2,28 +2,26 @@ //! generic IO. The defaults are the obvious Rust native //! functions. -/// Rust native I/O handling. -pub struct StdIo; - +/// A trait that abstracts out I/O operations #[async_trait::async_trait(?Send)] -impl Io for StdIo {} - -#[async_trait::async_trait(?Send)] -#[allow(missing_docs)] pub trait Io { + /// Print the given string fn print(&self, output: impl AsRef) { print!("{}", output.as_ref()); } + /// Flush the output fn flush(&self) { use std::io::Write; std::io::stdout().flush().unwrap(); } + /// Print the given string with a newline fn println(&self, output: impl AsRef) { println!("{}", output.as_ref()); } + /// Print the given string into the given Writer fn write( &self, mut writer: W, @@ -32,6 +30,7 @@ pub trait Io { write!(writer, "{}", output.as_ref()) } + /// Print the given string into the given Writer and terminate with newline fn writeln( &self, mut writer: W, @@ -40,10 +39,12 @@ pub trait Io { writeln!(writer, "{}", output.as_ref()) } + /// Print the given error string fn eprintln(&self, output: impl AsRef) { eprintln!("{}", output.as_ref()); } + /// Read a string from input async fn read(&self) -> std::io::Result { #[cfg(not(target_family = "wasm"))] { @@ -55,6 +56,7 @@ pub trait Io { } } + /// Display the given prompt and return the string input async fn prompt(&self, question: impl AsRef) -> String { #[cfg(not(target_family = "wasm"))] { @@ -76,6 +78,50 @@ pub trait Io { } } +/// Rust native I/O handling. +pub struct StdIo; + +#[async_trait::async_trait(?Send)] +impl Io for StdIo {} + +/// Ignores all I/O operations. +pub struct NullIo; + +#[async_trait::async_trait(?Send)] +impl Io for NullIo { + fn print(&self, _output: impl AsRef) {} + + fn flush(&self) {} + + fn println(&self, _output: impl AsRef) {} + + fn write( + &self, + mut _writer: W, + _output: impl AsRef, + ) -> std::io::Result<()> { + Ok(()) + } + + fn writeln( + &self, + mut _writer: W, + _output: impl AsRef, + ) -> std::io::Result<()> { + Ok(()) + } + + fn eprintln(&self, _output: impl AsRef) {} + + async fn read(&self) -> std::io::Result { + panic!("Unsupported operation") + } + + async fn prompt(&self, _question: impl AsRef) -> String { + panic!("Unsupported operation") + } +} + /// A generic function for displaying a prompt to users and reading /// in their response. #[cfg(not(target_family = "wasm"))]