From 53c7ad5e0a496f88cd95a0484c47322e6f2cbfa7 Mon Sep 17 00:00:00 2001 From: Murisi Tarusenga Date: Wed, 30 Nov 2022 08:38:53 +0200 Subject: [PATCH 01/51] Reaggregated transaction creation code. --- apps/src/lib/cli.rs | 38 ---------- apps/src/lib/client/tx.rs | 140 +++++++++++++++++------------------ apps/src/lib/client/types.rs | 79 -------------------- 3 files changed, 67 insertions(+), 190 deletions(-) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index 4d0ebcad23..5fc81f4dc4 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -1526,7 +1526,6 @@ pub mod args { use super::context::*; use super::utils::*; use super::{ArgGroup, ArgMatches}; - use crate::client::types::{ParsedTxArgs, ParsedTxTransferArgs}; use crate::config; use crate::config::TendermintMode; use crate::facade::tendermint::Timeout; @@ -1766,21 +1765,6 @@ pub mod args { pub amount: token::Amount, } - impl TxTransfer { - pub fn parse_from_context( - &self, - ctx: &mut Context, - ) -> ParsedTxTransferArgs { - ParsedTxTransferArgs { - tx: self.tx.parse_from_context(ctx), - source: ctx.get_cached(&self.source), - target: ctx.get(&self.target), - token: ctx.get(&self.token), - amount: self.amount, - } - } - } - impl Args for TxTransfer { fn parse(matches: &ArgMatches) -> Self { let tx = Tx::parse(matches); @@ -2731,28 +2715,6 @@ pub mod args { pub signer: Option, } - impl Tx { - pub fn parse_from_context(&self, ctx: &mut Context) -> ParsedTxArgs { - ParsedTxArgs { - dry_run: self.dry_run, - force: self.force, - broadcast_only: self.broadcast_only, - ledger_address: self.ledger_address.clone(), - initialized_account_alias: self - .initialized_account_alias - .clone(), - fee_amount: self.fee_amount, - fee_token: ctx.get(&self.fee_token), - gas_limit: self.gas_limit.clone(), - signing_key: self - .signing_key - .as_ref() - .map(|sk| ctx.get_cached(sk)), - signer: self.signer.as_ref().map(|signer| ctx.get(signer)), - } - } - } - impl Args for Tx { fn def(app: App) -> App { app.arg( diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index bb667cabec..332ae75d8e 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -65,13 +65,11 @@ use sha2::Digest; use tokio::time::{Duration, Instant}; use super::rpc; -use super::types::ShieldedTransferContext; use crate::cli::context::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}; use crate::client::tendermint_rpc_types::{TxBroadcastData, TxResponse}; -use crate::client::types::ParsedTxTransferArgs; use crate::facade::tendermint_config::net::Address as TendermintAddress; use crate::facade::tendermint_rpc::endpoint::broadcast::tx_sync::Response; use crate::facade::tendermint_rpc::error::Error as RpcError; @@ -1293,18 +1291,32 @@ fn convert_amount( /// transactions balanced, but it is understood that transparent account changes /// are effected only by the amounts and signatures specified by the containing /// Transfer object. -async fn gen_shielded_transfer( - ctx: &mut C, - args: &ParsedTxTransferArgs, +async fn gen_shielded_transfer( + ctx: &mut Context, + args: &args::TxTransfer, shielded_gas: bool, -) -> Result, builder::Error> -where - C: ShieldedTransferContext, -{ - let spending_key = args.source.spending_key().map(|x| x.into()); - let payment_address = args.target.payment_address(); +) -> Result, builder::Error> { + // No shielded components are needed when neither source nor destination + // are shielded + let spending_key = ctx.get_cached(&args.source).spending_key(); + let payment_address = ctx.get(&args.target).payment_address(); + if spending_key.is_none() && payment_address.is_none() { + return Ok(None); + } + // We want to fund our transaction solely from supplied spending key + let spending_key = spending_key.map(|x| x.into()); + 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 + .fetch(&args.tx.ledger_address, &spending_keys, &[]) + .await; + // Save the update state so that future fetches can be short-circuited + let _ = ctx.shielded.save(); // Determine epoch in which to submit potential shielded transaction - let epoch = ctx.query_epoch(args.tx.ledger_address.clone()).await; + let epoch = rpc::query_epoch(args::Query { + ledger_address: args.tx.ledger_address.clone() + }).await; // Context required for storing which notes are in the source's possesion let consensus_branch_id = BranchId::Sapling; let amt: u64 = args.amount.into(); @@ -1313,7 +1325,7 @@ where // Now we build up the transaction within this object let mut builder = Builder::::new(0u32); // Convert transaction amount into MASP types - let (asset_type, amount) = convert_amount(epoch, &args.token, args.amount); + let (asset_type, amount) = convert_amount(epoch, &ctx.get(&args.token), args.amount); // Transactions with transparent input and shielded output // may be affected if constructed close to epoch boundary @@ -1323,13 +1335,14 @@ where // Transaction fees need to match the amount in the wrapper Transfer // when MASP source is used let (_, fee) = - convert_amount(epoch, &args.tx.fee_token, args.tx.fee_amount); + convert_amount(epoch, &ctx.get(&args.tx.fee_token), args.tx.fee_amount); builder.set_fee(fee.clone())?; // If the gas is coming from the shielded pool, then our shielded inputs // must also cover the gas fee let required_amt = if shielded_gas { amount + fee } else { amount }; // Locate unspent notes that can help us meet the transaction amount let (_, unspent_notes, used_convs) = ctx + .shielded .collect_unspent_notes( args.tx.ledger_address.clone(), &to_viewing_key(&sk).vk, @@ -1392,8 +1405,8 @@ where epoch_sensitive = false; // Embed the transparent target address into the shielded transaction so // that it can be signed - let target_enc = args - .target + let target = ctx.get(&args.target); + let target_enc = target .address() .expect("target address should be transparent") .try_to_vec() @@ -1422,7 +1435,9 @@ where let mut tx = builder.build(consensus_branch_id, &prover); if epoch_sensitive { - let new_epoch = ctx.query_epoch(args.tx.ledger_address.clone()).await; + let new_epoch = rpc::query_epoch(args::Query { + ledger_address: args.tx.ledger_address.clone() + }).await; // If epoch has changed, recalculate shielded outputs to match new epoch if new_epoch != epoch { @@ -1431,7 +1446,7 @@ where replay_builder.set_fee(Amount::zero())?; let ovk_opt = spending_key.map(|x| x.expsk.ovk); let (new_asset_type, _) = - convert_amount(new_epoch, &args.token, args.amount); + convert_amount(new_epoch, &ctx.get(&args.token), args.amount); replay_builder.add_sapling_output( ovk_opt, payment_address.unwrap().into(), @@ -1476,9 +1491,8 @@ where } pub async fn submit_transfer(mut ctx: Context, args: args::TxTransfer) { - let parsed_args = args.parse_from_context(&mut ctx); - let source = parsed_args.source.effective_address(); - let target = parsed_args.target.effective_address(); + let source = ctx.get_cached(&args.source).effective_address(); + let target = ctx.get(&args.target).effective_address(); // Check that the source address exists on chain let source_exists = rpc::known_address(&source, args.tx.ledger_address.clone()).await; @@ -1497,25 +1511,26 @@ pub async fn submit_transfer(mut ctx: Context, args: args::TxTransfer) { safe_exit(1) } } + let token = ctx.get(&args.token); // Check that the token address exists on chain let token_exists = - rpc::known_address(&parsed_args.token, args.tx.ledger_address.clone()) + rpc::known_address(&token, args.tx.ledger_address.clone()) .await; if !token_exists { eprintln!( "The token address {} doesn't exist on chain.", - parsed_args.token + token ); if !args.tx.force { safe_exit(1) } } // Check source balance - let (sub_prefix, balance_key) = match args.sub_prefix { + let (sub_prefix, balance_key) = match &args.sub_prefix { Some(sub_prefix) => { let sub_prefix = storage::Key::parse(sub_prefix).unwrap(); let prefix = token::multitoken_balance_prefix( - &parsed_args.token, + &token, &sub_prefix, ); ( @@ -1523,7 +1538,7 @@ pub async fn submit_transfer(mut ctx: Context, args: args::TxTransfer) { token::multitoken_balance_key(&prefix, &source), ) } - None => (None, token::balance_key(&parsed_args.token, &source)), + None => (None, token::balance_key(&token, &source)), }; let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); match rpc::query_storage_value::(&client, &balance_key).await @@ -1534,7 +1549,7 @@ pub async fn submit_transfer(mut ctx: Context, args: args::TxTransfer) { "The balance of the source {} of token {} is lower than \ the amount to be transferred. Amount to transfer is {} \ and the balance is {}.", - source, parsed_args.token, args.amount, balance + source, token, args.amount, balance ); if !args.tx.force { safe_exit(1) @@ -1544,7 +1559,7 @@ pub async fn submit_transfer(mut ctx: Context, args: args::TxTransfer) { None => { eprintln!( "No balance found for the source {} of token {}", - source, parsed_args.token + source, token ); if !args.tx.force { safe_exit(1) @@ -1570,13 +1585,13 @@ pub async fn submit_transfer(mut ctx: Context, args: args::TxTransfer) { ( TxSigningKey::SecretKey(masp_tx_key()), args.amount, - parsed_args.token.clone(), + token.clone(), ) } else { ( TxSigningKey::WalletAddress(args.source.to_address()), args.amount, - parsed_args.token.clone(), + token.clone(), ) }; // If our chosen signer is the MASP sentinel key, then our shielded inputs @@ -1591,6 +1606,27 @@ pub async fn submit_transfer(mut ctx: Context, args: args::TxTransfer) { _ => None, }; + let stx_result = + gen_shielded_transfer(&mut ctx, &args, shielded_gas) + .await; + let shielded = match stx_result { + Ok(stx) => stx.map(|x| x.0), + Err(builder::Error::ChangeIsNegative(_)) => { + eprintln!( + "The balance of the source {} is lower than the \ + amount to be transferred and fees. Amount to \ + transfer is {} {} and fees are {} {}.", + source, + args.amount, + token, + args.tx.fee_amount, + ctx.get(&args.tx.fee_token), + ); + safe_exit(1) + } + Err(err) => panic!("{}", err), + }; + let transfer = token::Transfer { source, target, @@ -1598,49 +1634,7 @@ pub async fn submit_transfer(mut ctx: Context, args: args::TxTransfer) { sub_prefix, amount, key, - shielded: { - let spending_key = parsed_args.source.spending_key(); - let payment_address = parsed_args.target.payment_address(); - // No shielded components are needed when neither source nor - // destination are shielded - if spending_key.is_none() && payment_address.is_none() { - None - } else { - // We want to fund our transaction solely from supplied spending - // key - let spending_key = spending_key.map(|x| x.into()); - 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 - .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; - match stx_result { - Ok(stx) => stx.map(|x| x.0), - Err(builder::Error::ChangeIsNegative(_)) => { - eprintln!( - "The balance of the source {} is lower than the \ - amount to be transferred and fees. Amount to \ - transfer is {} {} and fees are {} {}.", - parsed_args.source, - args.amount, - parsed_args.token, - args.tx.fee_amount, - parsed_args.tx.fee_token, - ); - safe_exit(1) - } - Err(err) => panic!("{}", err), - } - } - }, + shielded, }; tracing::debug!("Transfer data {:?}", transfer); let data = transfer diff --git a/apps/src/lib/client/types.rs b/apps/src/lib/client/types.rs index 5a26244474..7246e436b8 100644 --- a/apps/src/lib/client/types.rs +++ b/apps/src/lib/client/types.rs @@ -13,82 +13,3 @@ use super::rpc; use crate::cli::{args, Context}; use crate::client::tx::Conversions; use crate::facade::tendermint_config::net::Address as TendermintAddress; - -#[derive(Clone, Debug)] -pub struct ParsedTxArgs { - /// Simulate applying the transaction - pub dry_run: bool, - /// Submit the transaction even if it doesn't pass client checks - pub force: bool, - /// Do not wait for the transaction to be added to the blockchain - pub broadcast_only: bool, - /// The address of the ledger node as host:port - pub ledger_address: TendermintAddress, - /// If any new account is initialized by the tx, use the given alias to - /// save it in the wallet. - pub initialized_account_alias: Option, - /// The amount being payed to include the transaction - pub fee_amount: token::Amount, - /// The token in which the fee is being paid - pub fee_token: Address, - /// The max amount of gas used to process tx - pub gas_limit: GasLimit, - /// Sign the tx with the key for the given alias from your wallet - pub signing_key: Option, - /// Sign the tx with the keypair of the public key of the given address - pub signer: Option
, -} - -#[derive(Clone, Debug)] -pub struct ParsedTxTransferArgs { - /// Common tx arguments - pub tx: ParsedTxArgs, - /// Transfer source address - pub source: TransferSource, - /// Transfer target address - pub target: TransferTarget, - /// Transferred token address - pub token: Address, - /// Transferred token amount - pub amount: token::Amount, -} - -#[async_trait] -pub trait ShieldedTransferContext { - async fn collect_unspent_notes( - &mut self, - ledger_address: TendermintAddress, - vk: &ViewingKey, - target: Amount, - target_epoch: Epoch, - ) -> ( - Amount, - Vec<(Diversifier, Note, MerklePath)>, - Conversions, - ); - - async fn query_epoch(&self, ledger_address: TendermintAddress) -> Epoch; -} - -#[async_trait] -impl ShieldedTransferContext for Context { - async fn collect_unspent_notes( - &mut self, - ledger_address: TendermintAddress, - vk: &ViewingKey, - target: Amount, - target_epoch: Epoch, - ) -> ( - Amount, - Vec<(Diversifier, Note, MerklePath)>, - Conversions, - ) { - self.shielded - .collect_unspent_notes(ledger_address, vk, target, target_epoch) - .await - } - - async fn query_epoch(&self, ledger_address: TendermintAddress) -> Epoch { - rpc::query_epoch(args::Query { ledger_address }).await - } -} From 312f2c48179577ae0772055c9aaa1be1c949c000 Mon Sep 17 00:00:00 2001 From: Murisi Tarusenga Date: Wed, 30 Nov 2022 09:20:35 +0200 Subject: [PATCH 02/51] Moved gen_shielded_transfer into ShieldedContext. --- apps/src/lib/client/tx.rs | 434 ++++++++++++++++++++------------------ 1 file changed, 224 insertions(+), 210 deletions(-) diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index 332ae75d8e..b27fa1f52e 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -45,7 +45,7 @@ use namada::types::governance::{ OfflineProposal, OfflineVote, Proposal, ProposalVote, }; use namada::types::key::*; -use namada::types::masp::{PaymentAddress, TransferTarget}; +use namada::types::masp::{PaymentAddress, TransferSource, TransferTarget}; use namada::types::storage::{ BlockHeight, Epoch, Key, KeySeg, TxIndex, RESERVED_ADDRESS_PREFIX, }; @@ -1259,6 +1259,215 @@ impl ShieldedContext { } res } + + /// Make shielded components to embed within a Transfer object. If no shielded + /// payment address nor spending key is specified, then no shielded components + /// are produced. Otherwise a transaction containing nullifiers and/or note + /// commitments are produced. Dummy transparent UTXOs are sometimes used to make + /// transactions balanced, but it is understood that transparent account changes + /// are effected only by the amounts and signatures specified by the containing + /// Transfer object. + async fn gen_shielded_transfer( + &mut self, + ledger_address: &TendermintAddress, + source: TransferSource, + target: TransferTarget, + args_amount: token::Amount, + token: Address, + fee_amount: token::Amount, + fee_token: Address, + shielded_gas: bool, + ) -> Result, builder::Error> { + // No shielded components are needed when neither source nor destination + // are shielded + let spending_key = source.spending_key(); + let payment_address = target.payment_address(); + if spending_key.is_none() && payment_address.is_none() { + return Ok(None); + } + // We want to fund our transaction solely from supplied spending key + let spending_key = spending_key.map(|x| x.into()); + let spending_keys: Vec<_> = spending_key.into_iter().collect(); + // Load the current shielded context given the spending key we possess + let _ = self.load(); + self.fetch(&ledger_address, &spending_keys, &[]) + .await; + // Save the update state so that future fetches can be short-circuited + let _ = self.save(); + // Determine epoch in which to submit potential shielded transaction + let epoch = rpc::query_epoch(args::Query { + ledger_address: ledger_address.clone() + }).await; + // Context required for storing which notes are in the source's possesion + let consensus_branch_id = BranchId::Sapling; + let amt: u64 = args_amount.into(); + let memo: Option = None; + + // Now we build up the transaction within this object + let mut builder = Builder::::new(0u32); + // Convert transaction amount into MASP types + let (asset_type, amount) = convert_amount(epoch, &token, args_amount); + + // Transactions with transparent input and shielded output + // may be affected if constructed close to epoch boundary + let mut epoch_sensitive: bool = false; + // If there are shielded inputs + if let Some(sk) = spending_key { + // Transaction fees need to match the amount in the wrapper Transfer + // when MASP source is used + let (_, fee) = + convert_amount(epoch, &fee_token, fee_amount); + builder.set_fee(fee.clone())?; + // If the gas is coming from the shielded pool, then our shielded inputs + // must also cover the gas fee + let required_amt = if shielded_gas { amount + fee } else { amount }; + // Locate unspent notes that can help us meet the transaction amount + let (_, unspent_notes, used_convs) = self + .collect_unspent_notes( + ledger_address.clone(), + &to_viewing_key(&sk).vk, + required_amt, + epoch, + ) + .await; + // Commit the notes found to our transaction + for (diversifier, note, merkle_path) in unspent_notes { + builder.add_sapling_spend(sk, diversifier, note, merkle_path)?; + } + // Commit the conversion notes used during summation + for (conv, wit, value) in used_convs.values() { + if *value > 0 { + builder.add_convert( + conv.clone(), + *value as u64, + wit.clone(), + )?; + } + } + } else { + // No transfer fees come from the shielded transaction for non-MASP + // sources + builder.set_fee(Amount::zero())?; + // We add a dummy UTXO to our transaction, but only the source of the + // parent Transfer object is used to validate fund availability + let secp_sk = + secp256k1::SecretKey::from_slice(&[0xcd; 32]).expect("secret key"); + let secp_ctx = secp256k1::Secp256k1::::gen_new(); + let secp_pk = + secp256k1::PublicKey::from_secret_key(&secp_ctx, &secp_sk) + .serialize(); + let hash = + ripemd160::Ripemd160::digest(&sha2::Sha256::digest(&secp_pk)); + let script = TransparentAddress::PublicKey(hash.into()).script(); + epoch_sensitive = true; + builder.add_transparent_input( + secp_sk, + OutPoint::new([0u8; 32], 0), + TxOut { + asset_type, + value: amt, + script_pubkey: script, + }, + )?; + } + // Now handle the outputs of this transaction + // If there is a shielded output + if let Some(pa) = payment_address { + let ovk_opt = spending_key.map(|x| x.expsk.ovk); + builder.add_sapling_output( + ovk_opt, + pa.into(), + asset_type, + amt, + memo.clone(), + )?; + } else { + epoch_sensitive = false; + // Embed the transparent target address into the shielded transaction so + // that it can be signed + let target_enc = target + .address() + .expect("target address should be transparent") + .try_to_vec() + .expect("target address encoding"); + let hash = ripemd160::Ripemd160::digest(&sha2::Sha256::digest( + target_enc.as_ref(), + )); + builder.add_transparent_output( + &TransparentAddress::PublicKey(hash.into()), + asset_type, + amt, + )?; + } + let prover = if let Ok(params_dir) = env::var(masp::ENV_VAR_MASP_PARAMS_DIR) + { + let params_dir = PathBuf::from(params_dir); + let spend_path = params_dir.join(masp::SPEND_NAME); + let convert_path = params_dir.join(masp::CONVERT_NAME); + let output_path = params_dir.join(masp::OUTPUT_NAME); + LocalTxProver::new(&spend_path, &output_path, &convert_path) + } else { + LocalTxProver::with_default_location() + .expect("unable to load MASP Parameters") + }; + // Build and return the constructed transaction + let mut tx = builder.build(consensus_branch_id, &prover); + + if epoch_sensitive { + let new_epoch = rpc::query_epoch(args::Query { + ledger_address: ledger_address.clone() + }).await; + + // If epoch has changed, recalculate shielded outputs to match new epoch + if new_epoch != epoch { + // Hack: build new shielded transfer with updated outputs + let mut replay_builder = Builder::::new(0u32); + replay_builder.set_fee(Amount::zero())?; + let ovk_opt = spending_key.map(|x| x.expsk.ovk); + let (new_asset_type, _) = + convert_amount(new_epoch, &token, args_amount); + replay_builder.add_sapling_output( + ovk_opt, + payment_address.unwrap().into(), + new_asset_type, + amt, + memo, + )?; + + let secp_sk = secp256k1::SecretKey::from_slice(&[0xcd; 32]) + .expect("secret key"); + let secp_ctx = + secp256k1::Secp256k1::::gen_new(); + let secp_pk = + secp256k1::PublicKey::from_secret_key(&secp_ctx, &secp_sk) + .serialize(); + let hash = + ripemd160::Ripemd160::digest(&sha2::Sha256::digest(&secp_pk)); + let script = TransparentAddress::PublicKey(hash.into()).script(); + replay_builder.add_transparent_input( + secp_sk, + OutPoint::new([0u8; 32], 0), + TxOut { + asset_type: new_asset_type, + value: amt, + script_pubkey: script, + }, + )?; + + let (replay_tx, _) = + replay_builder.build(consensus_branch_id, &prover)?; + tx = tx.map(|(t, tm)| { + let mut temp = t.deref().clone(); + temp.shielded_outputs = replay_tx.shielded_outputs.clone(); + temp.value_balance = temp.value_balance.reject(asset_type) + - Amount::from_pair(new_asset_type, amt).unwrap(); + (temp.freeze().unwrap(), tm) + }); + } + } + + tx.map(Some) + } } /// Make asset type corresponding to given address and epoch @@ -1284,215 +1493,11 @@ fn convert_amount( (asset_type, amount) } -/// Make shielded components to embed within a Transfer object. If no shielded -/// payment address nor spending key is specified, then no shielded components -/// are produced. Otherwise a transaction containing nullifiers and/or note -/// commitments are produced. Dummy transparent UTXOs are sometimes used to make -/// transactions balanced, but it is understood that transparent account changes -/// are effected only by the amounts and signatures specified by the containing -/// Transfer object. -async fn gen_shielded_transfer( - ctx: &mut Context, - args: &args::TxTransfer, - shielded_gas: bool, -) -> Result, builder::Error> { - // No shielded components are needed when neither source nor destination - // are shielded - let spending_key = ctx.get_cached(&args.source).spending_key(); - let payment_address = ctx.get(&args.target).payment_address(); - if spending_key.is_none() && payment_address.is_none() { - return Ok(None); - } - // We want to fund our transaction solely from supplied spending key - let spending_key = spending_key.map(|x| x.into()); - 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 - .fetch(&args.tx.ledger_address, &spending_keys, &[]) - .await; - // Save the update state so that future fetches can be short-circuited - let _ = ctx.shielded.save(); - // Determine epoch in which to submit potential shielded transaction - let epoch = rpc::query_epoch(args::Query { - ledger_address: args.tx.ledger_address.clone() - }).await; - // Context required for storing which notes are in the source's possesion - let consensus_branch_id = BranchId::Sapling; - let amt: u64 = args.amount.into(); - let memo: Option = None; - - // Now we build up the transaction within this object - let mut builder = Builder::::new(0u32); - // Convert transaction amount into MASP types - let (asset_type, amount) = convert_amount(epoch, &ctx.get(&args.token), args.amount); - - // Transactions with transparent input and shielded output - // may be affected if constructed close to epoch boundary - let mut epoch_sensitive: bool = false; - // If there are shielded inputs - if let Some(sk) = spending_key { - // Transaction fees need to match the amount in the wrapper Transfer - // when MASP source is used - let (_, fee) = - convert_amount(epoch, &ctx.get(&args.tx.fee_token), args.tx.fee_amount); - builder.set_fee(fee.clone())?; - // If the gas is coming from the shielded pool, then our shielded inputs - // must also cover the gas fee - let required_amt = if shielded_gas { amount + fee } else { amount }; - // Locate unspent notes that can help us meet the transaction amount - let (_, unspent_notes, used_convs) = ctx - .shielded - .collect_unspent_notes( - args.tx.ledger_address.clone(), - &to_viewing_key(&sk).vk, - required_amt, - epoch, - ) - .await; - // Commit the notes found to our transaction - for (diversifier, note, merkle_path) in unspent_notes { - builder.add_sapling_spend(sk, diversifier, note, merkle_path)?; - } - // Commit the conversion notes used during summation - for (conv, wit, value) in used_convs.values() { - if *value > 0 { - builder.add_convert( - conv.clone(), - *value as u64, - wit.clone(), - )?; - } - } - } else { - // No transfer fees come from the shielded transaction for non-MASP - // sources - builder.set_fee(Amount::zero())?; - // We add a dummy UTXO to our transaction, but only the source of the - // parent Transfer object is used to validate fund availability - let secp_sk = - secp256k1::SecretKey::from_slice(&[0xcd; 32]).expect("secret key"); - let secp_ctx = secp256k1::Secp256k1::::gen_new(); - let secp_pk = - secp256k1::PublicKey::from_secret_key(&secp_ctx, &secp_sk) - .serialize(); - let hash = - ripemd160::Ripemd160::digest(&sha2::Sha256::digest(&secp_pk)); - let script = TransparentAddress::PublicKey(hash.into()).script(); - epoch_sensitive = true; - builder.add_transparent_input( - secp_sk, - OutPoint::new([0u8; 32], 0), - TxOut { - asset_type, - value: amt, - script_pubkey: script, - }, - )?; - } - // Now handle the outputs of this transaction - // If there is a shielded output - if let Some(pa) = payment_address { - let ovk_opt = spending_key.map(|x| x.expsk.ovk); - builder.add_sapling_output( - ovk_opt, - pa.into(), - asset_type, - amt, - memo.clone(), - )?; - } else { - epoch_sensitive = false; - // Embed the transparent target address into the shielded transaction so - // that it can be signed - let target = ctx.get(&args.target); - let target_enc = target - .address() - .expect("target address should be transparent") - .try_to_vec() - .expect("target address encoding"); - let hash = ripemd160::Ripemd160::digest(&sha2::Sha256::digest( - target_enc.as_ref(), - )); - builder.add_transparent_output( - &TransparentAddress::PublicKey(hash.into()), - asset_type, - amt, - )?; - } - let prover = if let Ok(params_dir) = env::var(masp::ENV_VAR_MASP_PARAMS_DIR) - { - let params_dir = PathBuf::from(params_dir); - let spend_path = params_dir.join(masp::SPEND_NAME); - let convert_path = params_dir.join(masp::CONVERT_NAME); - let output_path = params_dir.join(masp::OUTPUT_NAME); - LocalTxProver::new(&spend_path, &output_path, &convert_path) - } else { - LocalTxProver::with_default_location() - .expect("unable to load MASP Parameters") - }; - // Build and return the constructed transaction - let mut tx = builder.build(consensus_branch_id, &prover); - - if epoch_sensitive { - let new_epoch = rpc::query_epoch(args::Query { - ledger_address: args.tx.ledger_address.clone() - }).await; - - // If epoch has changed, recalculate shielded outputs to match new epoch - if new_epoch != epoch { - // Hack: build new shielded transfer with updated outputs - let mut replay_builder = Builder::::new(0u32); - replay_builder.set_fee(Amount::zero())?; - let ovk_opt = spending_key.map(|x| x.expsk.ovk); - let (new_asset_type, _) = - convert_amount(new_epoch, &ctx.get(&args.token), args.amount); - replay_builder.add_sapling_output( - ovk_opt, - payment_address.unwrap().into(), - new_asset_type, - amt, - memo, - )?; - - let secp_sk = secp256k1::SecretKey::from_slice(&[0xcd; 32]) - .expect("secret key"); - let secp_ctx = - secp256k1::Secp256k1::::gen_new(); - let secp_pk = - secp256k1::PublicKey::from_secret_key(&secp_ctx, &secp_sk) - .serialize(); - let hash = - ripemd160::Ripemd160::digest(&sha2::Sha256::digest(&secp_pk)); - let script = TransparentAddress::PublicKey(hash.into()).script(); - replay_builder.add_transparent_input( - secp_sk, - OutPoint::new([0u8; 32], 0), - TxOut { - asset_type: new_asset_type, - value: amt, - script_pubkey: script, - }, - )?; - - let (replay_tx, _) = - replay_builder.build(consensus_branch_id, &prover)?; - tx = tx.map(|(t, tm)| { - let mut temp = t.deref().clone(); - temp.shielded_outputs = replay_tx.shielded_outputs.clone(); - temp.value_balance = temp.value_balance.reject(asset_type) - - Amount::from_pair(new_asset_type, amt).unwrap(); - (temp.freeze().unwrap(), tm) - }); - } - } - - tx.map(Some) -} - pub async fn submit_transfer(mut ctx: Context, args: args::TxTransfer) { - let source = ctx.get_cached(&args.source).effective_address(); - let target = ctx.get(&args.target).effective_address(); + let transfer_source = ctx.get_cached(&args.source); + let source = transfer_source.effective_address(); + let transfer_target = ctx.get(&args.target); + let target = transfer_target.effective_address(); // Check that the source address exists on chain let source_exists = rpc::known_address(&source, args.tx.ledger_address.clone()).await; @@ -1607,7 +1612,16 @@ pub async fn submit_transfer(mut ctx: Context, args: args::TxTransfer) { }; let stx_result = - gen_shielded_transfer(&mut ctx, &args, shielded_gas) + ctx.shielded.gen_shielded_transfer( + &args.tx.ledger_address, + transfer_source, + transfer_target, + args.amount, + ctx.get(&args.token), + args.tx.fee_amount, + ctx.get(&args.tx.fee_token), + shielded_gas, + ) .await; let shielded = match stx_result { Ok(stx) => stx.map(|x| x.0), From 2fefb6b15feaf63f9a2416c90e9cbdb744e99bf7 Mon Sep 17 00:00:00 2001 From: Murisi Tarusenga Date: Wed, 30 Nov 2022 13:32:44 +0200 Subject: [PATCH 03/51] Started factoring platform dependent code out of ShieldedContext. --- apps/src/lib/cli/context.rs | 6 +- apps/src/lib/client/rpc.rs | 32 ++-- apps/src/lib/client/tx.rs | 326 ++++++++++++++++++++++-------------- 3 files changed, 212 insertions(+), 152 deletions(-) diff --git a/apps/src/lib/cli/context.rs b/apps/src/lib/cli/context.rs index 04bd91e6ce..4461cd4319 100644 --- a/apps/src/lib/cli/context.rs +++ b/apps/src/lib/cli/context.rs @@ -12,7 +12,7 @@ use namada::types::key::*; use namada::types::masp::*; use super::args; -use crate::client::tx::ShieldedContext; +use crate::client::tx::{ShieldedContext, CLIShieldedUtils}; use crate::config::genesis::genesis_config; use crate::config::global::GlobalConfig; use crate::config::{self, Config}; @@ -72,7 +72,7 @@ pub struct Context { /// The ledger configuration for a specific chain ID pub config: Config, /// The context fr shielded operations - pub shielded: ShieldedContext, + pub shielded: ShieldedContext, /// Native token's address pub native_token: Address, } @@ -118,7 +118,7 @@ impl Context { wallet, global_config, config, - shielded: ShieldedContext::new(chain_dir), + shielded: CLIShieldedUtils::new(chain_dir), native_token, }) } diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index 3fb4d8eea8..667370413b 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -162,13 +162,14 @@ pub async fn query_tx_deltas( // Connect to the Tendermint server holding the transactions let client = HttpClient::new(ledger_address.clone()).unwrap(); // Build up the context that will be queried for transactions + ctx.shielded.utils.ledger_address = Some(ledger_address.clone()); let _ = ctx.shielded.load(); let vks = ctx.wallet.get_viewing_keys(); let fvks: Vec<_> = vks .values() .map(|fvk| ExtendedFullViewingKey::from(*fvk).fvk.vk) .collect(); - ctx.shielded.fetch(&ledger_address, &[], &fvks).await; + ctx.shielded.fetch(&[], &fvks).await; // Save the update state so that future fetches can be short-circuited let _ = ctx.shielded.save(); // Required for filtering out rejected transactions from Tendermint @@ -282,8 +283,6 @@ pub async fn query_transfers(mut ctx: Context, args: args::QueryTransfers) { .values() .map(|fvk| (ExtendedFullViewingKey::from(*fvk).fvk.vk, fvk)) .collect(); - // Connect to the Tendermint server holding the transactions - let client = HttpClient::new(args.query.ledger_address.clone()).unwrap(); // Now display historical shielded and transparent transactions for ((height, idx), (epoch, tfer_delta, tx_delta)) in transfers { // Check if this transfer pertains to the supplied owner @@ -304,7 +303,6 @@ pub async fn query_transfers(mut ctx: Context, args: args::QueryTransfers) { let amt = ctx .shielded .compute_exchanged_amount( - client.clone(), amt, epoch, Conversions::new(), @@ -312,7 +310,7 @@ pub async fn query_transfers(mut ctx: Context, args: args::QueryTransfers) { .await .0; let dec = - ctx.shielded.decode_amount(client.clone(), amt, epoch).await; + ctx.shielded.decode_amount(amt, epoch).await; shielded_accounts.insert(acc, dec); } // Check if this transfer pertains to the supplied token @@ -554,9 +552,8 @@ pub async fn query_pinned_balance(ctx: &mut Context, args: args::QueryBalance) { .map(|fvk| ExtendedFullViewingKey::from(*fvk).fvk.vk) .collect(); // Build up the context that will be queried for asset decodings + ctx.shielded.utils.ledger_address = Some(args.query.ledger_address.clone()); let _ = ctx.shielded.load(); - // Establish connection with which to do exchange rate queries - let client = HttpClient::new(args.query.ledger_address.clone()).unwrap(); // Print the token balances by payment address for owner in owners { let mut balance = Err(PinnedBalanceError::InvalidViewingKey); @@ -566,7 +563,6 @@ pub async fn query_pinned_balance(ctx: &mut Context, args: args::QueryBalance) { balance = ctx .shielded .compute_exchanged_pinned_balance( - &args.query.ledger_address, owner, vk, ) @@ -593,7 +589,6 @@ pub async fn query_pinned_balance(ctx: &mut Context, args: args::QueryBalance) { balance = ctx .shielded .compute_exchanged_pinned_balance( - &args.query.ledger_address, owner, &vk, ) @@ -637,7 +632,7 @@ pub async fn query_pinned_balance(ctx: &mut Context, args: args::QueryBalance) { // Print balances by human-readable token names let balance = ctx .shielded - .decode_amount(client.clone(), balance, epoch) + .decode_amount(balance, epoch) .await; for (addr, value) in balance.components() { let asset_value = token::Amount::from(*value as u64); @@ -878,20 +873,17 @@ pub async fn query_shielded_balance( None => ctx.wallet.get_viewing_keys().values().copied().collect(), }; // Build up the context that will be queried for balances + ctx.shielded.utils.ledger_address = Some(args.query.ledger_address.clone()); let _ = ctx.shielded.load(); let fvks: Vec<_> = viewing_keys .iter() .map(|fvk| ExtendedFullViewingKey::from(*fvk).fvk.vk) .collect(); - ctx.shielded - .fetch(&args.query.ledger_address, &[], &fvks) - .await; + ctx.shielded.fetch(&[], &fvks).await; // Save the update state so that future fetches can be short-circuited let _ = ctx.shielded.save(); // The epoch is required to identify timestamped tokens let epoch = query_epoch(args.query.clone()).await; - // Establish connection with which to do exchange rate queries - let client = HttpClient::new(args.query.ledger_address.clone()).unwrap(); // Map addresses to token names let tokens = address::tokens(); match (args.token, owner.is_some()) { @@ -907,7 +899,6 @@ pub async fn query_shielded_balance( } else { ctx.shielded .compute_exchanged_balance( - client.clone(), &viewing_key, epoch, ) @@ -952,7 +943,6 @@ pub async fn query_shielded_balance( } else { ctx.shielded .compute_exchanged_balance( - client.clone(), &viewing_key, epoch, ) @@ -974,7 +964,7 @@ pub async fn query_shielded_balance( // Decode the asset type let decoded = ctx .shielded - .decode_asset_type(client.clone(), asset_type) + .decode_asset_type(asset_type) .await; match decoded { Some((addr, asset_epoch)) if asset_epoch == epoch => { @@ -1044,7 +1034,6 @@ pub async fn query_shielded_balance( } else { ctx.shielded .compute_exchanged_balance( - client.clone(), &viewing_key, epoch, ) @@ -1079,14 +1068,13 @@ pub async fn query_shielded_balance( // Print balances by human-readable token names let decoded_balance = ctx .shielded - .decode_all_amounts(client.clone(), balance) + .decode_all_amounts(balance) .await; print_decoded_balance_with_epoch(decoded_balance); } else { balance = ctx .shielded .compute_exchanged_balance( - client.clone(), &viewing_key, epoch, ) @@ -1095,7 +1083,7 @@ pub async fn query_shielded_balance( // Print balances by human-readable token names let decoded_balance = ctx .shielded - .decode_amount(client.clone(), balance, epoch) + .decode_amount(balance, epoch) .await; print_decoded_balance(decoded_balance); } diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index b27fa1f52e..0f5b469d23 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -63,6 +63,7 @@ use rand_core::{CryptoRng, OsRng, RngCore}; use rust_decimal::Decimal; use sha2::Digest; use tokio::time::{Duration, Instant}; +use async_trait::async_trait; use super::rpc; use crate::cli::context::WalletAddress; @@ -387,6 +388,170 @@ pub async fn submit_init_validator( } } +#[async_trait] +pub trait ShieldedUtils : Sized + BorshDeserialize + BorshSerialize + Default + Clone { + async fn query_storage_value( + &self, + key: &storage::Key, + ) -> Option + where + T: BorshDeserialize; + + async fn query_epoch(&self) -> Epoch; + + //fn download_parameters() -> Result<(), Error>; + + fn local_tx_prover(&self) -> LocalTxProver; + + fn load(self) -> std::io::Result>; + + fn save(&self, ctx: &ShieldedContext) -> std::io::Result<()>; + + async fn query_conversion( + &self, + asset_type: AssetType, + ) -> Option<( + Address, + Epoch, + masp_primitives::transaction::components::Amount, + MerklePath, + )>; +} + +/// Shielded context file name +const FILE_NAME: &str = "shielded.dat"; +const TMP_FILE_NAME: &str = "shielded.tmp"; + +#[derive(Debug, BorshSerialize, BorshDeserialize, Clone)] +pub struct CLIShieldedUtils { + #[borsh_skip] + context_dir: PathBuf, + #[borsh_skip] + pub ledger_address: Option, +} + +impl CLIShieldedUtils { + /// Initialize a shielded transaction context that identifies notes + /// decryptable by any viewing key in the given set + pub fn new( + context_dir: PathBuf, + ) -> ShieldedContext { + // Make sure that MASP parameters are downloaded to enable MASP + // transaction building and verification later on + let params_dir = masp::get_params_dir(); + let spend_path = params_dir.join(masp::SPEND_NAME); + let convert_path = params_dir.join(masp::CONVERT_NAME); + let output_path = params_dir.join(masp::OUTPUT_NAME); + if !(spend_path.exists() + && convert_path.exists() + && output_path.exists()) + { + println!("MASP parameters not present, downloading..."); + masp_proofs::download_parameters() + .expect("MASP parameters not present or downloadable"); + println!("MASP parameter download complete, resuming execution..."); + } + // Finally initialize a shielded context with the supplied directory + let utils = Self { context_dir, ledger_address: None }; + ShieldedContext { utils, ..Default::default() } + } +} + +impl Default for CLIShieldedUtils { + fn default() -> Self { + Self { context_dir: PathBuf::from(FILE_NAME), ledger_address: None } + } +} + +#[async_trait] +impl ShieldedUtils for CLIShieldedUtils { + async fn query_storage_value( + &self, + key: &storage::Key, + ) -> Option + where T: BorshDeserialize { + let client = HttpClient::new(self.ledger_address.clone().unwrap()).unwrap(); + query_storage_value::(&client, &key).await + } + + async fn query_epoch(&self) -> Epoch { + rpc::query_epoch(args::Query { + ledger_address: self.ledger_address.clone().unwrap() + }).await + } + + fn local_tx_prover(&self) -> LocalTxProver { + if let Ok(params_dir) = env::var(masp::ENV_VAR_MASP_PARAMS_DIR) + { + let params_dir = PathBuf::from(params_dir); + let spend_path = params_dir.join(masp::SPEND_NAME); + let convert_path = params_dir.join(masp::CONVERT_NAME); + let output_path = params_dir.join(masp::OUTPUT_NAME); + LocalTxProver::new(&spend_path, &output_path, &convert_path) + } else { + LocalTxProver::with_default_location() + .expect("unable to load MASP Parameters") + } + } + + /// Try to load the last saved shielded context from the given context + /// directory. If this fails, then leave the current context unchanged. + fn load(self) -> std::io::Result> { + // Try to load shielded context from file + let mut ctx_file = File::open(self.context_dir.join(FILE_NAME))?; + let mut bytes = Vec::new(); + ctx_file.read_to_end(&mut bytes)?; + let mut new_ctx = ShieldedContext::deserialize(&mut &bytes[..])?; + // Associate the originating context directory with the + // shielded context under construction + new_ctx.utils = self; + Ok(new_ctx) + } + + /// Save this shielded context into its associated context directory + fn save(&self, ctx: &ShieldedContext) -> std::io::Result<()> { + // TODO: use mktemp crate? + let tmp_path = self.context_dir.join(TMP_FILE_NAME); + { + // First serialize the shielded context into a temporary file. + // Inability to create this file implies a simultaneuous write is in + // progress. In this case, immediately fail. This is unproblematic + // because the data intended to be stored can always be re-fetched + // from the blockchain. + let mut ctx_file = OpenOptions::new() + .write(true) + .create_new(true) + .open(tmp_path.clone())?; + let mut bytes = Vec::new(); + ctx.serialize(&mut bytes) + .expect("cannot serialize shielded context"); + ctx_file.write_all(&bytes[..])?; + } + // Atomically update the old shielded context file with new data. + // Atomicity is required to prevent other client instances from reading + // corrupt data. + std::fs::rename(tmp_path.clone(), self.context_dir.join(FILE_NAME))?; + // Finally, remove our temporary file to allow future saving of shielded + // contexts. + std::fs::remove_file(tmp_path)?; + Ok(()) + } + + /// Query a conversion. + async fn query_conversion( + &self, + asset_type: AssetType, + ) -> Option<( + Address, + Epoch, + masp_primitives::transaction::components::Amount, + MerklePath, + )> { + let client = HttpClient::new(self.ledger_address.clone().unwrap()).unwrap(); + query_conversion(client, asset_type).await + } +} + /// Make a ViewingKey that can view notes encrypted by given ExtendedSpendingKey pub fn to_viewing_key(esk: &ExtendedSpendingKey) -> FullViewingKey { ExtendedFullViewingKey::from(esk).fvk @@ -453,10 +618,10 @@ pub type TransactionDelta = HashMap; /// Represents the current state of the shielded pool from the perspective of /// the chosen viewing keys. #[derive(BorshSerialize, BorshDeserialize, Debug)] -pub struct ShieldedContext { +pub struct ShieldedContext { /// Location where this shielded context is saved #[borsh_skip] - context_dir: PathBuf, + pub utils: U, /// The last transaction index to be processed in this context last_txidx: u64, /// The commitment tree produced by scanning all transactions up to tx_pos @@ -486,16 +651,12 @@ pub struct ShieldedContext { vk_map: HashMap, } -/// Shielded context file name -const FILE_NAME: &str = "shielded.dat"; -const TMP_FILE_NAME: &str = "shielded.tmp"; - /// Default implementation to ease construction of TxContexts. Derive cannot be /// used here due to CommitmentTree not implementing Default. -impl Default for ShieldedContext { - fn default() -> ShieldedContext { - ShieldedContext { - context_dir: PathBuf::from(FILE_NAME), +impl Default for ShieldedContext { + fn default() -> ShieldedContext { + ShieldedContext:: { + utils: U::default(), last_txidx: u64::default(), tree: CommitmentTree::empty(), pos_map: HashMap::default(), @@ -512,55 +673,24 @@ impl Default for ShieldedContext { } } -impl ShieldedContext { +impl ShieldedContext { /// Try to load the last saved shielded context from the given context /// directory. If this fails, then leave the current context unchanged. pub fn load(&mut self) -> std::io::Result<()> { - // Try to load shielded context from file - let mut ctx_file = File::open(self.context_dir.join(FILE_NAME))?; - let mut bytes = Vec::new(); - ctx_file.read_to_end(&mut bytes)?; - let mut new_ctx = Self::deserialize(&mut &bytes[..])?; - // Associate the originating context directory with the - // shielded context under construction - new_ctx.context_dir = self.context_dir.clone(); + let new_ctx = self.utils.clone().load()?; *self = new_ctx; Ok(()) } /// Save this shielded context into its associated context directory pub fn save(&self) -> std::io::Result<()> { - // TODO: use mktemp crate? - let tmp_path = self.context_dir.join(TMP_FILE_NAME); - { - // First serialize the shielded context into a temporary file. - // Inability to create this file implies a simultaneuous write is in - // progress. In this case, immediately fail. This is unproblematic - // because the data intended to be stored can always be re-fetched - // from the blockchain. - let mut ctx_file = OpenOptions::new() - .write(true) - .create_new(true) - .open(tmp_path.clone())?; - let mut bytes = Vec::new(); - self.serialize(&mut bytes) - .expect("cannot serialize shielded context"); - ctx_file.write_all(&bytes[..])?; - } - // Atomically update the old shielded context file with new data. - // Atomicity is required to prevent other client instances from reading - // corrupt data. - std::fs::rename(tmp_path.clone(), self.context_dir.join(FILE_NAME))?; - // Finally, remove our temporary file to allow future saving of shielded - // contexts. - std::fs::remove_file(tmp_path)?; - Ok(()) + self.utils.save(self) } /// Merge data from the given shielded context into the current shielded /// context. It must be the case that the two shielded contexts share the /// same last transaction ID and share identical commitment trees. - pub fn merge(&mut self, new_ctx: ShieldedContext) { + pub fn merge(&mut self, new_ctx: ShieldedContext) { debug_assert_eq!(self.last_txidx, new_ctx.last_txidx); // Merge by simply extending maps. Identical keys should contain // identical values, so overwriting should not be problematic. @@ -590,7 +720,6 @@ impl ShieldedContext { /// ShieldedContext pub async fn fetch( &mut self, - ledger_address: &TendermintAddress, sks: &[ExtendedSpendingKey], fvks: &[ViewingKey], ) { @@ -615,10 +744,10 @@ impl ShieldedContext { let (txs, mut tx_iter); if !unknown_keys.is_empty() { // Load all transactions accepted until this point - txs = Self::fetch_shielded_transfers(ledger_address, 0).await; + txs = Self::fetch_shielded_transfers(&self.utils, 0).await; tx_iter = txs.iter(); // Do this by constructing a shielding context only for unknown keys - let mut tx_ctx = ShieldedContext::new(self.context_dir.clone()); + let mut tx_ctx = Self { utils: self.utils.clone(), ..Default::default() }; for vk in unknown_keys { tx_ctx.pos_map.entry(vk).or_insert_with(HashSet::new); } @@ -636,7 +765,7 @@ impl ShieldedContext { } else { // Load only transactions accepted from last_txid until this point txs = - Self::fetch_shielded_transfers(ledger_address, self.last_txidx) + Self::fetch_shielded_transfers(&self.utils, self.last_txidx) .await; tx_iter = txs.iter(); } @@ -647,41 +776,15 @@ impl ShieldedContext { } } - /// Initialize a shielded transaction context that identifies notes - /// decryptable by any viewing key in the given set - pub fn new(context_dir: PathBuf) -> ShieldedContext { - // Make sure that MASP parameters are downloaded to enable MASP - // transaction building and verification later on - let params_dir = masp::get_params_dir(); - let spend_path = params_dir.join(masp::SPEND_NAME); - let convert_path = params_dir.join(masp::CONVERT_NAME); - let output_path = params_dir.join(masp::OUTPUT_NAME); - if !(spend_path.exists() - && convert_path.exists() - && output_path.exists()) - { - println!("MASP parameters not present, downloading..."); - masp_proofs::download_parameters() - .expect("MASP parameters not present or downloadable"); - println!("MASP parameter download complete, resuming execution..."); - } - // Finally initialize a shielded context with the supplied directory - Self { - context_dir, - ..Default::default() - } - } - /// Obtain a chronologically-ordered list of all accepted shielded /// transactions from the ledger. The ledger conceptually stores /// transactions as a vector. More concretely, the HEAD_TX_KEY location /// stores the index of the last accepted transaction and each transaction /// is stored at a key derived from its index. pub async fn fetch_shielded_transfers( - ledger_address: &TendermintAddress, + utils: &U, last_txidx: u64, ) -> BTreeMap<(BlockHeight, TxIndex), (Epoch, Transfer)> { - let client = HttpClient::new(ledger_address.clone()).unwrap(); // The address of the MASP account let masp_addr = masp(); // Construct the key where last transaction pointer is stored @@ -689,7 +792,7 @@ impl ShieldedContext { .push(&HEAD_TX_KEY.to_owned()) .expect("Cannot obtain a storage key"); // Query for the index of the last accepted transaction - let head_txidx = query_storage_value::(&client, &head_tx_key) + let head_txidx = utils.query_storage_value::(&head_tx_key) .await .unwrap_or(0); let mut shielded_txs = BTreeMap::new(); @@ -701,8 +804,7 @@ impl ShieldedContext { .expect("Cannot obtain a storage key"); // Obtain the current transaction let (tx_epoch, tx_height, tx_index, current_tx) = - query_storage_value::<(Epoch, BlockHeight, TxIndex, Transfer)>( - &client, + utils.query_storage_value::<(Epoch, BlockHeight, TxIndex, Transfer)>( ¤t_tx_key, ) .await @@ -863,7 +965,6 @@ impl ShieldedContext { /// if it is found. pub async fn decode_asset_type( &mut self, - client: HttpClient, asset_type: AssetType, ) -> Option<(Address, Epoch)> { // Try to find the decoding in the cache @@ -872,7 +973,7 @@ impl ShieldedContext { } // Query for the ID of the last accepted transaction let (addr, ep, _conv, _path): (Address, _, Amount, MerklePath) = - query_conversion(client, asset_type).await?; + self.utils.query_conversion(asset_type).await?; self.asset_types.insert(asset_type, (addr.clone(), ep)); Some((addr, ep)) } @@ -881,7 +982,6 @@ impl ShieldedContext { /// type and cache it. async fn query_allowed_conversion<'a>( &'a mut self, - client: HttpClient, asset_type: AssetType, conversions: &'a mut Conversions, ) -> Option<&'a mut (AllowedConversion, MerklePath, i64)> { @@ -890,7 +990,7 @@ impl ShieldedContext { Entry::Vacant(conv_entry) => { // Query for the ID of the last accepted transaction let (addr, ep, conv, path): (Address, _, _, _) = - query_conversion(client, asset_type).await?; + self.utils.query_conversion(asset_type).await?; self.asset_types.insert(asset_type, (addr, ep)); // If the conversion is 0, then we just have a pure decoding if conv == Amount::zero() { @@ -908,7 +1008,6 @@ impl ShieldedContext { /// balance and hence we return None. pub async fn compute_exchanged_balance( &mut self, - client: HttpClient, vk: &ViewingKey, target_epoch: Epoch, ) -> Option { @@ -917,7 +1016,6 @@ impl ShieldedContext { // And then exchange balance into current asset types Some( self.compute_exchanged_amount( - client, balance, target_epoch, HashMap::new(), @@ -973,7 +1071,6 @@ impl ShieldedContext { /// terms of the latest asset types. pub async fn compute_exchanged_amount( &mut self, - client: HttpClient, mut input: Amount, target_epoch: Epoch, mut conversions: Conversions, @@ -985,14 +1082,13 @@ impl ShieldedContext { input.components().next().map(cloned_pair) { let target_asset_type = self - .decode_asset_type(client.clone(), asset_type) + .decode_asset_type(asset_type) .await .map(|(addr, _epoch)| make_asset_type(target_epoch, &addr)) .unwrap_or(asset_type); let at_target_asset_type = asset_type == target_asset_type; if let (Some((conv, _wit, usage)), false) = ( self.query_allowed_conversion( - client.clone(), asset_type, &mut conversions, ) @@ -1015,7 +1111,6 @@ impl ShieldedContext { ); } else if let (Some((conv, _wit, usage)), false) = ( self.query_allowed_conversion( - client.clone(), target_asset_type, &mut conversions, ) @@ -1053,7 +1148,6 @@ impl ShieldedContext { /// achieve the total value. pub async fn collect_unspent_notes( &mut self, - ledger_address: TendermintAddress, vk: &ViewingKey, target: Amount, target_epoch: Epoch, @@ -1063,7 +1157,6 @@ impl ShieldedContext { Conversions, ) { // Establish connection with which to do exchange rate queries - let client = HttpClient::new(ledger_address.clone()).unwrap(); let mut conversions = HashMap::new(); let mut val_acc = Amount::zero(); let mut notes = Vec::new(); @@ -1087,7 +1180,6 @@ impl ShieldedContext { .expect("received note has invalid value or asset type"); let (contr, proposed_convs) = self .compute_exchanged_amount( - client.clone(), pre_contr, target_epoch, conversions.clone(), @@ -1122,7 +1214,7 @@ impl ShieldedContext { /// the given payment address fails with /// `PinnedBalanceError::NoTransactionPinned`. pub async fn compute_pinned_balance( - ledger_address: &TendermintAddress, + utils: &U, owner: PaymentAddress, viewing_key: &ViewingKey, ) -> Result<(Amount, Epoch), PinnedBalanceError> { @@ -1137,7 +1229,6 @@ impl ShieldedContext { Some(counter_owner) if counter_owner == owner.into() => {} _ => return Err(PinnedBalanceError::InvalidViewingKey), } - let client = HttpClient::new(ledger_address.clone()).unwrap(); // The address of the MASP account let masp_addr = masp(); // Construct the key for where the transaction ID would be stored @@ -1145,7 +1236,7 @@ impl ShieldedContext { .push(&(PIN_KEY_PREFIX.to_owned() + &owner.hash())) .expect("Cannot obtain a storage key"); // Obtain the transaction pointer at the key - let txidx = query_storage_value::(&client, &pin_key) + let txidx = utils.query_storage_value::(&pin_key) .await .ok_or(PinnedBalanceError::NoTransactionPinned)?; // Construct the key for where the pinned transaction is stored @@ -1154,8 +1245,8 @@ impl ShieldedContext { .expect("Cannot obtain a storage key"); // Obtain the pointed to transaction let (tx_epoch, _tx_height, _tx_index, tx) = - query_storage_value::<(Epoch, BlockHeight, TxIndex, Transfer)>( - &client, &tx_key, + utils.query_storage_value::<(Epoch, BlockHeight, TxIndex, Transfer)>( + &tx_key, ) .await .expect("Ill-formed epoch, transaction pair"); @@ -1196,19 +1287,16 @@ impl ShieldedContext { /// would have been displayed in the epoch of the transaction. pub async fn compute_exchanged_pinned_balance( &mut self, - ledger_address: &TendermintAddress, owner: PaymentAddress, viewing_key: &ViewingKey, ) -> Result<(Amount, Epoch), PinnedBalanceError> { // Obtain the balance that will be exchanged let (amt, ep) = - Self::compute_pinned_balance(ledger_address, owner, viewing_key) + Self::compute_pinned_balance(&self.utils, owner, viewing_key) .await?; - // Establish connection with which to do exchange rate queries - let client = HttpClient::new(ledger_address.clone()).unwrap(); // Finally, exchange the balance to the transaction's epoch Ok(( - self.compute_exchanged_amount(client, amt, ep, HashMap::new()) + self.compute_exchanged_amount(amt, ep, HashMap::new()) .await .0, ep, @@ -1220,7 +1308,6 @@ impl ShieldedContext { /// the given epoch are ignored. pub async fn decode_amount( &mut self, - client: HttpClient, amt: Amount, target_epoch: Epoch, ) -> Amount
{ @@ -1228,7 +1315,7 @@ impl ShieldedContext { for (asset_type, val) in amt.components() { // Decode the asset type let decoded = - self.decode_asset_type(client.clone(), *asset_type).await; + self.decode_asset_type(*asset_type).await; // Only assets with the target timestamp count match decoded { Some((addr, epoch)) if epoch == target_epoch => { @@ -1244,14 +1331,13 @@ impl ShieldedContext { /// Addresses that they decode to. pub async fn decode_all_amounts( &mut self, - client: HttpClient, amt: Amount, ) -> Amount<(Address, Epoch)> { let mut res = Amount::zero(); for (asset_type, val) in amt.components() { // Decode the asset type let decoded = - self.decode_asset_type(client.clone(), *asset_type).await; + self.decode_asset_type(*asset_type).await; // Only assets with the target timestamp count if let Some((addr, epoch)) = decoded { res += &Amount::from_pair((addr, epoch), *val).unwrap() @@ -1269,7 +1355,6 @@ impl ShieldedContext { /// Transfer object. async fn gen_shielded_transfer( &mut self, - ledger_address: &TendermintAddress, source: TransferSource, target: TransferTarget, args_amount: token::Amount, @@ -1290,14 +1375,12 @@ impl ShieldedContext { let spending_keys: Vec<_> = spending_key.into_iter().collect(); // Load the current shielded context given the spending key we possess let _ = self.load(); - self.fetch(&ledger_address, &spending_keys, &[]) + self.fetch(&spending_keys, &[]) .await; // Save the update state so that future fetches can be short-circuited let _ = self.save(); // Determine epoch in which to submit potential shielded transaction - let epoch = rpc::query_epoch(args::Query { - ledger_address: ledger_address.clone() - }).await; + let epoch = self.utils.query_epoch().await; // Context required for storing which notes are in the source's possesion let consensus_branch_id = BranchId::Sapling; let amt: u64 = args_amount.into(); @@ -1324,7 +1407,6 @@ impl ShieldedContext { // Locate unspent notes that can help us meet the transaction amount let (_, unspent_notes, used_convs) = self .collect_unspent_notes( - ledger_address.clone(), &to_viewing_key(&sk).vk, required_amt, epoch, @@ -1399,24 +1481,12 @@ impl ShieldedContext { amt, )?; } - let prover = if let Ok(params_dir) = env::var(masp::ENV_VAR_MASP_PARAMS_DIR) - { - let params_dir = PathBuf::from(params_dir); - let spend_path = params_dir.join(masp::SPEND_NAME); - let convert_path = params_dir.join(masp::CONVERT_NAME); - let output_path = params_dir.join(masp::OUTPUT_NAME); - LocalTxProver::new(&spend_path, &output_path, &convert_path) - } else { - LocalTxProver::with_default_location() - .expect("unable to load MASP Parameters") - }; + let prover = self.utils.local_tx_prover(); // Build and return the constructed transaction let mut tx = builder.build(consensus_branch_id, &prover); if epoch_sensitive { - let new_epoch = rpc::query_epoch(args::Query { - ledger_address: ledger_address.clone() - }).await; + let new_epoch = self.utils.query_epoch().await; // If epoch has changed, recalculate shielded outputs to match new epoch if new_epoch != epoch { @@ -1611,9 +1681,11 @@ pub async fn submit_transfer(mut ctx: Context, args: args::TxTransfer) { _ => None, }; + // Update the context with the current ledger address + ctx.shielded.utils.ledger_address = Some(args.tx.ledger_address.clone()); + let stx_result = ctx.shielded.gen_shielded_transfer( - &args.tx.ledger_address, transfer_source, transfer_target, args.amount, From 24c4da0ed179793d8ea7b563ae2864699b32777f Mon Sep 17 00:00:00 2001 From: Murisi Tarusenga Date: Wed, 30 Nov 2022 15:42:36 +0200 Subject: [PATCH 04/51] Moved ShieldedContext into shared crate. --- Cargo.lock | 2 + apps/Cargo.toml | 2 +- apps/src/bin/anoma-wallet/cli.rs | 2 +- apps/src/lib/cli/context.rs | 3 +- apps/src/lib/client/rpc.rs | 2 +- apps/src/lib/client/tx.rs | 1081 +---------------------------- apps/src/lib/client/types.rs | 1 - shared/Cargo.toml | 7 + shared/src/ledger/masp.rs | 1087 ++++++++++++++++++++++++++++++ tests/src/e2e/ledger_tests.rs | 8 +- 10 files changed, 1116 insertions(+), 1079 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 79a4079e7b..9a2cd54a75 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3655,6 +3655,8 @@ dependencies = [ "proptest", "prost", "pwasm-utils", + "rand 0.8.5", + "rand_core 0.6.4", "rayon", "rust_decimal", "serde_json", diff --git a/apps/Cargo.toml b/apps/Cargo.toml index 896168c63f..edd67efb1d 100644 --- a/apps/Cargo.toml +++ b/apps/Cargo.toml @@ -67,7 +67,7 @@ abciplus = [ ] [dependencies] -namada = {path = "../shared", default-features = false, features = ["wasm-runtime", "ferveo-tpke"]} +namada = {path = "../shared", default-features = false, features = ["wasm-runtime", "ferveo-tpke", "masp-tx-gen"]} ark-serialize = "0.3.0" ark-std = "0.3.0" # branch = "bat/arse-merkle-tree" diff --git a/apps/src/bin/anoma-wallet/cli.rs b/apps/src/bin/anoma-wallet/cli.rs index 62857e76f3..b7d4d78a02 100644 --- a/apps/src/bin/anoma-wallet/cli.rs +++ b/apps/src/bin/anoma-wallet/cli.rs @@ -11,7 +11,7 @@ use namada::types::key::*; 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::ledger::masp::find_valid_diversifier; use namada_apps::wallet::{DecryptionError, FindKeyError}; use rand_core::OsRng; diff --git a/apps/src/lib/cli/context.rs b/apps/src/lib/cli/context.rs index 4461cd4319..2a05e6847c 100644 --- a/apps/src/lib/cli/context.rs +++ b/apps/src/lib/cli/context.rs @@ -10,9 +10,10 @@ use namada::types::address::Address; use namada::types::chain::ChainId; use namada::types::key::*; use namada::types::masp::*; +use namada::ledger::masp::ShieldedContext; use super::args; -use crate::client::tx::{ShieldedContext, CLIShieldedUtils}; +use crate::client::tx::CLIShieldedUtils; use crate::config::genesis::genesis_config; use crate::config::global::GlobalConfig; use crate::config::{self, Config}; diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index 667370413b..7ed6760890 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -56,7 +56,7 @@ use tokio::time::{Duration, Instant}; use crate::cli::{self, args, Context}; use crate::client::tendermint_rpc_types::TxResponse; -use crate::client::tx::{ +use namada::ledger::masp::{ Conversions, PinnedBalanceError, TransactionDelta, TransferDelta, }; use crate::facade::tendermint::merkle::proof::Proof; diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index 0f5b469d23..94ffe42311 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -1,11 +1,9 @@ use std::borrow::Cow; -use std::collections::hash_map::Entry; -use std::collections::{BTreeMap, HashMap, HashSet}; +use std::collections::HashSet; use std::env; use std::fmt::Debug; use std::fs::{File, OpenOptions}; use std::io::{Read, Write}; -use std::ops::Deref; use std::path::PathBuf; use async_std::io::prelude::WriteExt; @@ -13,22 +11,9 @@ use async_std::io::{self}; use borsh::{BorshDeserialize, BorshSerialize}; use itertools::Either::*; use masp_primitives::asset_type::AssetType; -use masp_primitives::consensus::{BranchId, TestNetwork}; -use masp_primitives::convert::AllowedConversion; -use masp_primitives::ff::PrimeField; -use masp_primitives::group::cofactor::CofactorGroup; -use masp_primitives::keys::FullViewingKey; -use masp_primitives::legacy::TransparentAddress; -use masp_primitives::merkle_tree::{ - CommitmentTree, IncrementalWitness, MerklePath, -}; -use masp_primitives::note_encryption::*; -use masp_primitives::primitives::{Diversifier, Note, ViewingKey}; +use masp_primitives::merkle_tree::MerklePath; use masp_primitives::sapling::Node; -use masp_primitives::transaction::builder::{self, secp256k1, *}; -use masp_primitives::transaction::components::{Amount, OutPoint, TxOut}; -use masp_primitives::transaction::Transaction; -use masp_primitives::zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}; +use masp_primitives::transaction::builder; use masp_proofs::prover::LocalTxProver; use namada::ibc::applications::ics20_fungible_token_transfer::msgs::transfer::MsgTransfer; use namada::ibc::signer::Signer; @@ -45,23 +30,18 @@ use namada::types::governance::{ OfflineProposal, OfflineVote, Proposal, ProposalVote, }; use namada::types::key::*; -use namada::types::masp::{PaymentAddress, TransferSource, TransferTarget}; +use namada::types::masp::TransferTarget; use namada::types::storage::{ - BlockHeight, Epoch, Key, KeySeg, TxIndex, RESERVED_ADDRESS_PREFIX, + Epoch, RESERVED_ADDRESS_PREFIX, }; use namada::types::time::DateTimeUtc; -use namada::types::token::{ - Transfer, HEAD_TX_KEY, PIN_KEY_PREFIX, TX_KEY_PREFIX, -}; use namada::types::transaction::governance::{ InitProposalData, VoteProposalData, }; use namada::types::transaction::{pos, InitAccount, InitValidator, UpdateVp}; use namada::types::{storage, token}; use namada::{ledger, vm}; -use rand_core::{CryptoRng, OsRng, RngCore}; use rust_decimal::Decimal; -use sha2::Digest; use tokio::time::{Duration, Instant}; use async_trait::async_trait; @@ -388,36 +368,6 @@ pub async fn submit_init_validator( } } -#[async_trait] -pub trait ShieldedUtils : Sized + BorshDeserialize + BorshSerialize + Default + Clone { - async fn query_storage_value( - &self, - key: &storage::Key, - ) -> Option - where - T: BorshDeserialize; - - async fn query_epoch(&self) -> Epoch; - - //fn download_parameters() -> Result<(), Error>; - - fn local_tx_prover(&self) -> LocalTxProver; - - fn load(self) -> std::io::Result>; - - fn save(&self, ctx: &ShieldedContext) -> std::io::Result<()>; - - async fn query_conversion( - &self, - asset_type: AssetType, - ) -> Option<( - Address, - Epoch, - masp_primitives::transaction::components::Amount, - MerklePath, - )>; -} - /// Shielded context file name const FILE_NAME: &str = "shielded.dat"; const TMP_FILE_NAME: &str = "shielded.tmp"; @@ -435,7 +385,7 @@ impl CLIShieldedUtils { /// decryptable by any viewing key in the given set pub fn new( context_dir: PathBuf, - ) -> ShieldedContext { + ) -> masp::ShieldedContext { // Make sure that MASP parameters are downloaded to enable MASP // transaction building and verification later on let params_dir = masp::get_params_dir(); @@ -453,7 +403,7 @@ impl CLIShieldedUtils { } // Finally initialize a shielded context with the supplied directory let utils = Self { context_dir, ledger_address: None }; - ShieldedContext { utils, ..Default::default() } + masp::ShieldedContext { utils, ..Default::default() } } } @@ -464,7 +414,7 @@ impl Default for CLIShieldedUtils { } #[async_trait] -impl ShieldedUtils for CLIShieldedUtils { +impl masp::ShieldedUtils for CLIShieldedUtils { async fn query_storage_value( &self, key: &storage::Key, @@ -496,12 +446,12 @@ impl ShieldedUtils for CLIShieldedUtils { /// Try to load the last saved shielded context from the given context /// directory. If this fails, then leave the current context unchanged. - fn load(self) -> std::io::Result> { + fn load(self) -> std::io::Result> { // Try to load shielded context from file let mut ctx_file = File::open(self.context_dir.join(FILE_NAME))?; let mut bytes = Vec::new(); ctx_file.read_to_end(&mut bytes)?; - let mut new_ctx = ShieldedContext::deserialize(&mut &bytes[..])?; + let mut new_ctx = masp::ShieldedContext::deserialize(&mut &bytes[..])?; // Associate the originating context directory with the // shielded context under construction new_ctx.utils = self; @@ -509,7 +459,7 @@ impl ShieldedUtils for CLIShieldedUtils { } /// Save this shielded context into its associated context directory - fn save(&self, ctx: &ShieldedContext) -> std::io::Result<()> { + fn save(&self, ctx: &masp::ShieldedContext) -> std::io::Result<()> { // TODO: use mktemp crate? let tmp_path = self.context_dir.join(TMP_FILE_NAME); { @@ -552,1016 +502,7 @@ impl ShieldedUtils for CLIShieldedUtils { } } -/// Make a ViewingKey that can view notes encrypted by given ExtendedSpendingKey -pub fn to_viewing_key(esk: &ExtendedSpendingKey) -> FullViewingKey { - ExtendedFullViewingKey::from(esk).fvk -} - -/// Generate a valid diversifier, i.e. one that has a diversified base. Return -/// also this diversified base. -pub fn find_valid_diversifier( - rng: &mut R, -) -> (Diversifier, masp_primitives::jubjub::SubgroupPoint) { - let mut diversifier; - let g_d; - // Keep generating random diversifiers until one has a diversified base - loop { - let mut d = [0; 11]; - rng.fill_bytes(&mut d); - diversifier = Diversifier(d); - if let Some(val) = diversifier.g_d() { - g_d = val; - break; - } - } - (diversifier, g_d) -} - -/// Determine if using the current note would actually bring us closer to our -/// target -pub fn is_amount_required(src: Amount, dest: Amount, delta: Amount) -> bool { - if delta > Amount::zero() { - let gap = dest - src; - for (asset_type, value) in gap.components() { - if *value > 0 && delta[asset_type] > 0 { - return true; - } - } - } - false -} - -/// An extension of Option's cloned method for pair types -fn cloned_pair((a, b): (&T, &U)) -> (T, U) { - (a.clone(), b.clone()) -} - -/// Errors that can occur when trying to retrieve pinned transaction -#[derive(PartialEq, Eq)] -pub enum PinnedBalanceError { - /// No transaction has yet been pinned to the given payment address - NoTransactionPinned, - /// The supplied viewing key does not recognize payments to given address - InvalidViewingKey, -} - -/// Represents the amount used of different conversions -pub type Conversions = - HashMap, i64)>; - -/// Represents the changes that were made to a list of transparent accounts -pub type TransferDelta = HashMap>; - -/// Represents the changes that were made to a list of shielded accounts -pub type TransactionDelta = HashMap; - -/// Represents the current state of the shielded pool from the perspective of -/// the chosen viewing keys. -#[derive(BorshSerialize, BorshDeserialize, Debug)] -pub struct ShieldedContext { - /// Location where this shielded context is saved - #[borsh_skip] - pub utils: U, - /// The last transaction index to be processed in this context - last_txidx: u64, - /// The commitment tree produced by scanning all transactions up to tx_pos - tree: CommitmentTree, - /// Maps viewing keys to applicable note positions - pos_map: HashMap>, - /// Maps a nullifier to the note position to which it applies - nf_map: HashMap<[u8; 32], usize>, - /// Maps note positions to their corresponding notes - note_map: HashMap, - /// Maps note positions to their corresponding memos - memo_map: HashMap, - /// Maps note positions to the diversifier of their payment address - div_map: HashMap, - /// Maps note positions to their witness (used to make merkle paths) - witness_map: HashMap>, - /// Tracks what each transaction does to various account balances - delta_map: BTreeMap< - (BlockHeight, TxIndex), - (Epoch, TransferDelta, TransactionDelta), - >, - /// The set of note positions that have been spent - spents: HashSet, - /// Maps asset types to their decodings - asset_types: HashMap, - /// Maps note positions to their corresponding viewing keys - vk_map: HashMap, -} - -/// Default implementation to ease construction of TxContexts. Derive cannot be -/// used here due to CommitmentTree not implementing Default. -impl Default for ShieldedContext { - fn default() -> ShieldedContext { - ShieldedContext:: { - utils: U::default(), - last_txidx: u64::default(), - tree: CommitmentTree::empty(), - pos_map: HashMap::default(), - nf_map: HashMap::default(), - note_map: HashMap::default(), - memo_map: HashMap::default(), - div_map: HashMap::default(), - witness_map: HashMap::default(), - spents: HashSet::default(), - delta_map: BTreeMap::default(), - asset_types: HashMap::default(), - vk_map: HashMap::default(), - } - } -} - -impl ShieldedContext { - /// Try to load the last saved shielded context from the given context - /// directory. If this fails, then leave the current context unchanged. - pub fn load(&mut self) -> std::io::Result<()> { - let new_ctx = self.utils.clone().load()?; - *self = new_ctx; - Ok(()) - } - - /// Save this shielded context into its associated context directory - pub fn save(&self) -> std::io::Result<()> { - self.utils.save(self) - } - - /// Merge data from the given shielded context into the current shielded - /// context. It must be the case that the two shielded contexts share the - /// same last transaction ID and share identical commitment trees. - pub fn merge(&mut self, new_ctx: ShieldedContext) { - debug_assert_eq!(self.last_txidx, new_ctx.last_txidx); - // Merge by simply extending maps. Identical keys should contain - // identical values, so overwriting should not be problematic. - self.pos_map.extend(new_ctx.pos_map); - self.nf_map.extend(new_ctx.nf_map); - self.note_map.extend(new_ctx.note_map); - self.memo_map.extend(new_ctx.memo_map); - self.div_map.extend(new_ctx.div_map); - self.witness_map.extend(new_ctx.witness_map); - self.spents.extend(new_ctx.spents); - self.asset_types.extend(new_ctx.asset_types); - self.vk_map.extend(new_ctx.vk_map); - // The deltas are the exception because different keys can reveal - // different parts of the same transaction. Hence each delta needs to be - // merged separately. - for ((height, idx), (ep, ntfer_delta, ntx_delta)) in new_ctx.delta_map { - let (_ep, tfer_delta, tx_delta) = self - .delta_map - .entry((height, idx)) - .or_insert((ep, TransferDelta::new(), TransactionDelta::new())); - tfer_delta.extend(ntfer_delta); - tx_delta.extend(ntx_delta); - } - } - - /// Fetch the current state of the multi-asset shielded pool into a - /// ShieldedContext - pub async fn fetch( - &mut self, - sks: &[ExtendedSpendingKey], - fvks: &[ViewingKey], - ) { - // First determine which of the keys requested to be fetched are new. - // Necessary because old transactions will need to be scanned for new - // keys. - let mut unknown_keys = Vec::new(); - for esk in sks { - let vk = to_viewing_key(esk).vk; - if !self.pos_map.contains_key(&vk) { - unknown_keys.push(vk); - } - } - for vk in fvks { - if !self.pos_map.contains_key(vk) { - unknown_keys.push(*vk); - } - } - - // If unknown keys are being used, we need to scan older transactions - // for any unspent notes - let (txs, mut tx_iter); - if !unknown_keys.is_empty() { - // Load all transactions accepted until this point - txs = Self::fetch_shielded_transfers(&self.utils, 0).await; - tx_iter = txs.iter(); - // Do this by constructing a shielding context only for unknown keys - let mut tx_ctx = Self { utils: self.utils.clone(), ..Default::default() }; - for vk in unknown_keys { - tx_ctx.pos_map.entry(vk).or_insert_with(HashSet::new); - } - // Update this unknown shielded context until it is level with self - while tx_ctx.last_txidx != self.last_txidx { - if let Some(((height, idx), (epoch, tx))) = tx_iter.next() { - tx_ctx.scan_tx(*height, *idx, *epoch, tx); - } else { - break; - } - } - // Merge the context data originating from the unknown keys into the - // current context - self.merge(tx_ctx); - } else { - // Load only transactions accepted from last_txid until this point - txs = - Self::fetch_shielded_transfers(&self.utils, self.last_txidx) - .await; - tx_iter = txs.iter(); - } - // Now that we possess the unspent notes corresponding to both old and - // new keys up until tx_pos, proceed to scan the new transactions. - for ((height, idx), (epoch, tx)) in &mut tx_iter { - self.scan_tx(*height, *idx, *epoch, tx); - } - } - - /// Obtain a chronologically-ordered list of all accepted shielded - /// transactions from the ledger. The ledger conceptually stores - /// transactions as a vector. More concretely, the HEAD_TX_KEY location - /// stores the index of the last accepted transaction and each transaction - /// is stored at a key derived from its index. - pub async fn fetch_shielded_transfers( - utils: &U, - last_txidx: u64, - ) -> BTreeMap<(BlockHeight, TxIndex), (Epoch, Transfer)> { - // The address of the MASP account - let masp_addr = masp(); - // Construct the key where last transaction pointer is stored - let head_tx_key = Key::from(masp_addr.to_db_key()) - .push(&HEAD_TX_KEY.to_owned()) - .expect("Cannot obtain a storage key"); - // Query for the index of the last accepted transaction - let head_txidx = utils.query_storage_value::(&head_tx_key) - .await - .unwrap_or(0); - let mut shielded_txs = BTreeMap::new(); - // Fetch all the transactions we do not have yet - for i in last_txidx..head_txidx { - // Construct the key for where the current transaction is stored - let current_tx_key = Key::from(masp_addr.to_db_key()) - .push(&(TX_KEY_PREFIX.to_owned() + &i.to_string())) - .expect("Cannot obtain a storage key"); - // Obtain the current transaction - let (tx_epoch, tx_height, tx_index, current_tx) = - utils.query_storage_value::<(Epoch, BlockHeight, TxIndex, Transfer)>( - ¤t_tx_key, - ) - .await - .unwrap(); - // Collect the current transaction - shielded_txs.insert((tx_height, tx_index), (tx_epoch, current_tx)); - } - shielded_txs - } - - /// Applies the given transaction to the supplied context. More precisely, - /// the shielded transaction's outputs are added to the commitment tree. - /// Newly discovered notes are associated to the supplied viewing keys. Note - /// nullifiers are mapped to their originating notes. Note positions are - /// associated to notes, memos, and diversifiers. And the set of notes that - /// we have spent are updated. The witness map is maintained to make it - /// easier to construct note merkle paths in other code. See - /// https://zips.z.cash/protocol/protocol.pdf#scan - pub fn scan_tx( - &mut self, - height: BlockHeight, - index: TxIndex, - epoch: Epoch, - tx: &Transfer, - ) { - // Ignore purely transparent transactions - let shielded = if let Some(shielded) = &tx.shielded { - shielded - } else { - return; - }; - // For tracking the account changes caused by this Transaction - let mut transaction_delta = TransactionDelta::new(); - // Listen for notes sent to our viewing keys - for so in &shielded.shielded_outputs { - // Create merkle tree leaf node from note commitment - let node = Node::new(so.cmu.to_repr()); - // Update each merkle tree in the witness map with the latest - // addition - for (_, witness) in self.witness_map.iter_mut() { - witness.append(node).expect("note commitment tree is full"); - } - let note_pos = self.tree.size(); - self.tree - .append(node) - .expect("note commitment tree is full"); - // Finally, make it easier to construct merkle paths to this new - // note - let witness = IncrementalWitness::::from_tree(&self.tree); - self.witness_map.insert(note_pos, witness); - // Let's try to see if any of our viewing keys can decrypt latest - // note - for (vk, notes) in self.pos_map.iter_mut() { - let decres = try_sapling_note_decryption::( - 0, - &vk.ivk().0, - &so.ephemeral_key.into_subgroup().unwrap(), - &so.cmu, - &so.enc_ciphertext, - ); - // So this current viewing key does decrypt this current note... - if let Some((note, pa, memo)) = decres { - // Add this note to list of notes decrypted by this viewing - // key - notes.insert(note_pos); - // Compute the nullifier now to quickly recognize when spent - let nf = note.nf(vk, note_pos.try_into().unwrap()); - self.note_map.insert(note_pos, note); - self.memo_map.insert(note_pos, memo); - // The payment address' diversifier is required to spend - // note - self.div_map.insert(note_pos, *pa.diversifier()); - self.nf_map.insert(nf.0, note_pos); - // Note the account changes - let balance = transaction_delta - .entry(*vk) - .or_insert_with(Amount::zero); - *balance += - Amount::from_nonnegative(note.asset_type, note.value) - .expect( - "found note with invalid value or asset type", - ); - self.vk_map.insert(note_pos, *vk); - break; - } - } - } - // Cancel out those of our notes that have been spent - for ss in &shielded.shielded_spends { - // If the shielded spend's nullifier is in our map, then target note - // is rendered unusable - if let Some(note_pos) = self.nf_map.get(&ss.nullifier) { - self.spents.insert(*note_pos); - // Note the account changes - let balance = transaction_delta - .entry(self.vk_map[note_pos]) - .or_insert_with(Amount::zero); - let note = self.note_map[note_pos]; - *balance -= - Amount::from_nonnegative(note.asset_type, note.value) - .expect("found note with invalid value or asset type"); - } - } - // Record the changes to the transparent accounts - let transparent_delta = - Amount::from_nonnegative(tx.token.clone(), u64::from(tx.amount)) - .expect("invalid value for amount"); - let mut transfer_delta = TransferDelta::new(); - transfer_delta - .insert(tx.source.clone(), Amount::zero() - &transparent_delta); - transfer_delta.insert(tx.target.clone(), transparent_delta); - self.delta_map.insert( - (height, index), - (epoch, transfer_delta, transaction_delta), - ); - self.last_txidx += 1; - } - - /// Summarize the effects on shielded and transparent accounts of each - /// Transfer in this context - pub fn get_tx_deltas( - &self, - ) -> &BTreeMap< - (BlockHeight, TxIndex), - (Epoch, TransferDelta, TransactionDelta), - > { - &self.delta_map - } - - /// Compute the total unspent notes associated with the viewing key in the - /// context. If the key is not in the context, then we do not know the - /// balance and hence we return None. - pub fn compute_shielded_balance(&self, vk: &ViewingKey) -> Option { - // Cannot query the balance of a key that's not in the map - if !self.pos_map.contains_key(vk) { - return None; - } - let mut val_acc = Amount::zero(); - // Retrieve the notes that can be spent by this key - if let Some(avail_notes) = self.pos_map.get(vk) { - for note_idx in avail_notes { - // Spent notes cannot contribute a new transaction's pool - if self.spents.contains(note_idx) { - continue; - } - // Get note associated with this ID - let note = self.note_map.get(note_idx).unwrap(); - // Finally add value to multi-asset accumulator - val_acc += - Amount::from_nonnegative(note.asset_type, note.value) - .expect("found note with invalid value or asset type"); - } - } - Some(val_acc) - } - - /// Query the ledger for the decoding of the given asset type and cache it - /// if it is found. - pub async fn decode_asset_type( - &mut self, - asset_type: AssetType, - ) -> Option<(Address, Epoch)> { - // Try to find the decoding in the cache - if let decoded @ Some(_) = self.asset_types.get(&asset_type) { - return decoded.cloned(); - } - // Query for the ID of the last accepted transaction - let (addr, ep, _conv, _path): (Address, _, Amount, MerklePath) = - self.utils.query_conversion(asset_type).await?; - self.asset_types.insert(asset_type, (addr.clone(), ep)); - Some((addr, ep)) - } - - /// Query the ledger for the conversion that is allowed for the given asset - /// type and cache it. - async fn query_allowed_conversion<'a>( - &'a mut self, - asset_type: AssetType, - conversions: &'a mut Conversions, - ) -> Option<&'a mut (AllowedConversion, MerklePath, i64)> { - match conversions.entry(asset_type) { - Entry::Occupied(conv_entry) => Some(conv_entry.into_mut()), - Entry::Vacant(conv_entry) => { - // Query for the ID of the last accepted transaction - let (addr, ep, conv, path): (Address, _, _, _) = - self.utils.query_conversion(asset_type).await?; - self.asset_types.insert(asset_type, (addr, ep)); - // If the conversion is 0, then we just have a pure decoding - if conv == Amount::zero() { - None - } else { - Some(conv_entry.insert((Amount::into(conv), path, 0))) - } - } - } - } - - /// Compute the total unspent notes associated with the viewing key in the - /// context and express that value in terms of the currently timestamped - /// asset types. If the key is not in the context, then we do not know the - /// balance and hence we return None. - pub async fn compute_exchanged_balance( - &mut self, - vk: &ViewingKey, - target_epoch: Epoch, - ) -> Option { - // First get the unexchanged balance - if let Some(balance) = self.compute_shielded_balance(vk) { - // And then exchange balance into current asset types - Some( - self.compute_exchanged_amount( - balance, - target_epoch, - HashMap::new(), - ) - .await - .0, - ) - } else { - None - } - } - - /// Try to convert as much of the given asset type-value pair using the - /// given allowed conversion. usage is incremented by the amount of the - /// conversion used, the conversions are applied to the given input, and - /// the trace amount that could not be converted is moved from input to - /// output. - fn apply_conversion( - conv: AllowedConversion, - asset_type: AssetType, - value: i64, - usage: &mut i64, - input: &mut Amount, - output: &mut Amount, - ) { - // If conversion if possible, accumulate the exchanged amount - let conv: Amount = conv.into(); - // The amount required of current asset to qualify for conversion - let threshold = -conv[&asset_type]; - if threshold == 0 { - eprintln!( - "Asset threshold of selected conversion for asset type {} is \ - 0, this is a bug, please report it.", - asset_type - ); - } - // We should use an amount of the AllowedConversion that almost - // cancels the original amount - let required = value / threshold; - // Forget about the trace amount left over because we cannot - // realize its value - let trace = Amount::from_pair(asset_type, value % threshold).unwrap(); - // Record how much more of the given conversion has been used - *usage += required; - // Apply the conversions to input and move the trace amount to output - *input += conv * required - &trace; - *output += trace; - } - - /// Convert the given amount into the latest asset types whilst making a - /// note of the conversions that were used. Note that this function does - /// not assume that allowed conversions from the ledger are expressed in - /// terms of the latest asset types. - pub async fn compute_exchanged_amount( - &mut self, - mut input: Amount, - target_epoch: Epoch, - mut conversions: Conversions, - ) -> (Amount, Conversions) { - // Where we will store our exchanged value - let mut output = Amount::zero(); - // Repeatedly exchange assets until it is no longer possible - while let Some((asset_type, value)) = - input.components().next().map(cloned_pair) - { - let target_asset_type = self - .decode_asset_type(asset_type) - .await - .map(|(addr, _epoch)| make_asset_type(target_epoch, &addr)) - .unwrap_or(asset_type); - let at_target_asset_type = asset_type == target_asset_type; - if let (Some((conv, _wit, usage)), false) = ( - self.query_allowed_conversion( - asset_type, - &mut conversions, - ) - .await, - at_target_asset_type, - ) { - println!( - "converting current asset type to latest asset type..." - ); - // Not at the target asset type, not at the latest asset type. - // Apply conversion to get from current asset type to the latest - // asset type. - Self::apply_conversion( - conv.clone(), - asset_type, - value, - usage, - &mut input, - &mut output, - ); - } else if let (Some((conv, _wit, usage)), false) = ( - self.query_allowed_conversion( - target_asset_type, - &mut conversions, - ) - .await, - at_target_asset_type, - ) { - println!( - "converting latest asset type to target asset type..." - ); - // Not at the target asset type, yes at the latest asset type. - // Apply inverse conversion to get from latest asset type to - // the target asset type. - Self::apply_conversion( - conv.clone(), - asset_type, - value, - usage, - &mut input, - &mut output, - ); - } else { - // At the target asset type. Then move component over to output. - let comp = input.project(asset_type); - output += ∁ - // Strike from input to avoid repeating computation - input -= comp; - } - } - (output, conversions) - } - - /// Collect enough unspent notes in this context to exceed the given amount - /// of the specified asset type. Return the total value accumulated plus - /// notes and the corresponding diversifiers/merkle paths that were used to - /// achieve the total value. - pub async fn collect_unspent_notes( - &mut self, - vk: &ViewingKey, - target: Amount, - target_epoch: Epoch, - ) -> ( - Amount, - Vec<(Diversifier, Note, MerklePath)>, - Conversions, - ) { - // Establish connection with which to do exchange rate queries - let mut conversions = HashMap::new(); - let mut val_acc = Amount::zero(); - let mut notes = Vec::new(); - // Retrieve the notes that can be spent by this key - if let Some(avail_notes) = self.pos_map.get(vk).cloned() { - for note_idx in &avail_notes { - // No more transaction inputs are required once we have met - // the target amount - if val_acc >= target { - break; - } - // Spent notes cannot contribute a new transaction's pool - if self.spents.contains(note_idx) { - continue; - } - // Get note, merkle path, diversifier associated with this ID - let note = *self.note_map.get(note_idx).unwrap(); - - // The amount contributed by this note before conversion - let pre_contr = Amount::from_pair(note.asset_type, note.value) - .expect("received note has invalid value or asset type"); - let (contr, proposed_convs) = self - .compute_exchanged_amount( - pre_contr, - target_epoch, - conversions.clone(), - ) - .await; - - // Use this note only if it brings us closer to our target - if is_amount_required( - val_acc.clone(), - target.clone(), - contr.clone(), - ) { - // Be sure to record the conversions used in computing - // accumulated value - val_acc += contr; - // Commit the conversions that were used to exchange - conversions = proposed_convs; - let merkle_path = - self.witness_map.get(note_idx).unwrap().path().unwrap(); - let diversifier = self.div_map.get(note_idx).unwrap(); - // Commit this note to our transaction - notes.push((*diversifier, note, merkle_path)); - } - } - } - (val_acc, notes, conversions) - } - - /// Compute the combined value of the output notes of the transaction pinned - /// at the given payment address. This computation uses the supplied viewing - /// keys to try to decrypt the output notes. If no transaction is pinned at - /// the given payment address fails with - /// `PinnedBalanceError::NoTransactionPinned`. - pub async fn compute_pinned_balance( - utils: &U, - owner: PaymentAddress, - viewing_key: &ViewingKey, - ) -> Result<(Amount, Epoch), PinnedBalanceError> { - // Check that the supplied viewing key corresponds to given payment - // address - let counter_owner = viewing_key.to_payment_address( - *masp_primitives::primitives::PaymentAddress::diversifier( - &owner.into(), - ), - ); - match counter_owner { - Some(counter_owner) if counter_owner == owner.into() => {} - _ => return Err(PinnedBalanceError::InvalidViewingKey), - } - // The address of the MASP account - let masp_addr = masp(); - // Construct the key for where the transaction ID would be stored - let pin_key = Key::from(masp_addr.to_db_key()) - .push(&(PIN_KEY_PREFIX.to_owned() + &owner.hash())) - .expect("Cannot obtain a storage key"); - // Obtain the transaction pointer at the key - let txidx = utils.query_storage_value::(&pin_key) - .await - .ok_or(PinnedBalanceError::NoTransactionPinned)?; - // Construct the key for where the pinned transaction is stored - let tx_key = Key::from(masp_addr.to_db_key()) - .push(&(TX_KEY_PREFIX.to_owned() + &txidx.to_string())) - .expect("Cannot obtain a storage key"); - // Obtain the pointed to transaction - let (tx_epoch, _tx_height, _tx_index, tx) = - utils.query_storage_value::<(Epoch, BlockHeight, TxIndex, Transfer)>( - &tx_key, - ) - .await - .expect("Ill-formed epoch, transaction pair"); - // Accumulate the combined output note value into this Amount - let mut val_acc = Amount::zero(); - let tx = tx - .shielded - .expect("Pinned Transfers should have shielded part"); - for so in &tx.shielded_outputs { - // Let's try to see if our viewing key can decrypt current note - let decres = try_sapling_note_decryption::( - 0, - &viewing_key.ivk().0, - &so.ephemeral_key.into_subgroup().unwrap(), - &so.cmu, - &so.enc_ciphertext, - ); - match decres { - // So the given viewing key does decrypt this current note... - Some((note, pa, _memo)) if pa == owner.into() => { - val_acc += - Amount::from_nonnegative(note.asset_type, note.value) - .expect( - "found note with invalid value or asset type", - ); - break; - } - _ => {} - } - } - Ok((val_acc, tx_epoch)) - } - - /// Compute the combined value of the output notes of the pinned transaction - /// at the given payment address if there's any. The asset types may be from - /// the epoch of the transaction or even before, so exchange all these - /// amounts to the epoch of the transaction in order to get the value that - /// would have been displayed in the epoch of the transaction. - pub async fn compute_exchanged_pinned_balance( - &mut self, - owner: PaymentAddress, - viewing_key: &ViewingKey, - ) -> Result<(Amount, Epoch), PinnedBalanceError> { - // Obtain the balance that will be exchanged - let (amt, ep) = - Self::compute_pinned_balance(&self.utils, owner, viewing_key) - .await?; - // Finally, exchange the balance to the transaction's epoch - Ok(( - self.compute_exchanged_amount(amt, ep, HashMap::new()) - .await - .0, - ep, - )) - } - - /// Convert an amount whose units are AssetTypes to one whose units are - /// Addresses that they decode to. All asset types not corresponding to - /// the given epoch are ignored. - pub async fn decode_amount( - &mut self, - amt: Amount, - target_epoch: Epoch, - ) -> Amount
{ - let mut res = Amount::zero(); - for (asset_type, val) in amt.components() { - // Decode the asset type - let decoded = - self.decode_asset_type(*asset_type).await; - // Only assets with the target timestamp count - match decoded { - Some((addr, epoch)) if epoch == target_epoch => { - res += &Amount::from_pair(addr, *val).unwrap() - } - _ => {} - } - } - res - } - - /// Convert an amount whose units are AssetTypes to one whose units are - /// Addresses that they decode to. - pub async fn decode_all_amounts( - &mut self, - amt: Amount, - ) -> Amount<(Address, Epoch)> { - let mut res = Amount::zero(); - for (asset_type, val) in amt.components() { - // Decode the asset type - let decoded = - self.decode_asset_type(*asset_type).await; - // Only assets with the target timestamp count - if let Some((addr, epoch)) = decoded { - res += &Amount::from_pair((addr, epoch), *val).unwrap() - } - } - res - } - - /// Make shielded components to embed within a Transfer object. If no shielded - /// payment address nor spending key is specified, then no shielded components - /// are produced. Otherwise a transaction containing nullifiers and/or note - /// commitments are produced. Dummy transparent UTXOs are sometimes used to make - /// transactions balanced, but it is understood that transparent account changes - /// are effected only by the amounts and signatures specified by the containing - /// Transfer object. - async fn gen_shielded_transfer( - &mut self, - source: TransferSource, - target: TransferTarget, - args_amount: token::Amount, - token: Address, - fee_amount: token::Amount, - fee_token: Address, - shielded_gas: bool, - ) -> Result, builder::Error> { - // No shielded components are needed when neither source nor destination - // are shielded - let spending_key = source.spending_key(); - let payment_address = target.payment_address(); - if spending_key.is_none() && payment_address.is_none() { - return Ok(None); - } - // We want to fund our transaction solely from supplied spending key - let spending_key = spending_key.map(|x| x.into()); - let spending_keys: Vec<_> = spending_key.into_iter().collect(); - // Load the current shielded context given the spending key we possess - let _ = self.load(); - self.fetch(&spending_keys, &[]) - .await; - // Save the update state so that future fetches can be short-circuited - let _ = self.save(); - // Determine epoch in which to submit potential shielded transaction - let epoch = self.utils.query_epoch().await; - // Context required for storing which notes are in the source's possesion - let consensus_branch_id = BranchId::Sapling; - let amt: u64 = args_amount.into(); - let memo: Option = None; - - // Now we build up the transaction within this object - let mut builder = Builder::::new(0u32); - // Convert transaction amount into MASP types - let (asset_type, amount) = convert_amount(epoch, &token, args_amount); - - // Transactions with transparent input and shielded output - // may be affected if constructed close to epoch boundary - let mut epoch_sensitive: bool = false; - // If there are shielded inputs - if let Some(sk) = spending_key { - // Transaction fees need to match the amount in the wrapper Transfer - // when MASP source is used - let (_, fee) = - convert_amount(epoch, &fee_token, fee_amount); - builder.set_fee(fee.clone())?; - // If the gas is coming from the shielded pool, then our shielded inputs - // must also cover the gas fee - let required_amt = if shielded_gas { amount + fee } else { amount }; - // Locate unspent notes that can help us meet the transaction amount - let (_, unspent_notes, used_convs) = self - .collect_unspent_notes( - &to_viewing_key(&sk).vk, - required_amt, - epoch, - ) - .await; - // Commit the notes found to our transaction - for (diversifier, note, merkle_path) in unspent_notes { - builder.add_sapling_spend(sk, diversifier, note, merkle_path)?; - } - // Commit the conversion notes used during summation - for (conv, wit, value) in used_convs.values() { - if *value > 0 { - builder.add_convert( - conv.clone(), - *value as u64, - wit.clone(), - )?; - } - } - } else { - // No transfer fees come from the shielded transaction for non-MASP - // sources - builder.set_fee(Amount::zero())?; - // We add a dummy UTXO to our transaction, but only the source of the - // parent Transfer object is used to validate fund availability - let secp_sk = - secp256k1::SecretKey::from_slice(&[0xcd; 32]).expect("secret key"); - let secp_ctx = secp256k1::Secp256k1::::gen_new(); - let secp_pk = - secp256k1::PublicKey::from_secret_key(&secp_ctx, &secp_sk) - .serialize(); - let hash = - ripemd160::Ripemd160::digest(&sha2::Sha256::digest(&secp_pk)); - let script = TransparentAddress::PublicKey(hash.into()).script(); - epoch_sensitive = true; - builder.add_transparent_input( - secp_sk, - OutPoint::new([0u8; 32], 0), - TxOut { - asset_type, - value: amt, - script_pubkey: script, - }, - )?; - } - // Now handle the outputs of this transaction - // If there is a shielded output - if let Some(pa) = payment_address { - let ovk_opt = spending_key.map(|x| x.expsk.ovk); - builder.add_sapling_output( - ovk_opt, - pa.into(), - asset_type, - amt, - memo.clone(), - )?; - } else { - epoch_sensitive = false; - // Embed the transparent target address into the shielded transaction so - // that it can be signed - let target_enc = target - .address() - .expect("target address should be transparent") - .try_to_vec() - .expect("target address encoding"); - let hash = ripemd160::Ripemd160::digest(&sha2::Sha256::digest( - target_enc.as_ref(), - )); - builder.add_transparent_output( - &TransparentAddress::PublicKey(hash.into()), - asset_type, - amt, - )?; - } - let prover = self.utils.local_tx_prover(); - // Build and return the constructed transaction - let mut tx = builder.build(consensus_branch_id, &prover); - - if epoch_sensitive { - let new_epoch = self.utils.query_epoch().await; - - // If epoch has changed, recalculate shielded outputs to match new epoch - if new_epoch != epoch { - // Hack: build new shielded transfer with updated outputs - let mut replay_builder = Builder::::new(0u32); - replay_builder.set_fee(Amount::zero())?; - let ovk_opt = spending_key.map(|x| x.expsk.ovk); - let (new_asset_type, _) = - convert_amount(new_epoch, &token, args_amount); - replay_builder.add_sapling_output( - ovk_opt, - payment_address.unwrap().into(), - new_asset_type, - amt, - memo, - )?; - - let secp_sk = secp256k1::SecretKey::from_slice(&[0xcd; 32]) - .expect("secret key"); - let secp_ctx = - secp256k1::Secp256k1::::gen_new(); - let secp_pk = - secp256k1::PublicKey::from_secret_key(&secp_ctx, &secp_sk) - .serialize(); - let hash = - ripemd160::Ripemd160::digest(&sha2::Sha256::digest(&secp_pk)); - let script = TransparentAddress::PublicKey(hash.into()).script(); - replay_builder.add_transparent_input( - secp_sk, - OutPoint::new([0u8; 32], 0), - TxOut { - asset_type: new_asset_type, - value: amt, - script_pubkey: script, - }, - )?; - - let (replay_tx, _) = - replay_builder.build(consensus_branch_id, &prover)?; - tx = tx.map(|(t, tm)| { - let mut temp = t.deref().clone(); - temp.shielded_outputs = replay_tx.shielded_outputs.clone(); - temp.value_balance = temp.value_balance.reject(asset_type) - - Amount::from_pair(new_asset_type, amt).unwrap(); - (temp.freeze().unwrap(), tm) - }); - } - } - - tx.map(Some) - } -} - -/// Make asset type corresponding to given address and epoch -fn make_asset_type(epoch: Epoch, token: &Address) -> AssetType { - // Typestamp the chosen token with the current epoch - let token_bytes = (token, epoch.0) - .try_to_vec() - .expect("token should serialize"); - // Generate the unique asset identifier from the unique token address - AssetType::new(token_bytes.as_ref()).expect("unable to create asset type") -} -/// Convert Anoma amount and token type to MASP equivalents -fn convert_amount( - epoch: Epoch, - token: &Address, - val: token::Amount, -) -> (AssetType, Amount) { - let asset_type = make_asset_type(epoch, token); - // Combine the value and unit into one amount - let amount = Amount::from_nonnegative(asset_type, u64::from(val)) - .expect("invalid value for amount"); - (asset_type, amount) -} pub async fn submit_transfer(mut ctx: Context, args: args::TxTransfer) { let transfer_source = ctx.get_cached(&args.source); diff --git a/apps/src/lib/client/types.rs b/apps/src/lib/client/types.rs index 7246e436b8..10ae25182a 100644 --- a/apps/src/lib/client/types.rs +++ b/apps/src/lib/client/types.rs @@ -11,5 +11,4 @@ use namada::types::{key, token}; use super::rpc; use crate::cli::{args, Context}; -use crate::client::tx::Conversions; use crate::facade::tendermint_config::net::Address as TendermintAddress; diff --git a/shared/Cargo.toml b/shared/Cargo.toml index 47bac417e3..3112c314e6 100644 --- a/shared/Cargo.toml +++ b/shared/Cargo.toml @@ -66,6 +66,11 @@ ibc-mocks-abcipp = [ "namada_core/ibc-mocks-abcipp", ] +masp-tx-gen = [ + "rand", + "rand_core", +] + # for integration tests and test utilies testing = [ "namada_core/testing", @@ -124,6 +129,8 @@ wasmparser = "0.83.0" #libmasp = { git = "https://github.com/anoma/masp", branch = "murisi/masp-incentive" } masp_primitives = { git = "https://github.com/anoma/masp", rev = "bee40fc465f6afbd10558d12fe96eb1742eee45c" } masp_proofs = { git = "https://github.com/anoma/masp", rev = "bee40fc465f6afbd10558d12fe96eb1742eee45c" } +rand = {version = "0.8", default-features = false, optional = true} +rand_core = {version = "0.6", default-features = false, optional = true} zeroize = "1.5.5" [dev-dependencies] diff --git a/shared/src/ledger/masp.rs b/shared/src/ledger/masp.rs index 03c289eefd..2c470323ef 100644 --- a/shared/src/ledger/masp.rs +++ b/shared/src/ledger/masp.rs @@ -18,6 +18,52 @@ use masp_primitives::transaction::{ }; use masp_proofs::sapling::SaplingVerificationContext; +use std::collections::hash_map::Entry; +use std::collections::{BTreeMap, HashMap, HashSet}; +use std::fmt::Debug; +#[cfg(feature = "masp-tx-gen")] +use std::io::Read; + +//use async_std::io::prelude::WriteExt; +//use async_std::io::{self}; +use borsh::{BorshDeserialize, BorshSerialize}; +use masp_primitives::consensus::{BranchId, TestNetwork}; +use masp_primitives::convert::AllowedConversion; +use masp_primitives::ff::PrimeField; +use masp_primitives::group::cofactor::CofactorGroup; +use masp_primitives::keys::FullViewingKey; +#[cfg(feature = "masp-tx-gen")] +use masp_primitives::legacy::TransparentAddress; +use masp_primitives::merkle_tree::{ + CommitmentTree, IncrementalWitness, MerklePath, +}; +use masp_primitives::note_encryption::*; +use masp_primitives::primitives::{Diversifier, Note, ViewingKey}; +use masp_primitives::sapling::Node; +#[cfg(feature = "masp-tx-gen")] +use masp_primitives::transaction::builder::{self, secp256k1, *}; +use masp_primitives::transaction::components::Amount; +#[cfg(feature = "masp-tx-gen")] +use masp_primitives::transaction::components::{OutPoint, TxOut}; +use masp_primitives::zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}; +use masp_proofs::prover::LocalTxProver; +use crate::types::address::{masp, Address}; +use crate::types::masp::PaymentAddress; +#[cfg(feature = "masp-tx-gen")] +use crate::types::masp::{TransferSource, TransferTarget}; +use crate::types::storage::{ + BlockHeight, Epoch, Key, KeySeg, TxIndex, +}; +use crate::types::token::{ + Transfer, HEAD_TX_KEY, PIN_KEY_PREFIX, TX_KEY_PREFIX, +}; +use crate::types::{storage, token}; +#[cfg(feature = "masp-tx-gen")] +use rand_core::{CryptoRng, OsRng, RngCore}; +#[cfg(feature = "masp-tx-gen")] +use sha2::Digest; +use async_trait::async_trait; + /// Env var to point to a dir with MASP parameters. When not specified, /// the default OS specific path is used. pub const ENV_VAR_MASP_PARAMS_DIR: &str = "ANOMA_MASP_PARAMS_DIR"; @@ -200,3 +246,1044 @@ pub fn get_params_dir() -> PathBuf { masp_proofs::default_params_folder().unwrap() } } + +#[async_trait] +pub trait ShieldedUtils : Sized + BorshDeserialize + BorshSerialize + Default + Clone { + async fn query_storage_value( + &self, + key: &storage::Key, + ) -> Option + where + T: BorshDeserialize; + + async fn query_epoch(&self) -> Epoch; + + fn local_tx_prover(&self) -> LocalTxProver; + + fn load(self) -> std::io::Result>; + + fn save(&self, ctx: &ShieldedContext) -> std::io::Result<()>; + + async fn query_conversion( + &self, + asset_type: AssetType, + ) -> Option<( + Address, + Epoch, + masp_primitives::transaction::components::Amount, + MerklePath, + )>; +} + +/// Make a ViewingKey that can view notes encrypted by given ExtendedSpendingKey +pub fn to_viewing_key(esk: &ExtendedSpendingKey) -> FullViewingKey { + ExtendedFullViewingKey::from(esk).fvk +} + +/// Generate a valid diversifier, i.e. one that has a diversified base. Return +/// also this diversified base. +#[cfg(feature = "masp-tx-gen")] +pub fn find_valid_diversifier( + rng: &mut R, +) -> (Diversifier, masp_primitives::jubjub::SubgroupPoint) { + let mut diversifier; + let g_d; + // Keep generating random diversifiers until one has a diversified base + loop { + let mut d = [0; 11]; + rng.fill_bytes(&mut d); + diversifier = Diversifier(d); + if let Some(val) = diversifier.g_d() { + g_d = val; + break; + } + } + (diversifier, g_d) +} + +/// Determine if using the current note would actually bring us closer to our +/// target +pub fn is_amount_required(src: Amount, dest: Amount, delta: Amount) -> bool { + if delta > Amount::zero() { + let gap = dest - src; + for (asset_type, value) in gap.components() { + if *value > 0 && delta[asset_type] > 0 { + return true; + } + } + } + false +} + +/// An extension of Option's cloned method for pair types +fn cloned_pair((a, b): (&T, &U)) -> (T, U) { + (a.clone(), b.clone()) +} + +/// Errors that can occur when trying to retrieve pinned transaction +#[derive(PartialEq, Eq)] +pub enum PinnedBalanceError { + /// No transaction has yet been pinned to the given payment address + NoTransactionPinned, + /// The supplied viewing key does not recognize payments to given address + InvalidViewingKey, +} + +/// Represents the amount used of different conversions +pub type Conversions = + HashMap, i64)>; + +/// Represents the changes that were made to a list of transparent accounts +pub type TransferDelta = HashMap>; + +/// Represents the changes that were made to a list of shielded accounts +pub type TransactionDelta = HashMap; + +/// Represents the current state of the shielded pool from the perspective of +/// the chosen viewing keys. +#[derive(BorshSerialize, BorshDeserialize, Debug)] +pub struct ShieldedContext { + /// Location where this shielded context is saved + #[borsh_skip] + pub utils: U, + /// The last transaction index to be processed in this context + pub last_txidx: u64, + /// The commitment tree produced by scanning all transactions up to tx_pos + pub tree: CommitmentTree, + /// Maps viewing keys to applicable note positions + pub pos_map: HashMap>, + /// Maps a nullifier to the note position to which it applies + pub nf_map: HashMap<[u8; 32], usize>, + /// Maps note positions to their corresponding notes + pub note_map: HashMap, + /// Maps note positions to their corresponding memos + pub memo_map: HashMap, + /// Maps note positions to the diversifier of their payment address + pub div_map: HashMap, + /// Maps note positions to their witness (used to make merkle paths) + pub witness_map: HashMap>, + /// Tracks what each transaction does to various account balances + pub delta_map: BTreeMap< + (BlockHeight, TxIndex), + (Epoch, TransferDelta, TransactionDelta), + >, + /// The set of note positions that have been spent + pub spents: HashSet, + /// Maps asset types to their decodings + pub asset_types: HashMap, + /// Maps note positions to their corresponding viewing keys + pub vk_map: HashMap, +} + +/// Default implementation to ease construction of TxContexts. Derive cannot be +/// used here due to CommitmentTree not implementing Default. +impl Default for ShieldedContext { + fn default() -> ShieldedContext { + ShieldedContext:: { + utils: U::default(), + last_txidx: u64::default(), + tree: CommitmentTree::empty(), + pos_map: HashMap::default(), + nf_map: HashMap::default(), + note_map: HashMap::default(), + memo_map: HashMap::default(), + div_map: HashMap::default(), + witness_map: HashMap::default(), + spents: HashSet::default(), + delta_map: BTreeMap::default(), + asset_types: HashMap::default(), + vk_map: HashMap::default(), + } + } +} + +impl ShieldedContext { + /// Try to load the last saved shielded context from the given context + /// directory. If this fails, then leave the current context unchanged. + pub fn load(&mut self) -> std::io::Result<()> { + let new_ctx = self.utils.clone().load()?; + *self = new_ctx; + Ok(()) + } + + /// Save this shielded context into its associated context directory + pub fn save(&self) -> std::io::Result<()> { + self.utils.save(self) + } + + /// Merge data from the given shielded context into the current shielded + /// context. It must be the case that the two shielded contexts share the + /// same last transaction ID and share identical commitment trees. + pub fn merge(&mut self, new_ctx: ShieldedContext) { + debug_assert_eq!(self.last_txidx, new_ctx.last_txidx); + // Merge by simply extending maps. Identical keys should contain + // identical values, so overwriting should not be problematic. + self.pos_map.extend(new_ctx.pos_map); + self.nf_map.extend(new_ctx.nf_map); + self.note_map.extend(new_ctx.note_map); + self.memo_map.extend(new_ctx.memo_map); + self.div_map.extend(new_ctx.div_map); + self.witness_map.extend(new_ctx.witness_map); + self.spents.extend(new_ctx.spents); + self.asset_types.extend(new_ctx.asset_types); + self.vk_map.extend(new_ctx.vk_map); + // The deltas are the exception because different keys can reveal + // different parts of the same transaction. Hence each delta needs to be + // merged separately. + for ((height, idx), (ep, ntfer_delta, ntx_delta)) in new_ctx.delta_map { + let (_ep, tfer_delta, tx_delta) = self + .delta_map + .entry((height, idx)) + .or_insert((ep, TransferDelta::new(), TransactionDelta::new())); + tfer_delta.extend(ntfer_delta); + tx_delta.extend(ntx_delta); + } + } + + /// Fetch the current state of the multi-asset shielded pool into a + /// ShieldedContext + pub async fn fetch( + &mut self, + sks: &[ExtendedSpendingKey], + fvks: &[ViewingKey], + ) { + // First determine which of the keys requested to be fetched are new. + // Necessary because old transactions will need to be scanned for new + // keys. + let mut unknown_keys = Vec::new(); + for esk in sks { + let vk = to_viewing_key(esk).vk; + if !self.pos_map.contains_key(&vk) { + unknown_keys.push(vk); + } + } + for vk in fvks { + if !self.pos_map.contains_key(vk) { + unknown_keys.push(*vk); + } + } + + // If unknown keys are being used, we need to scan older transactions + // for any unspent notes + let (txs, mut tx_iter); + if !unknown_keys.is_empty() { + // Load all transactions accepted until this point + txs = Self::fetch_shielded_transfers(&self.utils, 0).await; + tx_iter = txs.iter(); + // Do this by constructing a shielding context only for unknown keys + let mut tx_ctx = Self { utils: self.utils.clone(), ..Default::default() }; + for vk in unknown_keys { + tx_ctx.pos_map.entry(vk).or_insert_with(HashSet::new); + } + // Update this unknown shielded context until it is level with self + while tx_ctx.last_txidx != self.last_txidx { + if let Some(((height, idx), (epoch, tx))) = tx_iter.next() { + tx_ctx.scan_tx(*height, *idx, *epoch, tx); + } else { + break; + } + } + // Merge the context data originating from the unknown keys into the + // current context + self.merge(tx_ctx); + } else { + // Load only transactions accepted from last_txid until this point + txs = + Self::fetch_shielded_transfers(&self.utils, self.last_txidx) + .await; + tx_iter = txs.iter(); + } + // Now that we possess the unspent notes corresponding to both old and + // new keys up until tx_pos, proceed to scan the new transactions. + for ((height, idx), (epoch, tx)) in &mut tx_iter { + self.scan_tx(*height, *idx, *epoch, tx); + } + } + + /// Obtain a chronologically-ordered list of all accepted shielded + /// transactions from the ledger. The ledger conceptually stores + /// transactions as a vector. More concretely, the HEAD_TX_KEY location + /// stores the index of the last accepted transaction and each transaction + /// is stored at a key derived from its index. + pub async fn fetch_shielded_transfers( + utils: &U, + last_txidx: u64, + ) -> BTreeMap<(BlockHeight, TxIndex), (Epoch, Transfer)> { + // The address of the MASP account + let masp_addr = masp(); + // Construct the key where last transaction pointer is stored + let head_tx_key = Key::from(masp_addr.to_db_key()) + .push(&HEAD_TX_KEY.to_owned()) + .expect("Cannot obtain a storage key"); + // Query for the index of the last accepted transaction + let head_txidx = utils.query_storage_value::(&head_tx_key) + .await + .unwrap_or(0); + let mut shielded_txs = BTreeMap::new(); + // Fetch all the transactions we do not have yet + for i in last_txidx..head_txidx { + // Construct the key for where the current transaction is stored + let current_tx_key = Key::from(masp_addr.to_db_key()) + .push(&(TX_KEY_PREFIX.to_owned() + &i.to_string())) + .expect("Cannot obtain a storage key"); + // Obtain the current transaction + let (tx_epoch, tx_height, tx_index, current_tx) = + utils.query_storage_value::<(Epoch, BlockHeight, TxIndex, Transfer)>( + ¤t_tx_key, + ) + .await + .unwrap(); + // Collect the current transaction + shielded_txs.insert((tx_height, tx_index), (tx_epoch, current_tx)); + } + shielded_txs + } + + /// Applies the given transaction to the supplied context. More precisely, + /// the shielded transaction's outputs are added to the commitment tree. + /// Newly discovered notes are associated to the supplied viewing keys. Note + /// nullifiers are mapped to their originating notes. Note positions are + /// associated to notes, memos, and diversifiers. And the set of notes that + /// we have spent are updated. The witness map is maintained to make it + /// easier to construct note merkle paths in other code. See + /// https://zips.z.cash/protocol/protocol.pdf#scan + pub fn scan_tx( + &mut self, + height: BlockHeight, + index: TxIndex, + epoch: Epoch, + tx: &Transfer, + ) { + // Ignore purely transparent transactions + let shielded = if let Some(shielded) = &tx.shielded { + shielded + } else { + return; + }; + // For tracking the account changes caused by this Transaction + let mut transaction_delta = TransactionDelta::new(); + // Listen for notes sent to our viewing keys + for so in &shielded.shielded_outputs { + // Create merkle tree leaf node from note commitment + let node = Node::new(so.cmu.to_repr()); + // Update each merkle tree in the witness map with the latest + // addition + for (_, witness) in self.witness_map.iter_mut() { + witness.append(node).expect("note commitment tree is full"); + } + let note_pos = self.tree.size(); + self.tree + .append(node) + .expect("note commitment tree is full"); + // Finally, make it easier to construct merkle paths to this new + // note + let witness = IncrementalWitness::::from_tree(&self.tree); + self.witness_map.insert(note_pos, witness); + // Let's try to see if any of our viewing keys can decrypt latest + // note + for (vk, notes) in self.pos_map.iter_mut() { + let decres = try_sapling_note_decryption::( + 0, + &vk.ivk().0, + &so.ephemeral_key.into_subgroup().unwrap(), + &so.cmu, + &so.enc_ciphertext, + ); + // So this current viewing key does decrypt this current note... + if let Some((note, pa, memo)) = decres { + // Add this note to list of notes decrypted by this viewing + // key + notes.insert(note_pos); + // Compute the nullifier now to quickly recognize when spent + let nf = note.nf(vk, note_pos.try_into().unwrap()); + self.note_map.insert(note_pos, note); + self.memo_map.insert(note_pos, memo); + // The payment address' diversifier is required to spend + // note + self.div_map.insert(note_pos, *pa.diversifier()); + self.nf_map.insert(nf.0, note_pos); + // Note the account changes + let balance = transaction_delta + .entry(*vk) + .or_insert_with(Amount::zero); + *balance += + Amount::from_nonnegative(note.asset_type, note.value) + .expect( + "found note with invalid value or asset type", + ); + self.vk_map.insert(note_pos, *vk); + break; + } + } + } + // Cancel out those of our notes that have been spent + for ss in &shielded.shielded_spends { + // If the shielded spend's nullifier is in our map, then target note + // is rendered unusable + if let Some(note_pos) = self.nf_map.get(&ss.nullifier) { + self.spents.insert(*note_pos); + // Note the account changes + let balance = transaction_delta + .entry(self.vk_map[note_pos]) + .or_insert_with(Amount::zero); + let note = self.note_map[note_pos]; + *balance -= + Amount::from_nonnegative(note.asset_type, note.value) + .expect("found note with invalid value or asset type"); + } + } + // Record the changes to the transparent accounts + let transparent_delta = + Amount::from_nonnegative(tx.token.clone(), u64::from(tx.amount)) + .expect("invalid value for amount"); + let mut transfer_delta = TransferDelta::new(); + transfer_delta + .insert(tx.source.clone(), Amount::zero() - &transparent_delta); + transfer_delta.insert(tx.target.clone(), transparent_delta); + self.delta_map.insert( + (height, index), + (epoch, transfer_delta, transaction_delta), + ); + self.last_txidx += 1; + } + + /// Summarize the effects on shielded and transparent accounts of each + /// Transfer in this context + pub fn get_tx_deltas( + &self, + ) -> &BTreeMap< + (BlockHeight, TxIndex), + (Epoch, TransferDelta, TransactionDelta), + > { + &self.delta_map + } + + /// Compute the total unspent notes associated with the viewing key in the + /// context. If the key is not in the context, then we do not know the + /// balance and hence we return None. + pub fn compute_shielded_balance(&self, vk: &ViewingKey) -> Option { + // Cannot query the balance of a key that's not in the map + if !self.pos_map.contains_key(vk) { + return None; + } + let mut val_acc = Amount::zero(); + // Retrieve the notes that can be spent by this key + if let Some(avail_notes) = self.pos_map.get(vk) { + for note_idx in avail_notes { + // Spent notes cannot contribute a new transaction's pool + if self.spents.contains(note_idx) { + continue; + } + // Get note associated with this ID + let note = self.note_map.get(note_idx).unwrap(); + // Finally add value to multi-asset accumulator + val_acc += + Amount::from_nonnegative(note.asset_type, note.value) + .expect("found note with invalid value or asset type"); + } + } + Some(val_acc) + } + + /// Query the ledger for the decoding of the given asset type and cache it + /// if it is found. + pub async fn decode_asset_type( + &mut self, + asset_type: AssetType, + ) -> Option<(Address, Epoch)> { + // Try to find the decoding in the cache + if let decoded @ Some(_) = self.asset_types.get(&asset_type) { + return decoded.cloned(); + } + // Query for the ID of the last accepted transaction + let (addr, ep, _conv, _path): (Address, _, Amount, MerklePath) = + self.utils.query_conversion(asset_type).await?; + self.asset_types.insert(asset_type, (addr.clone(), ep)); + Some((addr, ep)) + } + + /// Query the ledger for the conversion that is allowed for the given asset + /// type and cache it. + async fn query_allowed_conversion<'a>( + &'a mut self, + asset_type: AssetType, + conversions: &'a mut Conversions, + ) -> Option<&'a mut (AllowedConversion, MerklePath, i64)> { + match conversions.entry(asset_type) { + Entry::Occupied(conv_entry) => Some(conv_entry.into_mut()), + Entry::Vacant(conv_entry) => { + // Query for the ID of the last accepted transaction + let (addr, ep, conv, path): (Address, _, _, _) = + self.utils.query_conversion(asset_type).await?; + self.asset_types.insert(asset_type, (addr, ep)); + // If the conversion is 0, then we just have a pure decoding + if conv == Amount::zero() { + None + } else { + Some(conv_entry.insert((Amount::into(conv), path, 0))) + } + } + } + } + + /// Compute the total unspent notes associated with the viewing key in the + /// context and express that value in terms of the currently timestamped + /// asset types. If the key is not in the context, then we do not know the + /// balance and hence we return None. + pub async fn compute_exchanged_balance( + &mut self, + vk: &ViewingKey, + target_epoch: Epoch, + ) -> Option { + // First get the unexchanged balance + if let Some(balance) = self.compute_shielded_balance(vk) { + // And then exchange balance into current asset types + Some( + self.compute_exchanged_amount( + balance, + target_epoch, + HashMap::new(), + ) + .await + .0, + ) + } else { + None + } + } + + /// Try to convert as much of the given asset type-value pair using the + /// given allowed conversion. usage is incremented by the amount of the + /// conversion used, the conversions are applied to the given input, and + /// the trace amount that could not be converted is moved from input to + /// output. + fn apply_conversion( + conv: AllowedConversion, + asset_type: AssetType, + value: i64, + usage: &mut i64, + input: &mut Amount, + output: &mut Amount, + ) { + // If conversion if possible, accumulate the exchanged amount + let conv: Amount = conv.into(); + // The amount required of current asset to qualify for conversion + let threshold = -conv[&asset_type]; + if threshold == 0 { + eprintln!( + "Asset threshold of selected conversion for asset type {} is \ + 0, this is a bug, please report it.", + asset_type + ); + } + // We should use an amount of the AllowedConversion that almost + // cancels the original amount + let required = value / threshold; + // Forget about the trace amount left over because we cannot + // realize its value + let trace = Amount::from_pair(asset_type, value % threshold).unwrap(); + // Record how much more of the given conversion has been used + *usage += required; + // Apply the conversions to input and move the trace amount to output + *input += conv * required - &trace; + *output += trace; + } + + /// Convert the given amount into the latest asset types whilst making a + /// note of the conversions that were used. Note that this function does + /// not assume that allowed conversions from the ledger are expressed in + /// terms of the latest asset types. + pub async fn compute_exchanged_amount( + &mut self, + mut input: Amount, + target_epoch: Epoch, + mut conversions: Conversions, + ) -> (Amount, Conversions) { + // Where we will store our exchanged value + let mut output = Amount::zero(); + // Repeatedly exchange assets until it is no longer possible + while let Some((asset_type, value)) = + input.components().next().map(cloned_pair) + { + let target_asset_type = self + .decode_asset_type(asset_type) + .await + .map(|(addr, _epoch)| make_asset_type(target_epoch, &addr)) + .unwrap_or(asset_type); + let at_target_asset_type = asset_type == target_asset_type; + if let (Some((conv, _wit, usage)), false) = ( + self.query_allowed_conversion( + asset_type, + &mut conversions, + ) + .await, + at_target_asset_type, + ) { + println!( + "converting current asset type to latest asset type..." + ); + // Not at the target asset type, not at the latest asset type. + // Apply conversion to get from current asset type to the latest + // asset type. + Self::apply_conversion( + conv.clone(), + asset_type, + value, + usage, + &mut input, + &mut output, + ); + } else if let (Some((conv, _wit, usage)), false) = ( + self.query_allowed_conversion( + target_asset_type, + &mut conversions, + ) + .await, + at_target_asset_type, + ) { + println!( + "converting latest asset type to target asset type..." + ); + // Not at the target asset type, yes at the latest asset type. + // Apply inverse conversion to get from latest asset type to + // the target asset type. + Self::apply_conversion( + conv.clone(), + asset_type, + value, + usage, + &mut input, + &mut output, + ); + } else { + // At the target asset type. Then move component over to output. + let comp = input.project(asset_type); + output += ∁ + // Strike from input to avoid repeating computation + input -= comp; + } + } + (output, conversions) + } + + /// Collect enough unspent notes in this context to exceed the given amount + /// of the specified asset type. Return the total value accumulated plus + /// notes and the corresponding diversifiers/merkle paths that were used to + /// achieve the total value. + pub async fn collect_unspent_notes( + &mut self, + vk: &ViewingKey, + target: Amount, + target_epoch: Epoch, + ) -> ( + Amount, + Vec<(Diversifier, Note, MerklePath)>, + Conversions, + ) { + // Establish connection with which to do exchange rate queries + let mut conversions = HashMap::new(); + let mut val_acc = Amount::zero(); + let mut notes = Vec::new(); + // Retrieve the notes that can be spent by this key + if let Some(avail_notes) = self.pos_map.get(vk).cloned() { + for note_idx in &avail_notes { + // No more transaction inputs are required once we have met + // the target amount + if val_acc >= target { + break; + } + // Spent notes cannot contribute a new transaction's pool + if self.spents.contains(note_idx) { + continue; + } + // Get note, merkle path, diversifier associated with this ID + let note = *self.note_map.get(note_idx).unwrap(); + + // The amount contributed by this note before conversion + let pre_contr = Amount::from_pair(note.asset_type, note.value) + .expect("received note has invalid value or asset type"); + let (contr, proposed_convs) = self + .compute_exchanged_amount( + pre_contr, + target_epoch, + conversions.clone(), + ) + .await; + + // Use this note only if it brings us closer to our target + if is_amount_required( + val_acc.clone(), + target.clone(), + contr.clone(), + ) { + // Be sure to record the conversions used in computing + // accumulated value + val_acc += contr; + // Commit the conversions that were used to exchange + conversions = proposed_convs; + let merkle_path = + self.witness_map.get(note_idx).unwrap().path().unwrap(); + let diversifier = self.div_map.get(note_idx).unwrap(); + // Commit this note to our transaction + notes.push((*diversifier, note, merkle_path)); + } + } + } + (val_acc, notes, conversions) + } + + /// Compute the combined value of the output notes of the transaction pinned + /// at the given payment address. This computation uses the supplied viewing + /// keys to try to decrypt the output notes. If no transaction is pinned at + /// the given payment address fails with + /// `PinnedBalanceError::NoTransactionPinned`. + pub async fn compute_pinned_balance( + utils: &U, + owner: PaymentAddress, + viewing_key: &ViewingKey, + ) -> Result<(Amount, Epoch), PinnedBalanceError> { + // Check that the supplied viewing key corresponds to given payment + // address + let counter_owner = viewing_key.to_payment_address( + *masp_primitives::primitives::PaymentAddress::diversifier( + &owner.into(), + ), + ); + match counter_owner { + Some(counter_owner) if counter_owner == owner.into() => {} + _ => return Err(PinnedBalanceError::InvalidViewingKey), + } + // The address of the MASP account + let masp_addr = masp(); + // Construct the key for where the transaction ID would be stored + let pin_key = Key::from(masp_addr.to_db_key()) + .push(&(PIN_KEY_PREFIX.to_owned() + &owner.hash())) + .expect("Cannot obtain a storage key"); + // Obtain the transaction pointer at the key + let txidx = utils.query_storage_value::(&pin_key) + .await + .ok_or(PinnedBalanceError::NoTransactionPinned)?; + // Construct the key for where the pinned transaction is stored + let tx_key = Key::from(masp_addr.to_db_key()) + .push(&(TX_KEY_PREFIX.to_owned() + &txidx.to_string())) + .expect("Cannot obtain a storage key"); + // Obtain the pointed to transaction + let (tx_epoch, _tx_height, _tx_index, tx) = + utils.query_storage_value::<(Epoch, BlockHeight, TxIndex, Transfer)>( + &tx_key, + ) + .await + .expect("Ill-formed epoch, transaction pair"); + // Accumulate the combined output note value into this Amount + let mut val_acc = Amount::zero(); + let tx = tx + .shielded + .expect("Pinned Transfers should have shielded part"); + for so in &tx.shielded_outputs { + // Let's try to see if our viewing key can decrypt current note + let decres = try_sapling_note_decryption::( + 0, + &viewing_key.ivk().0, + &so.ephemeral_key.into_subgroup().unwrap(), + &so.cmu, + &so.enc_ciphertext, + ); + match decres { + // So the given viewing key does decrypt this current note... + Some((note, pa, _memo)) if pa == owner.into() => { + val_acc += + Amount::from_nonnegative(note.asset_type, note.value) + .expect( + "found note with invalid value or asset type", + ); + break; + } + _ => {} + } + } + Ok((val_acc, tx_epoch)) + } + + /// Compute the combined value of the output notes of the pinned transaction + /// at the given payment address if there's any. The asset types may be from + /// the epoch of the transaction or even before, so exchange all these + /// amounts to the epoch of the transaction in order to get the value that + /// would have been displayed in the epoch of the transaction. + pub async fn compute_exchanged_pinned_balance( + &mut self, + owner: PaymentAddress, + viewing_key: &ViewingKey, + ) -> Result<(Amount, Epoch), PinnedBalanceError> { + // Obtain the balance that will be exchanged + let (amt, ep) = + Self::compute_pinned_balance(&self.utils, owner, viewing_key) + .await?; + // Finally, exchange the balance to the transaction's epoch + Ok(( + self.compute_exchanged_amount(amt, ep, HashMap::new()) + .await + .0, + ep, + )) + } + + /// Convert an amount whose units are AssetTypes to one whose units are + /// Addresses that they decode to. All asset types not corresponding to + /// the given epoch are ignored. + pub async fn decode_amount( + &mut self, + amt: Amount, + target_epoch: Epoch, + ) -> Amount
{ + let mut res = Amount::zero(); + for (asset_type, val) in amt.components() { + // Decode the asset type + let decoded = + self.decode_asset_type(*asset_type).await; + // Only assets with the target timestamp count + match decoded { + Some((addr, epoch)) if epoch == target_epoch => { + res += &Amount::from_pair(addr, *val).unwrap() + } + _ => {} + } + } + res + } + + /// Convert an amount whose units are AssetTypes to one whose units are + /// Addresses that they decode to. + pub async fn decode_all_amounts( + &mut self, + amt: Amount, + ) -> Amount<(Address, Epoch)> { + let mut res = Amount::zero(); + for (asset_type, val) in amt.components() { + // Decode the asset type + let decoded = + self.decode_asset_type(*asset_type).await; + // Only assets with the target timestamp count + if let Some((addr, epoch)) = decoded { + res += &Amount::from_pair((addr, epoch), *val).unwrap() + } + } + res + } + + /// Make shielded components to embed within a Transfer object. If no shielded + /// payment address nor spending key is specified, then no shielded components + /// are produced. Otherwise a transaction containing nullifiers and/or note + /// commitments are produced. Dummy transparent UTXOs are sometimes used to make + /// transactions balanced, but it is understood that transparent account changes + /// are effected only by the amounts and signatures specified by the containing + /// Transfer object. + #[cfg(feature = "masp-tx-gen")] + pub async fn gen_shielded_transfer( + &mut self, + source: TransferSource, + target: TransferTarget, + args_amount: token::Amount, + token: Address, + fee_amount: token::Amount, + fee_token: Address, + shielded_gas: bool, + ) -> Result, builder::Error> { + // No shielded components are needed when neither source nor destination + // are shielded + let spending_key = source.spending_key(); + let payment_address = target.payment_address(); + if spending_key.is_none() && payment_address.is_none() { + return Ok(None); + } + // We want to fund our transaction solely from supplied spending key + let spending_key = spending_key.map(|x| x.into()); + let spending_keys: Vec<_> = spending_key.into_iter().collect(); + // Load the current shielded context given the spending key we possess + let _ = self.load(); + self.fetch(&spending_keys, &[]) + .await; + // Save the update state so that future fetches can be short-circuited + let _ = self.save(); + // Determine epoch in which to submit potential shielded transaction + let epoch = self.utils.query_epoch().await; + // Context required for storing which notes are in the source's possesion + let consensus_branch_id = BranchId::Sapling; + let amt: u64 = args_amount.into(); + let memo: Option = None; + + // Now we build up the transaction within this object + let mut builder = Builder::::new(0u32); + // Convert transaction amount into MASP types + let (asset_type, amount) = convert_amount(epoch, &token, args_amount); + + // Transactions with transparent input and shielded output + // may be affected if constructed close to epoch boundary + let mut epoch_sensitive: bool = false; + // If there are shielded inputs + if let Some(sk) = spending_key { + // Transaction fees need to match the amount in the wrapper Transfer + // when MASP source is used + let (_, fee) = + convert_amount(epoch, &fee_token, fee_amount); + builder.set_fee(fee.clone())?; + // If the gas is coming from the shielded pool, then our shielded inputs + // must also cover the gas fee + let required_amt = if shielded_gas { amount + fee } else { amount }; + // Locate unspent notes that can help us meet the transaction amount + let (_, unspent_notes, used_convs) = self + .collect_unspent_notes( + &to_viewing_key(&sk).vk, + required_amt, + epoch, + ) + .await; + // Commit the notes found to our transaction + for (diversifier, note, merkle_path) in unspent_notes { + builder.add_sapling_spend(sk, diversifier, note, merkle_path)?; + } + // Commit the conversion notes used during summation + for (conv, wit, value) in used_convs.values() { + if *value > 0 { + builder.add_convert( + conv.clone(), + *value as u64, + wit.clone(), + )?; + } + } + } else { + // No transfer fees come from the shielded transaction for non-MASP + // sources + builder.set_fee(Amount::zero())?; + // We add a dummy UTXO to our transaction, but only the source of the + // parent Transfer object is used to validate fund availability + let secp_sk = + secp256k1::SecretKey::from_slice(&[0xcd; 32]).expect("secret key"); + let secp_ctx = secp256k1::Secp256k1::::gen_new(); + let secp_pk = + secp256k1::PublicKey::from_secret_key(&secp_ctx, &secp_sk) + .serialize(); + let hash = + ripemd160::Ripemd160::digest(&sha2::Sha256::digest(&secp_pk)); + let script = TransparentAddress::PublicKey(hash.into()).script(); + epoch_sensitive = true; + builder.add_transparent_input( + secp_sk, + OutPoint::new([0u8; 32], 0), + TxOut { + asset_type, + value: amt, + script_pubkey: script, + }, + )?; + } + // Now handle the outputs of this transaction + // If there is a shielded output + if let Some(pa) = payment_address { + let ovk_opt = spending_key.map(|x| x.expsk.ovk); + builder.add_sapling_output( + ovk_opt, + pa.into(), + asset_type, + amt, + memo.clone(), + )?; + } else { + epoch_sensitive = false; + // Embed the transparent target address into the shielded transaction so + // that it can be signed + let target_enc = target + .address() + .expect("target address should be transparent") + .try_to_vec() + .expect("target address encoding"); + let hash = ripemd160::Ripemd160::digest(&sha2::Sha256::digest( + target_enc.as_ref(), + )); + builder.add_transparent_output( + &TransparentAddress::PublicKey(hash.into()), + asset_type, + amt, + )?; + } + let prover = self.utils.local_tx_prover(); + // Build and return the constructed transaction + let mut tx = builder.build(consensus_branch_id, &prover); + + if epoch_sensitive { + let new_epoch = self.utils.query_epoch().await; + + // If epoch has changed, recalculate shielded outputs to match new epoch + if new_epoch != epoch { + // Hack: build new shielded transfer with updated outputs + let mut replay_builder = Builder::::new(0u32); + replay_builder.set_fee(Amount::zero())?; + let ovk_opt = spending_key.map(|x| x.expsk.ovk); + let (new_asset_type, _) = + convert_amount(new_epoch, &token, args_amount); + replay_builder.add_sapling_output( + ovk_opt, + payment_address.unwrap().into(), + new_asset_type, + amt, + memo, + )?; + + let secp_sk = secp256k1::SecretKey::from_slice(&[0xcd; 32]) + .expect("secret key"); + let secp_ctx = + secp256k1::Secp256k1::::gen_new(); + let secp_pk = + secp256k1::PublicKey::from_secret_key(&secp_ctx, &secp_sk) + .serialize(); + let hash = + ripemd160::Ripemd160::digest(&sha2::Sha256::digest(&secp_pk)); + let script = TransparentAddress::PublicKey(hash.into()).script(); + replay_builder.add_transparent_input( + secp_sk, + OutPoint::new([0u8; 32], 0), + TxOut { + asset_type: new_asset_type, + value: amt, + script_pubkey: script, + }, + )?; + + let (replay_tx, _) = + replay_builder.build(consensus_branch_id, &prover)?; + tx = tx.map(|(t, tm)| { + let mut temp = t.deref().clone(); + temp.shielded_outputs = replay_tx.shielded_outputs.clone(); + temp.value_balance = temp.value_balance.reject(asset_type) + - Amount::from_pair(new_asset_type, amt).unwrap(); + (temp.freeze().unwrap(), tm) + }); + } + } + + tx.map(Some) + } +} + +/// Make asset type corresponding to given address and epoch +fn make_asset_type(epoch: Epoch, token: &Address) -> AssetType { + // Typestamp the chosen token with the current epoch + let token_bytes = (token, epoch.0) + .try_to_vec() + .expect("token should serialize"); + // Generate the unique asset identifier from the unique token address + AssetType::new(token_bytes.as_ref()).expect("unable to create asset type") +} + +/// Convert Anoma amount and token type to MASP equivalents +fn convert_amount( + epoch: Epoch, + token: &Address, + val: token::Amount, +) -> (AssetType, Amount) { + let asset_type = make_asset_type(epoch, token); + // Combine the value and unit into one amount + let amount = Amount::from_nonnegative(asset_type, u64::from(val)) + .expect("invalid value for amount"); + (asset_type, amount) +} diff --git a/tests/src/e2e/ledger_tests.rs b/tests/src/e2e/ledger_tests.rs index 71ea121c4e..f16fe1abcb 100644 --- a/tests/src/e2e/ledger_tests.rs +++ b/tests/src/e2e/ledger_tests.rs @@ -21,7 +21,7 @@ use color_eyre::eyre::Result; use data_encoding::HEXLOWER; use namada::types::address::{btc, eth, masp_rewards, Address}; use namada::types::token; -use namada_apps::client::tx::ShieldedContext; +use namada_apps::client::tx::CLIShieldedUtils; use namada_apps::config::genesis::genesis_config::{ GenesisConfig, ParametersConfig, PosParamsConfig, }; @@ -477,7 +477,7 @@ fn ledger_txs_and_queries() -> Result<()> { #[test] fn masp_txs_and_queries() -> Result<()> { // Download the shielded pool parameters before starting node - let _ = ShieldedContext::new(PathBuf::new()); + let _ = CLIShieldedUtils::new(PathBuf::new()); // Lengthen epoch to ensure that a transaction can be constructed and // submitted within the same block. Necessary to ensure that conversion is // not invalidated. @@ -746,7 +746,7 @@ fn masp_txs_and_queries() -> Result<()> { #[test] fn masp_pinned_txs() -> Result<()> { // Download the shielded pool parameters before starting node - let _ = ShieldedContext::new(PathBuf::new()); + let _ = CLIShieldedUtils::new(PathBuf::new()); // Lengthen epoch to ensure that a transaction can be constructed and // submitted within the same block. Necessary to ensure that conversion is // not invalidated. @@ -906,7 +906,7 @@ fn masp_pinned_txs() -> Result<()> { #[test] fn masp_incentives() -> Result<()> { // Download the shielded pool parameters before starting node - let _ = ShieldedContext::new(PathBuf::new()); + let _ = CLIShieldedUtils::new(PathBuf::new()); // Lengthen epoch to ensure that a transaction can be constructed and // submitted within the same block. Necessary to ensure that conversion is // not invalidated. From a347134b43dcf00c3624ba58579e22600f959359 Mon Sep 17 00:00:00 2001 From: Murisi Tarusenga Date: Wed, 7 Dec 2022 18:45:14 +0200 Subject: [PATCH 05/51] Now attach events to decryptable transactions. Corrected the parsing of BlockResult keys. --- apps/src/lib/node/ledger/shims/abcipp_shim.rs | 13 +++++++++++-- shared/src/ledger/queries/shell.rs | 9 +++++---- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/apps/src/lib/node/ledger/shims/abcipp_shim.rs b/apps/src/lib/node/ledger/shims/abcipp_shim.rs index 74a56a4ddc..0728e007a0 100644 --- a/apps/src/lib/node/ledger/shims/abcipp_shim.rs +++ b/apps/src/lib/node/ledger/shims/abcipp_shim.rs @@ -5,6 +5,7 @@ use std::pin::Pin; use std::task::{Context, Poll}; use futures::future::FutureExt; +use namada::proto::Tx; use namada::types::address::Address; #[cfg(not(feature = "abcipp"))] use namada::types::hash::Hash; @@ -22,8 +23,10 @@ use super::abcipp_shim_types::shim::TxBytes; use super::abcipp_shim_types::shim::{Error, Request, Response}; use crate::config; #[cfg(not(feature = "abcipp"))] -use crate::facade::tendermint_proto::abci::RequestBeginBlock; +use crate::facade::tendermint_proto::abci::{RequestBeginBlock, ResponseDeliverTx}; use crate::facade::tower_abci::{BoxError, Request as Req, Response as Resp}; +#[cfg(not(feature = "abcipp"))] +use crate::facade::tower_abci::response::DeliverTx; /// The shim wraps the shell, which implements ABCI++. /// The shim makes a crude translation between the ABCI interface currently used @@ -132,8 +135,14 @@ impl AbcippShim { } #[cfg(not(feature = "abcipp"))] Req::DeliverTx(tx) => { + let mut deliver: DeliverTx = Default::default(); + // Attach events to this transaction if possible + if let Ok(tx) = Tx::try_from(&tx.tx[..]) { + let resp: ResponseDeliverTx = tx.into(); + deliver.events = resp.events; + } self.delivered_txs.push(tx.tx); - Ok(Resp::DeliverTx(Default::default())) + Ok(Resp::DeliverTx(deliver)) } #[cfg(not(feature = "abcipp"))] Req::EndBlock(_) => { diff --git a/shared/src/ledger/queries/shell.rs b/shared/src/ledger/queries/shell.rs index f428a2e572..a53107b9f1 100644 --- a/shared/src/ledger/queries/shell.rs +++ b/shared/src/ledger/queries/shell.rs @@ -4,7 +4,7 @@ use masp_primitives::merkle_tree::MerklePath; use masp_primitives::sapling::Node; use namada_core::types::address::Address; use namada_core::types::hash::Hash; -use namada_core::types::storage::BlockResults; +use namada_core::types::storage::{BlockResults, KeySeg}; use crate::ledger::events::log::dumb_queries; use crate::ledger::events::Event; @@ -111,12 +111,13 @@ where let mut results = vec![BlockResults::default(); ctx.storage.block.height.0 as usize + 1]; iter.for_each(|(key, value, _gas)| { - let key = key - .parse::() + let key = u64::parse(key) .expect("expected integer for block height"); let value = BlockResults::try_from_slice(&value) .expect("expected BlockResults bytes"); - results[key] = value; + let idx: usize = key.try_into() + .expect("expected block height to fit into usize"); + results[idx] = value; }); Ok(results) } From e479c7686cc89a6f2a7a0bd603765c641ada90ba Mon Sep 17 00:00:00 2001 From: Murisi Tarusenga Date: Thu, 8 Dec 2022 11:16:46 +0200 Subject: [PATCH 06/51] Moved query_tx_deltas into shared crate. --- apps/src/lib/client/rpc.rs | 171 ++++--------------------------------- apps/src/lib/client/tx.rs | 18 +++- shared/src/ledger/masp.rs | 165 ++++++++++++++++++++++++++++++++++- 3 files changed, 195 insertions(+), 159 deletions(-) diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index 7ed6760890..b1975a248e 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -15,7 +15,7 @@ use async_std::prelude::*; use borsh::{BorshDeserialize, BorshSerialize}; use data_encoding::HEXLOWER; use eyre::{eyre, Context as EyreContext}; -use itertools::Itertools; +use itertools::{Itertools, Either}; use masp_primitives::asset_type::AssetType; use masp_primitives::merkle_tree::MerklePath; use masp_primitives::primitives::ViewingKey; @@ -147,132 +147,22 @@ pub async fn query_results(args: args::Query) -> Vec { unwrap_client_response(RPC.shell().read_results(&client).await) } -/// Obtain the known effects of all accepted shielded and transparent -/// transactions. If an owner is specified, then restrict the set to only -/// 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, - ledger_address: TendermintAddress, - query_owner: &Option, - query_token: &Option
, -) -> BTreeMap<(BlockHeight, TxIndex), (Epoch, TransferDelta, TransactionDelta)> -{ - const TXS_PER_PAGE: u8 = 100; - // Connect to the Tendermint server holding the transactions - let client = HttpClient::new(ledger_address.clone()).unwrap(); - // Build up the context that will be queried for transactions - ctx.shielded.utils.ledger_address = Some(ledger_address.clone()); - let _ = ctx.shielded.load(); - let vks = ctx.wallet.get_viewing_keys(); - let fvks: Vec<_> = vks - .values() - .map(|fvk| ExtendedFullViewingKey::from(*fvk).fvk.vk) - .collect(); - ctx.shielded.fetch(&[], &fvks).await; - // Save the update state so that future fetches can be short-circuited - let _ = ctx.shielded.save(); - // Required for filtering out rejected transactions from Tendermint - // responses - let block_results = query_results(args::Query { ledger_address }).await; - let mut transfers = ctx.shielded.get_tx_deltas().clone(); - // Construct the set of addresses relevant to user's query - let relevant_addrs = match &query_owner { - Some(BalanceOwner::Address(owner)) => vec![owner.clone()], - // MASP objects are dealt with outside of tx_search - Some(BalanceOwner::FullViewingKey(_viewing_key)) => vec![], - Some(BalanceOwner::PaymentAddress(_owner)) => vec![], - // Unspecified owner means all known addresses are considered relevant - None => ctx.wallet.get_addresses().into_values().collect(), - }; - // Find all transactions to or from the relevant address set - for addr in relevant_addrs { - for prop in ["transfer.source", "transfer.target"] { - // Query transactions involving the current address - let mut tx_query = Query::eq(prop, addr.encode()); - // Elaborate the query if requested by the user - if let Some(token) = &query_token { - tx_query = tx_query.and_eq("transfer.token", token.encode()); - } - for page in 1.. { - let txs = &client - .tx_search( - tx_query.clone(), - true, - page, - TXS_PER_PAGE, - Order::Ascending, - ) - .await - .expect("Unable to query for transactions") - .txs; - for response_tx in txs { - let height = BlockHeight(response_tx.height.value()); - let idx = TxIndex(response_tx.index); - // Only process yet unprocessed transactions which have been - // accepted by node VPs - let should_process = !transfers - .contains_key(&(height, idx)) - && block_results[u64::from(height) as usize] - .is_accepted(idx.0 as usize); - if !should_process { - continue; - } - let tx = Tx::try_from(response_tx.tx.as_ref()) - .expect("Ill-formed Tx"); - let mut wrapper = None; - let mut transfer = None; - extract_payload(tx, &mut wrapper, &mut transfer); - // Epoch data is not needed for transparent transactions - let epoch = wrapper.map(|x| x.epoch).unwrap_or_default(); - if let Some(transfer) = transfer { - // Skip MASP addresses as they are already handled by - // ShieldedContext - if transfer.source == masp() - || transfer.target == masp() - { - continue; - } - // Describe how a Transfer simply subtracts from one - // account and adds the same to another - let mut delta = TransferDelta::default(); - let tfer_delta = Amount::from_nonnegative( - transfer.token.clone(), - u64::from(transfer.amount), - ) - .expect("invalid value for amount"); - delta.insert( - transfer.source, - Amount::zero() - &tfer_delta, - ); - delta.insert(transfer.target, tfer_delta); - // No shielded accounts are affected by this Transfer - transfers.insert( - (height, idx), - (epoch, delta, TransactionDelta::new()), - ); - } - } - // An incomplete page signifies no more transactions - if (txs.len() as u8) < TXS_PER_PAGE { - break; - } - } - } - } - transfers -} - /// 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 query_owner = args.owner.as_ref().map(|x| ctx.get_cached(x)) + .map_or_else( + || Either::Right(ctx.wallet.get_addresses().into_values().collect()), + Either::Left, + ); + // Build up the context that will be queried for asset decodings + ctx.shielded.utils.ledger_address = Some(args.query.ledger_address.clone()); + let _ = ctx.shielded.load(); // Obtain the effects of all shielded and transparent transactions - let transfers = query_tx_deltas( - &mut ctx, - args.query.ledger_address.clone(), + let transfers = ctx.shielded.query_tx_deltas( &query_owner, &query_token, + &ctx.wallet.get_viewing_keys(), ) .await; // To facilitate lookups of human-readable token names @@ -287,13 +177,13 @@ pub async fn query_transfers(mut ctx: Context, args: args::QueryTransfers) { for ((height, idx), (epoch, tfer_delta, tx_delta)) in transfers { // Check if this transfer pertains to the supplied owner let mut relevant = match &query_owner { - Some(BalanceOwner::FullViewingKey(fvk)) => tx_delta + Either::Left(BalanceOwner::FullViewingKey(fvk)) => tx_delta .contains_key(&ExtendedFullViewingKey::from(*fvk).fvk.vk), - Some(BalanceOwner::Address(owner)) => { + Either::Left(BalanceOwner::Address(owner)) => { tfer_delta.contains_key(owner) } - Some(BalanceOwner::PaymentAddress(_owner)) => false, - None => true, + Either::Left(BalanceOwner::PaymentAddress(_owner)) => false, + Either::Right(_) => true, }; // Realize and decode the shielded changes to enable relevance check let mut shielded_accounts = HashMap::new(); @@ -376,37 +266,6 @@ pub async fn query_transfers(mut ctx: Context, args: args::QueryTransfers) { } } -/// Extract the payload from the given Tx object -fn extract_payload( - tx: Tx, - wrapper: &mut Option, - transfer: &mut Option, -) { - match process_tx(tx) { - Ok(TxType::Wrapper(wrapper_tx)) => { - let privkey = ::G2Affine::prime_subgroup_generator(); - extract_payload( - Tx::from(match wrapper_tx.decrypt(privkey) { - Ok(tx) => DecryptedTx::Decrypted(tx), - _ => DecryptedTx::Undecryptable(wrapper_tx.clone()), - }), - wrapper, - transfer, - ); - *wrapper = Some(wrapper_tx); - } - Ok(TxType::Decrypted(DecryptedTx::Decrypted(tx))) => { - let empty_vec = vec![]; - let tx_data = tx.data.as_ref().unwrap_or(&empty_vec); - let _ = SignedTxData::try_from_slice(tx_data).map(|signed| { - Transfer::try_from_slice(&signed.data.unwrap()[..]) - .map(|tfer| *transfer = Some(tfer)) - }); - } - _ => {} - } -} - /// Query the raw bytes of given storage key pub async fn query_raw_bytes(_ctx: Context, args: args::QueryRawBytes) { let client = HttpClient::new(args.query.ledger_address).unwrap(); diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index 94ffe42311..9d56b53c5d 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -32,7 +32,7 @@ use namada::types::governance::{ use namada::types::key::*; use namada::types::masp::TransferTarget; use namada::types::storage::{ - Epoch, RESERVED_ADDRESS_PREFIX, + Epoch, BlockResults, RESERVED_ADDRESS_PREFIX, }; use namada::types::time::DateTimeUtc; use namada::types::transaction::governance::{ @@ -415,6 +415,8 @@ impl Default for CLIShieldedUtils { #[async_trait] impl masp::ShieldedUtils for CLIShieldedUtils { + type C = HttpClient; + async fn query_storage_value( &self, key: &storage::Key, @@ -500,6 +502,20 @@ impl masp::ShieldedUtils for CLIShieldedUtils { let client = HttpClient::new(self.ledger_address.clone().unwrap()).unwrap(); query_conversion(client, asset_type).await } + + fn client(&self) -> Self::C { + let ledger_address = self + .ledger_address + .clone() + .expect("ledger address must be set"); + HttpClient::new(ledger_address).unwrap() + } + + async fn query_results(&self) -> Vec { + rpc::query_results(args::Query { + ledger_address: self.ledger_address.clone().unwrap() + }).await + } } diff --git a/shared/src/ledger/masp.rs b/shared/src/ledger/masp.rs index 2c470323ef..ce19e2831a 100644 --- a/shared/src/ledger/masp.rs +++ b/shared/src/ledger/masp.rs @@ -48,11 +48,11 @@ use masp_primitives::transaction::components::{OutPoint, TxOut}; use masp_primitives::zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}; use masp_proofs::prover::LocalTxProver; use crate::types::address::{masp, Address}; -use crate::types::masp::PaymentAddress; +use crate::types::masp::{PaymentAddress, BalanceOwner}; #[cfg(feature = "masp-tx-gen")] use crate::types::masp::{TransferSource, TransferTarget}; use crate::types::storage::{ - BlockHeight, Epoch, Key, KeySeg, TxIndex, + BlockHeight, Epoch, Key, KeySeg, TxIndex, BlockResults, }; use crate::types::token::{ Transfer, HEAD_TX_KEY, PIN_KEY_PREFIX, TX_KEY_PREFIX, @@ -63,6 +63,14 @@ use rand_core::{CryptoRng, OsRng, RngCore}; #[cfg(feature = "masp-tx-gen")] use sha2::Digest; use async_trait::async_trait; +use crate::proto::{Tx, SignedTxData}; +use crate::types::transaction::{DecryptedTx, EllipticCurve, WrapperTx, TxType, PairingEngine, process_tx}; +use crate::tendermint_rpc::query::Query; +use crate::tendermint_rpc::Order; +use namada_core::types::transaction::AffineCurve; +use tendermint_rpc::Client; +use crate::types::masp::ExtendedViewingKey; +use itertools::Either; /// Env var to point to a dir with MASP parameters. When not specified, /// the default OS specific path is used. @@ -249,6 +257,8 @@ pub fn get_params_dir() -> PathBuf { #[async_trait] pub trait ShieldedUtils : Sized + BorshDeserialize + BorshSerialize + Default + Clone { + type C: tendermint_rpc::Client + std::marker::Sync; + async fn query_storage_value( &self, key: &storage::Key, @@ -273,6 +283,10 @@ pub trait ShieldedUtils : Sized + BorshDeserialize + BorshSerialize + Default + masp_primitives::transaction::components::Amount, MerklePath, )>; + + async fn query_results(&self) -> Vec; + + fn client(&self) -> Self::C; } /// Make a ViewingKey that can view notes encrypted by given ExtendedSpendingKey @@ -1263,6 +1277,153 @@ impl ShieldedContext { tx.map(Some) } + + /// Obtain the known effects of all accepted shielded and transparent + /// transactions. If an owner is specified, then restrict the set to only + /// 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( + &mut self, + query_owner: &Either>, + query_token: &Option
, + viewing_keys: &HashMap, + ) -> BTreeMap<(BlockHeight, TxIndex), (Epoch, TransferDelta, TransactionDelta)> + { + const TXS_PER_PAGE: u8 = 100; + // Connect to the Tendermint server holding the transactions + //let client = HttpClient::new(ledger_address.clone()).unwrap(); + // Build up the context that will be queried for transactions + //ctx.shielded.utils.ledger_address = Some(ledger_address.clone()); + let _ = self.load(); + let vks = viewing_keys; + let fvks: Vec<_> = vks + .values() + .map(|fvk| ExtendedFullViewingKey::from(*fvk).fvk.vk) + .collect(); + self.fetch(&[], &fvks).await; + // Save the update state so that future fetches can be short-circuited + let _ = self.save(); + // Required for filtering out rejected transactions from Tendermint + // responses + let block_results = self.utils.query_results().await; + let mut transfers = self.get_tx_deltas().clone(); + // Construct the set of addresses relevant to user's query + let relevant_addrs = match &query_owner { + Either::Left(BalanceOwner::Address(owner)) => vec![owner.clone()], + // MASP objects are dealt with outside of tx_search + Either::Left(BalanceOwner::FullViewingKey(_viewing_key)) => vec![], + Either::Left(BalanceOwner::PaymentAddress(_owner)) => vec![], + // Unspecified owner means all known addresses are considered relevant + Either::Right(addrs) => addrs.clone(), + }; + // Find all transactions to or from the relevant address set + for addr in relevant_addrs { + for prop in ["transfer.source", "transfer.target"] { + // Query transactions involving the current address + let mut tx_query = Query::eq(prop, addr.encode()); + // Elaborate the query if requested by the user + if let Some(token) = &query_token { + tx_query = tx_query.and_eq("transfer.token", token.encode()); + } + for page in 1.. { + let txs = &self.utils.client() + .tx_search( + tx_query.clone(), + true, + page, + TXS_PER_PAGE, + Order::Ascending, + ) + .await + .expect("Unable to query for transactions") + .txs; + for response_tx in txs { + let height = BlockHeight(response_tx.height.value()); + let idx = TxIndex(response_tx.index); + // Only process yet unprocessed transactions which have been + // accepted by node VPs + let should_process = !transfers + .contains_key(&(height, idx)) + && block_results[u64::from(height) as usize] + .is_accepted(idx.0 as usize); + if !should_process { + continue; + } + let tx = Tx::try_from(response_tx.tx.as_ref()) + .expect("Ill-formed Tx"); + let mut wrapper = None; + let mut transfer = None; + extract_payload(tx, &mut wrapper, &mut transfer); + // Epoch data is not needed for transparent transactions + let epoch = wrapper.map(|x| x.epoch).unwrap_or_default(); + if let Some(transfer) = transfer { + // Skip MASP addresses as they are already handled by + // ShieldedContext + if transfer.source == masp() + || transfer.target == masp() + { + continue; + } + // Describe how a Transfer simply subtracts from one + // account and adds the same to another + let mut delta = TransferDelta::default(); + let tfer_delta = Amount::from_nonnegative( + transfer.token.clone(), + u64::from(transfer.amount), + ) + .expect("invalid value for amount"); + delta.insert( + transfer.source, + Amount::zero() - &tfer_delta, + ); + delta.insert(transfer.target, tfer_delta); + // No shielded accounts are affected by this Transfer + transfers.insert( + (height, idx), + (epoch, delta, TransactionDelta::new()), + ); + } + } + // An incomplete page signifies no more transactions + if (txs.len() as u8) < TXS_PER_PAGE { + break; + } + } + } + } + transfers + } +} + +/// Extract the payload from the given Tx object +fn extract_payload( + tx: Tx, + wrapper: &mut Option, + transfer: &mut Option, +) { + match process_tx(tx) { + Ok(TxType::Wrapper(wrapper_tx)) => { + let privkey = ::G2Affine::prime_subgroup_generator(); + extract_payload( + Tx::from(match wrapper_tx.decrypt(privkey) { + Ok(tx) => DecryptedTx::Decrypted(tx), + _ => DecryptedTx::Undecryptable(wrapper_tx.clone()), + }), + wrapper, + transfer, + ); + *wrapper = Some(wrapper_tx); + } + Ok(TxType::Decrypted(DecryptedTx::Decrypted(tx))) => { + let empty_vec = vec![]; + let tx_data = tx.data.as_ref().unwrap_or(&empty_vec); + let _ = SignedTxData::try_from_slice(tx_data).map(|signed| { + Transfer::try_from_slice(&signed.data.unwrap()[..]) + .map(|tfer| *transfer = Some(tfer)) + }); + } + _ => {} + } } /// Make asset type corresponding to given address and epoch From 9c30c3444c44d26d2e4491bf8694cce75ed93513 Mon Sep 17 00:00:00 2001 From: Murisi Tarusenga Date: Thu, 8 Dec 2022 11:35:03 +0200 Subject: [PATCH 07/51] Started adding documentation. --- apps/src/lib/client/mod.rs | 1 - apps/src/lib/client/rpc.rs | 15 ++++----------- apps/src/lib/client/types.rs | 14 -------------- shared/src/ledger/masp.rs | 13 ++++++++++++- 4 files changed, 16 insertions(+), 27 deletions(-) delete mode 100644 apps/src/lib/client/types.rs diff --git a/apps/src/lib/client/mod.rs b/apps/src/lib/client/mod.rs index 486eb3c26d..9807ca6a30 100644 --- a/apps/src/lib/client/mod.rs +++ b/apps/src/lib/client/mod.rs @@ -2,5 +2,4 @@ pub mod rpc; pub mod signing; pub mod tendermint_rpc_types; pub mod tx; -pub mod types; pub mod utils; diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index b1975a248e..5f0533db3f 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -2,7 +2,7 @@ use std::borrow::Cow; use std::cmp::Ordering; -use std::collections::{BTreeMap, HashMap, HashSet}; +use std::collections::{HashMap, HashSet}; use std::convert::TryInto; use std::fs::File; use std::io::{self, Write}; @@ -33,7 +33,6 @@ use namada::ledger::pos::{ }; use namada::ledger::queries::{self, RPC}; use namada::ledger::storage::ConversionState; -use namada::proto::{SignedTxData, Tx}; use namada::types::address::{masp, tokens, Address}; use namada::types::governance::{ OfflineProposal, OfflineVote, ProposalResult, ProposalVote, TallyResult, @@ -43,22 +42,16 @@ use namada::types::hash::Hash; use namada::types::key::*; use namada::types::masp::{BalanceOwner, ExtendedViewingKey, PaymentAddress}; use namada::types::storage::{ - BlockHeight, BlockResults, Epoch, Key, KeySeg, PrefixValue, TxIndex, -}; -use namada::types::token::{balance_key, Transfer}; -use namada::types::transaction::{ - process_tx, AffineCurve, DecryptedTx, EllipticCurve, PairingEngine, TxType, - WrapperTx, + BlockHeight, BlockResults, Epoch, Key, KeySeg, PrefixValue, }; +use namada::types::token::balance_key; use namada::types::{address, storage, token}; use rust_decimal::Decimal; use tokio::time::{Duration, Instant}; use crate::cli::{self, args, Context}; use crate::client::tendermint_rpc_types::TxResponse; -use namada::ledger::masp::{ - Conversions, PinnedBalanceError, TransactionDelta, TransferDelta, -}; +use namada::ledger::masp::{Conversions, PinnedBalanceError}; use crate::facade::tendermint::merkle::proof::Proof; use crate::facade::tendermint_config::net::Address as TendermintAddress; use crate::facade::tendermint_rpc::error::Error as TError; diff --git a/apps/src/lib/client/types.rs b/apps/src/lib/client/types.rs deleted file mode 100644 index 10ae25182a..0000000000 --- a/apps/src/lib/client/types.rs +++ /dev/null @@ -1,14 +0,0 @@ -use async_trait::async_trait; -use masp_primitives::merkle_tree::MerklePath; -use masp_primitives::primitives::{Diversifier, Note, ViewingKey}; -use masp_primitives::sapling::Node; -use masp_primitives::transaction::components::Amount; -use namada::types::address::Address; -use namada::types::masp::{TransferSource, TransferTarget}; -use namada::types::storage::Epoch; -use namada::types::transaction::GasLimit; -use namada::types::{key, token}; - -use super::rpc; -use crate::cli::{args, Context}; -use crate::facade::tendermint_config::net::Address as TendermintAddress; diff --git a/shared/src/ledger/masp.rs b/shared/src/ledger/masp.rs index ce19e2831a..099fb73aee 100644 --- a/shared/src/ledger/masp.rs +++ b/shared/src/ledger/masp.rs @@ -255,10 +255,14 @@ pub fn get_params_dir() -> PathBuf { } } +/// Abstracts platform specific details away from the logic of shielded pool +/// operations. #[async_trait] pub trait ShieldedUtils : Sized + BorshDeserialize + BorshSerialize + Default + Clone { + /// The type of the Tendermint client to make queries with type C: tendermint_rpc::Client + std::marker::Sync; - + + /// Query the storage value at the given key async fn query_storage_value( &self, key: &storage::Key, @@ -266,14 +270,19 @@ pub trait ShieldedUtils : Sized + BorshDeserialize + BorshSerialize + Default + where T: BorshDeserialize; + /// Query the current epoch async fn query_epoch(&self) -> Epoch; + /// Get a MASP transaction prover fn local_tx_prover(&self) -> LocalTxProver; + /// Load up the currently saved ShieldedContext fn load(self) -> std::io::Result>; + /// Sace the given ShieldedContext for future loads fn save(&self, ctx: &ShieldedContext) -> std::io::Result<()>; + /// Query the designated conversion for the given AssetType async fn query_conversion( &self, asset_type: AssetType, @@ -284,8 +293,10 @@ pub trait ShieldedUtils : Sized + BorshDeserialize + BorshSerialize + Default + MerklePath, )>; + /// Query for all the accepted transactions that have occured to date async fn query_results(&self) -> Vec; + /// Get a client object with which to effect Tendermint queries fn client(&self) -> Self::C; } From a2cce3dc16bd1ff7cc4b814959cbd9279b6203f3 Mon Sep 17 00:00:00 2001 From: Murisi Tarusenga Date: Thu, 8 Dec 2022 12:46:39 +0200 Subject: [PATCH 08/51] Fixed help for show-transfers subcommand. --- apps/src/lib/cli.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index 5fc81f4dc4..fec87f5a30 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -1248,7 +1248,7 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) .about("Query the accepted transfers to date.") - .add_args::() + .add_args::() } } From 19f35c27b3fa9bb5a88b490d48bbda1945ac0f79 Mon Sep 17 00:00:00 2001 From: Murisi Tarusenga Date: Tue, 13 Dec 2022 07:51:37 +0200 Subject: [PATCH 09/51] Started to parameterize CLI arguments. --- apps/src/lib/cli.rs | 235 +++++++++++++++++++++++++++----------------- 1 file changed, 146 insertions(+), 89 deletions(-) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index fec87f5a30..0775656d04 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -1687,9 +1687,9 @@ pub mod args { /// Transaction associated results arguments #[derive(Clone, Debug)] - pub struct QueryResult { + pub struct QueryResult { /// Common query args - pub query: Query, + pub query: Query, /// Hash of transaction to lookup pub tx_hash: String, } @@ -1712,9 +1712,9 @@ pub mod args { /// Custom transaction arguments #[derive(Clone, Debug)] - pub struct TxCustom { + pub struct TxCustom { /// Common tx arguments - pub tx: Tx, + pub tx: Tx, /// Path to the tx WASM code file pub code_path: PathBuf, /// Path to the data file @@ -1750,15 +1750,15 @@ pub mod args { /// Transfer transaction arguments #[derive(Clone, Debug)] - pub struct TxTransfer { + pub struct TxTransfer { /// Common tx arguments - pub tx: Tx, + pub tx: Tx, /// Transfer source address - pub source: WalletTransferSource, + pub source: C::TransferSource, /// Transfer target address - pub target: WalletTransferTarget, + pub target: C::TransferTarget, /// Transferred token address - pub token: WalletAddress, + pub token: C::Address, /// Transferred token address pub sub_prefix: Option, /// Transferred token amount @@ -1801,15 +1801,15 @@ pub mod args { /// IBC transfer transaction arguments #[derive(Clone, Debug)] - pub struct TxIbcTransfer { + pub struct TxIbcTransfer { /// Common tx arguments - pub tx: Tx, + pub tx: Tx, /// Transfer source address - pub source: WalletAddress, + pub source: C::Address, /// Transfer target address pub receiver: String, /// Transferred token address - pub token: WalletAddress, + pub token: C::Address, /// Transferred token address pub sub_prefix: Option, /// Transferred token amount @@ -1875,15 +1875,15 @@ pub mod args { /// Transaction to initialize a new account #[derive(Clone, Debug)] - pub struct TxInitAccount { + pub struct TxInitAccount { /// Common tx arguments - pub tx: Tx, + pub tx: Tx, /// Address of the source account - pub source: WalletAddress, + pub source: C::Address, /// Path to the VP WASM code file for the new account pub vp_code_path: Option, /// Public key for the new account - pub public_key: WalletPublicKey, + pub public_key: C::PublicKey, } impl Args for TxInitAccount { @@ -1919,13 +1919,13 @@ pub mod args { /// Transaction to initialize a new account #[derive(Clone, Debug)] - pub struct TxInitValidator { - pub tx: Tx, - pub source: WalletAddress, + pub struct TxInitValidator { + pub tx: Tx, + pub source: C::Address, pub scheme: SchemeType, - pub account_key: Option, - pub consensus_key: Option, - pub protocol_key: Option, + pub account_key: Option, + pub consensus_key: Option, + pub protocol_key: Option, pub commission_rate: Decimal, pub max_commission_rate_change: Decimal, pub validator_vp_code_path: Option, @@ -2005,13 +2005,13 @@ pub mod args { /// Transaction to update a VP arguments #[derive(Clone, Debug)] - pub struct TxUpdateVp { + pub struct TxUpdateVp { /// Common tx arguments - pub tx: Tx, + pub tx: Tx, /// Path to the VP WASM code file pub vp_code_path: PathBuf, /// Address of the account whose VP is to be updated - pub addr: WalletAddress, + pub addr: C::Address, } impl Args for TxUpdateVp { @@ -2042,16 +2042,16 @@ pub mod args { /// Bond arguments #[derive(Clone, Debug)] - pub struct Bond { + pub struct Bond { /// Common tx arguments - pub tx: Tx, + pub tx: Tx, /// Validator address - pub validator: WalletAddress, + pub validator: C::Address, /// Amount of tokens to stake in a bond pub amount: token::Amount, /// Source address for delegations. For self-bonds, the validator is /// also the source. - pub source: Option, + pub source: Option, } impl Args for Bond { @@ -2081,16 +2081,16 @@ pub mod args { /// Unbond arguments #[derive(Clone, Debug)] - pub struct Unbond { + pub struct Unbond { /// Common tx arguments - pub tx: Tx, + pub tx: Tx, /// Validator address - pub validator: WalletAddress, + pub validator: C::Address, /// Amount of tokens to unbond from a bond pub amount: token::Amount, /// Source address for unbonding from delegations. For unbonding from /// self-bonds, the validator is also the source - pub source: Option, + pub source: Option, } impl Args for Unbond { @@ -2124,9 +2124,9 @@ pub mod args { } #[derive(Clone, Debug)] - pub struct InitProposal { + pub struct InitProposal { /// Common tx arguments - pub tx: Tx, + pub tx: Tx, /// The proposal file path pub proposal_data: PathBuf, /// Flag if proposal should be run offline @@ -2160,9 +2160,9 @@ pub mod args { } #[derive(Clone, Debug)] - pub struct VoteProposal { + pub struct VoteProposal { /// Common tx arguments - pub tx: Tx, + pub tx: Tx, /// Proposal id pub proposal_id: Option, /// The vote @@ -2225,11 +2225,11 @@ pub mod args { } #[derive(Clone, Debug)] - pub struct RevealPk { + pub struct RevealPk { /// Common tx arguments - pub tx: Tx, + pub tx: Tx, /// A public key to be revealed on-chain - pub public_key: WalletPublicKey, + pub public_key: C::PublicKey, } impl Args for RevealPk { @@ -2247,9 +2247,9 @@ pub mod args { } #[derive(Clone, Debug)] - pub struct QueryProposal { + pub struct QueryProposal { /// Common query args - pub query: Query, + pub query: Query, /// Proposal id pub proposal_id: Option, } @@ -2269,9 +2269,9 @@ pub mod args { } #[derive(Clone, Debug)] - pub struct QueryProposalResult { + pub struct QueryProposalResult { /// Common query args - pub query: Query, + pub query: Query, /// Proposal id pub proposal_id: Option, /// Flag if proposal result should be run on offline data @@ -2320,9 +2320,9 @@ pub mod args { } #[derive(Clone, Debug)] - pub struct QueryProtocolParameters { + pub struct QueryProtocolParameters { /// Common query args - pub query: Query, + pub query: Query, } impl Args for QueryProtocolParameters { @@ -2339,14 +2339,14 @@ pub mod args { /// Withdraw arguments #[derive(Clone, Debug)] - pub struct Withdraw { + pub struct Withdraw { /// Common tx arguments - pub tx: Tx, + pub tx: Tx, /// Validator address - pub validator: WalletAddress, + pub validator: C::Address, /// Source address for withdrawing from delegations. For withdrawing /// from self-bonds, the validator is also the source - pub source: Option, + pub source: Option, } impl Args for Withdraw { @@ -2374,11 +2374,11 @@ pub mod args { /// Query asset conversions #[derive(Clone, Debug)] - pub struct QueryConversions { + pub struct QueryConversions { /// Common query args - pub query: Query, + pub query: Query, /// Address of a token - pub token: Option, + pub token: Option, /// Epoch of the asset pub epoch: Option, } @@ -2412,13 +2412,13 @@ pub mod args { /// Query token balance(s) #[derive(Clone, Debug)] - pub struct QueryBalance { + pub struct QueryBalance { /// Common query args - pub query: Query, + pub query: Query, /// Address of an owner - pub owner: Option, + pub owner: Option, /// Address of a token - pub token: Option, + pub token: Option, /// Whether not to convert balances pub no_conversions: bool, /// Sub prefix of an account @@ -2468,13 +2468,13 @@ pub mod args { /// Query historical transfer(s) #[derive(Clone, Debug)] - pub struct QueryTransfers { + pub struct QueryTransfers { /// Common query args - pub query: Query, + pub query: Query, /// Address of an owner - pub owner: Option, + pub owner: Option, /// Address of a token - pub token: Option, + pub token: Option, } impl Args for QueryTransfers { @@ -2502,13 +2502,13 @@ pub mod args { /// Query PoS bond(s) #[derive(Clone, Debug)] - pub struct QueryBonds { + pub struct QueryBonds { /// Common query args - pub query: Query, + pub query: Query, /// Address of an owner - pub owner: Option, + pub owner: Option, /// Address of a validator - pub validator: Option, + pub validator: Option, } impl Args for QueryBonds { @@ -2540,11 +2540,11 @@ pub mod args { /// Query PoS bonded stake #[derive(Clone, Debug)] - pub struct QueryBondedStake { + pub struct QueryBondedStake { /// Common query args - pub query: Query, + pub query: Query, /// Address of a validator - pub validator: Option, + pub validator: Option, /// Epoch in which to find bonded stake pub epoch: Option, } @@ -2575,11 +2575,11 @@ pub mod args { #[derive(Clone, Debug)] /// Commission rate change args - pub struct TxCommissionRateChange { + pub struct TxCommissionRateChange { /// Common tx arguments - pub tx: Tx, + pub tx: Tx, /// Validator address (should be self) - pub validator: WalletAddress, + pub validator: C::Address, /// Value to which the tx changes the commission rate pub rate: Decimal, } @@ -2611,11 +2611,11 @@ pub mod args { /// Query PoS commission rate #[derive(Clone, Debug)] - pub struct QueryCommissionRate { + pub struct QueryCommissionRate { /// Common query args - pub query: Query, + pub query: Query, /// Address of a validator - pub validator: WalletAddress, + pub validator: C::Address, /// Epoch in which to find commission rate pub epoch: Option, } @@ -2646,11 +2646,11 @@ pub mod args { /// Query PoS slashes #[derive(Clone, Debug)] - pub struct QuerySlashes { + pub struct QuerySlashes { /// Common query args - pub query: Query, + pub query: Query, /// Address of a validator - pub validator: Option, + pub validator: Option, } impl Args for QuerySlashes { @@ -2670,11 +2670,11 @@ pub mod args { } /// Query the raw bytes of given storage key #[derive(Clone, Debug)] - pub struct QueryRawBytes { + pub struct QueryRawBytes { /// The storage key to query pub storage_key: storage::Key, /// Common query args - pub query: Query, + pub query: Query, } impl Args for QueryRawBytes { @@ -2689,9 +2689,66 @@ pub mod args { .arg(STORAGE_KEY.def().about("Storage key")) } } + + /// Abstraction of types being used in Namada + pub trait NamadaTypes: Clone + std::fmt::Debug { + type Address: Clone + std::fmt::Debug; + type Keypair: Clone + std::fmt::Debug; + type TendermintAddress: Clone + std::fmt::Debug; + type ViewingKey: Clone + std::fmt::Debug; + type BalanceOwner: Clone + std::fmt::Debug; + type PublicKey: Clone + std::fmt::Debug; + type TransferSource: Clone + std::fmt::Debug; + type TransferTarget: Clone + std::fmt::Debug; + } + + /// The concrete types being used in Namada SDK + #[derive(Clone, Debug)] + pub struct SdkTypes; + + impl NamadaTypes for SdkTypes { + type Address = Address; + + type Keypair = common::SecretKey; + + type TendermintAddress = (); + + type ViewingKey = namada::types::masp::ExtendedViewingKey; + + type BalanceOwner = namada::types::masp::BalanceOwner; + + type PublicKey = common::PublicKey; + + type TransferSource = namada::types::masp::TransferSource; + + type TransferTarget = namada::types::masp::TransferTarget; + } + + /// The concrete types being used in the CLI + #[derive(Clone, Debug)] + pub struct CliTypes; + + impl NamadaTypes for CliTypes { + type Address = WalletAddress; + + type Keypair = WalletKeypair; + + type TendermintAddress = TendermintAddress; + + type ViewingKey = WalletViewingKey; + + type BalanceOwner = WalletBalanceOwner; + + type PublicKey = WalletPublicKey; + + type TransferSource = WalletTransferSource; + + type TransferTarget = WalletTransferTarget; + } + /// Common transaction arguments #[derive(Clone, Debug)] - pub struct Tx { + pub struct Tx { /// Simulate applying the transaction pub dry_run: bool, /// Submit the transaction even if it doesn't pass client checks @@ -2699,20 +2756,20 @@ pub mod args { /// Do not wait for the transaction to be added to the blockchain pub broadcast_only: bool, /// The address of the ledger node as host:port - pub ledger_address: TendermintAddress, + pub ledger_address: C::TendermintAddress, /// If any new account is initialized by the tx, use the given alias to /// save it in the wallet. pub initialized_account_alias: Option, /// The amount being payed to include the transaction pub fee_amount: token::Amount, /// The token in which the fee is being paid - pub fee_token: WalletAddress, + pub fee_token: C::Address, /// The max amount of gas used to process tx pub gas_limit: GasLimit, /// Sign the tx with the key for the given alias from your wallet - pub signing_key: Option, + pub signing_key: Option, /// Sign the tx with the keypair of the public key of the given address - pub signer: Option, + pub signer: Option, } impl Args for Tx { @@ -2795,9 +2852,9 @@ pub mod args { /// Common query arguments #[derive(Clone, Debug)] - pub struct Query { + pub struct Query { /// The address of the ledger node as host:port - pub ledger_address: TendermintAddress, + pub ledger_address: C::TendermintAddress, } impl Args for Query { @@ -2886,11 +2943,11 @@ pub mod args { /// MASP generate payment address arguments #[derive(Clone, Debug)] - pub struct MaspPayAddrGen { + pub struct MaspPayAddrGen { /// Key alias pub alias: String, /// Viewing key - pub viewing_key: WalletViewingKey, + pub viewing_key: C::ViewingKey, /// Pin pub pin: bool, } From 85640e15c41c371736c8593f4feb724a8bb47866 Mon Sep 17 00:00:00 2001 From: Murisi Tarusenga Date: Tue, 13 Dec 2022 09:39:18 +0200 Subject: [PATCH 10/51] Added conversion functions for arguments. --- apps/src/lib/cli.rs | 303 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 303 insertions(+) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index 0775656d04..0ed4955de4 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -1694,6 +1694,15 @@ pub mod args { pub tx_hash: String, } + impl QueryResult { + fn to_sdk(self, ctx: &mut Context) -> QueryResult { + QueryResult:: { + query: self.query.to_sdk(ctx), + tx_hash: self.tx_hash, + } + } + } + impl Args for QueryResult { fn parse(matches: &ArgMatches) -> Self { let query = Query::parse(matches); @@ -1721,6 +1730,16 @@ pub mod args { pub data_path: Option, } + impl TxCustom { + fn to_sdk(self, ctx: &mut Context) -> TxCustom { + TxCustom:: { + tx: self.tx.to_sdk(ctx), + code_path: self.code_path, + data_path: self.data_path, + } + } + } + impl Args for TxCustom { fn parse(matches: &ArgMatches) -> Self { let tx = Tx::parse(matches); @@ -1765,6 +1784,19 @@ pub mod args { pub amount: token::Amount, } + impl TxTransfer { + fn to_sdk(self, ctx: &mut Context) -> TxTransfer { + TxTransfer:: { + tx: self.tx.to_sdk(ctx), + source: ctx.get_cached(&self.source), + target: ctx.get(&self.target), + token: ctx.get(&self.token), + sub_prefix: self.sub_prefix, + amount: self.amount, + } + } + } + impl Args for TxTransfer { fn parse(matches: &ArgMatches) -> Self { let tx = Tx::parse(matches); @@ -1824,6 +1856,23 @@ pub mod args { pub timeout_sec_offset: Option, } + impl TxIbcTransfer { + fn to_sdk(self, ctx: &mut Context) -> TxIbcTransfer { + TxIbcTransfer:: { + tx: self.tx.to_sdk(ctx), + source: ctx.get(&self.source), + receiver: self.receiver, + token: ctx.get(&self.token), + sub_prefix: self.sub_prefix, + amount: self.amount, + port_id: self.port_id, + channel_id: self.channel_id, + timeout_height: self.timeout_height, + timeout_sec_offset: self.timeout_sec_offset, + } + } + } + impl Args for TxIbcTransfer { fn parse(matches: &ArgMatches) -> Self { let tx = Tx::parse(matches); @@ -1886,6 +1935,17 @@ pub mod args { pub public_key: C::PublicKey, } + impl TxInitAccount { + fn to_sdk(self, ctx: &mut Context) -> TxInitAccount { + TxInitAccount:: { + tx: self.tx.to_sdk(ctx), + source: ctx.get(&self.source), + vp_code_path: self.vp_code_path, + public_key: ctx.get_cached(&self.public_key), + } + } + } + impl Args for TxInitAccount { fn parse(matches: &ArgMatches) -> Self { let tx = Tx::parse(matches); @@ -1932,6 +1992,23 @@ pub mod args { pub unsafe_dont_encrypt: bool, } + impl TxInitValidator { + fn to_sdk(self, ctx: &mut Context) -> TxInitValidator { + TxInitValidator:: { + tx: self.tx.to_sdk(ctx), + source: ctx.get(&self.source), + scheme: self.scheme, + account_key: self.account_key.map(|x| ctx.get_cached(&x)), + consensus_key: self.consensus_key.map(|x| ctx.get_cached(&x)), + protocol_key: self.protocol_key.map(|x| ctx.get_cached(&x)), + commission_rate: self.commission_rate, + max_commission_rate_change: self.max_commission_rate_change, + validator_vp_code_path: self.validator_vp_code_path, + unsafe_dont_encrypt: self.unsafe_dont_encrypt, + } + } + } + impl Args for TxInitValidator { fn parse(matches: &ArgMatches) -> Self { let tx = Tx::parse(matches); @@ -2014,6 +2091,16 @@ pub mod args { pub addr: C::Address, } + impl TxUpdateVp { + fn to_sdk(self, ctx: &mut Context) -> TxUpdateVp { + TxUpdateVp:: { + tx: self.tx.to_sdk(ctx), + vp_code_path: self.vp_code_path, + addr: ctx.get(&self.addr), + } + } + } + impl Args for TxUpdateVp { fn parse(matches: &ArgMatches) -> Self { let tx = Tx::parse(matches); @@ -2054,6 +2141,17 @@ pub mod args { pub source: Option, } + impl Bond { + fn to_sdk(self, ctx: &mut Context) -> Bond { + Bond:: { + tx: self.tx.to_sdk(ctx), + validator: ctx.get(&self.validator), + amount: self.amount, + source: self.source.map(|x| ctx.get(&x)), + } + } + } + impl Args for Bond { fn parse(matches: &ArgMatches) -> Self { let tx = Tx::parse(matches); @@ -2093,6 +2191,17 @@ pub mod args { pub source: Option, } + impl Unbond { + fn to_sdk(self, ctx: &mut Context) -> Unbond { + Unbond:: { + tx: self.tx.to_sdk(ctx), + validator: ctx.get(&self.validator), + amount: self.amount, + source: self.source.map(|x| ctx.get(&x)), + } + } + } + impl Args for Unbond { fn parse(matches: &ArgMatches) -> Self { let tx = Tx::parse(matches); @@ -2133,6 +2242,16 @@ pub mod args { pub offline: bool, } + impl InitProposal { + fn to_sdk(self, ctx: &mut Context) -> InitProposal { + InitProposal:: { + tx: self.tx.to_sdk(ctx), + proposal_data: self.proposal_data, + offline: self.offline, + } + } + } + impl Args for InitProposal { fn parse(matches: &ArgMatches) -> Self { let tx = Tx::parse(matches); @@ -2173,6 +2292,18 @@ pub mod args { pub proposal_data: Option, } + impl VoteProposal { + fn to_sdk(self, ctx: &mut Context) -> VoteProposal { + VoteProposal:: { + tx: self.tx.to_sdk(ctx), + proposal_id: self.proposal_id, + vote: self.vote, + offline: self.offline, + proposal_data: self.proposal_data, + } + } + } + impl Args for VoteProposal { fn parse(matches: &ArgMatches) -> Self { let tx = Tx::parse(matches); @@ -2232,6 +2363,15 @@ pub mod args { pub public_key: C::PublicKey, } + impl RevealPk { + fn to_sdk(self, ctx: &mut Context) -> RevealPk { + RevealPk:: { + tx: self.tx.to_sdk(ctx), + public_key: ctx.get_cached(&self.public_key), + } + } + } + impl Args for RevealPk { fn parse(matches: &ArgMatches) -> Self { let tx = Tx::parse(matches); @@ -2254,6 +2394,15 @@ pub mod args { pub proposal_id: Option, } + impl QueryProposal { + fn to_sdk(self, ctx: &mut Context) -> QueryProposal { + QueryProposal:: { + query: self.query.to_sdk(ctx), + proposal_id: self.proposal_id, + } + } + } + impl Args for QueryProposal { fn parse(matches: &ArgMatches) -> Self { let query = Query::parse(matches); @@ -2280,6 +2429,17 @@ pub mod args { pub proposal_folder: Option, } + impl QueryProposalResult { + fn to_sdk(self, ctx: &mut Context) -> QueryProposalResult { + QueryProposalResult:: { + query: self.query.to_sdk(ctx), + proposal_id: self.proposal_id, + offline: self.offline, + proposal_folder: self.proposal_folder, + } + } + } + impl Args for QueryProposalResult { fn parse(matches: &ArgMatches) -> Self { let query = Query::parse(matches); @@ -2325,6 +2485,14 @@ pub mod args { pub query: Query, } + impl QueryProtocolParameters { + fn to_sdk(self, ctx: &mut Context) -> QueryProtocolParameters { + QueryProtocolParameters:: { + query: self.query.to_sdk(ctx), + } + } + } + impl Args for QueryProtocolParameters { fn parse(matches: &ArgMatches) -> Self { let query = Query::parse(matches); @@ -2349,6 +2517,16 @@ pub mod args { pub source: Option, } + impl Withdraw { + fn to_sdk(self, ctx: &mut Context) -> Withdraw { + Withdraw:: { + tx: self.tx.to_sdk(ctx), + validator: ctx.get(&self.validator), + source: self.source.map(|x| ctx.get(&x)), + } + } + } + impl Args for Withdraw { fn parse(matches: &ArgMatches) -> Self { let tx = Tx::parse(matches); @@ -2383,6 +2561,16 @@ pub mod args { pub epoch: Option, } + impl QueryConversions { + fn to_sdk(self, ctx: &mut Context) -> QueryConversions { + QueryConversions:: { + query: self.query.to_sdk(ctx), + token: self.token.map(|x| ctx.get(&x)), + epoch: self.epoch, + } + } + } + impl Args for QueryConversions { fn parse(matches: &ArgMatches) -> Self { let query = Query::parse(matches); @@ -2425,6 +2613,18 @@ pub mod args { pub sub_prefix: Option, } + impl QueryBalance { + fn to_sdk(self, ctx: &mut Context) -> QueryBalance { + QueryBalance:: { + query: self.query.to_sdk(ctx), + owner: self.owner.map(|x| ctx.get_cached(&x)), + token: self.token.map(|x| ctx.get(&x)), + no_conversions: self.no_conversions, + sub_prefix: self.sub_prefix, + } + } + } + impl Args for QueryBalance { fn parse(matches: &ArgMatches) -> Self { let query = Query::parse(matches); @@ -2477,6 +2677,16 @@ pub mod args { pub token: Option, } + impl QueryTransfers { + fn to_sdk(self, ctx: &mut Context) -> QueryTransfers { + QueryTransfers:: { + query: self.query.to_sdk(ctx), + owner: self.owner.map(|x| ctx.get_cached(&x)), + token: self.token.map(|x| ctx.get(&x)), + } + } + } + impl Args for QueryTransfers { fn parse(matches: &ArgMatches) -> Self { let query = Query::parse(matches); @@ -2511,6 +2721,16 @@ pub mod args { pub validator: Option, } + impl QueryBonds { + fn to_sdk(self, ctx: &mut Context) -> QueryBonds { + QueryBonds:: { + query: self.query.to_sdk(ctx), + owner: self.owner.map(|x| ctx.get(&x)), + validator: self.validator.map(|x| ctx.get(&x)), + } + } + } + impl Args for QueryBonds { fn parse(matches: &ArgMatches) -> Self { let query = Query::parse(matches); @@ -2549,6 +2769,16 @@ pub mod args { pub epoch: Option, } + impl QueryBondedStake { + fn to_sdk(self, ctx: &mut Context) -> QueryBondedStake { + QueryBondedStake:: { + query: self.query.to_sdk(ctx), + validator: self.validator.map(|x| ctx.get(&x)), + epoch: self.epoch, + } + } + } + impl Args for QueryBondedStake { fn parse(matches: &ArgMatches) -> Self { let query = Query::parse(matches); @@ -2584,6 +2814,16 @@ pub mod args { pub rate: Decimal, } + impl TxCommissionRateChange { + fn to_sdk(self, ctx: &mut Context) -> TxCommissionRateChange { + TxCommissionRateChange:: { + tx: self.tx.to_sdk(ctx), + validator: ctx.get(&self.validator), + rate: self.rate, + } + } + } + impl Args for TxCommissionRateChange { fn parse(matches: &ArgMatches) -> Self { let tx = Tx::parse(matches); @@ -2620,6 +2860,16 @@ pub mod args { pub epoch: Option, } + impl QueryCommissionRate { + fn to_sdk(self, ctx: &mut Context) -> QueryCommissionRate { + QueryCommissionRate:: { + query: self.query.to_sdk(ctx), + validator: ctx.get(&self.validator), + epoch: self.epoch, + } + } + } + impl Args for QueryCommissionRate { fn parse(matches: &ArgMatches) -> Self { let query = Query::parse(matches); @@ -2653,6 +2903,15 @@ pub mod args { pub validator: Option, } + impl QuerySlashes { + fn to_sdk(self, ctx: &mut Context) -> QuerySlashes { + QuerySlashes:: { + query: self.query.to_sdk(ctx), + validator: self.validator.map(|x| ctx.get(&x)), + } + } + } + impl Args for QuerySlashes { fn parse(matches: &ArgMatches) -> Self { let query = Query::parse(matches); @@ -2677,6 +2936,15 @@ pub mod args { pub query: Query, } + impl QueryRawBytes { + fn to_sdk(self, ctx: &mut Context) -> QueryRawBytes { + QueryRawBytes:: { + query: self.query.to_sdk(ctx), + storage_key: self.storage_key, + } + } + } + impl Args for QueryRawBytes { fn parse(matches: &ArgMatches) -> Self { let storage_key = STORAGE_KEY.parse(matches); @@ -2772,6 +3040,23 @@ pub mod args { pub signer: Option, } + impl Tx { + fn to_sdk(self, ctx: &mut Context) -> Tx { + Tx:: { + dry_run: self.dry_run, + force: self.force, + broadcast_only: self.broadcast_only, + ledger_address: (), + initialized_account_alias: self.initialized_account_alias, + fee_amount: self.fee_amount, + fee_token: ctx.get(&self.fee_token), + gas_limit: self.gas_limit, + signing_key: self.signing_key.map(|x| ctx.get_cached(&x)), + signer: self.signer.map(|x| ctx.get(&x)), + } + } + } + impl Args for Tx { fn def(app: App) -> App { app.arg( @@ -2857,6 +3142,14 @@ pub mod args { pub ledger_address: C::TendermintAddress, } + impl Query { + fn to_sdk(self, ctx: &mut Context) -> Query { + Query:: { + ledger_address: (), + } + } + } + impl Args for Query { fn def(app: App) -> App { app.arg(LEDGER_ADDRESS_DEFAULT.def().about(LEDGER_ADDRESS_ABOUT)) @@ -2952,6 +3245,16 @@ pub mod args { pub pin: bool, } + impl MaspPayAddrGen { + fn to_sdk(self, ctx: &mut Context) -> MaspPayAddrGen { + MaspPayAddrGen:: { + alias: self.alias, + viewing_key: ctx.get_cached(&self.viewing_key), + pin: self.pin, + } + } + } + impl Args for MaspPayAddrGen { fn parse(matches: &ArgMatches) -> Self { let alias = ALIAS.parse(matches); From d562b655bf85897fb41d950fbb596fdbfca848d6 Mon Sep 17 00:00:00 2001 From: Murisi Tarusenga Date: Wed, 14 Dec 2022 16:41:24 +0200 Subject: [PATCH 11/51] Started parameterizing code by client. --- apps/src/bin/anoma-client/cli.rs | 112 ++++++++--- apps/src/bin/anoma-wallet/cli.rs | 5 +- apps/src/lib/cli.rs | 326 +++++++++++++++---------------- apps/src/lib/client/rpc.rs | 149 ++++++-------- apps/src/lib/client/signing.rs | 29 +-- apps/src/lib/client/tx.rs | 227 ++++++++++----------- 6 files changed, 427 insertions(+), 421 deletions(-) diff --git a/apps/src/bin/anoma-client/cli.rs b/apps/src/bin/anoma-client/cli.rs index 6cd40af328..1eeac1e090 100644 --- a/apps/src/bin/anoma-client/cli.rs +++ b/apps/src/bin/anoma-client/cli.rs @@ -4,93 +4,153 @@ use color_eyre::eyre::Result; use namada_apps::cli; use namada_apps::cli::cmds::*; use namada_apps::client::{rpc, tx, utils}; +use tendermint_rpc::{HttpClient, WebSocketClient, SubscriptionClient}; pub async fn main() -> Result<()> { match cli::anoma_client_cli()? { cli::AnomaClient::WithContext(cmd_box) => { - let (cmd, ctx) = *cmd_box; + let (cmd, mut ctx) = *cmd_box; use AnomaClientWithContext as Sub; match cmd { // Ledger cmds Sub::TxCustom(TxCustom(args)) => { - tx::submit_custom(ctx, args).await; + let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); + let args = args.to_sdk(&mut ctx); + tx::submit_custom(&client, ctx, args).await; } Sub::TxTransfer(TxTransfer(args)) => { - tx::submit_transfer(ctx, args).await; + let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); + let args = args.to_sdk(&mut ctx); + tx::submit_transfer(&client, ctx, args).await; } Sub::TxIbcTransfer(TxIbcTransfer(args)) => { - tx::submit_ibc_transfer(ctx, args).await; + let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); + let args = args.to_sdk(&mut ctx); + tx::submit_ibc_transfer(&client, ctx, args).await; } Sub::TxUpdateVp(TxUpdateVp(args)) => { - tx::submit_update_vp(ctx, args).await; + let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); + let args = args.to_sdk(&mut ctx); + tx::submit_update_vp(&client, ctx, args).await; } Sub::TxInitAccount(TxInitAccount(args)) => { - tx::submit_init_account(ctx, args).await; + let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); + let args = args.to_sdk(&mut ctx); + tx::submit_init_account(&client, ctx, args).await; } Sub::TxInitValidator(TxInitValidator(args)) => { - tx::submit_init_validator(ctx, args).await; + let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); + let args = args.to_sdk(&mut ctx); + tx::submit_init_validator(&client, ctx, args).await; } Sub::TxInitProposal(TxInitProposal(args)) => { - tx::submit_init_proposal(ctx, args).await; + let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); + let args = args.to_sdk(&mut ctx); + tx::submit_init_proposal(&client, ctx, args).await; } Sub::TxVoteProposal(TxVoteProposal(args)) => { - tx::submit_vote_proposal(ctx, args).await; + let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); + let args = args.to_sdk(&mut ctx); + tx::submit_vote_proposal(&client, ctx, args).await; } Sub::TxRevealPk(TxRevealPk(args)) => { - tx::submit_reveal_pk(ctx, args).await; + let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); + let args = args.to_sdk(&mut ctx); + tx::submit_reveal_pk(&client, ctx, args).await; } Sub::Bond(Bond(args)) => { - tx::submit_bond(ctx, args).await; + let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); + let args = args.to_sdk(&mut ctx); + tx::submit_bond(&client, ctx, args).await; } Sub::Unbond(Unbond(args)) => { - tx::submit_unbond(ctx, args).await; + let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); + let args = args.to_sdk(&mut ctx); + tx::submit_unbond(&client, ctx, args).await; } Sub::Withdraw(Withdraw(args)) => { - tx::submit_withdraw(ctx, args).await; + let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); + let args = args.to_sdk(&mut ctx); + tx::submit_withdraw(&client, ctx, args).await; } // Ledger queries Sub::QueryEpoch(QueryEpoch(args)) => { - rpc::query_epoch(args).await; + let client = HttpClient::new(args.ledger_address).unwrap(); + rpc::query_epoch(&client).await; } Sub::QueryTransfers(QueryTransfers(args)) => { + let args = args.to_sdk(&mut ctx); rpc::query_transfers(ctx, args).await; } Sub::QueryConversions(QueryConversions(args)) => { - rpc::query_conversions(ctx, args).await; + let client = HttpClient::new(args.query.ledger_address.clone()).unwrap(); + let args = args.to_sdk(&mut ctx); + rpc::query_conversions(&client, ctx, args).await; } Sub::QueryBlock(QueryBlock(args)) => { - rpc::query_block(args).await; + let client = HttpClient::new(args.ledger_address.clone()).unwrap(); + let args = args.to_sdk(&mut ctx); + rpc::query_block(&client, args).await; } Sub::QueryBalance(QueryBalance(args)) => { - rpc::query_balance(ctx, args).await; + let client = HttpClient::new(args.query.ledger_address.clone()).unwrap(); + let args = args.to_sdk(&mut ctx); + rpc::query_balance(&client, ctx, args).await; } Sub::QueryBonds(QueryBonds(args)) => { - rpc::query_bonds(ctx, args).await; + let client = HttpClient::new(args.query.ledger_address.clone()).unwrap(); + let args = args.to_sdk(&mut ctx); + rpc::query_bonds(&client, ctx, args).await; } Sub::QueryBondedStake(QueryBondedStake(args)) => { - rpc::query_bonded_stake(ctx, args).await; + let client = HttpClient::new(args.query.ledger_address.clone()).unwrap(); + let args = args.to_sdk(&mut ctx); + rpc::query_bonded_stake(&client, ctx, args).await; } Sub::QueryCommissionRate(QueryCommissionRate(args)) => { - rpc::query_commission_rate(ctx, args).await; + let client = HttpClient::new(args.query.ledger_address.clone()).unwrap(); + let args = args.to_sdk(&mut ctx); + rpc::query_commission_rate(&client, ctx, args).await; } Sub::QuerySlashes(QuerySlashes(args)) => { - rpc::query_slashes(ctx, args).await; + let client = HttpClient::new(args.query.ledger_address.clone()).unwrap(); + let args = args.to_sdk(&mut ctx); + rpc::query_slashes(&client, ctx, args).await; } Sub::QueryResult(QueryResult(args)) => { - rpc::query_result(ctx, args).await; + // Connect to the Tendermint server holding the transactions + let (client, driver) = WebSocketClient::new(args.query.ledger_address.clone()).await?; + let driver_handle = tokio::spawn(async move { driver.run().await }); + let args = args.to_sdk(&mut ctx); + rpc::query_result(&client, ctx, args).await; + // Signal to the driver to terminate. + client.close()?; + // Await the driver's termination to ensure proper connection closure. + let _ = driver_handle.await.unwrap_or_else(|x| { + eprintln!("{}", x); + cli::safe_exit(1) + }); } Sub::QueryRawBytes(QueryRawBytes(args)) => { - rpc::query_raw_bytes(ctx, args).await; + let client = HttpClient::new(args.query.ledger_address.clone()).unwrap(); + let args = args.to_sdk(&mut ctx); + rpc::query_raw_bytes(&client, ctx, args).await; } Sub::QueryProposal(QueryProposal(args)) => { - rpc::query_proposal(ctx, args).await; + let client = HttpClient::new(args.query.ledger_address.clone()).unwrap(); + let args = args.to_sdk(&mut ctx); + rpc::query_proposal(&client, ctx, args).await; } Sub::QueryProposalResult(QueryProposalResult(args)) => { - rpc::query_proposal_result(ctx, args).await; + let client = HttpClient::new(args.query.ledger_address.clone()).unwrap(); + let args = args.to_sdk(&mut ctx); + rpc::query_proposal_result(&client, ctx, args).await; } Sub::QueryProtocolParameters(QueryProtocolParameters(args)) => { - rpc::query_protocol_parameters(ctx, args).await; + let client = HttpClient::new(args.query.ledger_address.clone()).unwrap(); + let args = args.to_sdk(&mut ctx); + rpc::query_protocol_parameters(&client, ctx, args).await; } } } diff --git a/apps/src/bin/anoma-wallet/cli.rs b/apps/src/bin/anoma-wallet/cli.rs index b7d4d78a02..e0e5f68f70 100644 --- a/apps/src/bin/anoma-wallet/cli.rs +++ b/apps/src/bin/anoma-wallet/cli.rs @@ -16,7 +16,7 @@ use namada_apps::wallet::{DecryptionError, FindKeyError}; use rand_core::OsRng; pub fn main() -> Result<()> { - let (cmd, ctx) = cli::anoma_wallet_cli()?; + let (cmd, mut ctx) = cli::anoma_wallet_cli()?; match cmd { cmds::AnomaWallet::Key(sub) => match sub { cmds::WalletKey::Gen(cmds::KeyGen(args)) => { @@ -45,6 +45,7 @@ pub fn main() -> Result<()> { spending_key_gen(ctx, args) } cmds::WalletMasp::GenPayAddr(cmds::MaspGenPayAddr(args)) => { + let args = args.to_sdk(&mut ctx); payment_address_gen(ctx, args) } cmds::WalletMasp::AddAddrKey(cmds::MaspAddAddrKey(args)) => { @@ -218,7 +219,7 @@ fn payment_address_gen( ) { let alias = alias.to_lowercase(); let viewing_key = - ExtendedFullViewingKey::from(ctx.get_cached(&viewing_key)) + ExtendedFullViewingKey::from(viewing_key) .fvk .vk; let (div, _g_d) = find_valid_diversifier(&mut OsRng); diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index 0ed4955de4..9dc970bb88 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -612,7 +612,7 @@ pub mod cmds { /// Generate a payment address from a viewing key or payment address #[derive(Clone, Debug)] - pub struct MaspGenPayAddr(pub args::MaspPayAddrGen); + pub struct MaspGenPayAddr(pub args::MaspPayAddrGen); impl SubCmd for MaspGenPayAddr { const CMD: &'static str = "gen-addr"; @@ -628,7 +628,7 @@ pub mod cmds { .about( "Generates a payment address from the given spending key", ) - .add_args::() + .add_args::>() } } @@ -853,7 +853,7 @@ pub mod cmds { } #[derive(Clone, Debug)] - pub struct QueryResult(pub args::QueryResult); + pub struct QueryResult(pub args::QueryResult::); impl SubCmd for QueryResult { const CMD: &'static str = "tx-result"; @@ -867,12 +867,12 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) .about("Query the result of a transaction.") - .add_args::() + .add_args::>() } } #[derive(Clone, Debug)] - pub struct QueryProposal(pub args::QueryProposal); + pub struct QueryProposal(pub args::QueryProposal::); impl SubCmd for QueryProposal { const CMD: &'static str = "query-proposal"; @@ -889,12 +889,12 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) .about("Query proposals.") - .add_args::() + .add_args::>() } } #[derive(Clone, Debug)] - pub struct QueryProposalResult(pub args::QueryProposalResult); + pub struct QueryProposalResult(pub args::QueryProposalResult::); impl SubCmd for QueryProposalResult { const CMD: &'static str = "query-proposal-result"; @@ -911,12 +911,12 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) .about("Query proposals result.") - .add_args::() + .add_args::>() } } #[derive(Clone, Debug)] - pub struct QueryProtocolParameters(pub args::QueryProtocolParameters); + pub struct QueryProtocolParameters(pub args::QueryProtocolParameters::); impl SubCmd for QueryProtocolParameters { const CMD: &'static str = "query-protocol-parameters"; @@ -935,12 +935,12 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) .about("Query protocol parameters.") - .add_args::() + .add_args::>() } } #[derive(Clone, Debug)] - pub struct TxCustom(pub args::TxCustom); + pub struct TxCustom(pub args::TxCustom::); impl SubCmd for TxCustom { const CMD: &'static str = "tx"; @@ -954,12 +954,12 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) .about("Send a transaction with custom WASM code.") - .add_args::() + .add_args::>() } } #[derive(Clone, Debug)] - pub struct TxTransfer(pub args::TxTransfer); + pub struct TxTransfer(pub args::TxTransfer::); impl SubCmd for TxTransfer { const CMD: &'static str = "transfer"; @@ -973,12 +973,12 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) .about("Send a signed transfer transaction.") - .add_args::() + .add_args::>() } } #[derive(Clone, Debug)] - pub struct TxIbcTransfer(pub args::TxIbcTransfer); + pub struct TxIbcTransfer(pub args::TxIbcTransfer::); impl SubCmd for TxIbcTransfer { const CMD: &'static str = "ibc-transfer"; @@ -992,12 +992,12 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) .about("Send a signed IBC transfer transaction.") - .add_args::() + .add_args::>() } } #[derive(Clone, Debug)] - pub struct TxUpdateVp(pub args::TxUpdateVp); + pub struct TxUpdateVp(pub args::TxUpdateVp::); impl SubCmd for TxUpdateVp { const CMD: &'static str = "update"; @@ -1014,12 +1014,12 @@ pub mod cmds { "Send a signed transaction to update account's validity \ predicate.", ) - .add_args::() + .add_args::>() } } #[derive(Clone, Debug)] - pub struct TxInitAccount(pub args::TxInitAccount); + pub struct TxInitAccount(pub args::TxInitAccount::); impl SubCmd for TxInitAccount { const CMD: &'static str = "init-account"; @@ -1036,12 +1036,12 @@ pub mod cmds { "Send a signed transaction to create a new established \ account.", ) - .add_args::() + .add_args::>() } } #[derive(Clone, Debug)] - pub struct TxInitValidator(pub args::TxInitValidator); + pub struct TxInitValidator(pub args::TxInitValidator::); impl SubCmd for TxInitValidator { const CMD: &'static str = "init-validator"; @@ -1058,12 +1058,12 @@ pub mod cmds { "Send a signed transaction to create a new validator \ account.", ) - .add_args::() + .add_args::>() } } #[derive(Clone, Debug)] - pub struct Bond(pub args::Bond); + pub struct Bond(pub args::Bond::); impl SubCmd for Bond { const CMD: &'static str = "bond"; @@ -1077,12 +1077,12 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) .about("Bond tokens in PoS system.") - .add_args::() + .add_args::>() } } #[derive(Clone, Debug)] - pub struct Unbond(pub args::Unbond); + pub struct Unbond(pub args::Unbond::); impl SubCmd for Unbond { const CMD: &'static str = "unbond"; @@ -1096,12 +1096,12 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) .about("Unbond tokens from a PoS bond.") - .add_args::() + .add_args::>() } } #[derive(Clone, Debug)] - pub struct Withdraw(pub args::Withdraw); + pub struct Withdraw(pub args::Withdraw::); impl SubCmd for Withdraw { const CMD: &'static str = "withdraw"; @@ -1115,12 +1115,12 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) .about("Withdraw tokens from previously unbonded PoS bond.") - .add_args::() + .add_args::>() } } #[derive(Clone, Debug)] - pub struct QueryEpoch(pub args::Query); + pub struct QueryEpoch(pub args::Query::); impl SubCmd for QueryEpoch { const CMD: &'static str = "epoch"; @@ -1134,12 +1134,12 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) .about("Query the epoch of the last committed block.") - .add_args::() + .add_args::>() } } #[derive(Clone, Debug)] - pub struct QueryConversions(pub args::QueryConversions); + pub struct QueryConversions(pub args::QueryConversions::); impl SubCmd for QueryConversions { const CMD: &'static str = "conversions"; @@ -1153,12 +1153,12 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) .about("Query currently applicable conversions.") - .add_args::() + .add_args::>() } } #[derive(Clone, Debug)] - pub struct QueryBlock(pub args::Query); + pub struct QueryBlock(pub args::Query::); impl SubCmd for QueryBlock { const CMD: &'static str = "block"; @@ -1172,12 +1172,12 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) .about("Query the last committed block.") - .add_args::() + .add_args::>() } } #[derive(Clone, Debug)] - pub struct QueryBalance(pub args::QueryBalance); + pub struct QueryBalance(pub args::QueryBalance::); impl SubCmd for QueryBalance { const CMD: &'static str = "balance"; @@ -1191,12 +1191,12 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) .about("Query balance(s) of tokens.") - .add_args::() + .add_args::>() } } #[derive(Clone, Debug)] - pub struct QueryBonds(pub args::QueryBonds); + pub struct QueryBonds(pub args::QueryBonds::); impl SubCmd for QueryBonds { const CMD: &'static str = "bonds"; @@ -1210,12 +1210,12 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) .about("Query PoS bond(s).") - .add_args::() + .add_args::>() } } #[derive(Clone, Debug)] - pub struct QueryBondedStake(pub args::QueryBondedStake); + pub struct QueryBondedStake(pub args::QueryBondedStake::); impl SubCmd for QueryBondedStake { const CMD: &'static str = "bonded-stake"; @@ -1229,12 +1229,12 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) .about("Query PoS bonded stake.") - .add_args::() + .add_args::>() } } #[derive(Clone, Debug)] - pub struct QueryTransfers(pub args::QueryTransfers); + pub struct QueryTransfers(pub args::QueryTransfers::); impl SubCmd for QueryTransfers { const CMD: &'static str = "show-transfers"; @@ -1248,12 +1248,12 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) .about("Query the accepted transfers to date.") - .add_args::() + .add_args::>() } } #[derive(Clone, Debug)] - pub struct QueryCommissionRate(pub args::QueryCommissionRate); + pub struct QueryCommissionRate(pub args::QueryCommissionRate::); impl SubCmd for QueryCommissionRate { const CMD: &'static str = "commission-rate"; @@ -1267,12 +1267,12 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) .about("Query commission rate.") - .add_args::() + .add_args::>() } } #[derive(Clone, Debug)] - pub struct QuerySlashes(pub args::QuerySlashes); + pub struct QuerySlashes(pub args::QuerySlashes::); impl SubCmd for QuerySlashes { const CMD: &'static str = "slashes"; @@ -1289,12 +1289,12 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) .about("Query PoS applied slashes.") - .add_args::() + .add_args::>() } } #[derive(Clone, Debug)] - pub struct QueryRawBytes(pub args::QueryRawBytes); + pub struct QueryRawBytes(pub args::QueryRawBytes::); impl SubCmd for QueryRawBytes { const CMD: &'static str = "query-bytes"; @@ -1308,12 +1308,12 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) .about("Query the raw bytes of a given storage key") - .add_args::() + .add_args::>() } } #[derive(Clone, Debug)] - pub struct TxInitProposal(pub args::InitProposal); + pub struct TxInitProposal(pub args::InitProposal::); impl SubCmd for TxInitProposal { const CMD: &'static str = "init-proposal"; @@ -1330,12 +1330,12 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) .about("Create a new proposal.") - .add_args::() + .add_args::>() } } #[derive(Clone, Debug)] - pub struct TxVoteProposal(pub args::VoteProposal); + pub struct TxVoteProposal(pub args::VoteProposal::); impl SubCmd for TxVoteProposal { const CMD: &'static str = "vote-proposal"; @@ -1352,12 +1352,12 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) .about("Vote a proposal.") - .add_args::() + .add_args::>() } } #[derive(Clone, Debug)] - pub struct TxRevealPk(pub args::RevealPk); + pub struct TxRevealPk(pub args::RevealPk::); impl SubCmd for TxRevealPk { const CMD: &'static str = "reveal-pk"; @@ -1382,7 +1382,7 @@ pub mod cmds { signature verification on transactions authorized by \ this account.", ) - .add_args::() + .add_args::>() } } @@ -1687,7 +1687,7 @@ pub mod args { /// Transaction associated results arguments #[derive(Clone, Debug)] - pub struct QueryResult { + pub struct QueryResult { /// Common query args pub query: Query, /// Hash of transaction to lookup @@ -1695,7 +1695,7 @@ pub mod args { } impl QueryResult { - fn to_sdk(self, ctx: &mut Context) -> QueryResult { + pub fn to_sdk(self, ctx: &mut Context) -> QueryResult { QueryResult:: { query: self.query.to_sdk(ctx), tx_hash: self.tx_hash, @@ -1703,7 +1703,7 @@ pub mod args { } } - impl Args for QueryResult { + impl Args for QueryResult { fn parse(matches: &ArgMatches) -> Self { let query = Query::parse(matches); let tx_hash = TX_HASH.parse(matches); @@ -1711,7 +1711,7 @@ pub mod args { } fn def(app: App) -> App { - app.add_args::().arg( + app.add_args::>().arg( TX_HASH .def() .about("The hash of the transaction being looked up."), @@ -1721,7 +1721,7 @@ pub mod args { /// Custom transaction arguments #[derive(Clone, Debug)] - pub struct TxCustom { + pub struct TxCustom { /// Common tx arguments pub tx: Tx, /// Path to the tx WASM code file @@ -1731,7 +1731,7 @@ pub mod args { } impl TxCustom { - fn to_sdk(self, ctx: &mut Context) -> TxCustom { + pub fn to_sdk(self, ctx: &mut Context) -> TxCustom { TxCustom:: { tx: self.tx.to_sdk(ctx), code_path: self.code_path, @@ -1740,7 +1740,7 @@ pub mod args { } } - impl Args for TxCustom { + impl Args for TxCustom { fn parse(matches: &ArgMatches) -> Self { let tx = Tx::parse(matches); let code_path = CODE_PATH.parse(matches); @@ -1753,7 +1753,7 @@ pub mod args { } fn def(app: App) -> App { - app.add_args::() + app.add_args::>() .arg( CODE_PATH .def() @@ -1769,7 +1769,7 @@ pub mod args { /// Transfer transaction arguments #[derive(Clone, Debug)] - pub struct TxTransfer { + pub struct TxTransfer { /// Common tx arguments pub tx: Tx, /// Transfer source address @@ -1785,7 +1785,7 @@ pub mod args { } impl TxTransfer { - fn to_sdk(self, ctx: &mut Context) -> TxTransfer { + pub fn to_sdk(self, ctx: &mut Context) -> TxTransfer { TxTransfer:: { tx: self.tx.to_sdk(ctx), source: ctx.get_cached(&self.source), @@ -1797,7 +1797,7 @@ pub mod args { } } - impl Args for TxTransfer { + impl Args for TxTransfer { fn parse(matches: &ArgMatches) -> Self { let tx = Tx::parse(matches); let source = TRANSFER_SOURCE.parse(matches); @@ -1816,7 +1816,7 @@ pub mod args { } fn def(app: App) -> App { - app.add_args::() + app.add_args::>() .arg(TRANSFER_SOURCE.def().about( "The source account address. The source's key may be used \ to produce the signature.", @@ -1833,7 +1833,7 @@ pub mod args { /// IBC transfer transaction arguments #[derive(Clone, Debug)] - pub struct TxIbcTransfer { + pub struct TxIbcTransfer { /// Common tx arguments pub tx: Tx, /// Transfer source address @@ -1857,7 +1857,7 @@ pub mod args { } impl TxIbcTransfer { - fn to_sdk(self, ctx: &mut Context) -> TxIbcTransfer { + pub fn to_sdk(self, ctx: &mut Context) -> TxIbcTransfer { TxIbcTransfer:: { tx: self.tx.to_sdk(ctx), source: ctx.get(&self.source), @@ -1873,7 +1873,7 @@ pub mod args { } } - impl Args for TxIbcTransfer { + impl Args for TxIbcTransfer { fn parse(matches: &ArgMatches) -> Self { let tx = Tx::parse(matches); let source = SOURCE.parse(matches); @@ -1900,7 +1900,7 @@ pub mod args { } fn def(app: App) -> App { - app.add_args::() + app.add_args::>() .arg(SOURCE.def().about( "The source account address. The source's key is used to \ produce the signature.", @@ -1924,7 +1924,7 @@ pub mod args { /// Transaction to initialize a new account #[derive(Clone, Debug)] - pub struct TxInitAccount { + pub struct TxInitAccount { /// Common tx arguments pub tx: Tx, /// Address of the source account @@ -1936,7 +1936,7 @@ pub mod args { } impl TxInitAccount { - fn to_sdk(self, ctx: &mut Context) -> TxInitAccount { + pub fn to_sdk(self, ctx: &mut Context) -> TxInitAccount { TxInitAccount:: { tx: self.tx.to_sdk(ctx), source: ctx.get(&self.source), @@ -1946,7 +1946,7 @@ pub mod args { } } - impl Args for TxInitAccount { + impl Args for TxInitAccount { fn parse(matches: &ArgMatches) -> Self { let tx = Tx::parse(matches); let source = SOURCE.parse(matches); @@ -1961,7 +1961,7 @@ pub mod args { } fn def(app: App) -> App { - app.add_args::() + app.add_args::>() .arg(SOURCE.def().about( "The source account's address that signs the transaction.", )) @@ -1979,7 +1979,7 @@ pub mod args { /// Transaction to initialize a new account #[derive(Clone, Debug)] - pub struct TxInitValidator { + pub struct TxInitValidator { pub tx: Tx, pub source: C::Address, pub scheme: SchemeType, @@ -1993,7 +1993,7 @@ pub mod args { } impl TxInitValidator { - fn to_sdk(self, ctx: &mut Context) -> TxInitValidator { + pub fn to_sdk(self, ctx: &mut Context) -> TxInitValidator { TxInitValidator:: { tx: self.tx.to_sdk(ctx), source: ctx.get(&self.source), @@ -2009,7 +2009,7 @@ pub mod args { } } - impl Args for TxInitValidator { + impl Args for TxInitValidator { fn parse(matches: &ArgMatches) -> Self { let tx = Tx::parse(matches); let source = SOURCE.parse(matches); @@ -2037,7 +2037,7 @@ pub mod args { } fn def(app: App) -> App { - app.add_args::() + app.add_args::>() .arg(SOURCE.def().about( "The source account's address that signs the transaction.", )) @@ -2082,7 +2082,7 @@ pub mod args { /// Transaction to update a VP arguments #[derive(Clone, Debug)] - pub struct TxUpdateVp { + pub struct TxUpdateVp { /// Common tx arguments pub tx: Tx, /// Path to the VP WASM code file @@ -2092,7 +2092,7 @@ pub mod args { } impl TxUpdateVp { - fn to_sdk(self, ctx: &mut Context) -> TxUpdateVp { + pub fn to_sdk(self, ctx: &mut Context) -> TxUpdateVp { TxUpdateVp:: { tx: self.tx.to_sdk(ctx), vp_code_path: self.vp_code_path, @@ -2101,7 +2101,7 @@ pub mod args { } } - impl Args for TxUpdateVp { + impl Args for TxUpdateVp { fn parse(matches: &ArgMatches) -> Self { let tx = Tx::parse(matches); let vp_code_path = CODE_PATH.parse(matches); @@ -2114,7 +2114,7 @@ pub mod args { } fn def(app: App) -> App { - app.add_args::() + app.add_args::>() .arg( CODE_PATH.def().about( "The path to the new validity predicate WASM code.", @@ -2129,7 +2129,7 @@ pub mod args { /// Bond arguments #[derive(Clone, Debug)] - pub struct Bond { + pub struct Bond { /// Common tx arguments pub tx: Tx, /// Validator address @@ -2142,7 +2142,7 @@ pub mod args { } impl Bond { - fn to_sdk(self, ctx: &mut Context) -> Bond { + pub fn to_sdk(self, ctx: &mut Context) -> Bond { Bond:: { tx: self.tx.to_sdk(ctx), validator: ctx.get(&self.validator), @@ -2152,7 +2152,7 @@ pub mod args { } } - impl Args for Bond { + impl Args for Bond { fn parse(matches: &ArgMatches) -> Self { let tx = Tx::parse(matches); let validator = VALIDATOR.parse(matches); @@ -2167,7 +2167,7 @@ pub mod args { } fn def(app: App) -> App { - app.add_args::() + app.add_args::>() .arg(VALIDATOR.def().about("Validator address.")) .arg(AMOUNT.def().about("Amount of tokens to stake in a bond.")) .arg(SOURCE_OPT.def().about( @@ -2179,7 +2179,7 @@ pub mod args { /// Unbond arguments #[derive(Clone, Debug)] - pub struct Unbond { + pub struct Unbond { /// Common tx arguments pub tx: Tx, /// Validator address @@ -2192,7 +2192,7 @@ pub mod args { } impl Unbond { - fn to_sdk(self, ctx: &mut Context) -> Unbond { + pub fn to_sdk(self, ctx: &mut Context) -> Unbond { Unbond:: { tx: self.tx.to_sdk(ctx), validator: ctx.get(&self.validator), @@ -2202,7 +2202,7 @@ pub mod args { } } - impl Args for Unbond { + impl Args for Unbond { fn parse(matches: &ArgMatches) -> Self { let tx = Tx::parse(matches); let validator = VALIDATOR.parse(matches); @@ -2217,7 +2217,7 @@ pub mod args { } fn def(app: App) -> App { - app.add_args::() + app.add_args::>() .arg(VALIDATOR.def().about("Validator address.")) .arg( AMOUNT @@ -2233,7 +2233,7 @@ pub mod args { } #[derive(Clone, Debug)] - pub struct InitProposal { + pub struct InitProposal { /// Common tx arguments pub tx: Tx, /// The proposal file path @@ -2243,7 +2243,7 @@ pub mod args { } impl InitProposal { - fn to_sdk(self, ctx: &mut Context) -> InitProposal { + pub fn to_sdk(self, ctx: &mut Context) -> InitProposal { InitProposal:: { tx: self.tx.to_sdk(ctx), proposal_data: self.proposal_data, @@ -2252,7 +2252,7 @@ pub mod args { } } - impl Args for InitProposal { + impl Args for InitProposal { fn parse(matches: &ArgMatches) -> Self { let tx = Tx::parse(matches); let proposal_data = DATA_PATH.parse(matches); @@ -2266,7 +2266,7 @@ pub mod args { } fn def(app: App) -> App { - app.add_args::() + app.add_args::>() .arg(DATA_PATH.def().about( "The data path file (json) that describes the proposal.", )) @@ -2279,7 +2279,7 @@ pub mod args { } #[derive(Clone, Debug)] - pub struct VoteProposal { + pub struct VoteProposal { /// Common tx arguments pub tx: Tx, /// Proposal id @@ -2293,7 +2293,7 @@ pub mod args { } impl VoteProposal { - fn to_sdk(self, ctx: &mut Context) -> VoteProposal { + pub fn to_sdk(self, ctx: &mut Context) -> VoteProposal { VoteProposal:: { tx: self.tx.to_sdk(ctx), proposal_id: self.proposal_id, @@ -2304,7 +2304,7 @@ pub mod args { } } - impl Args for VoteProposal { + impl Args for VoteProposal { fn parse(matches: &ArgMatches) -> Self { let tx = Tx::parse(matches); let proposal_id = PROPOSAL_ID_OPT.parse(matches); @@ -2322,7 +2322,7 @@ pub mod args { } fn def(app: App) -> App { - app.add_args::() + app.add_args::>() .arg( PROPOSAL_ID_OPT .def() @@ -2356,7 +2356,7 @@ pub mod args { } #[derive(Clone, Debug)] - pub struct RevealPk { + pub struct RevealPk { /// Common tx arguments pub tx: Tx, /// A public key to be revealed on-chain @@ -2364,7 +2364,7 @@ pub mod args { } impl RevealPk { - fn to_sdk(self, ctx: &mut Context) -> RevealPk { + pub fn to_sdk(self, ctx: &mut Context) -> RevealPk { RevealPk:: { tx: self.tx.to_sdk(ctx), public_key: ctx.get_cached(&self.public_key), @@ -2372,7 +2372,7 @@ pub mod args { } } - impl Args for RevealPk { + impl Args for RevealPk { fn parse(matches: &ArgMatches) -> Self { let tx = Tx::parse(matches); let public_key = PUBLIC_KEY.parse(matches); @@ -2381,13 +2381,13 @@ pub mod args { } fn def(app: App) -> App { - app.add_args::() + app.add_args::>() .arg(PUBLIC_KEY.def().about("A public key to reveal.")) } } #[derive(Clone, Debug)] - pub struct QueryProposal { + pub struct QueryProposal { /// Common query args pub query: Query, /// Proposal id @@ -2395,7 +2395,7 @@ pub mod args { } impl QueryProposal { - fn to_sdk(self, ctx: &mut Context) -> QueryProposal { + pub fn to_sdk(self, ctx: &mut Context) -> QueryProposal { QueryProposal:: { query: self.query.to_sdk(ctx), proposal_id: self.proposal_id, @@ -2403,7 +2403,7 @@ pub mod args { } } - impl Args for QueryProposal { + impl Args for QueryProposal { fn parse(matches: &ArgMatches) -> Self { let query = Query::parse(matches); let proposal_id = PROPOSAL_ID_OPT.parse(matches); @@ -2412,13 +2412,13 @@ pub mod args { } fn def(app: App) -> App { - app.add_args::() + app.add_args::>() .arg(PROPOSAL_ID_OPT.def().about("The proposal identifier.")) } } #[derive(Clone, Debug)] - pub struct QueryProposalResult { + pub struct QueryProposalResult { /// Common query args pub query: Query, /// Proposal id @@ -2430,7 +2430,7 @@ pub mod args { } impl QueryProposalResult { - fn to_sdk(self, ctx: &mut Context) -> QueryProposalResult { + pub fn to_sdk(self, ctx: &mut Context) -> QueryProposalResult { QueryProposalResult:: { query: self.query.to_sdk(ctx), proposal_id: self.proposal_id, @@ -2440,7 +2440,7 @@ pub mod args { } } - impl Args for QueryProposalResult { + impl Args for QueryProposalResult { fn parse(matches: &ArgMatches) -> Self { let query = Query::parse(matches); let proposal_id = PROPOSAL_ID_OPT.parse(matches); @@ -2456,7 +2456,7 @@ pub mod args { } fn def(app: App) -> App { - app.add_args::() + app.add_args::>() .arg(PROPOSAL_ID_OPT.def().about("The proposal identifier.")) .arg( PROPOSAL_OFFLINE @@ -2480,20 +2480,20 @@ pub mod args { } #[derive(Clone, Debug)] - pub struct QueryProtocolParameters { + pub struct QueryProtocolParameters { /// Common query args pub query: Query, } impl QueryProtocolParameters { - fn to_sdk(self, ctx: &mut Context) -> QueryProtocolParameters { + pub fn to_sdk(self, ctx: &mut Context) -> QueryProtocolParameters { QueryProtocolParameters:: { query: self.query.to_sdk(ctx), } } } - impl Args for QueryProtocolParameters { + impl Args for QueryProtocolParameters { fn parse(matches: &ArgMatches) -> Self { let query = Query::parse(matches); @@ -2501,13 +2501,13 @@ pub mod args { } fn def(app: App) -> App { - app.add_args::() + app.add_args::>() } } /// Withdraw arguments #[derive(Clone, Debug)] - pub struct Withdraw { + pub struct Withdraw { /// Common tx arguments pub tx: Tx, /// Validator address @@ -2518,7 +2518,7 @@ pub mod args { } impl Withdraw { - fn to_sdk(self, ctx: &mut Context) -> Withdraw { + pub fn to_sdk(self, ctx: &mut Context) -> Withdraw { Withdraw:: { tx: self.tx.to_sdk(ctx), validator: ctx.get(&self.validator), @@ -2527,7 +2527,7 @@ pub mod args { } } - impl Args for Withdraw { + impl Args for Withdraw { fn parse(matches: &ArgMatches) -> Self { let tx = Tx::parse(matches); let validator = VALIDATOR.parse(matches); @@ -2540,7 +2540,7 @@ pub mod args { } fn def(app: App) -> App { - app.add_args::() + app.add_args::>() .arg(VALIDATOR.def().about("Validator address.")) .arg(SOURCE_OPT.def().about( "Source address for withdrawing from delegations. For \ @@ -2552,7 +2552,7 @@ pub mod args { /// Query asset conversions #[derive(Clone, Debug)] - pub struct QueryConversions { + pub struct QueryConversions { /// Common query args pub query: Query, /// Address of a token @@ -2562,7 +2562,7 @@ pub mod args { } impl QueryConversions { - fn to_sdk(self, ctx: &mut Context) -> QueryConversions { + pub fn to_sdk(self, ctx: &mut Context) -> QueryConversions { QueryConversions:: { query: self.query.to_sdk(ctx), token: self.token.map(|x| ctx.get(&x)), @@ -2571,7 +2571,7 @@ pub mod args { } } - impl Args for QueryConversions { + impl Args for QueryConversions { fn parse(matches: &ArgMatches) -> Self { let query = Query::parse(matches); let token = TOKEN_OPT.parse(matches); @@ -2584,7 +2584,7 @@ pub mod args { } fn def(app: App) -> App { - app.add_args::() + app.add_args::>() .arg( EPOCH .def() @@ -2600,7 +2600,7 @@ pub mod args { /// Query token balance(s) #[derive(Clone, Debug)] - pub struct QueryBalance { + pub struct QueryBalance { /// Common query args pub query: Query, /// Address of an owner @@ -2614,7 +2614,7 @@ pub mod args { } impl QueryBalance { - fn to_sdk(self, ctx: &mut Context) -> QueryBalance { + pub fn to_sdk(self, ctx: &mut Context) -> QueryBalance { QueryBalance:: { query: self.query.to_sdk(ctx), owner: self.owner.map(|x| ctx.get_cached(&x)), @@ -2625,7 +2625,7 @@ pub mod args { } } - impl Args for QueryBalance { + impl Args for QueryBalance { fn parse(matches: &ArgMatches) -> Self { let query = Query::parse(matches); let owner = BALANCE_OWNER.parse(matches); @@ -2642,7 +2642,7 @@ pub mod args { } fn def(app: App) -> App { - app.add_args::() + app.add_args::>() .arg( BALANCE_OWNER .def() @@ -2668,7 +2668,7 @@ pub mod args { /// Query historical transfer(s) #[derive(Clone, Debug)] - pub struct QueryTransfers { + pub struct QueryTransfers { /// Common query args pub query: Query, /// Address of an owner @@ -2678,7 +2678,7 @@ pub mod args { } impl QueryTransfers { - fn to_sdk(self, ctx: &mut Context) -> QueryTransfers { + pub fn to_sdk(self, ctx: &mut Context) -> QueryTransfers { QueryTransfers:: { query: self.query.to_sdk(ctx), owner: self.owner.map(|x| ctx.get_cached(&x)), @@ -2687,7 +2687,7 @@ pub mod args { } } - impl Args for QueryTransfers { + impl Args for QueryTransfers { fn parse(matches: &ArgMatches) -> Self { let query = Query::parse(matches); let owner = BALANCE_OWNER.parse(matches); @@ -2700,7 +2700,7 @@ pub mod args { } fn def(app: App) -> App { - app.add_args::() + app.add_args::>() .arg(BALANCE_OWNER.def().about( "The account address that queried transfers must involve.", )) @@ -2712,7 +2712,7 @@ pub mod args { /// Query PoS bond(s) #[derive(Clone, Debug)] - pub struct QueryBonds { + pub struct QueryBonds { /// Common query args pub query: Query, /// Address of an owner @@ -2722,7 +2722,7 @@ pub mod args { } impl QueryBonds { - fn to_sdk(self, ctx: &mut Context) -> QueryBonds { + pub fn to_sdk(self, ctx: &mut Context) -> QueryBonds { QueryBonds:: { query: self.query.to_sdk(ctx), owner: self.owner.map(|x| ctx.get(&x)), @@ -2731,7 +2731,7 @@ pub mod args { } } - impl Args for QueryBonds { + impl Args for QueryBonds { fn parse(matches: &ArgMatches) -> Self { let query = Query::parse(matches); let owner = OWNER.parse(matches); @@ -2744,7 +2744,7 @@ pub mod args { } fn def(app: App) -> App { - app.add_args::() + app.add_args::>() .arg( OWNER.def().about( "The owner account address whose bonds to query.", @@ -2760,7 +2760,7 @@ pub mod args { /// Query PoS bonded stake #[derive(Clone, Debug)] - pub struct QueryBondedStake { + pub struct QueryBondedStake { /// Common query args pub query: Query, /// Address of a validator @@ -2770,7 +2770,7 @@ pub mod args { } impl QueryBondedStake { - fn to_sdk(self, ctx: &mut Context) -> QueryBondedStake { + pub fn to_sdk(self, ctx: &mut Context) -> QueryBondedStake { QueryBondedStake:: { query: self.query.to_sdk(ctx), validator: self.validator.map(|x| ctx.get(&x)), @@ -2779,7 +2779,7 @@ pub mod args { } } - impl Args for QueryBondedStake { + impl Args for QueryBondedStake { fn parse(matches: &ArgMatches) -> Self { let query = Query::parse(matches); let validator = VALIDATOR_OPT.parse(matches); @@ -2792,7 +2792,7 @@ pub mod args { } fn def(app: App) -> App { - app.add_args::() + app.add_args::>() .arg(VALIDATOR_OPT.def().about( "The validator's address whose bonded stake to query.", )) @@ -2805,7 +2805,7 @@ pub mod args { #[derive(Clone, Debug)] /// Commission rate change args - pub struct TxCommissionRateChange { + pub struct TxCommissionRateChange { /// Common tx arguments pub tx: Tx, /// Validator address (should be self) @@ -2815,7 +2815,7 @@ pub mod args { } impl TxCommissionRateChange { - fn to_sdk(self, ctx: &mut Context) -> TxCommissionRateChange { + pub fn to_sdk(self, ctx: &mut Context) -> TxCommissionRateChange { TxCommissionRateChange:: { tx: self.tx.to_sdk(ctx), validator: ctx.get(&self.validator), @@ -2824,7 +2824,7 @@ pub mod args { } } - impl Args for TxCommissionRateChange { + impl Args for TxCommissionRateChange { fn parse(matches: &ArgMatches) -> Self { let tx = Tx::parse(matches); let validator = VALIDATOR.parse(matches); @@ -2837,7 +2837,7 @@ pub mod args { } fn def(app: App) -> App { - app.add_args::() + app.add_args::>() .arg(VALIDATOR.def().about( "The validator's address whose commission rate to change.", )) @@ -2851,7 +2851,7 @@ pub mod args { /// Query PoS commission rate #[derive(Clone, Debug)] - pub struct QueryCommissionRate { + pub struct QueryCommissionRate { /// Common query args pub query: Query, /// Address of a validator @@ -2861,7 +2861,7 @@ pub mod args { } impl QueryCommissionRate { - fn to_sdk(self, ctx: &mut Context) -> QueryCommissionRate { + pub fn to_sdk(self, ctx: &mut Context) -> QueryCommissionRate { QueryCommissionRate:: { query: self.query.to_sdk(ctx), validator: ctx.get(&self.validator), @@ -2870,7 +2870,7 @@ pub mod args { } } - impl Args for QueryCommissionRate { + impl Args for QueryCommissionRate { fn parse(matches: &ArgMatches) -> Self { let query = Query::parse(matches); let validator = VALIDATOR.parse(matches); @@ -2883,7 +2883,7 @@ pub mod args { } fn def(app: App) -> App { - app.add_args::() + app.add_args::>() .arg(VALIDATOR.def().about( "The validator's address whose commission rate to query.", )) @@ -2896,7 +2896,7 @@ pub mod args { /// Query PoS slashes #[derive(Clone, Debug)] - pub struct QuerySlashes { + pub struct QuerySlashes { /// Common query args pub query: Query, /// Address of a validator @@ -2904,7 +2904,7 @@ pub mod args { } impl QuerySlashes { - fn to_sdk(self, ctx: &mut Context) -> QuerySlashes { + pub fn to_sdk(self, ctx: &mut Context) -> QuerySlashes { QuerySlashes:: { query: self.query.to_sdk(ctx), validator: self.validator.map(|x| ctx.get(&x)), @@ -2912,7 +2912,7 @@ pub mod args { } } - impl Args for QuerySlashes { + impl Args for QuerySlashes { fn parse(matches: &ArgMatches) -> Self { let query = Query::parse(matches); let validator = VALIDATOR_OPT.parse(matches); @@ -2920,7 +2920,7 @@ pub mod args { } fn def(app: App) -> App { - app.add_args::().arg( + app.add_args::>().arg( VALIDATOR_OPT .def() .about("The validator's address whose slashes to query."), @@ -2929,7 +2929,7 @@ pub mod args { } /// Query the raw bytes of given storage key #[derive(Clone, Debug)] - pub struct QueryRawBytes { + pub struct QueryRawBytes { /// The storage key to query pub storage_key: storage::Key, /// Common query args @@ -2937,7 +2937,7 @@ pub mod args { } impl QueryRawBytes { - fn to_sdk(self, ctx: &mut Context) -> QueryRawBytes { + pub fn to_sdk(self, ctx: &mut Context) -> QueryRawBytes { QueryRawBytes:: { query: self.query.to_sdk(ctx), storage_key: self.storage_key, @@ -2945,7 +2945,7 @@ pub mod args { } } - impl Args for QueryRawBytes { + impl Args for QueryRawBytes { fn parse(matches: &ArgMatches) -> Self { let storage_key = STORAGE_KEY.parse(matches); let query = Query::parse(matches); @@ -2953,7 +2953,7 @@ pub mod args { } fn def(app: App) -> App { - app.add_args::() + app.add_args::>() .arg(STORAGE_KEY.def().about("Storage key")) } } @@ -3016,7 +3016,7 @@ pub mod args { /// Common transaction arguments #[derive(Clone, Debug)] - pub struct Tx { + pub struct Tx { /// Simulate applying the transaction pub dry_run: bool, /// Submit the transaction even if it doesn't pass client checks @@ -3041,7 +3041,7 @@ pub mod args { } impl Tx { - fn to_sdk(self, ctx: &mut Context) -> Tx { + pub fn to_sdk(self, ctx: &mut Context) -> Tx { Tx:: { dry_run: self.dry_run, force: self.force, @@ -3057,7 +3057,7 @@ pub mod args { } } - impl Args for Tx { + impl Args for Tx { fn def(app: App) -> App { app.arg( DRY_RUN_TX @@ -3137,20 +3137,20 @@ pub mod args { /// Common query arguments #[derive(Clone, Debug)] - pub struct Query { + pub struct Query { /// The address of the ledger node as host:port pub ledger_address: C::TendermintAddress, } impl Query { - fn to_sdk(self, ctx: &mut Context) -> Query { + pub fn to_sdk(self, ctx: &mut Context) -> Query { Query:: { ledger_address: (), } } } - impl Args for Query { + impl Args for Query { fn def(app: App) -> App { app.arg(LEDGER_ADDRESS_DEFAULT.def().about(LEDGER_ADDRESS_ABOUT)) } @@ -3236,7 +3236,7 @@ pub mod args { /// MASP generate payment address arguments #[derive(Clone, Debug)] - pub struct MaspPayAddrGen { + pub struct MaspPayAddrGen { /// Key alias pub alias: String, /// Viewing key @@ -3246,7 +3246,7 @@ pub mod args { } impl MaspPayAddrGen { - fn to_sdk(self, ctx: &mut Context) -> MaspPayAddrGen { + pub fn to_sdk(self, ctx: &mut Context) -> MaspPayAddrGen { MaspPayAddrGen:: { alias: self.alias, viewing_key: ctx.get_cached(&self.viewing_key), @@ -3255,7 +3255,7 @@ pub mod args { } } - impl Args for MaspPayAddrGen { + impl Args for MaspPayAddrGen { fn parse(matches: &ArgMatches) -> Self { let alias = ALIAS.parse(matches); let viewing_key = VIEWING_KEY.parse(matches); diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index 5f0533db3f..fa1f6a7e5b 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -65,8 +65,8 @@ use crate::facade::tendermint_rpc::{ /// If a response is not delivered until `deadline`, we exit the cli with an /// error. pub async fn query_tx_status( + client: &HttpClient, status: TxEventQuery<'_>, - address: TendermintAddress, deadline: Instant, ) -> Event { const ONE_SECOND: Duration = Duration::from_secs(1); @@ -84,7 +84,6 @@ pub async fn query_tx_status( *backoff += ONE_SECOND; } tokio::time::timeout_at(deadline, async move { - let client = HttpClient::new(address).unwrap(); let mut backoff = ONE_SECOND; loop { @@ -112,18 +111,17 @@ pub async fn query_tx_status( } /// Query the epoch of the last committed block -pub async fn query_epoch(args: args::Query) -> Epoch { - let client = HttpClient::new(args.ledger_address).unwrap(); - let epoch = unwrap_client_response(RPC.shell().epoch(&client).await); +pub async fn query_epoch(client: &HttpClient) -> Epoch { + let epoch = unwrap_client_response(RPC.shell().epoch(&client.clone()).await); println!("Last committed epoch: {}", epoch); epoch } /// Query the last committed block pub async fn query_block( + client: &HttpClient, args: args::Query, ) -> crate::facade::tendermint_rpc::endpoint::block::Response { - let client = HttpClient::new(args.ledger_address).unwrap(); let response = client.latest_block().await.unwrap(); println!( "Last committed block ID: {}, height: {}, time: {}", @@ -135,21 +133,18 @@ pub async fn query_block( } /// Query the results of the last committed block -pub async fn query_results(args: args::Query) -> Vec { - let client = HttpClient::new(args.ledger_address).unwrap(); - unwrap_client_response(RPC.shell().read_results(&client).await) +pub async fn query_results(client: &HttpClient) -> Vec { + unwrap_client_response(RPC.shell().read_results(&client.clone()).await) } /// 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 query_token = args.token; + let query_owner = args.owner .map_or_else( || Either::Right(ctx.wallet.get_addresses().into_values().collect()), Either::Left, ); - // Build up the context that will be queried for asset decodings - ctx.shielded.utils.ledger_address = Some(args.query.ledger_address.clone()); let _ = ctx.shielded.load(); // Obtain the effects of all shielded and transparent transactions let transfers = ctx.shielded.query_tx_deltas( @@ -260,11 +255,10 @@ pub async fn query_transfers(mut ctx: Context, args: args::QueryTransfers) { } /// Query the raw bytes of given storage key -pub async fn query_raw_bytes(_ctx: Context, args: args::QueryRawBytes) { - let client = HttpClient::new(args.query.ledger_address).unwrap(); +pub async fn query_raw_bytes(client: &HttpClient, _ctx: Context, args: args::QueryRawBytes) { let response = unwrap_client_response( RPC.shell() - .storage_value(&client, None, None, false, &args.storage_key) + .storage_value(client, None, None, false, &args.storage_key) .await, ); if !response.data.is_empty() { @@ -275,15 +269,15 @@ 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) { +pub async fn query_balance(client: &HttpClient, mut ctx: Context, args: args::QueryBalance) { // 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 { Some(BalanceOwner::FullViewingKey(_viewing_key)) => { - query_shielded_balance(&mut ctx, args).await + query_shielded_balance(client, &mut ctx, args).await } Some(BalanceOwner::Address(_owner)) => { - query_transparent_balance(&mut ctx, args).await + query_transparent_balance(client, &mut ctx, args).await } Some(BalanceOwner::PaymentAddress(_owner)) => { query_pinned_balance(&mut ctx, args).await @@ -292,24 +286,22 @@ pub async fn query_balance(mut ctx: Context, args: args::QueryBalance) { // Print pinned balance query_pinned_balance(&mut ctx, args.clone()).await; // Print shielded balance - query_shielded_balance(&mut ctx, args.clone()).await; + query_shielded_balance(client, &mut ctx, args.clone()).await; // Then print transparent balance - query_transparent_balance(&mut ctx, args).await; + query_transparent_balance(client, &mut ctx, args).await; } }; } /// Query token balance(s) pub async fn query_transparent_balance( + client: &HttpClient, ctx: &mut Context, args: args::QueryBalance, ) { - let client = HttpClient::new(args.query.ledger_address).unwrap(); let tokens = address::tokens(); match (args.token, args.owner) { (Some(token), Some(owner)) => { - let token = ctx.get(&token); - let owner = ctx.get_cached(&owner); let key = match &args.sub_prefix { Some(sub_prefix) => { let sub_prefix = Key::parse(sub_prefix).unwrap(); @@ -342,7 +334,6 @@ pub async fn query_transparent_balance( } } (None, Some(owner)) => { - let owner = ctx.get_cached(&owner); for (token, _) in tokens { let prefix = token.to_db_key().into(); let balances = @@ -359,7 +350,6 @@ pub async fn query_transparent_balance( } } (Some(token), None) => { - let token = ctx.get(&token); let prefix = token.to_db_key().into(); let balances = query_storage_prefix::(&client, &prefix).await; @@ -386,7 +376,7 @@ pub async fn query_pinned_balance(ctx: &mut Context, args: args::QueryBalance) { let tokens = address::tokens(); let owners = if let Some(pa) = args .owner - .and_then(|x| ctx.get_cached(&x).payment_address()) + .and_then(|x| x.payment_address()) { vec![pa] } else { @@ -403,8 +393,6 @@ pub async fn query_pinned_balance(ctx: &mut Context, args: args::QueryBalance) { .values() .map(|fvk| ExtendedFullViewingKey::from(*fvk).fvk.vk) .collect(); - // Build up the context that will be queried for asset decodings - ctx.shielded.utils.ledger_address = Some(args.query.ledger_address.clone()); let _ = ctx.shielded.load(); // Print the token balances by payment address for owner in owners { @@ -456,7 +444,6 @@ pub async fn query_pinned_balance(ctx: &mut Context, args: args::QueryBalance) { println!("Payment address {} has not yet been consumed.", owner) } (Ok((balance, epoch)), Some(token)) => { - let token = ctx.get(token); // Extract and print only the specified token from the total let (_asset_type, balance) = value_by_address(&balance, token.clone(), epoch); @@ -580,7 +567,7 @@ fn print_balances( } /// Query Proposals -pub async fn query_proposal(_ctx: Context, args: args::QueryProposal) { +pub async fn query_proposal(client: &HttpClient, _ctx: Context, args: args::QueryProposal) { async fn print_proposal( client: &HttpClient, id: u64, @@ -659,8 +646,7 @@ pub async fn query_proposal(_ctx: Context, args: args::QueryProposal) { Some(()) } - let client = HttpClient::new(args.query.ledger_address.clone()).unwrap(); - let current_epoch = query_epoch(args.query.clone()).await; + let current_epoch = query_epoch(client).await; match args.proposal_id { Some(id) => { if print_proposal(&client, id, current_epoch, true) @@ -708,6 +694,7 @@ pub fn value_by_address( /// Query token shielded balance(s) pub async fn query_shielded_balance( + client: &HttpClient, ctx: &mut Context, args: args::QueryBalance, ) { @@ -715,7 +702,7 @@ pub async fn query_shielded_balance( // printed let owner = args .owner - .and_then(|x| ctx.get_cached(&x).full_viewing_key()); + .and_then(|x| x.full_viewing_key()); // Used to control whether conversions are automatically performed let no_conversions = args.no_conversions; // Viewing keys are used to query shielded balances. If a spending key is @@ -724,8 +711,6 @@ pub async fn query_shielded_balance( Some(viewing_key) => vec![viewing_key], None => ctx.wallet.get_viewing_keys().values().copied().collect(), }; - // Build up the context that will be queried for balances - ctx.shielded.utils.ledger_address = Some(args.query.ledger_address.clone()); let _ = ctx.shielded.load(); let fvks: Vec<_> = viewing_keys .iter() @@ -735,7 +720,7 @@ pub async fn query_shielded_balance( // Save the update state so that future fetches can be short-circuited let _ = ctx.shielded.save(); // The epoch is required to identify timestamped tokens - let epoch = query_epoch(args.query.clone()).await; + let epoch = query_epoch(client).await; // Map addresses to token names let tokens = address::tokens(); match (args.token, owner.is_some()) { @@ -758,7 +743,7 @@ pub async fn query_shielded_balance( .expect("context should contain viewing key") }; // Compute the unique asset identifier from the token address - let token = ctx.get(&token); + let token = token; let asset_type = AssetType::new( (token.clone(), epoch.0) .try_to_vec() @@ -862,7 +847,7 @@ pub async fn query_shielded_balance( // users (Some(token), false) => { // Compute the unique asset identifier from the token address - let token = ctx.get(&token); + let token = token; let asset_type = AssetType::new( (token.clone(), epoch.0) .try_to_vec() @@ -993,11 +978,11 @@ pub async fn get_token_balance( } pub async fn query_proposal_result( + client: &HttpClient, _ctx: Context, args: args::QueryProposalResult, ) { - let client = HttpClient::new(args.query.ledger_address.clone()).unwrap(); - let current_epoch = query_epoch(args.query.clone()).await; + let current_epoch = query_epoch(client).await; match args.proposal_id { Some(id) => { @@ -1086,8 +1071,8 @@ pub async fn query_proposal_result( ); let public_key = get_public_key( + client, &proposal.address, - args.query.ledger_address.clone(), ) .await .expect("Public key should exist."); @@ -1128,11 +1113,10 @@ pub async fn query_proposal_result( } pub async fn query_protocol_parameters( + client: &HttpClient, _ctx: Context, args: args::QueryProtocolParameters, ) { - let client = HttpClient::new(args.query.ledger_address).unwrap(); - let gov_parameters = get_governance_parameters(&client).await; println!("Governance Parameters\n {:4}", gov_parameters); @@ -1199,13 +1183,12 @@ pub async fn query_protocol_parameters( } /// Query PoS bond(s) -pub async fn query_bonds(ctx: Context, args: args::QueryBonds) { - let epoch = query_epoch(args.query.clone()).await; - let client = HttpClient::new(args.query.ledger_address).unwrap(); +pub async fn query_bonds(client: &HttpClient, ctx: Context, args: args::QueryBonds) { + let epoch = query_epoch(client).await; match (args.owner, args.validator) { (Some(owner), Some(validator)) => { - let source = ctx.get(&owner); - let validator = ctx.get(&validator); + let source = owner; + let validator = validator; // Find owner's delegations to the given validator let bond_id = pos::BondId { source, validator }; let bond_key = pos::bond_key(&bond_id); @@ -1261,7 +1244,7 @@ pub async fn query_bonds(ctx: Context, args: args::QueryBonds) { } } (None, Some(validator)) => { - let validator = ctx.get(&validator); + let validator = validator; // Find validator's self-bonds let bond_id = pos::BondId { source: validator.clone(), @@ -1308,7 +1291,7 @@ pub async fn query_bonds(ctx: Context, args: args::QueryBonds) { } } (Some(owner), None) => { - let owner = ctx.get(&owner); + let owner = owner; // Find owner's bonds to any validator let bonds_prefix = pos::bonds_for_source_prefix(&owner); let bonds = @@ -1546,12 +1529,11 @@ 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) { +pub async fn query_bonded_stake(client: &HttpClient, ctx: Context, args: args::QueryBondedStake) { let epoch = match args.epoch { Some(epoch) => epoch, - None => query_epoch(args.query.clone()).await, + None => query_epoch(client).await, }; - let client = HttpClient::new(args.query.ledger_address).unwrap(); // Find the validator set let validator_set_key = pos::validator_set_key(); @@ -1565,7 +1547,7 @@ pub async fn query_bonded_stake(ctx: Context, args: args::QueryBondedStake) { match args.validator { Some(validator) => { - let validator = ctx.get(&validator); + let validator = validator; // Find bonded stake for the given validator let validator_deltas_key = pos::validator_deltas_key(&validator); let validator_deltas = query_storage_value::( @@ -1647,17 +1629,17 @@ pub async fn query_bonded_stake(ctx: Context, args: args::QueryBondedStake) { /// Query PoS validator's commission rate pub async fn query_commission_rate( + client: &HttpClient, ctx: Context, args: args::QueryCommissionRate, ) { let epoch = match args.epoch { Some(epoch) => epoch, - None => query_epoch(args.query.clone()).await, + None => query_epoch(client).await, }; - let client = HttpClient::new(args.query.ledger_address.clone()).unwrap(); - let validator = ctx.get(&args.validator); + let validator = args.validator; let is_validator = - is_validator(&validator, args.query.ledger_address).await; + is_validator(client, &validator).await; if is_validator { let validator_commission_key = @@ -1702,11 +1684,10 @@ pub async fn query_commission_rate( } /// Query PoS slashes -pub async fn query_slashes(ctx: Context, args: args::QuerySlashes) { - let client = HttpClient::new(args.query.ledger_address).unwrap(); +pub async fn query_slashes(client: &HttpClient, ctx: Context, args: args::QuerySlashes) { match args.validator { Some(validator) => { - let validator = ctx.get(&validator); + let validator = validator; // Find slashes for the given validator let slashes_key = pos::validator_slashes_key(&validator); let slashes = @@ -1772,11 +1753,10 @@ pub async fn query_slashes(ctx: Context, args: args::QuerySlashes) { } /// Dry run a transaction -pub async fn dry_run_tx(ledger_address: &TendermintAddress, tx_bytes: Vec) { - let client = HttpClient::new(ledger_address.clone()).unwrap(); +pub async fn dry_run_tx(client: &HttpClient, tx_bytes: Vec) { let (data, height, prove) = (Some(tx_bytes), None, false); let result = unwrap_client_response( - RPC.shell().dry_run_tx(&client, data, height, prove).await, + RPC.shell().dry_run_tx(client, data, height, prove).await, ) .data; println!("Dry-run result: {}", result); @@ -1784,29 +1764,26 @@ pub async fn dry_run_tx(ledger_address: &TendermintAddress, tx_bytes: Vec) { /// Get account's public key stored in its storage sub-space pub async fn get_public_key( + client: &HttpClient, address: &Address, - ledger_address: TendermintAddress, ) -> Option { - let client = HttpClient::new(ledger_address).unwrap(); let key = pk_key(address); query_storage_value(&client, &key).await } /// Check if the given address is a known validator. pub async fn is_validator( + client: &HttpClient, address: &Address, - ledger_address: TendermintAddress, ) -> bool { - let client = HttpClient::new(ledger_address).unwrap(); - unwrap_client_response(RPC.vp().pos().is_validator(&client, address).await) + unwrap_client_response(RPC.vp().pos().is_validator(client, address).await) } /// Check if a given address is a known delegator pub async fn is_delegator( + client: &HttpClient, address: &Address, - ledger_address: TendermintAddress, ) -> bool { - let client = HttpClient::new(ledger_address).unwrap(); let bonds_prefix = pos::bonds_for_source_prefix(address); let bonds = query_storage_prefix::(&client, &bonds_prefix).await; @@ -1831,10 +1808,9 @@ pub async fn is_delegator_at( /// stored validity predicate. Implicit and internal addresses always return /// true. pub async fn known_address( + client: &HttpClient, address: &Address, - ledger_address: TendermintAddress, ) -> bool { - let client = HttpClient::new(ledger_address).unwrap(); match address { Address::Established(_) => { // Established account exists if it has a VP @@ -1980,12 +1956,11 @@ fn process_unbonds_query( } /// Query for all conversions. -pub async fn query_conversions(ctx: Context, args: args::QueryConversions) { +pub async fn query_conversions(client: &HttpClient, ctx: Context, args: args::QueryConversions) { // The chosen token type of the conversions - let target_token = args.token.as_ref().map(|x| ctx.get(x)); + let target_token = args.token; // To facilitate human readable token addresses let tokens = address::tokens(); - let client = HttpClient::new(args.query.ledger_address).unwrap(); let masp_addr = masp(); let key_prefix: Key = masp_addr.to_db_key().into(); let state_key = key_prefix @@ -2225,12 +2200,9 @@ pub async fn query_tx_events( /// Lookup the full response accompanying the specified transaction event // TODO: maybe remove this in favor of `query_tx_status` pub async fn query_tx_response( - ledger_address: &TendermintAddress, + client: &WebSocketClient, tx_query: TxEventQuery<'_>, ) -> Result { - // Connect to the Tendermint server holding the transactions - let (client, driver) = WebSocketClient::new(ledger_address.clone()).await?; - let driver_handle = tokio::spawn(async move { driver.run().await }); // Find all blocks that apply a transaction with the specified hash let blocks = &client .block_search(tx_query.into(), 1, 255, Order::Ascending) @@ -2293,22 +2265,15 @@ pub async fn query_tx_response( ) .unwrap_or_default(), }; - // Signal to the driver to terminate. - client.close()?; - // Await the driver's termination to ensure proper connection closure. - let _ = driver_handle.await.unwrap_or_else(|x| { - eprintln!("{}", x); - cli::safe_exit(1) - }); Ok(result) } /// Lookup the results of applying the specified transaction to the /// blockchain. -pub async fn query_result(_ctx: Context, args: args::QueryResult) { +pub async fn query_result(client: &WebSocketClient, _ctx: Context, args: args::QueryResult) { // First try looking up application event pertaining to given hash. let tx_response = query_tx_response( - &args.query.ledger_address, + client, TxEventQuery::Applied(&args.tx_hash), ) .await; @@ -2322,7 +2287,7 @@ pub async fn query_result(_ctx: Context, args: args::QueryResult) { Err(err1) => { // If this fails then instead look for an acceptance event. let tx_response = query_tx_response( - &args.query.ledger_address, + client, TxEventQuery::Accepted(&args.tx_hash), ) .await; diff --git a/apps/src/lib/client/signing.rs b/apps/src/lib/client/signing.rs index ed7ab484a9..5192a16f89 100644 --- a/apps/src/lib/client/signing.rs +++ b/apps/src/lib/client/signing.rs @@ -7,6 +7,7 @@ use namada::types::address::{Address, ImplicitAddress}; use namada::types::key::*; use namada::types::storage::Epoch; use namada::types::transaction::{hash_tx, Fee, WrapperTx}; +use tendermint_rpc::HttpClient; use super::rpc; use crate::cli::context::{WalletAddress, WalletKeypair}; @@ -18,9 +19,9 @@ use crate::wallet::Wallet; /// Find the public key for the given address and try to load the keypair /// for it from the wallet. Panics if the key cannot be found or loaded. pub async fn find_keypair( + client: &HttpClient, wallet: &mut Wallet, addr: &Address, - ledger_address: TendermintAddress, ) -> common::SecretKey { match addr { Address::Established(_) => { @@ -28,7 +29,7 @@ pub async fn find_keypair( "Looking-up public key of {} from the ledger...", addr.encode() ); - let public_key = rpc::get_public_key(addr, ledger_address) + let public_key = rpc::get_public_key(client, addr) .await .unwrap_or_else(|| { eprintln!( @@ -74,9 +75,9 @@ pub enum TxSigningKey { // Do not sign any transaction None, // Obtain the actual keypair from wallet and use that to sign - WalletKeypair(WalletKeypair), + WalletKeypair(common::SecretKey), // Obtain the keypair corresponding to given address from wallet and sign - WalletAddress(WalletAddress), + WalletAddress(Address), // Directly use the given secret key to sign transactions SecretKey(common::SecretKey), } @@ -86,6 +87,7 @@ pub enum TxSigningKey { /// possible. If no explicit signer given, use the `default`. If no `default` /// is given, panics. pub async fn tx_signer( + client: &HttpClient, ctx: &mut Context, args: &args::Tx, mut default: TxSigningKey, @@ -99,28 +101,28 @@ pub async fn tx_signer( // Now actually fetch the signing key and apply it match default { TxSigningKey::WalletKeypair(signing_key) => { - ctx.get_cached(&signing_key) + signing_key } TxSigningKey::WalletAddress(signer) => { - let signer = ctx.get(&signer); + let signer = signer; let signing_key = find_keypair( + client, &mut ctx.wallet, &signer, - args.ledger_address.clone(), ) .await; // Check if the signer is implicit account that needs to reveal its // PK first if matches!(signer, Address::Implicit(_)) { let pk: common::PublicKey = signing_key.ref_to(); - super::tx::reveal_pk_if_needed(ctx, &pk, args).await; + super::tx::reveal_pk_if_needed(client, ctx, &pk, args).await; } signing_key } TxSigningKey::SecretKey(signing_key) => { // Check if the signing key needs to reveal its PK first let pk: common::PublicKey = signing_key.ref_to(); - super::tx::reveal_pk_if_needed(ctx, &pk, args).await; + super::tx::reveal_pk_if_needed(client, ctx, &pk, args).await; signing_key } TxSigningKey::None => { @@ -141,17 +143,16 @@ 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( + client: &HttpClient, mut ctx: Context, tx: Tx, args: &args::Tx, default: TxSigningKey, ) -> (Context, TxBroadcastData) { - let keypair = tx_signer(&mut ctx, args, default).await; + let keypair = tx_signer(client, &mut ctx, args, default).await; let tx = tx.sign(&keypair); - let epoch = rpc::query_epoch(args::Query { - ledger_address: args.ledger_address.clone(), - }) + let epoch = rpc::query_epoch(client) .await; let broadcast_data = if args.dry_run { TxBroadcastData::DryRun(tx) @@ -175,7 +176,7 @@ pub async fn sign_wrapper( WrapperTx::new( Fee { amount: args.fee_amount, - token: ctx.get(&args.fee_token), + token: args.fee_token.clone(), }, keypair, epoch, diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index 9d56b53c5d..2efe3642c8 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -80,25 +80,25 @@ 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) { +pub async fn submit_custom(client: &HttpClient, ctx: Context, args: args::TxCustom) { let tx_code = 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, &args.tx, tx, TxSigningKey::None).await; + process_tx(client, ctx, &args.tx, tx, TxSigningKey::None).await; save_initialized_accounts(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(client: &HttpClient, ctx: Context, args: args::TxUpdateVp) { + let addr = args.addr.clone(); // Check that the address is established and exists on chain match &addr { Address::Established(_) => { let exists = - rpc::known_address(&addr, args.tx.ledger_address.clone()).await; + rpc::known_address(client, &addr).await; if !exists { eprintln!("The address {} doesn't exist on chain.", addr); if !args.tx.force { @@ -142,11 +142,11 @@ pub async fn submit_update_vp(ctx: Context, args: args::TxUpdateVp) { let data = data.try_to_vec().expect("Encoding tx data shouldn't fail"); let tx = Tx::new(tx_code, Some(data)); - process_tx(ctx, &args.tx, tx, TxSigningKey::WalletAddress(args.addr)).await; + process_tx(client, ctx, &args.tx, tx, TxSigningKey::WalletAddress(args.addr)).await; } -pub async fn submit_init_account(mut ctx: Context, args: args::TxInitAccount) { - let public_key = ctx.get_cached(&args.public_key); +pub async fn submit_init_account(client: &HttpClient, mut ctx: Context, args: args::TxInitAccount) { + let public_key = args.public_key; let vp_code = args .vp_code_path .map(|path| ctx.read_wasm(path)) @@ -168,12 +168,13 @@ pub async fn submit_init_account(mut ctx: Context, args: args::TxInitAccount) { let tx = Tx::new(tx_code, Some(data)); let (ctx, initialized_accounts) = - process_tx(ctx, &args.tx, tx, TxSigningKey::WalletAddress(args.source)) + process_tx(client, ctx, &args.tx, tx, TxSigningKey::WalletAddress(args.source)) .await; save_initialized_accounts(ctx, &args.tx, initialized_accounts).await; } pub async fn submit_init_validator( + client: &HttpClient, mut ctx: Context, args::TxInitValidator { tx: tx_args, @@ -196,7 +197,7 @@ 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(|| { + let account_key = account_key.unwrap_or_else(|| { println!("Generating validator account key..."); ctx.wallet .gen_key( @@ -208,8 +209,7 @@ pub async fn submit_init_validator( .ref_to() }); - let consensus_key = ctx - .get_opt_cached(&consensus_key) + let consensus_key = consensus_key .map(|key| match key { common::SecretKey::Ed25519(_) => key, common::SecretKey::Secp256k1(_) => { @@ -229,7 +229,7 @@ pub async fn submit_init_validator( .1 }); - let protocol_key = ctx.get_opt_cached(&protocol_key); + let protocol_key = protocol_key; if protocol_key.is_none() { println!("Generating protocol signing key..."); @@ -295,7 +295,7 @@ 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, &tx_args, tx, TxSigningKey::WalletAddress(source)) + process_tx(client, ctx, &tx_args, tx, TxSigningKey::WalletAddress(source)) .await; if !tx_args.dry_run { let (validator_address_alias, validator_address) = @@ -427,9 +427,7 @@ impl masp::ShieldedUtils for CLIShieldedUtils { } async fn query_epoch(&self) -> Epoch { - rpc::query_epoch(args::Query { - ledger_address: self.ledger_address.clone().unwrap() - }).await + rpc::query_epoch(&self.client()).await } fn local_tx_prover(&self) -> LocalTxProver { @@ -512,22 +510,20 @@ impl masp::ShieldedUtils for CLIShieldedUtils { } async fn query_results(&self) -> Vec { - rpc::query_results(args::Query { - ledger_address: self.ledger_address.clone().unwrap() - }).await + rpc::query_results(&self.client()).await } } -pub async fn submit_transfer(mut ctx: Context, args: args::TxTransfer) { - let transfer_source = ctx.get_cached(&args.source); +pub async fn submit_transfer(client: &HttpClient, mut ctx: Context, args: args::TxTransfer) { + let transfer_source = args.source; let source = transfer_source.effective_address(); - let transfer_target = ctx.get(&args.target); + let transfer_target = args.target.clone(); let target = transfer_target.effective_address(); // Check that the source address exists on chain let source_exists = - rpc::known_address(&source, args.tx.ledger_address.clone()).await; + rpc::known_address(client, &source).await; if !source_exists { eprintln!("The source address {} doesn't exist on chain.", source); if !args.tx.force { @@ -536,17 +532,17 @@ pub async fn submit_transfer(mut ctx: Context, args: args::TxTransfer) { } // Check that the target address exists on chain let target_exists = - rpc::known_address(&target, args.tx.ledger_address.clone()).await; + rpc::known_address(client, &target).await; if !target_exists { eprintln!("The target address {} doesn't exist on chain.", target); if !args.tx.force { safe_exit(1) } } - let token = ctx.get(&args.token); + let token = &args.token; // Check that the token address exists on chain let token_exists = - rpc::known_address(&token, args.tx.ledger_address.clone()) + rpc::known_address(client, &token) .await; if !token_exists { eprintln!( @@ -572,7 +568,6 @@ pub async fn submit_transfer(mut ctx: Context, args: args::TxTransfer) { } None => (None, token::balance_key(&token, &source)), }; - let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); match rpc::query_storage_value::(&client, &balance_key).await { Some(balance) => { @@ -621,34 +616,31 @@ pub async fn submit_transfer(mut ctx: Context, args: args::TxTransfer) { ) } else { ( - TxSigningKey::WalletAddress(args.source.to_address()), + TxSigningKey::WalletAddress(source.clone()), args.amount, token.clone(), ) }; // 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(client, &mut 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 &args.target { TransferTarget::PaymentAddress(pa) if pa.is_pinned() => Some(pa.hash()), _ => None, }; - // Update the context with the current ledger address - ctx.shielded.utils.ledger_address = Some(args.tx.ledger_address.clone()); - let stx_result = ctx.shielded.gen_shielded_transfer( transfer_source, transfer_target, args.amount, - ctx.get(&args.token), + args.token, args.tx.fee_amount, - ctx.get(&args.tx.fee_token), + args.tx.fee_token.clone(), shielded_gas, ) .await; @@ -663,7 +655,7 @@ pub async fn submit_transfer(mut ctx: Context, args: args::TxTransfer) { args.amount, token, args.tx.fee_amount, - ctx.get(&args.tx.fee_token), + &args.tx.fee_token, ); safe_exit(1) } @@ -671,7 +663,7 @@ pub async fn submit_transfer(mut ctx: Context, args: args::TxTransfer) { }; let transfer = token::Transfer { - source, + source: source.clone(), target, token, sub_prefix, @@ -685,15 +677,15 @@ pub async fn submit_transfer(mut ctx: Context, args: args::TxTransfer) { .expect("Encoding tx data shouldn't fail"); let tx = Tx::new(tx_code, Some(data)); - let signing_address = TxSigningKey::WalletAddress(args.source.to_address()); - process_tx(ctx, &args.tx, tx, signing_address).await; + let signing_address = TxSigningKey::WalletAddress(source); + process_tx(client, ctx, &args.tx, tx, signing_address).await; } -pub async fn submit_ibc_transfer(ctx: Context, args: args::TxIbcTransfer) { - let source = ctx.get(&args.source); +pub async fn submit_ibc_transfer(client: &HttpClient, ctx: Context, args: args::TxIbcTransfer) { + let source = args.source.clone(); // Check that the source address exists on chain let source_exists = - rpc::known_address(&source, args.tx.ledger_address.clone()).await; + rpc::known_address(client, &source).await; if !source_exists { eprintln!("The source address {} doesn't exist on chain.", source); if !args.tx.force { @@ -703,10 +695,10 @@ pub async fn submit_ibc_transfer(ctx: Context, args: args::TxIbcTransfer) { // We cannot check the receiver - let token = ctx.get(&args.token); + let token = args.token; // Check that the token address exists on chain let token_exists = - rpc::known_address(&token, args.tx.ledger_address.clone()).await; + rpc::known_address(client, &token).await; if !token_exists { eprintln!("The token address {} doesn't exist on chain.", token); if !args.tx.force { @@ -725,7 +717,6 @@ pub async fn submit_ibc_transfer(ctx: Context, args: args::TxIbcTransfer) { } None => (None, token::balance_key(&token, &source)), }; - let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); match rpc::query_storage_value::(&client, &balance_key).await { Some(balance) => { @@ -796,22 +787,18 @@ pub async fn submit_ibc_transfer(ctx: Context, args: args::TxIbcTransfer) { .expect("Encoding tx data shouldn't fail"); let tx = Tx::new(tx_code, Some(data)); - process_tx(ctx, &args.tx, tx, TxSigningKey::WalletAddress(args.source)) + process_tx(client, ctx, &args.tx, tx, TxSigningKey::WalletAddress(args.source)) .await; } -pub async fn submit_init_proposal(mut ctx: Context, args: args::InitProposal) { +pub async fn submit_init_proposal(client: &HttpClient, mut ctx: Context, args: args::InitProposal) { 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"); - let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); - let signer = WalletAddress::new(proposal.clone().author.to_string()); let governance_parameters = rpc::get_governance_parameters(&client).await; - let current_epoch = rpc::query_epoch(args::Query { - ledger_address: args.tx.ledger_address.clone(), - }) + let current_epoch = rpc::query_epoch(client) .await; if proposal.voting_start_epoch <= current_epoch @@ -871,9 +858,9 @@ pub async fn submit_init_proposal(mut ctx: Context, args: args::InitProposal) { if args.offline { let signer = ctx.get(&signer); let signing_key = find_keypair( + client, &mut ctx.wallet, &signer, - args.tx.ledger_address.clone(), ) .await; let offline_proposal = @@ -897,6 +884,7 @@ pub async fn submit_init_proposal(mut ctx: Context, args: args::InitProposal) { } } } else { + let signer = ctx.get(&signer); let tx_data: Result = proposal.clone().try_into(); let init_proposal_data = if let Ok(data) = tx_data { data @@ -935,12 +923,12 @@ pub async fn submit_init_proposal(mut ctx: Context, args: args::InitProposal) { let tx_code = ctx.read_wasm(TX_INIT_PROPOSAL); let tx = Tx::new(tx_code, Some(data)); - process_tx(ctx, &args.tx, tx, TxSigningKey::WalletAddress(signer)) + process_tx(client, ctx, &args.tx, tx, TxSigningKey::WalletAddress(signer)) .await; } } -pub async fn submit_vote_proposal(mut ctx: Context, args: args::VoteProposal) { +pub async fn submit_vote_proposal(client: &HttpClient, mut ctx: Context, args: args::VoteProposal) { let signer = if let Some(addr) = &args.tx.signer { addr } else { @@ -949,7 +937,7 @@ pub async fn submit_vote_proposal(mut ctx: Context, args: args::VoteProposal) { }; if args.offline { - let signer = ctx.get(signer); + let signer = signer; let proposal_file_path = args.proposal_data.expect("Proposal file should exist."); let file = File::open(&proposal_file_path).expect("File must exist."); @@ -957,8 +945,8 @@ pub async fn submit_vote_proposal(mut ctx: Context, args: args::VoteProposal) { let proposal: OfflineProposal = serde_json::from_reader(file).expect("JSON was not well-formatted"); let public_key = rpc::get_public_key( + client, &proposal.address, - args.tx.ledger_address.clone(), ) .await .expect("Public key should exist."); @@ -968,9 +956,9 @@ pub async fn submit_vote_proposal(mut ctx: Context, args: args::VoteProposal) { } let signing_key = find_keypair( + client, &mut ctx.wallet, &signer, - args.tx.ledger_address.clone(), ) .await; let offline_vote = OfflineVote::new( @@ -998,13 +986,10 @@ pub async fn submit_vote_proposal(mut ctx: Context, args: args::VoteProposal) { } } } else { - let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); - let current_epoch = rpc::query_epoch(args::Query { - ledger_address: args.tx.ledger_address.clone(), - }) + let current_epoch = rpc::query_epoch(client) .await; - let voter_address = ctx.get(signer); + let voter_address = signer.clone(); let proposal_id = args.proposal_id.unwrap(); let proposal_start_epoch_key = gov_storage::get_voting_start_epoch_key(proposal_id); @@ -1039,8 +1024,7 @@ pub async fn submit_vote_proposal(mut ctx: Context, args: args::VoteProposal) { // the delgator's vote if !args.tx.force && is_safe_voting_window( - args.tx.ledger_address.clone(), - &client, + client, proposal_id, epoch, ) @@ -1069,6 +1053,7 @@ pub async fn submit_vote_proposal(mut ctx: Context, args: args::VoteProposal) { let tx = Tx::new(tx_code, Some(data)); process_tx( + client, ctx, &args.tx, tx, @@ -1089,29 +1074,30 @@ pub async fn submit_vote_proposal(mut ctx: Context, args: args::VoteProposal) { } } -pub async fn submit_reveal_pk(mut ctx: Context, args: args::RevealPk) { +pub async fn submit_reveal_pk(client: &HttpClient, mut ctx: Context, args: args::RevealPk) { 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 = public_key; + if !reveal_pk_if_needed(client, &mut 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( + client: &HttpClient, ctx: &mut Context, public_key: &common::PublicKey, args: &args::Tx, ) -> bool { let addr: Address = public_key.into(); // Check if PK revealed - if args.force || !has_revealed_pk(&addr, args.ledger_address.clone()).await + if args.force || !has_revealed_pk(client, &addr).await { // If not, submit it - submit_reveal_pk_aux(ctx, public_key, args).await; + submit_reveal_pk_aux(client, ctx, public_key, args).await; true } else { false @@ -1119,13 +1105,14 @@ pub async fn reveal_pk_if_needed( } pub async fn has_revealed_pk( + client: &HttpClient, addr: &Address, - ledger_address: TendermintAddress, ) -> bool { - rpc::get_public_key(addr, ledger_address).await.is_some() + rpc::get_public_key(client, addr).await.is_some() } pub async fn submit_reveal_pk_aux( + client: &HttpClient, ctx: &mut Context, public_key: &common::PublicKey, args: &args::Tx, @@ -1140,17 +1127,15 @@ pub async fn submit_reveal_pk_aux( // submit_tx without signing the inner tx let keypair = if let Some(signing_key) = &args.signing_key { - ctx.get_cached(signing_key) + signing_key.clone() } else if let Some(signer) = args.signer.as_ref() { - let signer = ctx.get(signer); - find_keypair(&mut ctx.wallet, &signer, args.ledger_address.clone()) + let signer = signer; + find_keypair(client, &mut ctx.wallet, &signer) .await } else { - find_keypair(&mut ctx.wallet, &addr, args.ledger_address.clone()).await + find_keypair(client, &mut ctx.wallet, &addr).await }; - let epoch = rpc::query_epoch(args::Query { - ledger_address: args.ledger_address.clone(), - }) + let epoch = rpc::query_epoch(client) .await; let to_broadcast = if args.dry_run { TxBroadcastData::DryRun(tx) @@ -1160,7 +1145,7 @@ pub async fn submit_reveal_pk_aux( if args.dry_run { if let TxBroadcastData::DryRun(tx) = to_broadcast { - rpc::dry_run_tx(&args.ledger_address, tx.to_bytes()).await; + rpc::dry_run_tx(client, tx.to_bytes()).await; } else { panic!( "Expected a dry-run transaction, received a wrapper \ @@ -1171,9 +1156,9 @@ pub async fn submit_reveal_pk_aux( // Either broadcast or submit transaction and collect result into // sum type let result = if args.broadcast_only { - Left(broadcast_tx(args.ledger_address.clone(), &to_broadcast).await) + Left(broadcast_tx(client, &to_broadcast).await) } else { - Right(submit_tx(args.ledger_address.clone(), to_broadcast).await) + Right(submit_tx(client, to_broadcast).await) }; // Return result based on executed operation, otherwise deal with // the encountered errors uniformly @@ -1201,12 +1186,11 @@ pub async fn submit_reveal_pk_aux( /// proposal. This ensures that it is safe to optimize the vote writing to /// storage. async fn is_safe_voting_window( - ledger_address: TendermintAddress, client: &HttpClient, proposal_id: u64, proposal_start_epoch: Epoch, ) -> bool { - let current_epoch = rpc::query_epoch(args::Query { ledger_address }).await; + let current_epoch = rpc::query_epoch(client).await; let proposal_end_epoch_key = gov_storage::get_voting_end_epoch_key(proposal_id); @@ -1266,11 +1250,11 @@ 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(client: &HttpClient, ctx: Context, args: args::Bond) { + let validator = args.validator.clone(); // Check that the validator address exists on chain let is_validator = - rpc::is_validator(&validator, args.tx.ledger_address.clone()).await; + rpc::is_validator(client, &validator).await; if !is_validator { eprintln!( "The address {} doesn't belong to any known validator account.", @@ -1280,11 +1264,11 @@ pub async fn submit_bond(ctx: Context, args: args::Bond) { safe_exit(1) } } - let source = ctx.get_opt(&args.source); + let source = args.source.clone(); // Check that the source address exists on chain if let Some(source) = &source { let source_exists = - rpc::known_address(source, args.tx.ledger_address.clone()).await; + rpc::known_address(client, source).await; if !source_exists { eprintln!("The source address {} doesn't exist on chain.", source); if !args.tx.force { @@ -1296,7 +1280,6 @@ pub async fn submit_bond(ctx: Context, args: args::Bond) { // balance let bond_source = source.as_ref().unwrap_or(&validator); let balance_key = token::balance_key(&ctx.native_token, bond_source); - let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); match rpc::query_storage_value::(&client, &balance_key).await { Some(balance) => { @@ -1330,6 +1313,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( + client, ctx, &args.tx, tx, @@ -1338,11 +1322,11 @@ 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(client: &HttpClient, ctx: Context, args: args::Unbond) { + let validator = args.validator.clone(); // Check that the validator address exists on chain let is_validator = - rpc::is_validator(&validator, args.tx.ledger_address.clone()).await; + rpc::is_validator(client, &validator).await; if !is_validator { eprintln!( "The address {} doesn't belong to any known validator account.", @@ -1353,7 +1337,7 @@ pub async fn submit_unbond(ctx: Context, args: args::Unbond) { } } - let source = ctx.get_opt(&args.source); + let source = args.source.clone(); let tx_code = ctx.read_wasm(TX_UNBOND_WASM); // Check the source's current bond amount @@ -1363,7 +1347,6 @@ pub async fn submit_unbond(ctx: Context, args: args::Unbond) { validator: validator.clone(), }; let bond_key = ledger::pos::bond_key(&bond_id); - let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let bonds = rpc::query_storage_value::(&client, &bond_key).await; match bonds { Some(bonds) => { @@ -1403,6 +1386,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( + client, ctx, &args.tx, tx, @@ -1411,16 +1395,14 @@ pub async fn submit_unbond(ctx: Context, args: args::Unbond) { .await; } -pub async fn submit_withdraw(ctx: Context, args: args::Withdraw) { - let epoch = rpc::query_epoch(args::Query { - ledger_address: args.tx.ledger_address.clone(), - }) +pub async fn submit_withdraw(client: &HttpClient, ctx: Context, args: args::Withdraw) { + let epoch = rpc::query_epoch(client) .await; - let validator = ctx.get(&args.validator); + let validator = args.validator.clone(); // Check that the validator address exists on chain let is_validator = - rpc::is_validator(&validator, args.tx.ledger_address.clone()).await; + rpc::is_validator(client, &validator).await; if !is_validator { eprintln!( "The address {} doesn't belong to any known validator account.", @@ -1431,7 +1413,7 @@ pub async fn submit_withdraw(ctx: Context, args: args::Withdraw) { } } - let source = ctx.get_opt(&args.source); + let source = args.source.clone(); let tx_code = ctx.read_wasm(TX_WITHDRAW_WASM); // Check the source's current unbond amount @@ -1441,7 +1423,6 @@ pub async fn submit_withdraw(ctx: Context, args: args::Withdraw) { validator: validator.clone(), }; let bond_key = ledger::pos::unbond_key(&bond_id); - let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let unbonds = rpc::query_storage_value::(&client, &bond_key).await; match unbonds { Some(unbonds) => { @@ -1476,6 +1457,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( + client, ctx, &args.tx, tx, @@ -1485,19 +1467,17 @@ pub async fn submit_withdraw(ctx: Context, args: args::Withdraw) { } pub async fn submit_validator_commission_change( + client: &HttpClient, ctx: Context, args: args::TxCommissionRateChange, ) { - let epoch = rpc::query_epoch(args::Query { - ledger_address: args.tx.ledger_address.clone(), - }) + let epoch = rpc::query_epoch(client) .await; let tx_code = ctx.read_wasm(TX_CHANGE_COMMISSION_WASM); - let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); - let validator = ctx.get(&args.validator); - if rpc::is_validator(&validator, args.tx.ledger_address.clone()).await { + let validator = args.validator.clone(); + if rpc::is_validator(client, &validator).await { if args.rate < Decimal::ZERO || args.rate > Decimal::ONE { eprintln!("Invalid new commission rate, received {}", args.rate); if !args.tx.force { @@ -1550,14 +1530,15 @@ pub async fn submit_validator_commission_change( } let data = pos::CommissionChange { - validator: ctx.get(&args.validator), + validator: args.validator.clone(), new_rate: args.rate, }; let data = data.try_to_vec().expect("Encoding tx data shouldn't fail"); let tx = Tx::new(tx_code, Some(data)); - let default_signer = args.validator; + let default_signer = args.validator.clone(); process_tx( + client, ctx, &args.tx, tx, @@ -1569,12 +1550,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( + client: &HttpClient, ctx: Context, args: &args::Tx, tx: Tx, default_signer: TxSigningKey, ) -> (Context, Vec
) { - let (ctx, to_broadcast) = sign_tx(ctx, tx, args, default_signer).await; + let (ctx, to_broadcast) = sign_tx(client, ctx, tx, args, default_signer).await; // NOTE: use this to print the request JSON body: // let request = @@ -1587,7 +1569,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; + rpc::dry_run_tx(client, tx.to_bytes()).await; (ctx, vec![]) } else { panic!( @@ -1599,9 +1581,9 @@ async fn process_tx( // Either broadcast or submit transaction and collect result into // sum type let result = if args.broadcast_only { - Left(broadcast_tx(args.ledger_address.clone(), &to_broadcast).await) + Left(broadcast_tx(client, &to_broadcast).await) } else { - Right(submit_tx(args.ledger_address.clone(), to_broadcast).await) + Right(submit_tx(client, to_broadcast).await) }; // Return result based on executed operation, otherwise deal with // the encountered errors uniformly @@ -1690,7 +1672,7 @@ async fn save_initialized_accounts( /// /// In the case of errors in any of those stages, an error message is returned pub async fn broadcast_tx( - address: TendermintAddress, + rpc_cli: &HttpClient, to_broadcast: &TxBroadcastData, ) -> Result { let (tx, wrapper_tx_hash, decrypted_tx_hash) = match to_broadcast { @@ -1703,11 +1685,9 @@ pub async fn broadcast_tx( }; tracing::debug!( - tendermint_rpc_address = ?address, transaction = ?to_broadcast, "Broadcasting transaction", ); - let rpc_cli = HttpClient::new(address)?; // TODO: configure an explicit timeout value? we need to hack away at // `tendermint-rs` for this, which is currently using a hard-coded 30s @@ -1737,7 +1717,7 @@ pub async fn broadcast_tx( /// /// In the case of errors in any of those stages, an error message is returned pub async fn submit_tx( - address: TendermintAddress, + client: &HttpClient, to_broadcast: TxBroadcastData, ) -> Result { let (_, wrapper_hash, decrypted_hash) = match &to_broadcast { @@ -1750,7 +1730,7 @@ pub async fn submit_tx( }; // Broadcast the supplied transaction - broadcast_tx(address.clone(), &to_broadcast).await?; + broadcast_tx(client, &to_broadcast).await?; let max_wait_time = Duration::from_secs( env::var(ENV_VAR_NAMADA_EVENTS_MAX_WAIT_TIME_SECONDS) @@ -1761,7 +1741,6 @@ pub async fn submit_tx( let deadline = Instant::now() + max_wait_time; tracing::debug!( - tendermint_rpc_address = ?address, transaction = ?to_broadcast, ?deadline, "Awaiting transaction approval", @@ -1770,7 +1749,7 @@ pub async fn submit_tx( let parsed = { let wrapper_query = rpc::TxEventQuery::Accepted(wrapper_hash.as_str()); let event = - rpc::query_tx_status(wrapper_query, address.clone(), deadline) + rpc::query_tx_status(client, wrapper_query, deadline) .await; let parsed = TxResponse::from_event(event); @@ -1786,7 +1765,7 @@ pub async fn submit_tx( let decrypted_query = rpc::TxEventQuery::Applied(decrypted_hash.as_str()); let event = - rpc::query_tx_status(decrypted_query, address, deadline).await; + rpc::query_tx_status(client, decrypted_query, deadline).await; let parsed = TxResponse::from_event(event); println!( "Transaction applied with result: {}", From a5e3147e97df19e478bac53ece1935edd40afd6b Mon Sep 17 00:00:00 2001 From: Murisi Tarusenga Date: Wed, 14 Dec 2022 20:47:55 +0200 Subject: [PATCH 12/51] Removed some unnecessary parameters. --- apps/src/bin/anoma-client/cli.rs | 14 +++++++------- apps/src/bin/anoma-wallet/cli.rs | 2 +- apps/src/lib/client/rpc.rs | 14 +++++--------- apps/src/lib/client/signing.rs | 5 +---- apps/src/lib/client/tx.rs | 4 ++-- 5 files changed, 16 insertions(+), 23 deletions(-) diff --git a/apps/src/bin/anoma-client/cli.rs b/apps/src/bin/anoma-client/cli.rs index 1eeac1e090..a866de7fb9 100644 --- a/apps/src/bin/anoma-client/cli.rs +++ b/apps/src/bin/anoma-client/cli.rs @@ -85,12 +85,12 @@ pub async fn main() -> Result<()> { Sub::QueryConversions(QueryConversions(args)) => { let client = HttpClient::new(args.query.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - rpc::query_conversions(&client, ctx, args).await; + rpc::query_conversions(&client, args).await; } Sub::QueryBlock(QueryBlock(args)) => { let client = HttpClient::new(args.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - rpc::query_block(&client, args).await; + rpc::query_block(&client).await; } Sub::QueryBalance(QueryBalance(args)) => { let client = HttpClient::new(args.query.ledger_address.clone()).unwrap(); @@ -100,22 +100,22 @@ pub async fn main() -> Result<()> { Sub::QueryBonds(QueryBonds(args)) => { let client = HttpClient::new(args.query.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - rpc::query_bonds(&client, ctx, args).await; + rpc::query_bonds(&client, args).await; } Sub::QueryBondedStake(QueryBondedStake(args)) => { let client = HttpClient::new(args.query.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - rpc::query_bonded_stake(&client, ctx, args).await; + rpc::query_bonded_stake(&client, args).await; } Sub::QueryCommissionRate(QueryCommissionRate(args)) => { let client = HttpClient::new(args.query.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - rpc::query_commission_rate(&client, ctx, args).await; + rpc::query_commission_rate(&client, args).await; } Sub::QuerySlashes(QuerySlashes(args)) => { let client = HttpClient::new(args.query.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - rpc::query_slashes(&client, ctx, args).await; + rpc::query_slashes(&client, args).await; } Sub::QueryResult(QueryResult(args)) => { // Connect to the Tendermint server holding the transactions @@ -150,7 +150,7 @@ pub async fn main() -> Result<()> { Sub::QueryProtocolParameters(QueryProtocolParameters(args)) => { let client = HttpClient::new(args.query.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - rpc::query_protocol_parameters(&client, ctx, args).await; + rpc::query_protocol_parameters(&client, args).await; } } } diff --git a/apps/src/bin/anoma-wallet/cli.rs b/apps/src/bin/anoma-wallet/cli.rs index e0e5f68f70..cef4f58780 100644 --- a/apps/src/bin/anoma-wallet/cli.rs +++ b/apps/src/bin/anoma-wallet/cli.rs @@ -210,7 +210,7 @@ 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, diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index fa1f6a7e5b..121af27176 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -53,11 +53,10 @@ use crate::cli::{self, args, Context}; use crate::client::tendermint_rpc_types::TxResponse; use namada::ledger::masp::{Conversions, PinnedBalanceError}; use crate::facade::tendermint::merkle::proof::Proof; -use crate::facade::tendermint_config::net::Address as TendermintAddress; use crate::facade::tendermint_rpc::error::Error as TError; use crate::facade::tendermint_rpc::query::Query; use crate::facade::tendermint_rpc::{ - Client, HttpClient, Order, SubscriptionClient, WebSocketClient, + Client, HttpClient, Order, WebSocketClient, }; /// Query the status of a given transaction. @@ -120,7 +119,6 @@ pub async fn query_epoch(client: &HttpClient) -> Epoch { /// Query the last committed block pub async fn query_block( client: &HttpClient, - args: args::Query, ) -> crate::facade::tendermint_rpc::endpoint::block::Response { let response = client.latest_block().await.unwrap(); println!( @@ -1114,7 +1112,6 @@ pub async fn query_proposal_result( pub async fn query_protocol_parameters( client: &HttpClient, - _ctx: Context, args: args::QueryProtocolParameters, ) { let gov_parameters = get_governance_parameters(&client).await; @@ -1183,7 +1180,7 @@ pub async fn query_protocol_parameters( } /// Query PoS bond(s) -pub async fn query_bonds(client: &HttpClient, ctx: Context, args: args::QueryBonds) { +pub async fn query_bonds(client: &HttpClient, args: args::QueryBonds) { let epoch = query_epoch(client).await; match (args.owner, args.validator) { (Some(owner), Some(validator)) => { @@ -1529,7 +1526,7 @@ pub async fn query_bonds(client: &HttpClient, ctx: Context, args: args::QueryBon } /// Query PoS bonded stake -pub async fn query_bonded_stake(client: &HttpClient, ctx: Context, args: args::QueryBondedStake) { +pub async fn query_bonded_stake(client: &HttpClient, args: args::QueryBondedStake) { let epoch = match args.epoch { Some(epoch) => epoch, None => query_epoch(client).await, @@ -1630,7 +1627,6 @@ pub async fn query_bonded_stake(client: &HttpClient, ctx: Context, args: args::Q /// Query PoS validator's commission rate pub async fn query_commission_rate( client: &HttpClient, - ctx: Context, args: args::QueryCommissionRate, ) { let epoch = match args.epoch { @@ -1684,7 +1680,7 @@ pub async fn query_commission_rate( } /// Query PoS slashes -pub async fn query_slashes(client: &HttpClient, ctx: Context, args: args::QuerySlashes) { +pub async fn query_slashes(client: &HttpClient, args: args::QuerySlashes) { match args.validator { Some(validator) => { let validator = validator; @@ -1956,7 +1952,7 @@ fn process_unbonds_query( } /// Query for all conversions. -pub async fn query_conversions(client: &HttpClient, ctx: Context, args: args::QueryConversions) { +pub async fn query_conversions(client: &HttpClient, args: args::QueryConversions) { // The chosen token type of the conversions let target_token = args.token; // To facilitate human readable token addresses diff --git a/apps/src/lib/client/signing.rs b/apps/src/lib/client/signing.rs index 5192a16f89..34b3b06032 100644 --- a/apps/src/lib/client/signing.rs +++ b/apps/src/lib/client/signing.rs @@ -10,10 +10,8 @@ use namada::types::transaction::{hash_tx, Fee, WrapperTx}; use tendermint_rpc::HttpClient; use super::rpc; -use crate::cli::context::{WalletAddress, WalletKeypair}; use crate::cli::{self, args, Context}; use crate::client::tendermint_rpc_types::TxBroadcastData; -use crate::facade::tendermint_config::net::Address as TendermintAddress; use crate::wallet::Wallet; /// Find the public key for the given address and try to load the keypair @@ -157,7 +155,7 @@ pub async fn sign_tx( let broadcast_data = if args.dry_run { TxBroadcastData::DryRun(tx) } else { - sign_wrapper(&ctx, args, epoch, tx, &keypair).await + sign_wrapper(args, epoch, tx, &keypair).await }; (ctx, broadcast_data) } @@ -166,7 +164,6 @@ pub async fn sign_tx( /// wrapper and its payload which is needed for monitoring its /// progress on chain. pub async fn sign_wrapper( - ctx: &Context, args: &args::Tx, epoch: Epoch, tx: Tx, diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index 2efe3642c8..4824327e1f 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -145,7 +145,7 @@ pub async fn submit_update_vp(client: &HttpClient, ctx: Context, args: args::TxU process_tx(client, ctx, &args.tx, tx, TxSigningKey::WalletAddress(args.addr)).await; } -pub async fn submit_init_account(client: &HttpClient, mut ctx: Context, args: args::TxInitAccount) { +pub async fn submit_init_account(client: &HttpClient, ctx: Context, args: args::TxInitAccount) { let public_key = args.public_key; let vp_code = args .vp_code_path @@ -1140,7 +1140,7 @@ pub async fn submit_reveal_pk_aux( let to_broadcast = if args.dry_run { TxBroadcastData::DryRun(tx) } else { - super::signing::sign_wrapper(ctx, args, epoch, tx, &keypair).await + super::signing::sign_wrapper(args, epoch, tx, &keypair).await }; if args.dry_run { From 5b2708623fdbdf6b8f565c6a7cbea22c5e5aa272 Mon Sep 17 00:00:00 2001 From: Murisi Tarusenga Date: Thu, 15 Dec 2022 05:38:07 +0200 Subject: [PATCH 13/51] Pass native token into tx function through args instead of Context. --- apps/src/lib/cli.rs | 20 ++++++++++++++++++++ apps/src/lib/client/tx.rs | 6 +++--- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index 9dc970bb88..dbfad2328c 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -1782,6 +1782,8 @@ pub mod args { pub sub_prefix: Option, /// Transferred token amount pub amount: token::Amount, + /// Native token address + pub native_token: C::NativeAddress, } impl TxTransfer { @@ -1793,6 +1795,7 @@ pub mod args { token: ctx.get(&self.token), sub_prefix: self.sub_prefix, amount: self.amount, + native_token: ctx.native_token.clone(), } } } @@ -1805,6 +1808,7 @@ pub mod args { let token = TOKEN.parse(matches); let sub_prefix = SUB_PREFIX.parse(matches); let amount = AMOUNT.parse(matches); + let native_token = (); Self { tx, source, @@ -1812,6 +1816,7 @@ pub mod args { token, sub_prefix, amount, + native_token, } } @@ -2139,6 +2144,8 @@ pub mod args { /// Source address for delegations. For self-bonds, the validator is /// also the source. pub source: Option, + /// Native token address + pub native_token: C::NativeAddress, } impl Bond { @@ -2148,6 +2155,7 @@ pub mod args { validator: ctx.get(&self.validator), amount: self.amount, source: self.source.map(|x| ctx.get(&x)), + native_token: ctx.native_token.clone(), } } } @@ -2158,11 +2166,13 @@ pub mod args { let validator = VALIDATOR.parse(matches); let amount = AMOUNT.parse(matches); let source = SOURCE_OPT.parse(matches); + let native_token = (); Self { tx, validator, amount, source, + native_token, } } @@ -2240,6 +2250,8 @@ pub mod args { pub proposal_data: PathBuf, /// Flag if proposal should be run offline pub offline: bool, + /// Native token address + pub native_token: C::NativeAddress, } impl InitProposal { @@ -2248,6 +2260,7 @@ pub mod args { tx: self.tx.to_sdk(ctx), proposal_data: self.proposal_data, offline: self.offline, + native_token: ctx.native_token.clone(), } } } @@ -2257,11 +2270,13 @@ pub mod args { let tx = Tx::parse(matches); let proposal_data = DATA_PATH.parse(matches); let offline = PROPOSAL_OFFLINE.parse(matches); + let native_token = (); Self { tx, proposal_data, offline, + native_token, } } @@ -2961,6 +2976,7 @@ pub mod args { /// Abstraction of types being used in Namada pub trait NamadaTypes: Clone + std::fmt::Debug { type Address: Clone + std::fmt::Debug; + type NativeAddress: Clone + std::fmt::Debug; type Keypair: Clone + std::fmt::Debug; type TendermintAddress: Clone + std::fmt::Debug; type ViewingKey: Clone + std::fmt::Debug; @@ -2977,6 +2993,8 @@ pub mod args { impl NamadaTypes for SdkTypes { type Address = Address; + type NativeAddress = Address; + type Keypair = common::SecretKey; type TendermintAddress = (); @@ -2999,6 +3017,8 @@ pub mod args { impl NamadaTypes for CliTypes { type Address = WalletAddress; + type NativeAddress = (); + type Keypair = WalletKeypair; type TendermintAddress = TendermintAddress; diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index 4824327e1f..b15674c7c6 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -606,7 +606,7 @@ pub async fn submit_transfer(client: &HttpClient, mut ctx: Context, args: args:: ( TxSigningKey::SecretKey(masp_tx_key()), 0.into(), - ctx.native_token.clone(), + args.native_token.clone(), ) } else if source == masp_addr { ( @@ -895,7 +895,7 @@ pub async fn submit_init_proposal(client: &HttpClient, mut ctx: Context, args: a let balance = rpc::get_token_balance( &client, - &ctx.native_token, + &args.native_token, &proposal.author, ) .await @@ -1279,7 +1279,7 @@ pub async fn submit_bond(client: &HttpClient, 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(&args.native_token, bond_source); match rpc::query_storage_value::(&client, &balance_key).await { Some(balance) => { From a708536006cc9bda1a9ebb9341e36e8ca2151a5f Mon Sep 17 00:00:00 2001 From: Murisi Tarusenga Date: Thu, 15 Dec 2022 07:45:33 +0200 Subject: [PATCH 14/51] Removed the WASM file reading from tx.rs. --- apps/src/lib/cli.rs | 106 +++++++++++++++++++++++++++++++++----- apps/src/lib/client/tx.rs | 56 +++++++------------- 2 files changed, 112 insertions(+), 50 deletions(-) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index dbfad2328c..bc6f98099e 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -1531,6 +1531,20 @@ pub mod args { use crate::facade::tendermint::Timeout; use crate::facade::tendermint_config::net::Address as TendermintAddress; + const TX_INIT_ACCOUNT_WASM: &str = "tx_init_account.wasm"; + const TX_INIT_VALIDATOR_WASM: &str = "tx_init_validator.wasm"; + const TX_INIT_PROPOSAL: &str = "tx_init_proposal.wasm"; + const TX_VOTE_PROPOSAL: &str = "tx_vote_proposal.wasm"; + const TX_REVEAL_PK: &str = "tx_reveal_pk.wasm"; + const TX_UPDATE_VP_WASM: &str = "tx_update_vp.wasm"; + const TX_TRANSFER_WASM: &str = "tx_transfer.wasm"; + const TX_IBC_WASM: &str = "tx_ibc.wasm"; + const VP_USER_WASM: &str = "vp_user.wasm"; + const TX_BOND_WASM: &str = "tx_bond.wasm"; + const TX_UNBOND_WASM: &str = "tx_unbond.wasm"; + const TX_WITHDRAW_WASM: &str = "tx_withdraw.wasm"; + const TX_CHANGE_COMMISSION_WASM: &str = "tx_change_validator_commission.wasm"; + const ADDRESS: Arg = arg("address"); const ALIAS_OPT: ArgOpt = ALIAS.opt(); const ALIAS: Arg = arg("alias"); @@ -1725,17 +1739,19 @@ pub mod args { /// Common tx arguments pub tx: Tx, /// Path to the tx WASM code file - pub code_path: PathBuf, + pub code_path: C::Data, /// Path to the data file - pub data_path: Option, + pub data_path: Option, } impl TxCustom { pub fn to_sdk(self, ctx: &mut Context) -> TxCustom { TxCustom:: { tx: self.tx.to_sdk(ctx), - code_path: self.code_path, - data_path: self.data_path, + code_path: ctx.read_wasm(self.code_path), + data_path: self.data_path.map(|data_path| { + std::fs::read(data_path).expect("Expected a file at given data path") + }), } } } @@ -1784,6 +1800,8 @@ pub mod args { pub amount: token::Amount, /// Native token address pub native_token: C::NativeAddress, + /// Path to the TX WASM code file + pub tx_code_path: C::Data, } impl TxTransfer { @@ -1796,6 +1814,7 @@ pub mod args { sub_prefix: self.sub_prefix, amount: self.amount, native_token: ctx.native_token.clone(), + tx_code_path: ctx.read_wasm(self.tx_code_path), } } } @@ -1809,6 +1828,7 @@ pub mod args { let sub_prefix = SUB_PREFIX.parse(matches); let amount = AMOUNT.parse(matches); let native_token = (); + let tx_code_path = PathBuf::from(TX_TRANSFER_WASM); Self { tx, source, @@ -1817,6 +1837,7 @@ pub mod args { sub_prefix, amount, native_token, + tx_code_path, } } @@ -1859,6 +1880,8 @@ pub mod args { pub timeout_height: Option, /// Timeout timestamp offset pub timeout_sec_offset: Option, + /// Path to the TX WASM code file + pub tx_code_path: C::Data, } impl TxIbcTransfer { @@ -1874,6 +1897,7 @@ pub mod args { channel_id: self.channel_id, timeout_height: self.timeout_height, timeout_sec_offset: self.timeout_sec_offset, + tx_code_path: ctx.read_wasm(self.tx_code_path), } } } @@ -1890,6 +1914,7 @@ pub mod args { let channel_id = CHANNEL_ID.parse(matches); let timeout_height = TIMEOUT_HEIGHT.parse(matches); let timeout_sec_offset = TIMEOUT_SEC_OFFSET.parse(matches); + let tx_code_path = PathBuf::from(TX_IBC_WASM); Self { tx, source, @@ -1901,6 +1926,7 @@ pub mod args { channel_id, timeout_height, timeout_sec_offset, + tx_code_path, } } @@ -1935,7 +1961,9 @@ pub mod args { /// Address of the source account pub source: C::Address, /// Path to the VP WASM code file for the new account - pub vp_code_path: Option, + pub vp_code_path: C::Data, + /// Path to the TX WASM code file + pub tx_code_path: C::Data, /// Public key for the new account pub public_key: C::PublicKey, } @@ -1945,7 +1973,8 @@ pub mod args { TxInitAccount:: { tx: self.tx.to_sdk(ctx), source: ctx.get(&self.source), - vp_code_path: self.vp_code_path, + vp_code_path: ctx.read_wasm(self.vp_code_path), + tx_code_path: ctx.read_wasm(self.tx_code_path), public_key: ctx.get_cached(&self.public_key), } } @@ -1955,13 +1984,16 @@ pub mod args { fn parse(matches: &ArgMatches) -> Self { let tx = Tx::parse(matches); let source = SOURCE.parse(matches); - let vp_code_path = CODE_PATH_OPT.parse(matches); + let vp_code_path = CODE_PATH_OPT.parse(matches) + .unwrap_or(PathBuf::from(VP_USER_WASM)); + let tx_code_path = PathBuf::from(TX_INIT_ACCOUNT_WASM); let public_key = PUBLIC_KEY.parse(matches); Self { tx, source, vp_code_path, public_key, + tx_code_path, } } @@ -1993,7 +2025,8 @@ pub mod args { pub protocol_key: Option, pub commission_rate: Decimal, pub max_commission_rate_change: Decimal, - pub validator_vp_code_path: Option, + pub validator_vp_code_path: C::Data, + pub tx_code_path: C::Data, pub unsafe_dont_encrypt: bool, } @@ -2008,8 +2041,9 @@ pub mod args { protocol_key: self.protocol_key.map(|x| ctx.get_cached(&x)), commission_rate: self.commission_rate, max_commission_rate_change: self.max_commission_rate_change, - validator_vp_code_path: self.validator_vp_code_path, + validator_vp_code_path: ctx.read_wasm(self.validator_vp_code_path), unsafe_dont_encrypt: self.unsafe_dont_encrypt, + tx_code_path: ctx.read_wasm(self.tx_code_path), } } } @@ -2025,8 +2059,10 @@ pub mod args { let commission_rate = COMMISSION_RATE.parse(matches); let max_commission_rate_change = MAX_COMMISSION_RATE_CHANGE.parse(matches); - let validator_vp_code_path = VALIDATOR_CODE_PATH.parse(matches); + let validator_vp_code_path = VALIDATOR_CODE_PATH.parse(matches) + .unwrap_or(PathBuf::from(VP_USER_WASM)); let unsafe_dont_encrypt = UNSAFE_DONT_ENCRYPT.parse(matches); + let tx_code_path = PathBuf::from(TX_INIT_VALIDATOR_WASM); Self { tx, source, @@ -2038,6 +2074,7 @@ pub mod args { max_commission_rate_change, validator_vp_code_path, unsafe_dont_encrypt, + tx_code_path, } } @@ -2091,7 +2128,9 @@ pub mod args { /// Common tx arguments pub tx: Tx, /// Path to the VP WASM code file - pub vp_code_path: PathBuf, + pub vp_code_path: C::Data, + /// Path to the TX WASM code file + pub tx_code_path: C::Data, /// Address of the account whose VP is to be updated pub addr: C::Address, } @@ -2100,7 +2139,8 @@ pub mod args { pub fn to_sdk(self, ctx: &mut Context) -> TxUpdateVp { TxUpdateVp:: { tx: self.tx.to_sdk(ctx), - vp_code_path: self.vp_code_path, + vp_code_path: ctx.read_wasm(self.vp_code_path), + tx_code_path: ctx.read_wasm(self.tx_code_path), addr: ctx.get(&self.addr), } } @@ -2111,10 +2151,12 @@ pub mod args { let tx = Tx::parse(matches); let vp_code_path = CODE_PATH.parse(matches); let addr = ADDRESS.parse(matches); + let tx_code_path = PathBuf::from(TX_UPDATE_VP_WASM); Self { tx, vp_code_path, addr, + tx_code_path, } } @@ -2146,6 +2188,8 @@ pub mod args { pub source: Option, /// Native token address pub native_token: C::NativeAddress, + /// Path to the TX WASM code file + pub tx_code_path: C::Data, } impl Bond { @@ -2156,6 +2200,7 @@ pub mod args { amount: self.amount, source: self.source.map(|x| ctx.get(&x)), native_token: ctx.native_token.clone(), + tx_code_path: ctx.read_wasm(self.tx_code_path), } } } @@ -2167,12 +2212,14 @@ pub mod args { let amount = AMOUNT.parse(matches); let source = SOURCE_OPT.parse(matches); let native_token = (); + let tx_code_path = PathBuf::from(TX_BOND_WASM); Self { tx, validator, amount, source, native_token, + tx_code_path, } } @@ -2199,6 +2246,8 @@ pub mod args { /// Source address for unbonding from delegations. For unbonding from /// self-bonds, the validator is also the source pub source: Option, + /// Path to the TX WASM code file + pub tx_code_path: C::Data, } impl Unbond { @@ -2208,6 +2257,7 @@ pub mod args { validator: ctx.get(&self.validator), amount: self.amount, source: self.source.map(|x| ctx.get(&x)), + tx_code_path: ctx.read_wasm(self.tx_code_path), } } } @@ -2218,11 +2268,13 @@ pub mod args { let validator = VALIDATOR.parse(matches); let amount = AMOUNT.parse(matches); let source = SOURCE_OPT.parse(matches); + let tx_code_path = PathBuf::from(TX_UNBOND_WASM); Self { tx, validator, amount, source, + tx_code_path, } } @@ -2252,6 +2304,8 @@ pub mod args { pub offline: bool, /// Native token address pub native_token: C::NativeAddress, + /// Path to the TX WASM code file + pub tx_code_path: C::Data, } impl InitProposal { @@ -2261,6 +2315,7 @@ pub mod args { proposal_data: self.proposal_data, offline: self.offline, native_token: ctx.native_token.clone(), + tx_code_path: ctx.read_wasm(self.tx_code_path), } } } @@ -2271,12 +2326,14 @@ pub mod args { let proposal_data = DATA_PATH.parse(matches); let offline = PROPOSAL_OFFLINE.parse(matches); let native_token = (); + let tx_code_path = PathBuf::from(TX_INIT_PROPOSAL); Self { tx, proposal_data, offline, native_token, + tx_code_path, } } @@ -2305,6 +2362,8 @@ pub mod args { pub offline: bool, /// The proposal file path pub proposal_data: Option, + /// Path to the TX WASM code file + pub tx_code_path: C::Data, } impl VoteProposal { @@ -2315,6 +2374,7 @@ pub mod args { vote: self.vote, offline: self.offline, proposal_data: self.proposal_data, + tx_code_path: ctx.read_wasm(self.tx_code_path), } } } @@ -2326,6 +2386,7 @@ pub mod args { let vote = PROPOSAL_VOTE.parse(matches); let offline = PROPOSAL_OFFLINE.parse(matches); let proposal_data = DATA_PATH_OPT.parse(matches); + let tx_code_path = PathBuf::from(TX_VOTE_PROPOSAL); Self { tx, @@ -2333,6 +2394,7 @@ pub mod args { vote, offline, proposal_data, + tx_code_path, } } @@ -2530,6 +2592,8 @@ pub mod args { /// Source address for withdrawing from delegations. For withdrawing /// from self-bonds, the validator is also the source pub source: Option, + /// Path to the TX WASM code file + pub tx_code_path: C::Data, } impl Withdraw { @@ -2538,6 +2602,7 @@ pub mod args { tx: self.tx.to_sdk(ctx), validator: ctx.get(&self.validator), source: self.source.map(|x| ctx.get(&x)), + tx_code_path: ctx.read_wasm(self.tx_code_path), } } } @@ -2547,10 +2612,12 @@ pub mod args { let tx = Tx::parse(matches); let validator = VALIDATOR.parse(matches); let source = SOURCE_OPT.parse(matches); + let tx_code_path = PathBuf::from(TX_WITHDRAW_WASM); Self { tx, validator, source, + tx_code_path, } } @@ -2827,6 +2894,8 @@ pub mod args { pub validator: C::Address, /// Value to which the tx changes the commission rate pub rate: Decimal, + /// Path to the TX WASM code file + pub tx_code_path: C::Data, } impl TxCommissionRateChange { @@ -2835,6 +2904,7 @@ pub mod args { tx: self.tx.to_sdk(ctx), validator: ctx.get(&self.validator), rate: self.rate, + tx_code_path: ctx.read_wasm(self.tx_code_path), } } } @@ -2844,10 +2914,12 @@ pub mod args { let tx = Tx::parse(matches); let validator = VALIDATOR.parse(matches); let rate = COMMISSION_RATE.parse(matches); + let tx_code_path = PathBuf::from(TX_CHANGE_COMMISSION_WASM); Self { tx, validator, rate, + tx_code_path, } } @@ -2984,6 +3056,7 @@ pub mod args { type PublicKey: Clone + std::fmt::Debug; type TransferSource: Clone + std::fmt::Debug; type TransferTarget: Clone + std::fmt::Debug; + type Data: Clone + std::fmt::Debug; } /// The concrete types being used in Namada SDK @@ -3008,6 +3081,8 @@ pub mod args { type TransferSource = namada::types::masp::TransferSource; type TransferTarget = namada::types::masp::TransferTarget; + + type Data = Vec; } /// The concrete types being used in the CLI @@ -3032,6 +3107,8 @@ pub mod args { type TransferSource = WalletTransferSource; type TransferTarget = WalletTransferTarget; + + type Data = PathBuf; } /// Common transaction arguments @@ -3058,6 +3135,8 @@ pub mod args { pub signing_key: Option, /// Sign the tx with the keypair of the public key of the given address pub signer: Option, + /// Path to the TX WASM code file + pub tx_code_path: C::Data, } impl Tx { @@ -3073,6 +3152,7 @@ pub mod args { gas_limit: self.gas_limit, signing_key: self.signing_key.map(|x| ctx.get_cached(&x)), signer: self.signer.map(|x| ctx.get(&x)), + tx_code_path: ctx.read_wasm(self.tx_code_path), } } } @@ -3140,6 +3220,7 @@ pub mod args { let signing_key = SIGNING_KEY_OPT.parse(matches); let signer = SIGNER.parse(matches); + let tx_code_path = PathBuf::from(TX_REVEAL_PK); Self { dry_run, force, @@ -3151,6 +3232,7 @@ pub mod args { gas_limit, signing_key, signer, + tx_code_path, } } } diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index b15674c7c6..cddc9829c7 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -57,20 +57,6 @@ use crate::facade::tendermint_rpc::error::Error as RpcError; use crate::facade::tendermint_rpc::{Client, HttpClient}; use crate::node::ledger::tendermint_node; -const TX_INIT_ACCOUNT_WASM: &str = "tx_init_account.wasm"; -const TX_INIT_VALIDATOR_WASM: &str = "tx_init_validator.wasm"; -const TX_INIT_PROPOSAL: &str = "tx_init_proposal.wasm"; -const TX_VOTE_PROPOSAL: &str = "tx_vote_proposal.wasm"; -const TX_REVEAL_PK: &str = "tx_reveal_pk.wasm"; -const TX_UPDATE_VP_WASM: &str = "tx_update_vp.wasm"; -const TX_TRANSFER_WASM: &str = "tx_transfer.wasm"; -const TX_IBC_WASM: &str = "tx_ibc.wasm"; -const VP_USER_WASM: &str = "vp_user.wasm"; -const TX_BOND_WASM: &str = "tx_bond.wasm"; -const TX_UNBOND_WASM: &str = "tx_unbond.wasm"; -const TX_WITHDRAW_WASM: &str = "tx_withdraw.wasm"; -const TX_CHANGE_COMMISSION_WASM: &str = "tx_change_validator_commission.wasm"; - /// Timeout for requests to the `/accepted` and `/applied` /// ABCI query endpoints. const ENV_VAR_NAMADA_EVENTS_MAX_WAIT_TIME_SECONDS: &str = @@ -81,10 +67,8 @@ const ENV_VAR_NAMADA_EVENTS_MAX_WAIT_TIME_SECONDS: &str = const DEFAULT_NAMADA_EVENTS_MAX_WAIT_TIME_SECONDS: u64 = 60; pub async fn submit_custom(client: &HttpClient, ctx: Context, args: args::TxCustom) { - let tx_code = 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_code = args.code_path; + let data = args.data_path; let tx = Tx::new(tx_code, data); let (ctx, initialized_accounts) = process_tx(client, ctx, &args.tx, tx, TxSigningKey::None).await; @@ -127,7 +111,7 @@ pub async fn submit_update_vp(client: &HttpClient, ctx: Context, args: args::TxU } } - let vp_code = ctx.read_wasm(args.vp_code_path); + let vp_code = 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); @@ -136,7 +120,7 @@ pub async fn submit_update_vp(client: &HttpClient, ctx: Context, args: args::TxU } } - let tx_code = ctx.read_wasm(TX_UPDATE_VP_WASM); + let tx_code = args.tx_code_path; let data = UpdateVp { addr, vp_code }; let data = data.try_to_vec().expect("Encoding tx data shouldn't fail"); @@ -147,10 +131,7 @@ pub async fn submit_update_vp(client: &HttpClient, ctx: Context, args: args::TxU pub async fn submit_init_account(client: &HttpClient, ctx: Context, args: args::TxInitAccount) { let public_key = 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)); + let vp_code = 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); @@ -159,7 +140,7 @@ pub async fn submit_init_account(client: &HttpClient, ctx: Context, args: args:: } } - let tx_code = ctx.read_wasm(TX_INIT_ACCOUNT_WASM); + let tx_code = args.tx_code_path; let data = InitAccount { public_key, vp_code, @@ -187,6 +168,7 @@ pub async fn submit_init_validator( max_commission_rate_change, validator_vp_code_path, unsafe_dont_encrypt, + tx_code_path, }: args::TxInitValidator, ) { let alias = tx_args @@ -246,9 +228,7 @@ pub async fn submit_init_validator( 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)); + let validator_vp_code = validator_vp_code_path; // Validate the commission rate data if commission_rate > Decimal::ONE || commission_rate < Decimal::ZERO { @@ -281,7 +261,7 @@ pub async fn submit_init_validator( safe_exit(1) } } - let tx_code = ctx.read_wasm(TX_INIT_VALIDATOR_WASM); + let tx_code = tx_code_path; let data = InitValidator { account_key, @@ -594,7 +574,7 @@ pub async fn submit_transfer(client: &HttpClient, mut ctx: Context, args: args:: } }; - let tx_code = ctx.read_wasm(TX_TRANSFER_WASM); + let tx_code = args.tx_code_path; let masp_addr = masp(); // For MASP sources, use a special sentinel key recognized by VPs as default // signer. Also, if the transaction is shielded, redact the amount and token @@ -742,7 +722,7 @@ pub async fn submit_ibc_transfer(client: &HttpClient, ctx: Context, args: args:: } } } - let tx_code = ctx.read_wasm(TX_IBC_WASM); + let tx_code = args.tx_code_path; let denom = match sub_prefix { // To parse IbcToken address, remove the address prefix @@ -920,7 +900,7 @@ pub async fn submit_init_proposal(client: &HttpClient, mut ctx: Context, args: a 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 = args.tx_code_path; let tx = Tx::new(tx_code, Some(data)); process_tx(client, ctx, &args.tx, tx, TxSigningKey::WalletAddress(signer)) @@ -1049,7 +1029,7 @@ pub async fn submit_vote_proposal(client: &HttpClient, mut ctx: Context, args: a 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 = args.tx_code_path; let tx = Tx::new(tx_code, Some(data)); process_tx( @@ -1122,7 +1102,7 @@ pub async fn submit_reveal_pk_aux( let tx_data = public_key .try_to_vec() .expect("Encoding a public key shouldn't fail"); - let tx_code = ctx.read_wasm(TX_REVEAL_PK); + let tx_code = args.tx_code_path.clone(); let tx = Tx::new(tx_code, Some(tx_data)); // submit_tx without signing the inner tx @@ -1302,7 +1282,7 @@ pub async fn submit_bond(client: &HttpClient, ctx: Context, args: args::Bond) { } } } - let tx_code = ctx.read_wasm(TX_BOND_WASM); + let tx_code = args.tx_code_path; let bond = pos::Bond { validator, amount: args.amount, @@ -1338,7 +1318,7 @@ pub async fn submit_unbond(client: &HttpClient, ctx: Context, args: args::Unbond } let source = args.source.clone(); - let tx_code = ctx.read_wasm(TX_UNBOND_WASM); + let tx_code = args.tx_code_path; // Check the source's current bond amount let bond_source = source.clone().unwrap_or_else(|| validator.clone()); @@ -1414,7 +1394,7 @@ pub async fn submit_withdraw(client: &HttpClient, ctx: Context, args: args::With } let source = args.source.clone(); - let tx_code = ctx.read_wasm(TX_WITHDRAW_WASM); + let tx_code = args.tx_code_path; // Check the source's current unbond amount let bond_source = source.clone().unwrap_or_else(|| validator.clone()); @@ -1474,7 +1454,7 @@ pub async fn submit_validator_commission_change( let epoch = rpc::query_epoch(client) .await; - let tx_code = ctx.read_wasm(TX_CHANGE_COMMISSION_WASM); + let tx_code = args.tx_code_path; let validator = args.validator.clone(); if rpc::is_validator(client, &validator).await { From 5defee9f533b6ac8f589b7263aee182407f7ba91 Mon Sep 17 00:00:00 2001 From: Murisi Tarusenga Date: Thu, 15 Dec 2022 08:35:18 +0200 Subject: [PATCH 15/51] Replaced Context parameters with Wallets. --- apps/src/bin/anoma-client/cli.rs | 18 +++---- apps/src/lib/client/signing.rs | 16 +++--- apps/src/lib/client/tx.rs | 86 ++++++++++++++++---------------- 3 files changed, 60 insertions(+), 60 deletions(-) diff --git a/apps/src/bin/anoma-client/cli.rs b/apps/src/bin/anoma-client/cli.rs index a866de7fb9..7b59bc74e2 100644 --- a/apps/src/bin/anoma-client/cli.rs +++ b/apps/src/bin/anoma-client/cli.rs @@ -16,7 +16,7 @@ pub async fn main() -> Result<()> { Sub::TxCustom(TxCustom(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_custom(&client, ctx, args).await; + tx::submit_custom(&client, &mut ctx.wallet, args).await; } Sub::TxTransfer(TxTransfer(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); @@ -26,17 +26,17 @@ pub async fn main() -> Result<()> { Sub::TxIbcTransfer(TxIbcTransfer(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_ibc_transfer(&client, ctx, args).await; + tx::submit_ibc_transfer(&client, &mut ctx.wallet, args).await; } Sub::TxUpdateVp(TxUpdateVp(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_update_vp(&client, ctx, args).await; + tx::submit_update_vp(&client, &mut ctx.wallet, args).await; } Sub::TxInitAccount(TxInitAccount(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_init_account(&client, ctx, args).await; + tx::submit_init_account(&client, &mut ctx.wallet, args).await; } Sub::TxInitValidator(TxInitValidator(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); @@ -51,27 +51,27 @@ pub async fn main() -> Result<()> { Sub::TxVoteProposal(TxVoteProposal(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_vote_proposal(&client, ctx, args).await; + tx::submit_vote_proposal(&client, &mut ctx.wallet, args).await; } Sub::TxRevealPk(TxRevealPk(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_reveal_pk(&client, ctx, args).await; + tx::submit_reveal_pk(&client, &mut ctx.wallet, args).await; } Sub::Bond(Bond(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_bond(&client, ctx, args).await; + tx::submit_bond(&client, &mut ctx.wallet, args).await; } Sub::Unbond(Unbond(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_unbond(&client, ctx, args).await; + tx::submit_unbond(&client, &mut ctx.wallet, args).await; } Sub::Withdraw(Withdraw(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_withdraw(&client, ctx, args).await; + tx::submit_withdraw(&client, &mut ctx.wallet, args).await; } // Ledger queries Sub::QueryEpoch(QueryEpoch(args)) => { diff --git a/apps/src/lib/client/signing.rs b/apps/src/lib/client/signing.rs index 34b3b06032..0ca108bd57 100644 --- a/apps/src/lib/client/signing.rs +++ b/apps/src/lib/client/signing.rs @@ -86,7 +86,7 @@ pub enum TxSigningKey { /// is given, panics. pub async fn tx_signer( client: &HttpClient, - ctx: &mut Context, + wallet: &mut Wallet, args: &args::Tx, mut default: TxSigningKey, ) -> common::SecretKey { @@ -105,7 +105,7 @@ pub async fn tx_signer( let signer = signer; let signing_key = find_keypair( client, - &mut ctx.wallet, + wallet, &signer, ) .await; @@ -113,14 +113,14 @@ pub async fn tx_signer( // PK first if matches!(signer, Address::Implicit(_)) { let pk: common::PublicKey = signing_key.ref_to(); - super::tx::reveal_pk_if_needed(client, ctx, &pk, args).await; + super::tx::reveal_pk_if_needed(client, wallet, &pk, args).await; } signing_key } TxSigningKey::SecretKey(signing_key) => { // Check if the signing key needs to reveal its PK first let pk: common::PublicKey = signing_key.ref_to(); - super::tx::reveal_pk_if_needed(client, ctx, &pk, args).await; + super::tx::reveal_pk_if_needed(client, wallet, &pk, args).await; signing_key } TxSigningKey::None => { @@ -142,12 +142,12 @@ 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( client: &HttpClient, - mut ctx: Context, + wallet: &mut Wallet, tx: Tx, args: &args::Tx, default: TxSigningKey, -) -> (Context, TxBroadcastData) { - let keypair = tx_signer(client, &mut ctx, args, default).await; +) -> TxBroadcastData { + let keypair = tx_signer(client, wallet, args, default).await; let tx = tx.sign(&keypair); let epoch = rpc::query_epoch(client) @@ -157,7 +157,7 @@ pub async fn sign_tx( } else { sign_wrapper(args, epoch, tx, &keypair).await }; - (ctx, broadcast_data) + broadcast_data } /// Create a wrapper tx from a normal tx. Get the hash of the diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index cddc9829c7..c56a62bb37 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -56,6 +56,7 @@ use crate::facade::tendermint_rpc::endpoint::broadcast::tx_sync::Response; use crate::facade::tendermint_rpc::error::Error as RpcError; use crate::facade::tendermint_rpc::{Client, HttpClient}; use crate::node::ledger::tendermint_node; +use crate::wallet::Wallet; /// Timeout for requests to the `/accepted` and `/applied` /// ABCI query endpoints. @@ -66,16 +67,16 @@ 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(client: &HttpClient, ctx: Context, args: args::TxCustom) { +pub async fn submit_custom(client: &HttpClient, wallet: &mut Wallet, args: args::TxCustom) { let tx_code = args.code_path; let data = args.data_path; let tx = Tx::new(tx_code, data); - let (ctx, initialized_accounts) = - process_tx(client, ctx, &args.tx, tx, TxSigningKey::None).await; - save_initialized_accounts(ctx, &args.tx, initialized_accounts).await; + let initialized_accounts = + process_tx(client, wallet, &args.tx, tx, TxSigningKey::None).await; + save_initialized_accounts(wallet, &args.tx, initialized_accounts).await; } -pub async fn submit_update_vp(client: &HttpClient, ctx: Context, args: args::TxUpdateVp) { +pub async fn submit_update_vp(client: &HttpClient, wallet: &mut Wallet, args: args::TxUpdateVp) { let addr = args.addr.clone(); // Check that the address is established and exists on chain @@ -126,10 +127,10 @@ pub async fn submit_update_vp(client: &HttpClient, ctx: Context, args: args::TxU let data = data.try_to_vec().expect("Encoding tx data shouldn't fail"); let tx = Tx::new(tx_code, Some(data)); - process_tx(client, ctx, &args.tx, tx, TxSigningKey::WalletAddress(args.addr)).await; + process_tx(client, wallet, &args.tx, tx, TxSigningKey::WalletAddress(args.addr)).await; } -pub async fn submit_init_account(client: &HttpClient, ctx: Context, args: args::TxInitAccount) { +pub async fn submit_init_account(client: &HttpClient, wallet: &mut Wallet, args: args::TxInitAccount) { let public_key = args.public_key; let vp_code = args.vp_code_path; // Validate the VP code @@ -148,10 +149,10 @@ pub async fn submit_init_account(client: &HttpClient, ctx: Context, args: args:: 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(client, ctx, &args.tx, tx, TxSigningKey::WalletAddress(args.source)) + let initialized_accounts = + process_tx(client, wallet, &args.tx, tx, TxSigningKey::WalletAddress(args.source)) .await; - save_initialized_accounts(ctx, &args.tx, initialized_accounts).await; + save_initialized_accounts(wallet, &args.tx, initialized_accounts).await; } pub async fn submit_init_validator( @@ -274,8 +275,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(client, ctx, &tx_args, tx, TxSigningKey::WalletAddress(source)) + let initialized_accounts = + process_tx(client, &mut ctx.wallet, &tx_args, tx, TxSigningKey::WalletAddress(source)) .await; if !tx_args.dry_run { let (validator_address_alias, validator_address) = @@ -603,7 +604,7 @@ pub async fn submit_transfer(client: &HttpClient, mut ctx: Context, args: args:: }; // 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(client, &mut ctx, &args.tx, default_signer.clone()) + let chosen_signer = tx_signer(client, &mut ctx.wallet, &args.tx, default_signer.clone()) .await .ref_to(); let shielded_gas = masp_tx_key().ref_to() == chosen_signer; @@ -658,10 +659,10 @@ pub async fn submit_transfer(client: &HttpClient, mut ctx: Context, args: args:: let tx = Tx::new(tx_code, Some(data)); let signing_address = TxSigningKey::WalletAddress(source); - process_tx(client, ctx, &args.tx, tx, signing_address).await; + process_tx(client, &mut ctx.wallet, &args.tx, tx, signing_address).await; } -pub async fn submit_ibc_transfer(client: &HttpClient, ctx: Context, args: args::TxIbcTransfer) { +pub async fn submit_ibc_transfer(client: &HttpClient, wallet: &mut Wallet, args: args::TxIbcTransfer) { let source = args.source.clone(); // Check that the source address exists on chain let source_exists = @@ -767,7 +768,7 @@ pub async fn submit_ibc_transfer(client: &HttpClient, ctx: Context, args: args:: .expect("Encoding tx data shouldn't fail"); let tx = Tx::new(tx_code, Some(data)); - process_tx(client, ctx, &args.tx, tx, TxSigningKey::WalletAddress(args.source)) + process_tx(client, wallet, &args.tx, tx, TxSigningKey::WalletAddress(args.source)) .await; } @@ -903,12 +904,12 @@ pub async fn submit_init_proposal(client: &HttpClient, mut ctx: Context, args: a let tx_code = args.tx_code_path; let tx = Tx::new(tx_code, Some(data)); - process_tx(client, ctx, &args.tx, tx, TxSigningKey::WalletAddress(signer)) + process_tx(client, &mut ctx.wallet, &args.tx, tx, TxSigningKey::WalletAddress(signer)) .await; } } -pub async fn submit_vote_proposal(client: &HttpClient, mut ctx: Context, args: args::VoteProposal) { +pub async fn submit_vote_proposal(client: &HttpClient, wallet: &mut Wallet, args: args::VoteProposal) { let signer = if let Some(addr) = &args.tx.signer { addr } else { @@ -937,7 +938,7 @@ pub async fn submit_vote_proposal(client: &HttpClient, mut ctx: Context, args: a let signing_key = find_keypair( client, - &mut ctx.wallet, + wallet, &signer, ) .await; @@ -1034,7 +1035,7 @@ pub async fn submit_vote_proposal(client: &HttpClient, mut ctx: Context, args: a process_tx( client, - ctx, + wallet, &args.tx, tx, TxSigningKey::WalletAddress(signer.clone()), @@ -1054,13 +1055,13 @@ pub async fn submit_vote_proposal(client: &HttpClient, mut ctx: Context, args: a } } -pub async fn submit_reveal_pk(client: &HttpClient, mut ctx: Context, args: args::RevealPk) { +pub async fn submit_reveal_pk(client: &HttpClient, wallet: &mut Wallet, args: args::RevealPk) { let args::RevealPk { tx: args, public_key, } = args; let public_key = public_key; - if !reveal_pk_if_needed(client, &mut ctx, &public_key, &args).await { + if !reveal_pk_if_needed(client, wallet, &public_key, &args).await { let addr: Address = (&public_key).into(); println!("PK for {addr} is already revealed, nothing to do."); } @@ -1068,7 +1069,7 @@ pub async fn submit_reveal_pk(client: &HttpClient, mut ctx: Context, args: args: pub async fn reveal_pk_if_needed( client: &HttpClient, - ctx: &mut Context, + wallet: &mut Wallet, public_key: &common::PublicKey, args: &args::Tx, ) -> bool { @@ -1077,7 +1078,7 @@ pub async fn reveal_pk_if_needed( if args.force || !has_revealed_pk(client, &addr).await { // If not, submit it - submit_reveal_pk_aux(client, ctx, public_key, args).await; + submit_reveal_pk_aux(client, wallet, public_key, args).await; true } else { false @@ -1093,7 +1094,7 @@ pub async fn has_revealed_pk( pub async fn submit_reveal_pk_aux( client: &HttpClient, - ctx: &mut Context, + wallet: &mut Wallet, public_key: &common::PublicKey, args: &args::Tx, ) { @@ -1110,10 +1111,10 @@ pub async fn submit_reveal_pk_aux( signing_key.clone() } else if let Some(signer) = args.signer.as_ref() { let signer = signer; - find_keypair(client, &mut ctx.wallet, &signer) + find_keypair(client, wallet, &signer) .await } else { - find_keypair(client, &mut ctx.wallet, &addr).await + find_keypair(client, wallet, &addr).await }; let epoch = rpc::query_epoch(client) .await; @@ -1230,7 +1231,7 @@ async fn filter_delegations( delegations.into_iter().flatten().collect() } -pub async fn submit_bond(client: &HttpClient, ctx: Context, args: args::Bond) { +pub async fn submit_bond(client: &HttpClient, wallet: &mut Wallet, args: args::Bond) { let validator = args.validator.clone(); // Check that the validator address exists on chain let is_validator = @@ -1294,7 +1295,7 @@ pub async fn submit_bond(client: &HttpClient, ctx: Context, args: args::Bond) { let default_signer = args.source.unwrap_or(args.validator); process_tx( client, - ctx, + wallet, &args.tx, tx, TxSigningKey::WalletAddress(default_signer), @@ -1302,7 +1303,7 @@ pub async fn submit_bond(client: &HttpClient, ctx: Context, args: args::Bond) { .await; } -pub async fn submit_unbond(client: &HttpClient, ctx: Context, args: args::Unbond) { +pub async fn submit_unbond(client: &HttpClient, wallet: &mut Wallet, args: args::Unbond) { let validator = args.validator.clone(); // Check that the validator address exists on chain let is_validator = @@ -1367,7 +1368,7 @@ pub async fn submit_unbond(client: &HttpClient, ctx: Context, args: args::Unbond let default_signer = args.source.unwrap_or(args.validator); process_tx( client, - ctx, + wallet, &args.tx, tx, TxSigningKey::WalletAddress(default_signer), @@ -1375,7 +1376,7 @@ pub async fn submit_unbond(client: &HttpClient, ctx: Context, args: args::Unbond .await; } -pub async fn submit_withdraw(client: &HttpClient, ctx: Context, args: args::Withdraw) { +pub async fn submit_withdraw(client: &HttpClient, wallet: &mut Wallet, args: args::Withdraw) { let epoch = rpc::query_epoch(client) .await; @@ -1438,7 +1439,7 @@ pub async fn submit_withdraw(client: &HttpClient, ctx: Context, args: args::With let default_signer = args.source.unwrap_or(args.validator); process_tx( client, - ctx, + wallet, &args.tx, tx, TxSigningKey::WalletAddress(default_signer), @@ -1448,7 +1449,7 @@ pub async fn submit_withdraw(client: &HttpClient, ctx: Context, args: args::With pub async fn submit_validator_commission_change( client: &HttpClient, - ctx: Context, + wallet: &mut Wallet, args: args::TxCommissionRateChange, ) { let epoch = rpc::query_epoch(client) @@ -1519,7 +1520,7 @@ pub async fn submit_validator_commission_change( let default_signer = args.validator.clone(); process_tx( client, - ctx, + wallet, &args.tx, tx, TxSigningKey::WalletAddress(default_signer), @@ -1531,12 +1532,12 @@ pub async fn submit_validator_commission_change( /// initialized in the transaction if any. In dry run, this is always empty. async fn process_tx( client: &HttpClient, - ctx: Context, + wallet: &mut Wallet, args: &args::Tx, tx: Tx, default_signer: TxSigningKey, -) -> (Context, Vec
) { - let (ctx, to_broadcast) = sign_tx(client, ctx, tx, args, default_signer).await; +) -> Vec
{ + let to_broadcast = sign_tx(client, wallet, tx, args, default_signer).await; // NOTE: use this to print the request JSON body: // let request = @@ -1550,7 +1551,7 @@ async fn process_tx( if args.dry_run { if let TxBroadcastData::DryRun(tx) = to_broadcast { rpc::dry_run_tx(client, tx.to_bytes()).await; - (ctx, vec![]) + vec![] } else { panic!( "Expected a dry-run transaction, received a wrapper \ @@ -1568,8 +1569,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: {}", @@ -1590,7 +1591,7 @@ async fn process_tx( /// Save accounts initialized from a tx into the wallet, if any. async fn save_initialized_accounts( - mut ctx: Context, + wallet: &mut Wallet, args: &args::Tx, initialized_accounts: Vec
, ) { @@ -1603,7 +1604,6 @@ async fn save_initialized_accounts( if len == 1 { "" } else { "s" } ); // Store newly initialized account addresses in the wallet - let wallet = &mut ctx.wallet; for (ix, address) in initialized_accounts.iter().enumerate() { let encoded = address.encode(); let alias: Cow = match &args.initialized_account_alias { From b3a852bc6ba7508d096d5bc21615143f39c6bbcd Mon Sep 17 00:00:00 2001 From: Murisi Tarusenga Date: Thu, 15 Dec 2022 09:00:37 +0200 Subject: [PATCH 16/51] Removed Context dependency from submit_transfer. --- apps/src/bin/anoma-client/cli.rs | 3 +-- apps/src/lib/cli.rs | 2 +- apps/src/lib/client/rpc.rs | 2 +- apps/src/lib/client/signing.rs | 2 +- apps/src/lib/client/tx.rs | 14 ++++++++++---- 5 files changed, 14 insertions(+), 9 deletions(-) diff --git a/apps/src/bin/anoma-client/cli.rs b/apps/src/bin/anoma-client/cli.rs index 7b59bc74e2..7c1cbdc6cb 100644 --- a/apps/src/bin/anoma-client/cli.rs +++ b/apps/src/bin/anoma-client/cli.rs @@ -21,7 +21,7 @@ pub async fn main() -> Result<()> { Sub::TxTransfer(TxTransfer(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_transfer(&client, ctx, args).await; + tx::submit_transfer(&client, &mut ctx.wallet, &mut ctx.shielded, args).await; } Sub::TxIbcTransfer(TxIbcTransfer(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); @@ -89,7 +89,6 @@ pub async fn main() -> Result<()> { } Sub::QueryBlock(QueryBlock(args)) => { let client = HttpClient::new(args.ledger_address.clone()).unwrap(); - let args = args.to_sdk(&mut ctx); rpc::query_block(&client).await; } Sub::QueryBalance(QueryBalance(args)) => { diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index bc6f98099e..dc68bb288a 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -3245,7 +3245,7 @@ pub mod args { } impl Query { - pub fn to_sdk(self, ctx: &mut Context) -> Query { + pub fn to_sdk(self, _ctx: &mut Context) -> Query { Query:: { ledger_address: (), } diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index 121af27176..6ffbce62dc 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -1112,7 +1112,7 @@ pub async fn query_proposal_result( pub async fn query_protocol_parameters( client: &HttpClient, - args: args::QueryProtocolParameters, + _args: args::QueryProtocolParameters, ) { let gov_parameters = get_governance_parameters(&client).await; println!("Governance Parameters\n {:4}", gov_parameters); diff --git a/apps/src/lib/client/signing.rs b/apps/src/lib/client/signing.rs index 0ca108bd57..61946b7bc0 100644 --- a/apps/src/lib/client/signing.rs +++ b/apps/src/lib/client/signing.rs @@ -10,7 +10,7 @@ use namada::types::transaction::{hash_tx, Fee, WrapperTx}; use tendermint_rpc::HttpClient; use super::rpc; -use crate::cli::{self, args, Context}; +use crate::cli::{self, args}; use crate::client::tendermint_rpc_types::TxBroadcastData; use crate::wallet::Wallet; diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index c56a62bb37..0cbce5852d 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -23,6 +23,7 @@ use namada::ibc::Height as IbcHeight; use namada::ibc_proto::cosmos::base::v1beta1::Coin; use namada::ledger::governance::storage as gov_storage; use namada::ledger::masp; +use namada::ledger::masp::ShieldedContext; use namada::ledger::pos::{BondId, Bonds, CommissionRates, Unbonds}; use namada::proto::Tx; use namada::types::address::{masp, masp_tx_key, Address}; @@ -497,7 +498,12 @@ impl masp::ShieldedUtils for CLIShieldedUtils { -pub async fn submit_transfer(client: &HttpClient, mut ctx: Context, args: args::TxTransfer) { +pub async fn submit_transfer( + client: &HttpClient, + wallet: &mut Wallet, + shielded: &mut ShieldedContext, + args: args::TxTransfer, +) { let transfer_source = args.source; let source = transfer_source.effective_address(); let transfer_target = args.target.clone(); @@ -604,7 +610,7 @@ pub async fn submit_transfer(client: &HttpClient, mut ctx: Context, args: args:: }; // 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(client, &mut ctx.wallet, &args.tx, default_signer.clone()) + let chosen_signer = tx_signer(client, wallet, &args.tx, default_signer.clone()) .await .ref_to(); let shielded_gas = masp_tx_key().ref_to() == chosen_signer; @@ -615,7 +621,7 @@ pub async fn submit_transfer(client: &HttpClient, mut ctx: Context, args: args:: }; let stx_result = - ctx.shielded.gen_shielded_transfer( + shielded.gen_shielded_transfer( transfer_source, transfer_target, args.amount, @@ -659,7 +665,7 @@ pub async fn submit_transfer(client: &HttpClient, mut ctx: Context, args: args:: let tx = Tx::new(tx_code, Some(data)); let signing_address = TxSigningKey::WalletAddress(source); - process_tx(client, &mut ctx.wallet, &args.tx, tx, signing_address).await; + process_tx(client, wallet, &args.tx, tx, signing_address).await; } pub async fn submit_ibc_transfer(client: &HttpClient, wallet: &mut Wallet, args: args::TxIbcTransfer) { From 598c5874359d44f30c09107332eff87c2d588cbc Mon Sep 17 00:00:00 2001 From: Murisi Tarusenga Date: Thu, 15 Dec 2022 10:05:33 +0200 Subject: [PATCH 17/51] Reduced dependency of rpc.rs on Context. --- apps/src/bin/anoma-client/cli.rs | 12 +-- apps/src/lib/client/rpc.rs | 134 ++++++++++++++++--------------- apps/src/lib/client/tx.rs | 5 +- 3 files changed, 79 insertions(+), 72 deletions(-) diff --git a/apps/src/bin/anoma-client/cli.rs b/apps/src/bin/anoma-client/cli.rs index 7c1cbdc6cb..ccbd15887d 100644 --- a/apps/src/bin/anoma-client/cli.rs +++ b/apps/src/bin/anoma-client/cli.rs @@ -80,7 +80,7 @@ pub async fn main() -> Result<()> { } Sub::QueryTransfers(QueryTransfers(args)) => { let args = args.to_sdk(&mut ctx); - rpc::query_transfers(ctx, args).await; + rpc::query_transfers(&mut ctx.wallet, &mut ctx.shielded, args).await; } Sub::QueryConversions(QueryConversions(args)) => { let client = HttpClient::new(args.query.ledger_address.clone()).unwrap(); @@ -94,7 +94,7 @@ pub async fn main() -> Result<()> { Sub::QueryBalance(QueryBalance(args)) => { let client = HttpClient::new(args.query.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - rpc::query_balance(&client, ctx, args).await; + rpc::query_balance(&client, &mut ctx.wallet, &mut ctx.shielded, args).await; } Sub::QueryBonds(QueryBonds(args)) => { let client = HttpClient::new(args.query.ledger_address.clone()).unwrap(); @@ -121,7 +121,7 @@ pub async fn main() -> Result<()> { let (client, driver) = WebSocketClient::new(args.query.ledger_address.clone()).await?; let driver_handle = tokio::spawn(async move { driver.run().await }); let args = args.to_sdk(&mut ctx); - rpc::query_result(&client, ctx, args).await; + rpc::query_result(&client, args).await; // Signal to the driver to terminate. client.close()?; // Await the driver's termination to ensure proper connection closure. @@ -133,18 +133,18 @@ pub async fn main() -> Result<()> { Sub::QueryRawBytes(QueryRawBytes(args)) => { let client = HttpClient::new(args.query.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - rpc::query_raw_bytes(&client, ctx, args).await; + rpc::query_raw_bytes(&client, args).await; } Sub::QueryProposal(QueryProposal(args)) => { let client = HttpClient::new(args.query.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - rpc::query_proposal(&client, ctx, args).await; + rpc::query_proposal(&client, args).await; } Sub::QueryProposalResult(QueryProposalResult(args)) => { let client = HttpClient::new(args.query.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - rpc::query_proposal_result(&client, ctx, args).await; + rpc::query_proposal_result(&client, args).await; } Sub::QueryProtocolParameters(QueryProtocolParameters(args)) => { let client = HttpClient::new(args.query.ledger_address.clone()).unwrap(); diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index 6ffbce62dc..e53f0e4717 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -31,6 +31,7 @@ use namada::ledger::pos::types::{decimal_mult_u64, WeightedValidator}; use namada::ledger::pos::{ self, is_validator_slashes_key, BondId, Bonds, PosParams, Slash, Unbonds, }; +use namada::ledger::masp::ShieldedUtils; use namada::ledger::queries::{self, RPC}; use namada::ledger::storage::ConversionState; use namada::types::address::{masp, tokens, Address}; @@ -38,6 +39,7 @@ use namada::types::governance::{ OfflineProposal, OfflineVote, ProposalResult, ProposalVote, TallyResult, VotePower, }; +use namada::ledger::masp::ShieldedContext; use namada::types::hash::Hash; use namada::types::key::*; use namada::types::masp::{BalanceOwner, ExtendedViewingKey, PaymentAddress}; @@ -49,7 +51,8 @@ use namada::types::{address, storage, token}; use rust_decimal::Decimal; use tokio::time::{Duration, Instant}; -use crate::cli::{self, args, Context}; +use crate::wallet::Wallet; +use crate::cli::{self, args}; use crate::client::tendermint_rpc_types::TxResponse; use namada::ledger::masp::{Conversions, PinnedBalanceError}; use crate::facade::tendermint::merkle::proof::Proof; @@ -136,24 +139,28 @@ pub async fn query_results(client: &HttpClient) -> Vec { } /// Query the specified accepted transfers from the ledger -pub async fn query_transfers(mut ctx: Context, args: args::QueryTransfers) { +pub async fn query_transfers( + wallet: &mut Wallet, + shielded: &mut ShieldedContext, + args: args::QueryTransfers +) { let query_token = args.token; let query_owner = args.owner .map_or_else( - || Either::Right(ctx.wallet.get_addresses().into_values().collect()), + || Either::Right(wallet.get_addresses().into_values().collect()), Either::Left, ); - let _ = ctx.shielded.load(); + let _ = shielded.load(); // Obtain the effects of all shielded and transparent transactions - let transfers = ctx.shielded.query_tx_deltas( + let transfers = shielded.query_tx_deltas( &query_owner, &query_token, - &ctx.wallet.get_viewing_keys(), + &wallet.get_viewing_keys(), ) .await; // To facilitate lookups of human-readable token names let tokens = tokens(); - let vks = ctx.wallet.get_viewing_keys(); + let vks = wallet.get_viewing_keys(); // To enable ExtendedFullViewingKeys to be displayed instead of ViewingKeys let fvk_map: HashMap<_, _> = vks .values() @@ -176,8 +183,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 - .shielded + let amt = shielded .compute_exchanged_amount( amt, epoch, @@ -186,7 +192,7 @@ pub async fn query_transfers(mut ctx: Context, args: args::QueryTransfers) { .await .0; let dec = - ctx.shielded.decode_amount(amt, epoch).await; + shielded.decode_amount(amt, epoch).await; shielded_accounts.insert(acc, dec); } // Check if this transfer pertains to the supplied token @@ -253,7 +259,7 @@ pub async fn query_transfers(mut ctx: Context, args: args::QueryTransfers) { } /// Query the raw bytes of given storage key -pub async fn query_raw_bytes(client: &HttpClient, _ctx: Context, args: args::QueryRawBytes) { +pub async fn query_raw_bytes(client: &HttpClient, args: args::QueryRawBytes) { let response = unwrap_client_response( RPC.shell() .storage_value(client, None, None, false, &args.storage_key) @@ -267,26 +273,31 @@ pub async fn query_raw_bytes(client: &HttpClient, _ctx: Context, args: args::Que } /// Query token balance(s) -pub async fn query_balance(client: &HttpClient, mut ctx: Context, args: args::QueryBalance) { +pub async fn query_balance( + client: &HttpClient, + wallet: &mut Wallet, + shielded: &mut ShieldedContext, + args: args::QueryBalance, +) { // Query the balances of shielded or transparent account types depending on // the CLI arguments match &args.owner { Some(BalanceOwner::FullViewingKey(_viewing_key)) => { - query_shielded_balance(client, &mut ctx, args).await + query_shielded_balance(client, wallet, shielded, args).await } Some(BalanceOwner::Address(_owner)) => { - query_transparent_balance(client, &mut ctx, args).await + query_transparent_balance(client, wallet, args).await } Some(BalanceOwner::PaymentAddress(_owner)) => { - query_pinned_balance(&mut ctx, args).await + query_pinned_balance(wallet, shielded, args).await } None => { // Print pinned balance - query_pinned_balance(&mut ctx, args.clone()).await; + query_pinned_balance(wallet, shielded, args.clone()).await; // Print shielded balance - query_shielded_balance(client, &mut ctx, args.clone()).await; + query_shielded_balance(client, wallet, shielded, args.clone()).await; // Then print transparent balance - query_transparent_balance(client, &mut ctx, args).await; + query_transparent_balance(client, wallet, args).await; } }; } @@ -294,7 +305,7 @@ pub async fn query_balance(client: &HttpClient, mut ctx: Context, args: args::Qu /// Query token balance(s) pub async fn query_transparent_balance( client: &HttpClient, - ctx: &mut Context, + wallet: &mut Wallet, args: args::QueryBalance, ) { let tokens = address::tokens(); @@ -339,7 +350,7 @@ pub async fn query_transparent_balance( .await; if let Some(balances) = balances { print_balances( - ctx, + wallet, balances, &token, owner.address().as_ref(), @@ -352,7 +363,7 @@ pub async fn query_transparent_balance( let balances = query_storage_prefix::(&client, &prefix).await; if let Some(balances) = balances { - print_balances(ctx, balances, &token, None); + print_balances(wallet, balances, &token, None); } } (None, None) => { @@ -361,7 +372,7 @@ pub async fn query_transparent_balance( let balances = query_storage_prefix::(&client, &key).await; if let Some(balances) = balances { - print_balances(ctx, balances, &token, None); + print_balances(wallet, balances, &token, None); } } } @@ -369,7 +380,11 @@ 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( + wallet: &mut Wallet, + shielded: &mut ShieldedContext, + args: args::QueryBalance, +) { // Map addresses to token names let tokens = address::tokens(); let owners = if let Some(pa) = args @@ -378,28 +393,26 @@ pub async fn query_pinned_balance(ctx: &mut Context, args: args::QueryBalance) { { vec![pa] } else { - ctx.wallet + wallet .get_payment_addrs() .into_values() .filter(PaymentAddress::is_pinned) .collect() }; // Get the viewing keys with which to try note decryptions - let viewing_keys: Vec = ctx - .wallet + let viewing_keys: Vec = wallet .get_viewing_keys() .values() .map(|fvk| ExtendedFullViewingKey::from(*fvk).fvk.vk) .collect(); - let _ = ctx.shielded.load(); + let _ = shielded.load(); // Print the token balances by payment address for owner in owners { let mut balance = Err(PinnedBalanceError::InvalidViewingKey); // Find the viewing key that can recognize payments the current payment // address for vk in &viewing_keys { - balance = ctx - .shielded + balance = shielded .compute_exchanged_pinned_balance( owner, vk, @@ -424,8 +437,7 @@ pub async fn query_pinned_balance(ctx: &mut Context, args: args::QueryBalance) { }; let vk = ExtendedFullViewingKey::from(fvk).fvk.vk; // Use the given viewing key to decrypt pinned transaction data - balance = ctx - .shielded + balance = shielded .compute_exchanged_pinned_balance( owner, &vk, @@ -467,8 +479,7 @@ pub async fn query_pinned_balance(ctx: &mut Context, args: args::QueryBalance) { (Ok((balance, epoch)), None) => { let mut found_any = false; // Print balances by human-readable token names - let balance = ctx - .shielded + let balance = shielded .decode_amount(balance, epoch) .await; for (addr, value) in balance.components() { @@ -501,7 +512,7 @@ pub async fn query_pinned_balance(ctx: &mut Context, args: args::QueryBalance) { } fn print_balances( - ctx: &Context, + wallet: &Wallet, balances: impl Iterator, token: &Address, target: Option<&Address>, @@ -526,7 +537,7 @@ fn print_balances( "with {}: {}, owned by {}", sub_prefix, balance, - lookup_alias(ctx, owner) + lookup_alias(wallet, owner) ), )), None => token::is_any_token_balance_key(&key).map(|owner| { @@ -535,7 +546,7 @@ fn print_balances( format!( ": {}, owned by {}", balance, - lookup_alias(ctx, owner) + lookup_alias(wallet, owner) ), ) }), @@ -554,7 +565,7 @@ fn print_balances( if print_num == 0 { match target { Some(t) => { - writeln!(w, "No balances owned by {}", lookup_alias(ctx, t)) + writeln!(w, "No balances owned by {}", lookup_alias(wallet, t)) .unwrap() } None => { @@ -565,7 +576,7 @@ fn print_balances( } /// Query Proposals -pub async fn query_proposal(client: &HttpClient, _ctx: Context, args: args::QueryProposal) { +pub async fn query_proposal(client: &HttpClient, args: args::QueryProposal) { async fn print_proposal( client: &HttpClient, id: u64, @@ -691,9 +702,10 @@ pub fn value_by_address( } /// Query token shielded balance(s) -pub async fn query_shielded_balance( +pub async fn query_shielded_balance( client: &HttpClient, - ctx: &mut Context, + wallet: &mut Wallet, + shielded: &mut ShieldedContext, args: args::QueryBalance, ) { // Used to control whether balances for all keys or a specific key are @@ -707,16 +719,16 @@ pub async fn query_shielded_balance( // provided, then convert to a viewing key first. let viewing_keys = match owner { Some(viewing_key) => vec![viewing_key], - None => ctx.wallet.get_viewing_keys().values().copied().collect(), + None => wallet.get_viewing_keys().values().copied().collect(), }; - let _ = ctx.shielded.load(); + let _ = shielded.load(); let fvks: Vec<_> = viewing_keys .iter() .map(|fvk| ExtendedFullViewingKey::from(*fvk).fvk.vk) .collect(); - ctx.shielded.fetch(&[], &fvks).await; + shielded.fetch(&[], &fvks).await; // Save the update state so that future fetches can be short-circuited - let _ = ctx.shielded.save(); + let _ = shielded.save(); // The epoch is required to identify timestamped tokens let epoch = query_epoch(client).await; // Map addresses to token names @@ -728,11 +740,11 @@ pub async fn query_shielded_balance( let viewing_key = ExtendedFullViewingKey::from(viewing_keys[0]).fvk.vk; let balance: Amount = if no_conversions { - ctx.shielded + shielded .compute_shielded_balance(&viewing_key) .expect("context should contain viewing key") } else { - ctx.shielded + shielded .compute_exchanged_balance( &viewing_key, epoch, @@ -772,11 +784,11 @@ pub async fn query_shielded_balance( // Query the multi-asset balance at the given spending key let viewing_key = ExtendedFullViewingKey::from(fvk).fvk.vk; let balance = if no_conversions { - ctx.shielded + shielded .compute_shielded_balance(&viewing_key) .expect("context should contain viewing key") } else { - ctx.shielded + shielded .compute_exchanged_balance( &viewing_key, epoch, @@ -797,8 +809,7 @@ pub async fn query_shielded_balance( // Print non-zero balances whose asset types can be decoded for (asset_type, balances) in balances { // Decode the asset type - let decoded = ctx - .shielded + let decoded = shielded .decode_asset_type(asset_type) .await; match decoded { @@ -863,11 +874,11 @@ pub async fn query_shielded_balance( // Query the multi-asset balance at the given spending key let viewing_key = ExtendedFullViewingKey::from(fvk).fvk.vk; let balance = if no_conversions { - ctx.shielded + shielded .compute_shielded_balance(&viewing_key) .expect("context should contain viewing key") } else { - ctx.shielded + shielded .compute_exchanged_balance( &viewing_key, epoch, @@ -896,19 +907,16 @@ pub async fn query_shielded_balance( ExtendedFullViewingKey::from(viewing_keys[0]).fvk.vk; let balance; if no_conversions { - balance = ctx - .shielded + balance = shielded .compute_shielded_balance(&viewing_key) .expect("context should contain viewing key"); // Print balances by human-readable token names - let decoded_balance = ctx - .shielded + let decoded_balance = shielded .decode_all_amounts(balance) .await; print_decoded_balance_with_epoch(decoded_balance); } else { - balance = ctx - .shielded + balance = shielded .compute_exchanged_balance( &viewing_key, epoch, @@ -916,8 +924,7 @@ pub async fn query_shielded_balance( .await .expect("context should contain viewing key"); // Print balances by human-readable token names - let decoded_balance = ctx - .shielded + let decoded_balance = shielded .decode_amount(balance, epoch) .await; print_decoded_balance(decoded_balance); @@ -977,7 +984,6 @@ pub async fn get_token_balance( pub async fn query_proposal_result( client: &HttpClient, - _ctx: Context, args: args::QueryProposalResult, ) { let current_epoch = query_epoch(client).await; @@ -2266,7 +2272,7 @@ pub async fn query_tx_response( /// Lookup the results of applying the specified transaction to the /// blockchain. -pub async fn query_result(client: &WebSocketClient, _ctx: Context, args: args::QueryResult) { +pub async fn query_result(client: &WebSocketClient, args: args::QueryResult) { // First try looking up application event pertaining to given hash. let tx_response = query_tx_response( client, @@ -2690,8 +2696,8 @@ 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 { - match ctx.wallet.find_alias(addr) { +fn lookup_alias(wallet: &Wallet, addr: &Address) -> String { + match wallet.find_alias(addr) { Some(alias) => format!("{}", alias), None => format!("{}", addr), } diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index 0cbce5852d..6df48e9fa1 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -42,6 +42,7 @@ use namada::types::transaction::governance::{ use namada::types::transaction::{pos, InitAccount, InitValidator, UpdateVp}; use namada::types::{storage, token}; use namada::{ledger, vm}; +use namada::ledger::masp::ShieldedUtils; use rust_decimal::Decimal; use tokio::time::{Duration, Instant}; use async_trait::async_trait; @@ -498,10 +499,10 @@ impl masp::ShieldedUtils for CLIShieldedUtils { -pub async fn submit_transfer( +pub async fn submit_transfer( client: &HttpClient, wallet: &mut Wallet, - shielded: &mut ShieldedContext, + shielded: &mut ShieldedContext, args: args::TxTransfer, ) { let transfer_source = args.source; From 11e048f13cbb2022ea4d64ac9e6727555980e47d Mon Sep 17 00:00:00 2001 From: Murisi Tarusenga Date: Thu, 15 Dec 2022 11:18:58 +0200 Subject: [PATCH 18/51] Now pass down Client as a parameter in ShieldedContext. --- apps/src/bin/anoma-client/cli.rs | 3 +- apps/src/lib/client/rpc.rs | 34 ++++++++++----- apps/src/lib/client/tx.rs | 35 +++++---------- shared/src/ledger/masp.rs | 75 ++++++++++++++++++-------------- 4 files changed, 79 insertions(+), 68 deletions(-) diff --git a/apps/src/bin/anoma-client/cli.rs b/apps/src/bin/anoma-client/cli.rs index ccbd15887d..1e7d5c3bc9 100644 --- a/apps/src/bin/anoma-client/cli.rs +++ b/apps/src/bin/anoma-client/cli.rs @@ -79,8 +79,9 @@ pub async fn main() -> Result<()> { rpc::query_epoch(&client).await; } Sub::QueryTransfers(QueryTransfers(args)) => { + let client = HttpClient::new(args.query.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - rpc::query_transfers(&mut ctx.wallet, &mut ctx.shielded, args).await; + rpc::query_transfers(&client, &mut ctx.wallet, &mut ctx.shielded, args).await; } Sub::QueryConversions(QueryConversions(args)) => { let client = HttpClient::new(args.query.ledger_address.clone()).unwrap(); diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index e53f0e4717..056ce8b5d3 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -139,7 +139,8 @@ pub async fn query_results(client: &HttpClient) -> Vec { } /// Query the specified accepted transfers from the ledger -pub async fn query_transfers( +pub async fn query_transfers>( + client: &HttpClient, wallet: &mut Wallet, shielded: &mut ShieldedContext, args: args::QueryTransfers @@ -153,6 +154,7 @@ pub async fn query_transfers( let _ = shielded.load(); // Obtain the effects of all shielded and transparent transactions let transfers = shielded.query_tx_deltas( + client, &query_owner, &query_token, &wallet.get_viewing_keys(), @@ -185,6 +187,7 @@ pub async fn query_transfers( // transaction's reception let amt = shielded .compute_exchanged_amount( + client, amt, epoch, Conversions::new(), @@ -192,7 +195,7 @@ pub async fn query_transfers( .await .0; let dec = - shielded.decode_amount(amt, epoch).await; + shielded.decode_amount(client, amt, epoch).await; shielded_accounts.insert(acc, dec); } // Check if this transfer pertains to the supplied token @@ -273,7 +276,7 @@ pub async fn query_raw_bytes(client: &HttpClient, args: args::QueryRawBytes) { } /// Query token balance(s) -pub async fn query_balance( +pub async fn query_balance>( client: &HttpClient, wallet: &mut Wallet, shielded: &mut ShieldedContext, @@ -289,11 +292,11 @@ pub async fn query_balance( query_transparent_balance(client, wallet, args).await } Some(BalanceOwner::PaymentAddress(_owner)) => { - query_pinned_balance(wallet, shielded, args).await + query_pinned_balance(client, wallet, shielded, args).await } None => { // Print pinned balance - query_pinned_balance(wallet, shielded, args.clone()).await; + query_pinned_balance(client, wallet, shielded, args.clone()).await; // Print shielded balance query_shielded_balance(client, wallet, shielded, args.clone()).await; // Then print transparent balance @@ -380,7 +383,8 @@ pub async fn query_transparent_balance( } /// Query the token pinned balance(s) -pub async fn query_pinned_balance( +pub async fn query_pinned_balance>( + client: &HttpClient, wallet: &mut Wallet, shielded: &mut ShieldedContext, args: args::QueryBalance, @@ -414,6 +418,7 @@ pub async fn query_pinned_balance( for vk in &viewing_keys { balance = shielded .compute_exchanged_pinned_balance( + client, owner, vk, ) @@ -439,6 +444,7 @@ pub async fn query_pinned_balance( // Use the given viewing key to decrypt pinned transaction data balance = shielded .compute_exchanged_pinned_balance( + client, owner, &vk, ) @@ -480,7 +486,7 @@ pub async fn query_pinned_balance( let mut found_any = false; // Print balances by human-readable token names let balance = shielded - .decode_amount(balance, epoch) + .decode_amount(client, balance, epoch) .await; for (addr, value) in balance.components() { let asset_value = token::Amount::from(*value as u64); @@ -702,7 +708,7 @@ pub fn value_by_address( } /// Query token shielded balance(s) -pub async fn query_shielded_balance( +pub async fn query_shielded_balance>( client: &HttpClient, wallet: &mut Wallet, shielded: &mut ShieldedContext, @@ -726,7 +732,7 @@ pub async fn query_shielded_balance( .iter() .map(|fvk| ExtendedFullViewingKey::from(*fvk).fvk.vk) .collect(); - shielded.fetch(&[], &fvks).await; + shielded.fetch(client, &[], &fvks).await; // Save the update state so that future fetches can be short-circuited let _ = shielded.save(); // The epoch is required to identify timestamped tokens @@ -746,6 +752,7 @@ pub async fn query_shielded_balance( } else { shielded .compute_exchanged_balance( + client, &viewing_key, epoch, ) @@ -790,6 +797,7 @@ pub async fn query_shielded_balance( } else { shielded .compute_exchanged_balance( + client, &viewing_key, epoch, ) @@ -810,7 +818,7 @@ pub async fn query_shielded_balance( for (asset_type, balances) in balances { // Decode the asset type let decoded = shielded - .decode_asset_type(asset_type) + .decode_asset_type(client, asset_type) .await; match decoded { Some((addr, asset_epoch)) if asset_epoch == epoch => { @@ -880,6 +888,7 @@ pub async fn query_shielded_balance( } else { shielded .compute_exchanged_balance( + client, &viewing_key, epoch, ) @@ -912,12 +921,13 @@ pub async fn query_shielded_balance( .expect("context should contain viewing key"); // Print balances by human-readable token names let decoded_balance = shielded - .decode_all_amounts(balance) + .decode_all_amounts(client, balance) .await; print_decoded_balance_with_epoch(decoded_balance); } else { balance = shielded .compute_exchanged_balance( + client, &viewing_key, epoch, ) @@ -925,7 +935,7 @@ pub async fn query_shielded_balance( .expect("context should contain viewing key"); // Print balances by human-readable token names let decoded_balance = shielded - .decode_amount(balance, epoch) + .decode_amount(client, balance, epoch) .await; print_decoded_balance(decoded_balance); } diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index 6df48e9fa1..611b7d7976 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -359,8 +359,6 @@ const TMP_FILE_NAME: &str = "shielded.tmp"; pub struct CLIShieldedUtils { #[borsh_skip] context_dir: PathBuf, - #[borsh_skip] - pub ledger_address: Option, } impl CLIShieldedUtils { @@ -385,14 +383,14 @@ impl CLIShieldedUtils { println!("MASP parameter download complete, resuming execution..."); } // Finally initialize a shielded context with the supplied directory - let utils = Self { context_dir, ledger_address: None }; + let utils = Self { context_dir }; masp::ShieldedContext { utils, ..Default::default() } } } impl Default for CLIShieldedUtils { fn default() -> Self { - Self { context_dir: PathBuf::from(FILE_NAME), ledger_address: None } + Self { context_dir: PathBuf::from(FILE_NAME) } } } @@ -401,16 +399,15 @@ impl masp::ShieldedUtils for CLIShieldedUtils { type C = HttpClient; async fn query_storage_value( - &self, + client: &HttpClient, key: &storage::Key, ) -> Option where T: BorshDeserialize { - let client = HttpClient::new(self.ledger_address.clone().unwrap()).unwrap(); - query_storage_value::(&client, &key).await + query_storage_value::(client, &key).await } - async fn query_epoch(&self) -> Epoch { - rpc::query_epoch(&self.client()).await + async fn query_epoch(client: &HttpClient) -> Epoch { + rpc::query_epoch(client).await } fn local_tx_prover(&self) -> LocalTxProver { @@ -472,7 +469,7 @@ impl masp::ShieldedUtils for CLIShieldedUtils { /// Query a conversion. async fn query_conversion( - &self, + client: &HttpClient, asset_type: AssetType, ) -> Option<( Address, @@ -480,26 +477,17 @@ impl masp::ShieldedUtils for CLIShieldedUtils { masp_primitives::transaction::components::Amount, MerklePath, )> { - let client = HttpClient::new(self.ledger_address.clone().unwrap()).unwrap(); - query_conversion(client, asset_type).await - } - - fn client(&self) -> Self::C { - let ledger_address = self - .ledger_address - .clone() - .expect("ledger address must be set"); - HttpClient::new(ledger_address).unwrap() + query_conversion(client.clone(), asset_type).await } - async fn query_results(&self) -> Vec { - rpc::query_results(&self.client()).await + async fn query_results(client: &HttpClient) -> Vec { + rpc::query_results(client).await } } -pub async fn submit_transfer( +pub async fn submit_transfer>( client: &HttpClient, wallet: &mut Wallet, shielded: &mut ShieldedContext, @@ -623,6 +611,7 @@ pub async fn submit_transfer( let stx_result = shielded.gen_shielded_transfer( + client, transfer_source, transfer_target, args.amount, diff --git a/shared/src/ledger/masp.rs b/shared/src/ledger/masp.rs index 099fb73aee..6eb9f401de 100644 --- a/shared/src/ledger/masp.rs +++ b/shared/src/ledger/masp.rs @@ -264,14 +264,14 @@ pub trait ShieldedUtils : Sized + BorshDeserialize + BorshSerialize + Default + /// Query the storage value at the given key async fn query_storage_value( - &self, + client: &Self::C, key: &storage::Key, ) -> Option where T: BorshDeserialize; /// Query the current epoch - async fn query_epoch(&self) -> Epoch; + async fn query_epoch(client: &Self::C) -> Epoch; /// Get a MASP transaction prover fn local_tx_prover(&self) -> LocalTxProver; @@ -284,7 +284,7 @@ pub trait ShieldedUtils : Sized + BorshDeserialize + BorshSerialize + Default + /// Query the designated conversion for the given AssetType async fn query_conversion( - &self, + client: &Self::C, asset_type: AssetType, ) -> Option<( Address, @@ -294,10 +294,7 @@ pub trait ShieldedUtils : Sized + BorshDeserialize + BorshSerialize + Default + )>; /// Query for all the accepted transactions that have occured to date - async fn query_results(&self) -> Vec; - - /// Get a client object with which to effect Tendermint queries - fn client(&self) -> Self::C; + async fn query_results(client: &Self::C) -> Vec; } /// Make a ViewingKey that can view notes encrypted by given ExtendedSpendingKey @@ -469,6 +466,7 @@ impl ShieldedContext { /// ShieldedContext pub async fn fetch( &mut self, + client: &U::C, sks: &[ExtendedSpendingKey], fvks: &[ViewingKey], ) { @@ -493,7 +491,7 @@ impl ShieldedContext { let (txs, mut tx_iter); if !unknown_keys.is_empty() { // Load all transactions accepted until this point - txs = Self::fetch_shielded_transfers(&self.utils, 0).await; + txs = Self::fetch_shielded_transfers(client, 0).await; tx_iter = txs.iter(); // Do this by constructing a shielding context only for unknown keys let mut tx_ctx = Self { utils: self.utils.clone(), ..Default::default() }; @@ -514,7 +512,7 @@ impl ShieldedContext { } else { // Load only transactions accepted from last_txid until this point txs = - Self::fetch_shielded_transfers(&self.utils, self.last_txidx) + Self::fetch_shielded_transfers(client, self.last_txidx) .await; tx_iter = txs.iter(); } @@ -531,7 +529,7 @@ impl ShieldedContext { /// stores the index of the last accepted transaction and each transaction /// is stored at a key derived from its index. pub async fn fetch_shielded_transfers( - utils: &U, + client: &U::C, last_txidx: u64, ) -> BTreeMap<(BlockHeight, TxIndex), (Epoch, Transfer)> { // The address of the MASP account @@ -541,7 +539,7 @@ impl ShieldedContext { .push(&HEAD_TX_KEY.to_owned()) .expect("Cannot obtain a storage key"); // Query for the index of the last accepted transaction - let head_txidx = utils.query_storage_value::(&head_tx_key) + let head_txidx = U::query_storage_value::(client, &head_tx_key) .await .unwrap_or(0); let mut shielded_txs = BTreeMap::new(); @@ -553,7 +551,8 @@ impl ShieldedContext { .expect("Cannot obtain a storage key"); // Obtain the current transaction let (tx_epoch, tx_height, tx_index, current_tx) = - utils.query_storage_value::<(Epoch, BlockHeight, TxIndex, Transfer)>( + U::query_storage_value::<(Epoch, BlockHeight, TxIndex, Transfer)>( + client, ¤t_tx_key, ) .await @@ -714,6 +713,7 @@ impl ShieldedContext { /// if it is found. pub async fn decode_asset_type( &mut self, + client: &U::C, asset_type: AssetType, ) -> Option<(Address, Epoch)> { // Try to find the decoding in the cache @@ -722,7 +722,7 @@ impl ShieldedContext { } // Query for the ID of the last accepted transaction let (addr, ep, _conv, _path): (Address, _, Amount, MerklePath) = - self.utils.query_conversion(asset_type).await?; + U::query_conversion(client, asset_type).await?; self.asset_types.insert(asset_type, (addr.clone(), ep)); Some((addr, ep)) } @@ -731,6 +731,7 @@ impl ShieldedContext { /// type and cache it. async fn query_allowed_conversion<'a>( &'a mut self, + client: &U::C, asset_type: AssetType, conversions: &'a mut Conversions, ) -> Option<&'a mut (AllowedConversion, MerklePath, i64)> { @@ -739,7 +740,7 @@ impl ShieldedContext { Entry::Vacant(conv_entry) => { // Query for the ID of the last accepted transaction let (addr, ep, conv, path): (Address, _, _, _) = - self.utils.query_conversion(asset_type).await?; + U::query_conversion(client, asset_type).await?; self.asset_types.insert(asset_type, (addr, ep)); // If the conversion is 0, then we just have a pure decoding if conv == Amount::zero() { @@ -757,6 +758,7 @@ impl ShieldedContext { /// balance and hence we return None. pub async fn compute_exchanged_balance( &mut self, + client: &U::C, vk: &ViewingKey, target_epoch: Epoch, ) -> Option { @@ -765,6 +767,7 @@ impl ShieldedContext { // And then exchange balance into current asset types Some( self.compute_exchanged_amount( + client, balance, target_epoch, HashMap::new(), @@ -820,6 +823,7 @@ impl ShieldedContext { /// terms of the latest asset types. pub async fn compute_exchanged_amount( &mut self, + client: &U::C, mut input: Amount, target_epoch: Epoch, mut conversions: Conversions, @@ -831,13 +835,14 @@ impl ShieldedContext { input.components().next().map(cloned_pair) { let target_asset_type = self - .decode_asset_type(asset_type) + .decode_asset_type(client, asset_type) .await .map(|(addr, _epoch)| make_asset_type(target_epoch, &addr)) .unwrap_or(asset_type); let at_target_asset_type = asset_type == target_asset_type; if let (Some((conv, _wit, usage)), false) = ( self.query_allowed_conversion( + client, asset_type, &mut conversions, ) @@ -860,6 +865,7 @@ impl ShieldedContext { ); } else if let (Some((conv, _wit, usage)), false) = ( self.query_allowed_conversion( + client, target_asset_type, &mut conversions, ) @@ -897,6 +903,7 @@ impl ShieldedContext { /// achieve the total value. pub async fn collect_unspent_notes( &mut self, + client: &U::C, vk: &ViewingKey, target: Amount, target_epoch: Epoch, @@ -929,6 +936,7 @@ impl ShieldedContext { .expect("received note has invalid value or asset type"); let (contr, proposed_convs) = self .compute_exchanged_amount( + client, pre_contr, target_epoch, conversions.clone(), @@ -963,7 +971,7 @@ impl ShieldedContext { /// the given payment address fails with /// `PinnedBalanceError::NoTransactionPinned`. pub async fn compute_pinned_balance( - utils: &U, + client: &U::C, owner: PaymentAddress, viewing_key: &ViewingKey, ) -> Result<(Amount, Epoch), PinnedBalanceError> { @@ -985,7 +993,7 @@ impl ShieldedContext { .push(&(PIN_KEY_PREFIX.to_owned() + &owner.hash())) .expect("Cannot obtain a storage key"); // Obtain the transaction pointer at the key - let txidx = utils.query_storage_value::(&pin_key) + let txidx = U::query_storage_value::(client, &pin_key) .await .ok_or(PinnedBalanceError::NoTransactionPinned)?; // Construct the key for where the pinned transaction is stored @@ -994,7 +1002,8 @@ impl ShieldedContext { .expect("Cannot obtain a storage key"); // Obtain the pointed to transaction let (tx_epoch, _tx_height, _tx_index, tx) = - utils.query_storage_value::<(Epoch, BlockHeight, TxIndex, Transfer)>( + U::query_storage_value::<(Epoch, BlockHeight, TxIndex, Transfer)>( + client, &tx_key, ) .await @@ -1036,16 +1045,17 @@ impl ShieldedContext { /// would have been displayed in the epoch of the transaction. pub async fn compute_exchanged_pinned_balance( &mut self, + client: &U::C, owner: PaymentAddress, viewing_key: &ViewingKey, ) -> Result<(Amount, Epoch), PinnedBalanceError> { // Obtain the balance that will be exchanged let (amt, ep) = - Self::compute_pinned_balance(&self.utils, owner, viewing_key) + Self::compute_pinned_balance(client, owner, viewing_key) .await?; // Finally, exchange the balance to the transaction's epoch Ok(( - self.compute_exchanged_amount(amt, ep, HashMap::new()) + self.compute_exchanged_amount(client, amt, ep, HashMap::new()) .await .0, ep, @@ -1057,6 +1067,7 @@ impl ShieldedContext { /// the given epoch are ignored. pub async fn decode_amount( &mut self, + client: &U::C, amt: Amount, target_epoch: Epoch, ) -> Amount
{ @@ -1064,7 +1075,7 @@ impl ShieldedContext { for (asset_type, val) in amt.components() { // Decode the asset type let decoded = - self.decode_asset_type(*asset_type).await; + self.decode_asset_type(client, *asset_type).await; // Only assets with the target timestamp count match decoded { Some((addr, epoch)) if epoch == target_epoch => { @@ -1080,13 +1091,14 @@ impl ShieldedContext { /// Addresses that they decode to. pub async fn decode_all_amounts( &mut self, + client: &U::C, amt: Amount, ) -> Amount<(Address, Epoch)> { let mut res = Amount::zero(); for (asset_type, val) in amt.components() { // Decode the asset type let decoded = - self.decode_asset_type(*asset_type).await; + self.decode_asset_type(client, *asset_type).await; // Only assets with the target timestamp count if let Some((addr, epoch)) = decoded { res += &Amount::from_pair((addr, epoch), *val).unwrap() @@ -1105,6 +1117,7 @@ impl ShieldedContext { #[cfg(feature = "masp-tx-gen")] pub async fn gen_shielded_transfer( &mut self, + client: &U::C, source: TransferSource, target: TransferTarget, args_amount: token::Amount, @@ -1125,12 +1138,12 @@ impl ShieldedContext { let spending_keys: Vec<_> = spending_key.into_iter().collect(); // Load the current shielded context given the spending key we possess let _ = self.load(); - self.fetch(&spending_keys, &[]) + self.fetch(client, &spending_keys, &[]) .await; // Save the update state so that future fetches can be short-circuited let _ = self.save(); // Determine epoch in which to submit potential shielded transaction - let epoch = self.utils.query_epoch().await; + let epoch = U::query_epoch(client).await; // Context required for storing which notes are in the source's possesion let consensus_branch_id = BranchId::Sapling; let amt: u64 = args_amount.into(); @@ -1157,6 +1170,7 @@ impl ShieldedContext { // Locate unspent notes that can help us meet the transaction amount let (_, unspent_notes, used_convs) = self .collect_unspent_notes( + client, &to_viewing_key(&sk).vk, required_amt, epoch, @@ -1236,7 +1250,7 @@ impl ShieldedContext { let mut tx = builder.build(consensus_branch_id, &prover); if epoch_sensitive { - let new_epoch = self.utils.query_epoch().await; + let new_epoch = U::query_epoch(client).await; // If epoch has changed, recalculate shielded outputs to match new epoch if new_epoch != epoch { @@ -1295,28 +1309,25 @@ impl ShieldedContext { /// restrict set to only transactions involving the given token. pub async fn query_tx_deltas( &mut self, + client: &U::C, query_owner: &Either>, query_token: &Option
, viewing_keys: &HashMap, ) -> BTreeMap<(BlockHeight, TxIndex), (Epoch, TransferDelta, TransactionDelta)> { const TXS_PER_PAGE: u8 = 100; - // Connect to the Tendermint server holding the transactions - //let client = HttpClient::new(ledger_address.clone()).unwrap(); - // Build up the context that will be queried for transactions - //ctx.shielded.utils.ledger_address = Some(ledger_address.clone()); let _ = self.load(); let vks = viewing_keys; let fvks: Vec<_> = vks .values() .map(|fvk| ExtendedFullViewingKey::from(*fvk).fvk.vk) .collect(); - self.fetch(&[], &fvks).await; + self.fetch(client, &[], &fvks).await; // Save the update state so that future fetches can be short-circuited let _ = self.save(); // Required for filtering out rejected transactions from Tendermint // responses - let block_results = self.utils.query_results().await; + let block_results = U::query_results(client).await; let mut transfers = self.get_tx_deltas().clone(); // Construct the set of addresses relevant to user's query let relevant_addrs = match &query_owner { @@ -1337,7 +1348,7 @@ impl ShieldedContext { tx_query = tx_query.and_eq("transfer.token", token.encode()); } for page in 1.. { - let txs = &self.utils.client() + let txs = &client .tx_search( tx_query.clone(), true, From 9b0457c94ad1fbdd5a9361dabdebd23b7a4e2d84 Mon Sep 17 00:00:00 2001 From: Murisi Tarusenga Date: Thu, 15 Dec 2022 14:18:27 +0200 Subject: [PATCH 19/51] Factored out saving and loading code from Wallet. --- apps/src/bin/anoma-client/cli.rs | 12 ++ apps/src/bin/anoma-wallet/cli.rs | 10 +- apps/src/lib/cli/context.rs | 4 +- apps/src/lib/client/rpc.rs | 14 +-- apps/src/lib/client/signing.rs | 7 +- apps/src/lib/client/tx.rs | 75 ++++++++---- apps/src/lib/client/utils.rs | 18 +-- apps/src/lib/node/ledger/shell/mod.rs | 6 +- apps/src/lib/wallet/mod.rs | 164 +++++++++++++------------- apps/src/lib/wallet/store.rs | 146 +++++++++++------------ 10 files changed, 250 insertions(+), 206 deletions(-) diff --git a/apps/src/bin/anoma-client/cli.rs b/apps/src/bin/anoma-client/cli.rs index 1e7d5c3bc9..a8271d0ba9 100644 --- a/apps/src/bin/anoma-client/cli.rs +++ b/apps/src/bin/anoma-client/cli.rs @@ -16,7 +16,13 @@ pub async fn main() -> Result<()> { Sub::TxCustom(TxCustom(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); + let dry_run = args.tx.dry_run; tx::submit_custom(&client, &mut ctx.wallet, args).await; + if !dry_run { + namada_apps::wallet::save(&ctx.wallet).unwrap_or_else(|err| eprintln!("{}", err)); + } else { + println!("Transaction dry run. No addresses have been saved.") + } } Sub::TxTransfer(TxTransfer(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); @@ -36,7 +42,13 @@ pub async fn main() -> Result<()> { Sub::TxInitAccount(TxInitAccount(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); + let dry_run = args.tx.dry_run; tx::submit_init_account(&client, &mut ctx.wallet, args).await; + if !dry_run { + namada_apps::wallet::save(&ctx.wallet).unwrap_or_else(|err| eprintln!("{}", err)); + } else { + println!("Transaction dry run. No addresses have been saved.") + } } Sub::TxInitValidator(TxInitValidator(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); diff --git a/apps/src/bin/anoma-wallet/cli.rs b/apps/src/bin/anoma-wallet/cli.rs index cef4f58780..5acdbd7e1f 100644 --- a/apps/src/bin/anoma-wallet/cli.rs +++ b/apps/src/bin/anoma-wallet/cli.rs @@ -201,7 +201,7 @@ fn spending_key_gen( let mut wallet = ctx.wallet; let alias = alias.to_lowercase(); let (alias, _key) = wallet.gen_spending_key(alias, unsafe_dont_encrypt); - wallet.save().unwrap_or_else(|err| eprintln!("{}", err)); + namada_apps::wallet::save(&wallet).unwrap_or_else(|err| eprintln!("{}", err)); println!( "Successfully added a spending key with alias: \"{}\"", alias @@ -236,7 +236,7 @@ fn payment_address_gen( eprintln!("Payment address not added"); cli::safe_exit(1); }); - wallet.save().unwrap_or_else(|err| eprintln!("{}", err)); + namada_apps::wallet::save(&wallet).unwrap_or_else(|err| eprintln!("{}", err)); println!( "Successfully generated a payment address with the following alias: {}", alias, @@ -289,7 +289,7 @@ fn address_key_add( (alias, "payment address") } }; - ctx.wallet.save().unwrap_or_else(|err| eprintln!("{}", err)); + namada_apps::wallet::save(&ctx.wallet).unwrap_or_else(|err| eprintln!("{}", err)); println!( "Successfully added a {} with the following alias to wallet: {}", typ, alias, @@ -308,7 +308,7 @@ fn key_and_address_gen( ) { let mut wallet = ctx.wallet; let (alias, _key) = wallet.gen_key(scheme, alias, unsafe_dont_encrypt); - wallet.save().unwrap_or_else(|err| eprintln!("{}", err)); + namada_apps::wallet::save(&wallet).unwrap_or_else(|err| eprintln!("{}", err)); println!( "Successfully added a key and an address with alias: \"{}\"", alias @@ -489,7 +489,7 @@ fn address_add(ctx: Context, args: args::AddressAdd) { eprintln!("Address not added"); cli::safe_exit(1); } - wallet.save().unwrap_or_else(|err| eprintln!("{}", err)); + namada_apps::wallet::save(&wallet).unwrap_or_else(|err| eprintln!("{}", err)); println!( "Successfully added a key and an address with alias: \"{}\"", args.alias.to_lowercase() diff --git a/apps/src/lib/cli/context.rs b/apps/src/lib/cli/context.rs index 2a05e6847c..ec0ef763f8 100644 --- a/apps/src/lib/cli/context.rs +++ b/apps/src/lib/cli/context.rs @@ -67,7 +67,7 @@ pub struct Context { /// Global arguments pub global_args: args::Global, /// The wallet - pub wallet: Wallet, + pub wallet: Wallet, /// The global configuration pub global_config: GlobalConfig, /// The ledger configuration for a specific chain ID @@ -100,7 +100,7 @@ impl Context { let default_genesis = genesis_config::open_genesis_config(genesis_file_path)?; let wallet = - Wallet::load_or_new_from_genesis(&chain_dir, default_genesis); + crate::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() { diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index 056ce8b5d3..bed2eb5fcf 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -141,7 +141,7 @@ pub async fn query_results(client: &HttpClient) -> Vec { /// Query the specified accepted transfers from the ledger pub async fn query_transfers>( client: &HttpClient, - wallet: &mut Wallet, + wallet: &mut Wallet, shielded: &mut ShieldedContext, args: args::QueryTransfers ) { @@ -278,7 +278,7 @@ pub async fn query_raw_bytes(client: &HttpClient, args: args::QueryRawBytes) { /// Query token balance(s) pub async fn query_balance>( client: &HttpClient, - wallet: &mut Wallet, + wallet: &mut Wallet, shielded: &mut ShieldedContext, args: args::QueryBalance, ) { @@ -308,7 +308,7 @@ pub async fn query_balance>( /// Query token balance(s) pub async fn query_transparent_balance( client: &HttpClient, - wallet: &mut Wallet, + wallet: &mut Wallet, args: args::QueryBalance, ) { let tokens = address::tokens(); @@ -385,7 +385,7 @@ pub async fn query_transparent_balance( /// Query the token pinned balance(s) pub async fn query_pinned_balance>( client: &HttpClient, - wallet: &mut Wallet, + wallet: &mut Wallet, shielded: &mut ShieldedContext, args: args::QueryBalance, ) { @@ -518,7 +518,7 @@ pub async fn query_pinned_balance>( } fn print_balances( - wallet: &Wallet, + wallet: &Wallet, balances: impl Iterator, token: &Address, target: Option<&Address>, @@ -710,7 +710,7 @@ pub fn value_by_address( /// Query token shielded balance(s) pub async fn query_shielded_balance>( client: &HttpClient, - wallet: &mut Wallet, + wallet: &mut Wallet, shielded: &mut ShieldedContext, args: args::QueryBalance, ) { @@ -2706,7 +2706,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(wallet: &Wallet, addr: &Address) -> String { +fn lookup_alias(wallet: &Wallet, addr: &Address) -> String { match 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 61946b7bc0..adacb53c3b 100644 --- a/apps/src/lib/client/signing.rs +++ b/apps/src/lib/client/signing.rs @@ -8,6 +8,7 @@ use namada::types::key::*; use namada::types::storage::Epoch; use namada::types::transaction::{hash_tx, Fee, WrapperTx}; use tendermint_rpc::HttpClient; +use std::path::PathBuf; use super::rpc; use crate::cli::{self, args}; @@ -18,7 +19,7 @@ use crate::wallet::Wallet; /// for it from the wallet. Panics if the key cannot be found or loaded. pub async fn find_keypair( client: &HttpClient, - wallet: &mut Wallet, + wallet: &mut Wallet, addr: &Address, ) -> common::SecretKey { match addr { @@ -86,7 +87,7 @@ pub enum TxSigningKey { /// is given, panics. pub async fn tx_signer( client: &HttpClient, - wallet: &mut Wallet, + wallet: &mut Wallet, args: &args::Tx, mut default: TxSigningKey, ) -> common::SecretKey { @@ -142,7 +143,7 @@ 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( client: &HttpClient, - wallet: &mut Wallet, + wallet: &mut Wallet, tx: Tx, args: &args::Tx, default: TxSigningKey, diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index 611b7d7976..26ab555285 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -69,7 +69,11 @@ 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(client: &HttpClient, wallet: &mut Wallet, args: args::TxCustom) { +pub async fn submit_custom( + client: &HttpClient, + wallet: &mut Wallet, + args: args::TxCustom, +) { let tx_code = args.code_path; let data = args.data_path; let tx = Tx::new(tx_code, data); @@ -78,7 +82,11 @@ pub async fn submit_custom(client: &HttpClient, wallet: &mut Wallet, args: args: save_initialized_accounts(wallet, &args.tx, initialized_accounts).await; } -pub async fn submit_update_vp(client: &HttpClient, wallet: &mut Wallet, args: args::TxUpdateVp) { +pub async fn submit_update_vp( + client: &HttpClient, + wallet: &mut Wallet, + args: args::TxUpdateVp, +) { let addr = args.addr.clone(); // Check that the address is established and exists on chain @@ -132,7 +140,11 @@ pub async fn submit_update_vp(client: &HttpClient, wallet: &mut Wallet, args: ar process_tx(client, wallet, &args.tx, tx, TxSigningKey::WalletAddress(args.addr)).await; } -pub async fn submit_init_account(client: &HttpClient, wallet: &mut Wallet, args: args::TxInitAccount) { +pub async fn submit_init_account( + client: &HttpClient, + wallet: &mut Wallet, + args: args::TxInitAccount, +) { let public_key = args.public_key; let vp_code = args.vp_code_path; // Validate the VP code @@ -229,7 +241,7 @@ pub async fn submit_init_validator( .expect("DKG sessions keys should have been created") .public(); - ctx.wallet.save().unwrap_or_else(|err| eprintln!("{}", err)); + crate::wallet::save(&ctx.wallet).unwrap_or_else(|err| eprintln!("{}", err)); let validator_vp_code = validator_vp_code_path; @@ -329,7 +341,7 @@ pub async fn submit_init_validator( // add validator address and keys to the wallet ctx.wallet .add_validator_data(validator_address, validator_keys); - ctx.wallet.save().unwrap_or_else(|err| eprintln!("{}", err)); + crate::wallet::save(&ctx.wallet).unwrap_or_else(|err| eprintln!("{}", err)); let tendermint_home = ctx.config.ledger.tendermint_dir(); tendermint_node::write_validator_key(&tendermint_home, &consensus_key); @@ -489,7 +501,7 @@ impl masp::ShieldedUtils for CLIShieldedUtils { pub async fn submit_transfer>( client: &HttpClient, - wallet: &mut Wallet, + wallet: &mut Wallet, shielded: &mut ShieldedContext, args: args::TxTransfer, ) { @@ -658,7 +670,11 @@ pub async fn submit_transfer>( process_tx(client, wallet, &args.tx, tx, signing_address).await; } -pub async fn submit_ibc_transfer(client: &HttpClient, wallet: &mut Wallet, args: args::TxIbcTransfer) { +pub async fn submit_ibc_transfer( + client: &HttpClient, + wallet: &mut Wallet, + args: args::TxIbcTransfer, +) { let source = args.source.clone(); // Check that the source address exists on chain let source_exists = @@ -905,7 +921,11 @@ pub async fn submit_init_proposal(client: &HttpClient, mut ctx: Context, args: a } } -pub async fn submit_vote_proposal(client: &HttpClient, wallet: &mut Wallet, args: args::VoteProposal) { +pub async fn submit_vote_proposal( + client: &HttpClient, + wallet: &mut Wallet, + args: args::VoteProposal, +) { let signer = if let Some(addr) = &args.tx.signer { addr } else { @@ -1051,7 +1071,11 @@ pub async fn submit_vote_proposal(client: &HttpClient, wallet: &mut Wallet, args } } -pub async fn submit_reveal_pk(client: &HttpClient, wallet: &mut Wallet, args: args::RevealPk) { +pub async fn submit_reveal_pk( + client: &HttpClient, + wallet: &mut Wallet, + args: args::RevealPk, +) { let args::RevealPk { tx: args, public_key, @@ -1065,7 +1089,7 @@ pub async fn submit_reveal_pk(client: &HttpClient, wallet: &mut Wallet, args: ar pub async fn reveal_pk_if_needed( client: &HttpClient, - wallet: &mut Wallet, + wallet: &mut Wallet, public_key: &common::PublicKey, args: &args::Tx, ) -> bool { @@ -1090,7 +1114,7 @@ pub async fn has_revealed_pk( pub async fn submit_reveal_pk_aux( client: &HttpClient, - wallet: &mut Wallet, + wallet: &mut Wallet, public_key: &common::PublicKey, args: &args::Tx, ) { @@ -1227,7 +1251,11 @@ async fn filter_delegations( delegations.into_iter().flatten().collect() } -pub async fn submit_bond(client: &HttpClient, wallet: &mut Wallet, args: args::Bond) { +pub async fn submit_bond( + client: &HttpClient, + wallet: &mut Wallet, + args: args::Bond, +) { let validator = args.validator.clone(); // Check that the validator address exists on chain let is_validator = @@ -1299,7 +1327,11 @@ pub async fn submit_bond(client: &HttpClient, wallet: &mut Wallet, args: args::B .await; } -pub async fn submit_unbond(client: &HttpClient, wallet: &mut Wallet, args: args::Unbond) { +pub async fn submit_unbond( + client: &HttpClient, + wallet: &mut Wallet, + args: args::Unbond, +) { let validator = args.validator.clone(); // Check that the validator address exists on chain let is_validator = @@ -1372,7 +1404,11 @@ pub async fn submit_unbond(client: &HttpClient, wallet: &mut Wallet, args: args: .await; } -pub async fn submit_withdraw(client: &HttpClient, wallet: &mut Wallet, args: args::Withdraw) { +pub async fn submit_withdraw( + client: &HttpClient, + wallet: &mut Wallet, + args: args::Withdraw, +) { let epoch = rpc::query_epoch(client) .await; @@ -1445,7 +1481,7 @@ pub async fn submit_withdraw(client: &HttpClient, wallet: &mut Wallet, args: arg pub async fn submit_validator_commission_change( client: &HttpClient, - wallet: &mut Wallet, + wallet: &mut Wallet, args: args::TxCommissionRateChange, ) { let epoch = rpc::query_epoch(client) @@ -1528,7 +1564,7 @@ pub async fn submit_validator_commission_change( /// initialized in the transaction if any. In dry run, this is always empty. async fn process_tx( client: &HttpClient, - wallet: &mut Wallet, + wallet: &mut Wallet, args: &args::Tx, tx: Tx, default_signer: TxSigningKey, @@ -1587,7 +1623,7 @@ async fn process_tx( /// Save accounts initialized from a tx into the wallet, if any. async fn save_initialized_accounts( - wallet: &mut Wallet, + wallet: &mut Wallet, args: &args::Tx, initialized_accounts: Vec
, ) { @@ -1635,11 +1671,6 @@ async fn save_initialized_accounts( _ => println!("No alias added for address {}.", encoded), }; } - if !args.dry_run { - wallet.save().unwrap_or_else(|err| eprintln!("{}", err)); - } else { - println!("Transaction dry run. No addresses have been saved.") - } } } diff --git a/apps/src/lib/client/utils.rs b/apps/src/lib/client/utils.rs index 236dcea307..c6e8f6472d 100644 --- a/apps/src/lib/client/utils.rs +++ b/apps/src/lib/client/utils.rs @@ -259,7 +259,7 @@ pub async fn join_network( let genesis_file_path = base_dir.join(format!("{}.toml", chain_id.as_str())); - let mut wallet = Wallet::load_or_new_from_genesis( + let mut wallet = crate::wallet::load_or_new_from_genesis( &chain_dir, genesis_config::open_genesis_config(genesis_file_path).unwrap(), ); @@ -300,7 +300,7 @@ pub async fn join_network( pre_genesis_wallet, ); - wallet.save().unwrap(); + crate::wallet::save(&wallet).unwrap(); // Update the config from the default non-validator settings to // validator settings @@ -479,7 +479,7 @@ pub fn init_network( // Generate the consensus, account and reward keys, unless they're // pre-defined. - let mut wallet = Wallet::load_or_new(&chain_dir); + let mut wallet = crate::wallet::load_or_new(&chain_dir); let consensus_pk = try_parse_public_key( format!("validator {name} consensus key"), @@ -571,12 +571,12 @@ pub fn init_network( // Write keypairs to wallet wallet.add_address(name.clone(), address); - wallet.save().unwrap(); + crate::wallet::save(&wallet).unwrap(); }); // Create a wallet for all accounts other than validators let mut wallet = - Wallet::load_or_new(&accounts_dir.join(NET_OTHER_ACCOUNTS_DIR)); + crate::wallet::load_or_new(&accounts_dir.join(NET_OTHER_ACCOUNTS_DIR)); if let Some(established) = &mut config.established { established.iter_mut().for_each(|(name, config)| { init_established_account( @@ -643,7 +643,7 @@ pub fn init_network( // Add genesis addresses and save the wallet with other account keys wallet.add_genesis_addresses(config_clean.clone()); - wallet.save().unwrap(); + crate::wallet::save(&wallet).unwrap(); // Write the global config setting the default chain ID let global_config = GlobalConfig::new(chain_id.clone()); @@ -692,9 +692,9 @@ pub fn init_network( ); global_config.write(validator_dir).unwrap(); // Add genesis addresses to the validator's wallet - let mut wallet = Wallet::load_or_new(&validator_chain_dir); + let mut wallet = crate::wallet::load_or_new(&validator_chain_dir); wallet.add_genesis_addresses(config_clean.clone()); - wallet.save().unwrap(); + crate::wallet::save(&wallet).unwrap(); }); // Generate the validators' ledger config @@ -845,7 +845,7 @@ pub fn init_network( fn init_established_account( name: impl AsRef, - wallet: &mut Wallet, + wallet: &mut Wallet, config: &mut genesis_config::EstablishedAccountConfig, unsafe_dont_encrypt: bool, ) { diff --git a/apps/src/lib/node/ledger/shell/mod.rs b/apps/src/lib/node/ledger/shell/mod.rs index f3fe955afd..078d37d931 100644 --- a/apps/src/lib/node/ledger/shell/mod.rs +++ b/apps/src/lib/node/ledger/shell/mod.rs @@ -63,7 +63,7 @@ use crate::node::ledger::shims::abcipp_shim_types::shim::response::TxResult; use crate::node::ledger::{storage, tendermint_node}; #[allow(unused_imports)] use crate::wallet::ValidatorData; -use crate::{config, wallet}; +use crate::config; fn key_to_tendermint( pk: &common::PublicKey, @@ -275,7 +275,7 @@ where "{}", wallet_path.as_path().to_str().unwrap() ); - let wallet = wallet::Wallet::load_or_new_from_genesis( + let wallet = crate::wallet::load_or_new_from_genesis( wallet_path, genesis::genesis_config::open_genesis_config( genesis_path, @@ -631,7 +631,7 @@ where let genesis_path = &self .base_dir .join(format!("{}.toml", self.chain_id.as_str())); - let mut wallet = wallet::Wallet::load_or_new_from_genesis( + let mut wallet = crate::wallet::load_or_new_from_genesis( wallet_path, genesis::genesis_config::open_genesis_config(genesis_path).unwrap(), ); diff --git a/apps/src/lib/wallet/mod.rs b/apps/src/lib/wallet/mod.rs index b2a0096e0b..6eca74409e 100644 --- a/apps/src/lib/wallet/mod.rs +++ b/apps/src/lib/wallet/mod.rs @@ -28,8 +28,8 @@ use crate::cli; use crate::config::genesis::genesis_config::GenesisConfig; #[derive(Debug)] -pub struct Wallet { - store_dir: PathBuf, +pub struct Wallet { + store_dir: C, store: Store, decrypted_key_cache: HashMap, decrypted_spendkey_cache: HashMap, @@ -43,89 +43,12 @@ pub enum FindKeyError { KeyDecryptionError(keys::DecryptionError), } -impl Wallet { - /// Load a wallet from the store file. - pub fn load(store_dir: &Path) -> Option { - let store = Store::load(store_dir).unwrap_or_else(|err| { - eprintln!("Unable to load the wallet: {}", err); - cli::safe_exit(1) - }); - Some(Self { - store_dir: store_dir.to_path_buf(), - store, - decrypted_key_cache: HashMap::default(), - decrypted_spendkey_cache: HashMap::default(), - }) - } - - /// Load a wallet from the store file or create a new wallet without any - /// keys or addresses. - pub fn load_or_new(store_dir: &Path) -> Self { - let store = Store::load_or_new(store_dir).unwrap_or_else(|err| { - eprintln!("Unable to load the wallet: {}", err); - cli::safe_exit(1) - }); - Self { - store_dir: store_dir.to_path_buf(), - store, - decrypted_key_cache: HashMap::default(), - decrypted_spendkey_cache: HashMap::default(), - } - } - - /// Load a wallet from the store file or create a new one with the default - /// addresses loaded from the genesis file, if not found. - pub fn load_or_new_from_genesis( - store_dir: &Path, - genesis_cfg: GenesisConfig, - ) -> Self { - let store = Store::load_or_new_from_genesis(store_dir, genesis_cfg) - .unwrap_or_else(|err| { - eprintln!("Unable to load the wallet: {}", err); - cli::safe_exit(1) - }); - Self { - store_dir: store_dir.to_path_buf(), - store, - decrypted_key_cache: HashMap::default(), - decrypted_spendkey_cache: HashMap::default(), - } - } - +impl Wallet { /// Add addresses from a genesis configuration. pub fn add_genesis_addresses(&mut self, genesis: GenesisConfig) { self.store.add_genesis_addresses(genesis) } - /// Save the wallet store to a file. - pub fn save(&self) -> std::io::Result<()> { - self.store.save(&self.store_dir) - } - - /// Prompt for pssword and confirm it if parameter is false - fn new_password_prompt(unsafe_dont_encrypt: bool) -> Option { - let password = if unsafe_dont_encrypt { - println!("Warning: The keypair will NOT be encrypted."); - None - } else { - Some(read_password("Enter your encryption password: ")) - }; - // Bis repetita for confirmation. - let pwd = if unsafe_dont_encrypt { - None - } else { - Some(read_password( - "To confirm, please enter the same encryption password once \ - more: ", - )) - }; - if pwd != password { - eprintln!("Your two inputs do not match!"); - cli::safe_exit(1) - } - password - } - /// Generate a new keypair and derive an implicit address from its public /// and insert them into the store with the provided alias, converted to /// lower case. If none provided, the alias will be the public key hash (in @@ -151,7 +74,7 @@ impl Wallet { alias: String, unsafe_dont_encrypt: bool, ) -> (String, ExtendedSpendingKey) { - let password = Self::new_password_prompt(unsafe_dont_encrypt); + let password = new_password_prompt(unsafe_dont_encrypt); let (alias, key) = self.store.gen_spending_key(alias, password); // Cache the newly added key self.decrypted_spendkey_cache.insert(alias.clone(), key); @@ -478,7 +401,7 @@ impl Wallet { spend_key: ExtendedSpendingKey, unsafe_dont_encrypt: bool, ) -> Option { - let password = Self::new_password_prompt(unsafe_dont_encrypt); + let password = new_password_prompt(unsafe_dont_encrypt); self.store .insert_spending_key( alias.into(), @@ -513,6 +436,83 @@ impl Wallet { } } +/// Save the wallet store to a file. +pub fn save(wallet: &Wallet) -> std::io::Result<()> { + self::store::save(&wallet.store, &wallet.store_dir) +} + +/// Load a wallet from the store file. +pub fn load(store_dir: &Path) -> Option> { + let store = self::store::load(store_dir).unwrap_or_else(|err| { + eprintln!("Unable to load the wallet: {}", err); + cli::safe_exit(1) + }); + Some(Wallet:: { + store_dir: store_dir.to_path_buf(), + store, + decrypted_key_cache: HashMap::default(), + decrypted_spendkey_cache: HashMap::default(), + }) +} + +/// Load a wallet from the store file or create a new wallet without any +/// keys or addresses. +pub fn load_or_new(store_dir: &Path) -> Wallet { + let store = self::store::load_or_new(store_dir).unwrap_or_else(|err| { + eprintln!("Unable to load the wallet: {}", err); + cli::safe_exit(1) + }); + Wallet:: { + store_dir: store_dir.to_path_buf(), + store, + decrypted_key_cache: HashMap::default(), + decrypted_spendkey_cache: HashMap::default(), + } +} + +/// Load a wallet from the store file or create a new one with the default +/// addresses loaded from the genesis file, if not found. +pub fn load_or_new_from_genesis( + store_dir: &Path, + genesis_cfg: GenesisConfig, +) -> Wallet { + let store = self::store::load_or_new_from_genesis(store_dir, genesis_cfg) + .unwrap_or_else(|err| { + eprintln!("Unable to load the wallet: {}", err); + cli::safe_exit(1) + }); + Wallet:: { + store_dir: store_dir.to_path_buf(), + store, + decrypted_key_cache: HashMap::default(), + decrypted_spendkey_cache: HashMap::default(), + } +} + +/// Prompt for pssword and confirm it if parameter is false +fn new_password_prompt(unsafe_dont_encrypt: bool) -> Option { + let password = if unsafe_dont_encrypt { + println!("Warning: The keypair will NOT be encrypted."); + None + } else { + Some(read_password("Enter your encryption password: ")) + }; + // Bis repetita for confirmation. + let pwd = if unsafe_dont_encrypt { + None + } else { + Some(read_password( + "To confirm, please enter the same encryption password once \ + more: ", + )) + }; + if pwd != password { + eprintln!("Your two inputs do not match!"); + cli::safe_exit(1) + } + password +} + /// Read the password for encryption from the file/env/stdin with confirmation. pub fn read_and_confirm_pwd(unsafe_dont_encrypt: bool) -> Option { let password = if unsafe_dont_encrypt { diff --git a/apps/src/lib/wallet/store.rs b/apps/src/lib/wallet/store.rs index 65e39f50a8..9193ff5ef8 100644 --- a/apps/src/lib/wallet/store.rs +++ b/apps/src/lib/wallet/store.rs @@ -115,79 +115,6 @@ impl Store { ); } - /// Save the wallet store to a file. - pub fn save(&self, store_dir: &Path) -> std::io::Result<()> { - let data = self.encode(); - let wallet_path = wallet_file(store_dir); - // Make sure the dir exists - let wallet_dir = wallet_path.parent().unwrap(); - fs::create_dir_all(wallet_dir)?; - // Write the file - let options = - FileOptions::new().create(true).write(true).truncate(true); - let mut filelock = - FileLock::lock(wallet_path.to_str().unwrap(), true, options)?; - filelock.file.write_all(&data) - } - - /// Load the store file or create a new one without any keys or addresses. - pub fn load_or_new(store_dir: &Path) -> Result { - Self::load(store_dir).or_else(|_| { - let store = Self::default(); - store.save(store_dir).map_err(|err| { - LoadStoreError::StoreNewWallet(err.to_string()) - })?; - Ok(store) - }) - } - - /// Load the store file or create a new one with the default addresses from - /// the genesis file, if not found. - pub fn load_or_new_from_genesis( - store_dir: &Path, - genesis_cfg: GenesisConfig, - ) -> Result { - Self::load(store_dir).or_else(|_| { - #[cfg(not(feature = "dev"))] - let store = Self::new(genesis_cfg); - #[cfg(feature = "dev")] - let store = { - // The function is unused in dev - let _ = genesis_cfg; - Self::new() - }; - store.save(store_dir).map_err(|err| { - LoadStoreError::StoreNewWallet(err.to_string()) - })?; - Ok(store) - }) - } - - /// Attempt to load the store file. - pub fn load(store_dir: &Path) -> Result { - let wallet_file = wallet_file(store_dir); - match FileLock::lock( - wallet_file.to_str().unwrap(), - true, - FileOptions::new().read(true).write(false), - ) { - Ok(mut filelock) => { - let mut store = Vec::::new(); - filelock.file.read_to_end(&mut store).map_err(|err| { - LoadStoreError::ReadWallet( - store_dir.to_str().unwrap().into(), - err.to_string(), - ) - })?; - Store::decode(store).map_err(LoadStoreError::Decode) - } - Err(err) => Err(LoadStoreError::ReadWallet( - wallet_file.to_string_lossy().into_owned(), - err.to_string(), - )), - } - } - /// Find the stored key by an alias, a public key hash or a public key. pub fn find_key( &self, @@ -756,6 +683,79 @@ pub fn gen_sk(scheme: SchemeType) -> common::SecretKey { } } +/// Save the wallet store to a file. +pub fn save(store: &Store, store_dir: &Path) -> std::io::Result<()> { + let data = store.encode(); + let wallet_path = wallet_file(store_dir); + // Make sure the dir exists + let wallet_dir = wallet_path.parent().unwrap(); + fs::create_dir_all(wallet_dir)?; + // Write the file + let options = + FileOptions::new().create(true).write(true).truncate(true); + let mut filelock = + FileLock::lock(wallet_path.to_str().unwrap(), true, options)?; + filelock.file.write_all(&data) +} + +/// Load the store file or create a new one without any keys or addresses. +pub fn load_or_new(store_dir: &Path) -> Result { + load(store_dir).or_else(|_| { + let store = Store::default(); + save(&store, store_dir).map_err(|err| { + LoadStoreError::StoreNewWallet(err.to_string()) + })?; + Ok(store) + }) +} + +/// Load the store file or create a new one with the default addresses from +/// the genesis file, if not found. +pub fn load_or_new_from_genesis( + store_dir: &Path, + genesis_cfg: GenesisConfig, +) -> Result { + load(store_dir).or_else(|_| { + #[cfg(not(feature = "dev"))] + let store = Store::new(genesis_cfg); + #[cfg(feature = "dev")] + let store = { + // The function is unused in dev + let _ = genesis_cfg; + Store::new() + }; + save(&store, store_dir).map_err(|err| { + LoadStoreError::StoreNewWallet(err.to_string()) + })?; + Ok(store) + }) +} + +/// Attempt to load the store file. +pub fn load(store_dir: &Path) -> Result { + let wallet_file = wallet_file(store_dir); + match FileLock::lock( + wallet_file.to_str().unwrap(), + true, + FileOptions::new().read(true).write(false), + ) { + Ok(mut filelock) => { + let mut store = Vec::::new(); + filelock.file.read_to_end(&mut store).map_err(|err| { + LoadStoreError::ReadWallet( + store_dir.to_str().unwrap().into(), + err.to_string(), + ) + })?; + Store::decode(store).map_err(LoadStoreError::Decode) + } + Err(err) => Err(LoadStoreError::ReadWallet( + wallet_file.to_string_lossy().into_owned(), + err.to_string(), + )), + } +} + #[cfg(all(test, feature = "dev"))] mod test_wallet { use super::*; From 326a7f746ee085ac50798906b2c83625935079e5 Mon Sep 17 00:00:00 2001 From: Murisi Tarusenga Date: Sat, 17 Dec 2022 12:06:46 +0200 Subject: [PATCH 20/51] Moved Wallet into the shared crate and parameterized it by its interactive components. --- Cargo.lock | 4 + apps/src/bin/anoma-client/cli.rs | 25 +- apps/src/bin/anoma-wallet/cli.rs | 30 +- apps/src/lib/cli/context.rs | 11 +- apps/src/lib/client/rpc.rs | 2 +- apps/src/lib/client/signing.rs | 21 +- apps/src/lib/client/tx.rs | 96 ++-- apps/src/lib/client/utils.rs | 32 +- apps/src/lib/node/ledger/shell/mod.rs | 21 +- apps/src/lib/wallet/alias.rs | 103 ---- apps/src/lib/wallet/defaults.rs | 4 +- apps/src/lib/wallet/keys.rs | 243 -------- apps/src/lib/wallet/mod.rs | 640 +++++---------------- apps/src/lib/wallet/pre_genesis.rs | 262 ++++----- apps/src/lib/wallet/store.rs | 709 +++--------------------- shared/Cargo.toml | 4 + shared/src/ledger/mod.rs | 1 + shared/src/ledger/wallet/alias.rs | 103 ++++ shared/src/ledger/wallet/keys.rs | 242 ++++++++ shared/src/ledger/wallet/mod.rs | 446 +++++++++++++++ shared/src/ledger/wallet/pre_genesis.rs | 73 +++ shared/src/ledger/wallet/store.rs | 557 +++++++++++++++++++ 22 files changed, 1874 insertions(+), 1755 deletions(-) create mode 100644 shared/src/ledger/wallet/alias.rs create mode 100644 shared/src/ledger/wallet/keys.rs create mode 100644 shared/src/ledger/wallet/mod.rs create mode 100644 shared/src/ledger/wallet/pre_genesis.rs create mode 100644 shared/src/ledger/wallet/store.rs diff --git a/Cargo.lock b/Cargo.lock index 9a2cd54a75..8d751eef10 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3631,6 +3631,7 @@ dependencies = [ "assert_matches", "async-trait", "bellman", + "bimap", "bls12_381", "borsh", "byte-unit", @@ -3649,6 +3650,7 @@ dependencies = [ "masp_proofs", "namada_core", "namada_proof_of_stake", + "orion", "parity-wasm", "paste", "pretty_assertions", @@ -3659,6 +3661,7 @@ dependencies = [ "rand_core 0.6.4", "rayon", "rust_decimal", + "serde 1.0.147", "serde_json", "sha2 0.9.9", "tempfile", @@ -3671,6 +3674,7 @@ dependencies = [ "test-log", "thiserror", "tokio", + "toml", "tracing 0.1.37", "tracing-subscriber 0.3.16", "wasmer", diff --git a/apps/src/bin/anoma-client/cli.rs b/apps/src/bin/anoma-client/cli.rs index a8271d0ba9..bc42d88dfe 100644 --- a/apps/src/bin/anoma-client/cli.rs +++ b/apps/src/bin/anoma-client/cli.rs @@ -5,6 +5,7 @@ use namada_apps::cli; use namada_apps::cli::cmds::*; use namada_apps::client::{rpc, tx, utils}; use tendermint_rpc::{HttpClient, WebSocketClient, SubscriptionClient}; +use namada_apps::wallet::CliWalletUtils; pub async fn main() -> Result<()> { match cli::anoma_client_cli()? { @@ -17,7 +18,7 @@ pub async fn main() -> Result<()> { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); let dry_run = args.tx.dry_run; - tx::submit_custom(&client, &mut ctx.wallet, args).await; + tx::submit_custom::(&client, &mut ctx.wallet, args).await; if !dry_run { namada_apps::wallet::save(&ctx.wallet).unwrap_or_else(|err| eprintln!("{}", err)); } else { @@ -27,23 +28,23 @@ pub async fn main() -> Result<()> { Sub::TxTransfer(TxTransfer(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_transfer(&client, &mut ctx.wallet, &mut ctx.shielded, args).await; + tx::submit_transfer::<_, CliWalletUtils>(&client, &mut ctx.wallet, &mut ctx.shielded, args).await; } Sub::TxIbcTransfer(TxIbcTransfer(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_ibc_transfer(&client, &mut ctx.wallet, args).await; + tx::submit_ibc_transfer::(&client, &mut ctx.wallet, args).await; } Sub::TxUpdateVp(TxUpdateVp(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_update_vp(&client, &mut ctx.wallet, args).await; + tx::submit_update_vp::(&client, &mut ctx.wallet, args).await; } Sub::TxInitAccount(TxInitAccount(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); let dry_run = args.tx.dry_run; - tx::submit_init_account(&client, &mut ctx.wallet, args).await; + tx::submit_init_account::(&client, &mut ctx.wallet, args).await; if !dry_run { namada_apps::wallet::save(&ctx.wallet).unwrap_or_else(|err| eprintln!("{}", err)); } else { @@ -53,37 +54,37 @@ pub async fn main() -> Result<()> { Sub::TxInitValidator(TxInitValidator(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_init_validator(&client, ctx, args).await; + tx::submit_init_validator::(&client, ctx, args).await; } Sub::TxInitProposal(TxInitProposal(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_init_proposal(&client, ctx, args).await; + tx::submit_init_proposal::(&client, ctx, args).await; } Sub::TxVoteProposal(TxVoteProposal(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_vote_proposal(&client, &mut ctx.wallet, args).await; + tx::submit_vote_proposal::(&client, &mut ctx.wallet, args).await; } Sub::TxRevealPk(TxRevealPk(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_reveal_pk(&client, &mut ctx.wallet, args).await; + tx::submit_reveal_pk::(&client, &mut ctx.wallet, args).await; } Sub::Bond(Bond(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_bond(&client, &mut ctx.wallet, args).await; + tx::submit_bond::(&client, &mut ctx.wallet, args).await; } Sub::Unbond(Unbond(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_unbond(&client, &mut ctx.wallet, args).await; + tx::submit_unbond::(&client, &mut ctx.wallet, args).await; } Sub::Withdraw(Withdraw(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_withdraw(&client, &mut ctx.wallet, args).await; + tx::submit_withdraw::(&client, &mut ctx.wallet, args).await; } // Ledger queries Sub::QueryEpoch(QueryEpoch(args)) => { diff --git a/apps/src/bin/anoma-wallet/cli.rs b/apps/src/bin/anoma-wallet/cli.rs index 5acdbd7e1f..ce4398ffc0 100644 --- a/apps/src/bin/anoma-wallet/cli.rs +++ b/apps/src/bin/anoma-wallet/cli.rs @@ -12,8 +12,10 @@ use namada::types::masp::{MaspValue, PaymentAddress}; use namada_apps::cli; use namada_apps::cli::{args, cmds, Context}; use namada::ledger::masp::find_valid_diversifier; -use namada_apps::wallet::{DecryptionError, FindKeyError}; +use namada_apps::wallet::DecryptionError; use rand_core::OsRng; +use namada_apps::wallet::CliWalletUtils; +use namada::ledger::wallet::FindKeyError; pub fn main() -> Result<()> { let (cmd, mut ctx) = cli::anoma_wallet_cli()?; @@ -80,7 +82,7 @@ fn address_key_find( println!("Viewing key: {}", viewing_key); if unsafe_show_secret { // Check if alias is also a spending key - match wallet.find_spending_key(&alias) { + match wallet.find_spending_key::(&alias) { Ok(spending_key) => println!("Spending key: {}", spending_key), Err(FindKeyError::KeyNotFound) => {} Err(err) => eprintln!("{}", err), @@ -142,7 +144,7 @@ fn spending_keys_list( // Print those too if they are available and requested. if unsafe_show_secret { if let Some(spending_key) = spending_key_opt { - match spending_key.get(decrypt, None) { + match spending_key.get::(decrypt, None) { // Here the spending key is unencrypted or successfully // decrypted Ok(spending_key) => { @@ -200,7 +202,7 @@ fn spending_key_gen( ) { let mut wallet = ctx.wallet; let alias = alias.to_lowercase(); - let (alias, _key) = wallet.gen_spending_key(alias, unsafe_dont_encrypt); + let (alias, _key) = wallet.gen_spending_key::(alias, unsafe_dont_encrypt); namada_apps::wallet::save(&wallet).unwrap_or_else(|err| eprintln!("{}", err)); println!( "Successfully added a spending key with alias: \"{}\"", @@ -228,7 +230,7 @@ fn payment_address_gen( .expect("a PaymentAddress"); let mut wallet = ctx.wallet; let alias = wallet - .insert_payment_addr( + .insert_payment_addr::( alias, PaymentAddress::from(payment_addr).pinned(pin), ) @@ -257,7 +259,7 @@ fn address_key_add( MaspValue::FullViewingKey(viewing_key) => { let alias = ctx .wallet - .insert_viewing_key(alias, viewing_key) + .insert_viewing_key::(alias, viewing_key) .unwrap_or_else(|| { eprintln!("Viewing key not added"); cli::safe_exit(1); @@ -267,7 +269,7 @@ fn address_key_add( MaspValue::ExtendedSpendingKey(spending_key) => { let alias = ctx .wallet - .encrypt_insert_spending_key( + .encrypt_insert_spending_key::( alias, spending_key, unsafe_dont_encrypt, @@ -281,7 +283,7 @@ fn address_key_add( MaspValue::PaymentAddress(payment_addr) => { let alias = ctx .wallet - .insert_payment_addr(alias, payment_addr) + .insert_payment_addr::(alias, payment_addr) .unwrap_or_else(|| { eprintln!("Payment address not added"); cli::safe_exit(1); @@ -307,7 +309,7 @@ fn key_and_address_gen( }: args::KeyAndAddressGen, ) { let mut wallet = ctx.wallet; - let (alias, _key) = wallet.gen_key(scheme, alias, unsafe_dont_encrypt); + let (alias, _key) = wallet.gen_key::(scheme, alias, unsafe_dont_encrypt); namada_apps::wallet::save(&wallet).unwrap_or_else(|err| eprintln!("{}", err)); println!( "Successfully added a key and an address with alias: \"{}\"", @@ -327,7 +329,7 @@ fn key_find( ) { let mut wallet = ctx.wallet; let found_keypair = match public_key { - Some(pk) => wallet.find_key_by_pk(&pk), + Some(pk) => wallet.find_key_by_pk::(&pk), None => { let alias = alias.or(value); match alias { @@ -338,7 +340,7 @@ fn key_find( ); cli::safe_exit(1) } - Some(alias) => wallet.find_key(alias.to_lowercase()), + Some(alias) => wallet.find_key::(alias.to_lowercase()), } } }; @@ -386,7 +388,7 @@ fn key_list( if let Some(pkh) = pkh { writeln!(w, " Public key hash: {}", pkh).unwrap(); } - match stored_keypair.get(decrypt, None) { + match stored_keypair.get::(decrypt, None) { Ok(keypair) => { writeln!(w, " Public key: {}", keypair.ref_to()) .unwrap(); @@ -410,7 +412,7 @@ fn key_list( fn key_export(ctx: Context, args::KeyExport { alias }: args::KeyExport) { let mut wallet = ctx.wallet; wallet - .find_key(alias.to_lowercase()) + .find_key::(alias.to_lowercase()) .map(|keypair| { let file_data = keypair .try_to_vec() @@ -483,7 +485,7 @@ fn address_or_alias_find(ctx: Context, args: args::AddressOrAliasFind) { fn address_add(ctx: Context, args: args::AddressAdd) { let mut wallet = ctx.wallet; if wallet - .add_address(args.alias.clone().to_lowercase(), args.address) + .add_address::(args.alias.clone().to_lowercase(), args.address) .is_none() { eprintln!("Address not added"); diff --git a/apps/src/lib/cli/context.rs b/apps/src/lib/cli/context.rs index ec0ef763f8..025767ceb0 100644 --- a/apps/src/lib/cli/context.rs +++ b/apps/src/lib/cli/context.rs @@ -17,8 +17,9 @@ use crate::client::tx::CLIShieldedUtils; use crate::config::genesis::genesis_config; use crate::config::global::GlobalConfig; use crate::config::{self, Config}; -use crate::wallet::Wallet; +use namada::ledger::wallet::Wallet; use crate::wasm_loader; +use crate::wallet::CliWalletUtils; /// Env. var to set chain ID const ENV_VAR_CHAIN_ID: &str = "ANOMA_CHAIN_ID"; @@ -344,7 +345,7 @@ impl ArgFromMutContext for common::SecretKey { FromStr::from_str(raw).or_else(|_parse_err| { // Or it can be an alias ctx.wallet - .find_key(raw) + .find_key::(raw) .map_err(|_find_err| format!("Unknown key {}", raw)) }) } @@ -361,13 +362,13 @@ impl ArgFromMutContext for common::PublicKey { // Or it can be a public key hash in hex string FromStr::from_str(raw) .map(|pkh: PublicKeyHash| { - let key = ctx.wallet.find_key_by_pkh(&pkh).unwrap(); + let key = ctx.wallet.find_key_by_pkh::(&pkh).unwrap(); key.ref_to() }) // Or it can be an alias that may be found in the wallet .or_else(|_parse_err| { ctx.wallet - .find_key(raw) + .find_key::(raw) .map(|x| x.ref_to()) .map_err(|x| x.to_string()) }) @@ -385,7 +386,7 @@ impl ArgFromMutContext for ExtendedSpendingKey { FromStr::from_str(raw).or_else(|_parse_err| { // Or it is a stored alias of one ctx.wallet - .find_spending_key(raw) + .find_spending_key::(raw) .map_err(|_find_err| format!("Unknown spending key {}", raw)) }) } diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index bed2eb5fcf..d87d044bca 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -51,7 +51,7 @@ use namada::types::{address, storage, token}; use rust_decimal::Decimal; use tokio::time::{Duration, Instant}; -use crate::wallet::Wallet; +use namada::ledger::wallet::Wallet; use crate::cli::{self, args}; use crate::client::tendermint_rpc_types::TxResponse; use namada::ledger::masp::{Conversions, PinnedBalanceError}; diff --git a/apps/src/lib/client/signing.rs b/apps/src/lib/client/signing.rs index adacb53c3b..8146dc249c 100644 --- a/apps/src/lib/client/signing.rs +++ b/apps/src/lib/client/signing.rs @@ -13,11 +13,12 @@ use std::path::PathBuf; use super::rpc; use crate::cli::{self, args}; use crate::client::tendermint_rpc_types::TxBroadcastData; -use crate::wallet::Wallet; +use namada::ledger::wallet::Wallet; +use namada::ledger::wallet::WalletUtils; /// Find the public key for the given address and try to load the keypair /// for it from the wallet. Panics if the key cannot be found or loaded. -pub async fn find_keypair( +pub async fn find_keypair( client: &HttpClient, wallet: &mut Wallet, addr: &Address, @@ -37,7 +38,7 @@ pub async fn find_keypair( ); cli::safe_exit(1); }); - wallet.find_key_by_pk(&public_key).unwrap_or_else(|err| { + wallet.find_key_by_pk::(&public_key).unwrap_or_else(|err| { eprintln!( "Unable to load the keypair from the wallet for public \ key {}. Failed with: {}", @@ -47,7 +48,7 @@ pub async fn find_keypair( }) } Address::Implicit(ImplicitAddress(pkh)) => { - wallet.find_key_by_pkh(pkh).unwrap_or_else(|err| { + wallet.find_key_by_pkh::(pkh).unwrap_or_else(|err| { eprintln!( "Unable to load the keypair from the wallet for the \ implicit address {}. Failed with: {}", @@ -85,7 +86,7 @@ pub enum TxSigningKey { /// signer. Return the given signing key or public key of the given signer if /// possible. If no explicit signer given, use the `default`. If no `default` /// is given, panics. -pub async fn tx_signer( +pub async fn tx_signer( client: &HttpClient, wallet: &mut Wallet, args: &args::Tx, @@ -104,7 +105,7 @@ pub async fn tx_signer( } TxSigningKey::WalletAddress(signer) => { let signer = signer; - let signing_key = find_keypair( + let signing_key = find_keypair::( client, wallet, &signer, @@ -114,14 +115,14 @@ pub async fn tx_signer( // PK first if matches!(signer, Address::Implicit(_)) { let pk: common::PublicKey = signing_key.ref_to(); - super::tx::reveal_pk_if_needed(client, wallet, &pk, args).await; + super::tx::reveal_pk_if_needed::(client, wallet, &pk, args).await; } signing_key } TxSigningKey::SecretKey(signing_key) => { // Check if the signing key needs to reveal its PK first let pk: common::PublicKey = signing_key.ref_to(); - super::tx::reveal_pk_if_needed(client, wallet, &pk, args).await; + super::tx::reveal_pk_if_needed::(client, wallet, &pk, args).await; signing_key } TxSigningKey::None => { @@ -141,14 +142,14 @@ pub async fn tx_signer( /// hashes needed for monitoring the tx on chain. /// /// If it is a dry run, it is not put in a wrapper, but returned as is. -pub async fn sign_tx( +pub async fn sign_tx( client: &HttpClient, wallet: &mut Wallet, tx: Tx, args: &args::Tx, default: TxSigningKey, ) -> TxBroadcastData { - let keypair = tx_signer(client, wallet, args, default).await; + let keypair = tx_signer::(client, wallet, args, default).await; let tx = tx.sign(&keypair); let epoch = rpc::query_epoch(client) diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index 26ab555285..f3248d277a 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -35,6 +35,7 @@ use namada::types::masp::TransferTarget; use namada::types::storage::{ Epoch, BlockResults, RESERVED_ADDRESS_PREFIX, }; +use crate::wallet::gen_validator_keys; use namada::types::time::DateTimeUtc; use namada::types::transaction::governance::{ InitProposalData, VoteProposalData, @@ -43,6 +44,7 @@ use namada::types::transaction::{pos, InitAccount, InitValidator, UpdateVp}; use namada::types::{storage, token}; use namada::{ledger, vm}; use namada::ledger::masp::ShieldedUtils; +use namada::ledger::wallet::WalletUtils; use rust_decimal::Decimal; use tokio::time::{Duration, Instant}; use async_trait::async_trait; @@ -58,7 +60,7 @@ use crate::facade::tendermint_rpc::endpoint::broadcast::tx_sync::Response; use crate::facade::tendermint_rpc::error::Error as RpcError; use crate::facade::tendermint_rpc::{Client, HttpClient}; use crate::node::ledger::tendermint_node; -use crate::wallet::Wallet; +use namada::ledger::wallet::Wallet; /// Timeout for requests to the `/accepted` and `/applied` /// ABCI query endpoints. @@ -69,7 +71,7 @@ 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( +pub async fn submit_custom( client: &HttpClient, wallet: &mut Wallet, args: args::TxCustom, @@ -78,11 +80,11 @@ pub async fn submit_custom( let data = args.data_path; let tx = Tx::new(tx_code, data); let initialized_accounts = - process_tx(client, wallet, &args.tx, tx, TxSigningKey::None).await; - save_initialized_accounts(wallet, &args.tx, initialized_accounts).await; + process_tx::(client, wallet, &args.tx, tx, TxSigningKey::None).await; + save_initialized_accounts::(wallet, &args.tx, initialized_accounts).await; } -pub async fn submit_update_vp( +pub async fn submit_update_vp( client: &HttpClient, wallet: &mut Wallet, args: args::TxUpdateVp, @@ -137,10 +139,10 @@ pub async fn submit_update_vp( let data = data.try_to_vec().expect("Encoding tx data shouldn't fail"); let tx = Tx::new(tx_code, Some(data)); - process_tx(client, wallet, &args.tx, tx, TxSigningKey::WalletAddress(args.addr)).await; + process_tx::(client, wallet, &args.tx, tx, TxSigningKey::WalletAddress(args.addr)).await; } -pub async fn submit_init_account( +pub async fn submit_init_account( client: &HttpClient, wallet: &mut Wallet, args: args::TxInitAccount, @@ -164,12 +166,12 @@ pub async fn submit_init_account( let tx = Tx::new(tx_code, Some(data)); let initialized_accounts = - process_tx(client, wallet, &args.tx, tx, TxSigningKey::WalletAddress(args.source)) + process_tx::(client, wallet, &args.tx, tx, TxSigningKey::WalletAddress(args.source)) .await; - save_initialized_accounts(wallet, &args.tx, initialized_accounts).await; + save_initialized_accounts::(wallet, &args.tx, initialized_accounts).await; } -pub async fn submit_init_validator( +pub async fn submit_init_validator( client: &HttpClient, mut ctx: Context, args::TxInitValidator { @@ -197,7 +199,7 @@ pub async fn submit_init_validator( let account_key = account_key.unwrap_or_else(|| { println!("Generating validator account key..."); ctx.wallet - .gen_key( + .gen_key::( scheme, Some(validator_key_alias.clone()), unsafe_dont_encrypt, @@ -217,7 +219,7 @@ pub async fn submit_init_validator( .unwrap_or_else(|| { println!("Generating consensus key..."); ctx.wallet - .gen_key( + .gen_key::( // Note that TM only allows ed25519 for consensus key SchemeType::Ed25519, Some(consensus_key_alias.clone()), @@ -233,7 +235,7 @@ pub async fn submit_init_validator( } // Generate the validator keys let validator_keys = - ctx.wallet.gen_validator_keys(protocol_key, scheme).unwrap(); + gen_validator_keys(&mut ctx.wallet, protocol_key, scheme).unwrap(); let protocol_key = validator_keys.get_protocol_keypair().ref_to(); let dkg_key = validator_keys .dkg_keypair @@ -290,7 +292,7 @@ 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 initialized_accounts = - process_tx(client, &mut ctx.wallet, &tx_args, tx, TxSigningKey::WalletAddress(source)) + process_tx::(client, &mut ctx.wallet, &tx_args, tx, TxSigningKey::WalletAddress(source)) .await; if !tx_args.dry_run { let (validator_address_alias, validator_address) = @@ -321,7 +323,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) = ctx.wallet.add_address::( validator_address_alias.clone(), validator_address.clone(), ) { @@ -499,7 +501,7 @@ impl masp::ShieldedUtils for CLIShieldedUtils { -pub async fn submit_transfer>( +pub async fn submit_transfer, V: WalletUtils>( client: &HttpClient, wallet: &mut Wallet, shielded: &mut ShieldedContext, @@ -611,7 +613,7 @@ pub async fn submit_transfer>( }; // 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(client, wallet, &args.tx, default_signer.clone()) + let chosen_signer = tx_signer::(client, wallet, &args.tx, default_signer.clone()) .await .ref_to(); let shielded_gas = masp_tx_key().ref_to() == chosen_signer; @@ -667,10 +669,10 @@ pub async fn submit_transfer>( let tx = Tx::new(tx_code, Some(data)); let signing_address = TxSigningKey::WalletAddress(source); - process_tx(client, wallet, &args.tx, tx, signing_address).await; + process_tx::(client, wallet, &args.tx, tx, signing_address).await; } -pub async fn submit_ibc_transfer( +pub async fn submit_ibc_transfer( client: &HttpClient, wallet: &mut Wallet, args: args::TxIbcTransfer, @@ -780,11 +782,15 @@ pub async fn submit_ibc_transfer( .expect("Encoding tx data shouldn't fail"); let tx = Tx::new(tx_code, Some(data)); - process_tx(client, wallet, &args.tx, tx, TxSigningKey::WalletAddress(args.source)) + process_tx::(client, wallet, &args.tx, tx, TxSigningKey::WalletAddress(args.source)) .await; } -pub async fn submit_init_proposal(client: &HttpClient, mut ctx: Context, args: args::InitProposal) { +pub async fn submit_init_proposal( + client: &HttpClient, + mut ctx: Context, + args: args::InitProposal, +) { 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"); @@ -850,7 +856,7 @@ pub async fn submit_init_proposal(client: &HttpClient, mut ctx: Context, args: a if args.offline { let signer = ctx.get(&signer); - let signing_key = find_keypair( + let signing_key = find_keypair::( client, &mut ctx.wallet, &signer, @@ -916,12 +922,12 @@ pub async fn submit_init_proposal(client: &HttpClient, mut ctx: Context, args: a let tx_code = args.tx_code_path; let tx = Tx::new(tx_code, Some(data)); - process_tx(client, &mut ctx.wallet, &args.tx, tx, TxSigningKey::WalletAddress(signer)) + process_tx::(client, &mut ctx.wallet, &args.tx, tx, TxSigningKey::WalletAddress(signer)) .await; } } -pub async fn submit_vote_proposal( +pub async fn submit_vote_proposal( client: &HttpClient, wallet: &mut Wallet, args: args::VoteProposal, @@ -952,7 +958,7 @@ pub async fn submit_vote_proposal( safe_exit(1) } - let signing_key = find_keypair( + let signing_key = find_keypair::( client, wallet, &signer, @@ -1049,7 +1055,7 @@ pub async fn submit_vote_proposal( let tx_code = args.tx_code_path; let tx = Tx::new(tx_code, Some(data)); - process_tx( + process_tx::( client, wallet, &args.tx, @@ -1071,7 +1077,7 @@ pub async fn submit_vote_proposal( } } -pub async fn submit_reveal_pk( +pub async fn submit_reveal_pk( client: &HttpClient, wallet: &mut Wallet, args: args::RevealPk, @@ -1081,13 +1087,13 @@ pub async fn submit_reveal_pk( public_key, } = args; let public_key = public_key; - if !reveal_pk_if_needed(client, wallet, &public_key, &args).await { + if !reveal_pk_if_needed::(client, wallet, &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( +pub async fn reveal_pk_if_needed( client: &HttpClient, wallet: &mut Wallet, public_key: &common::PublicKey, @@ -1098,7 +1104,7 @@ pub async fn reveal_pk_if_needed( if args.force || !has_revealed_pk(client, &addr).await { // If not, submit it - submit_reveal_pk_aux(client, wallet, public_key, args).await; + submit_reveal_pk_aux::(client, wallet, public_key, args).await; true } else { false @@ -1112,7 +1118,7 @@ pub async fn has_revealed_pk( rpc::get_public_key(client, addr).await.is_some() } -pub async fn submit_reveal_pk_aux( +pub async fn submit_reveal_pk_aux( client: &HttpClient, wallet: &mut Wallet, public_key: &common::PublicKey, @@ -1131,10 +1137,10 @@ pub async fn submit_reveal_pk_aux( signing_key.clone() } else if let Some(signer) = args.signer.as_ref() { let signer = signer; - find_keypair(client, wallet, &signer) + find_keypair::(client, wallet, &signer) .await } else { - find_keypair(client, wallet, &addr).await + find_keypair::(client, wallet, &addr).await }; let epoch = rpc::query_epoch(client) .await; @@ -1251,7 +1257,7 @@ async fn filter_delegations( delegations.into_iter().flatten().collect() } -pub async fn submit_bond( +pub async fn submit_bond( client: &HttpClient, wallet: &mut Wallet, args: args::Bond, @@ -1317,7 +1323,7 @@ pub async fn submit_bond( let tx = Tx::new(tx_code, Some(data)); let default_signer = args.source.unwrap_or(args.validator); - process_tx( + process_tx::( client, wallet, &args.tx, @@ -1327,7 +1333,7 @@ pub async fn submit_bond( .await; } -pub async fn submit_unbond( +pub async fn submit_unbond( client: &HttpClient, wallet: &mut Wallet, args: args::Unbond, @@ -1394,7 +1400,7 @@ pub async fn submit_unbond( let tx = Tx::new(tx_code, Some(data)); let default_signer = args.source.unwrap_or(args.validator); - process_tx( + process_tx::( client, wallet, &args.tx, @@ -1404,7 +1410,7 @@ pub async fn submit_unbond( .await; } -pub async fn submit_withdraw( +pub async fn submit_withdraw( client: &HttpClient, wallet: &mut Wallet, args: args::Withdraw, @@ -1469,7 +1475,7 @@ pub async fn submit_withdraw( let tx = Tx::new(tx_code, Some(data)); let default_signer = args.source.unwrap_or(args.validator); - process_tx( + process_tx::( client, wallet, &args.tx, @@ -1479,7 +1485,7 @@ pub async fn submit_withdraw( .await; } -pub async fn submit_validator_commission_change( +pub async fn submit_validator_commission_change( client: &HttpClient, wallet: &mut Wallet, args: args::TxCommissionRateChange, @@ -1550,7 +1556,7 @@ pub async fn submit_validator_commission_change( let tx = Tx::new(tx_code, Some(data)); let default_signer = args.validator.clone(); - process_tx( + process_tx::( client, wallet, &args.tx, @@ -1562,14 +1568,14 @@ 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( +async fn process_tx( client: &HttpClient, wallet: &mut Wallet, args: &args::Tx, tx: Tx, default_signer: TxSigningKey, ) -> Vec
{ - let to_broadcast = sign_tx(client, wallet, tx, args, default_signer).await; + let to_broadcast = sign_tx::(client, wallet, tx, args, default_signer).await; // NOTE: use this to print the request JSON body: // let request = @@ -1622,7 +1628,7 @@ async fn process_tx( } /// Save accounts initialized from a tx into the wallet, if any. -async fn save_initialized_accounts( +async fn save_initialized_accounts( wallet: &mut Wallet, args: &args::Tx, initialized_accounts: Vec
, @@ -1660,7 +1666,7 @@ async fn save_initialized_accounts( } }; let alias = alias.into_owned(); - let added = wallet.add_address(alias.clone(), address.clone()); + let added = wallet.add_address::(alias.clone(), address.clone()); match added { Some(new_alias) if new_alias != encoded => { println!( diff --git a/apps/src/lib/client/utils.rs b/apps/src/lib/client/utils.rs index c6e8f6472d..8719b9129b 100644 --- a/apps/src/lib/client/utils.rs +++ b/apps/src/lib/client/utils.rs @@ -20,6 +20,7 @@ use rust_decimal::Decimal; use serde_json::json; use sha2::{Digest, Sha256}; +use crate::wallet::CliWalletUtils; use crate::cli::context::ENV_VAR_WASM_DIR; use crate::cli::{self, args}; use crate::config::genesis::genesis_config::{ @@ -30,7 +31,8 @@ use crate::config::{self, Config, TendermintMode}; use crate::facade::tendermint::node::Id as TendermintNodeId; use crate::facade::tendermint_config::net::Address as TendermintAddress; use crate::node::ledger::tendermint_node; -use crate::wallet::{pre_genesis, Wallet}; +use crate::wallet::pre_genesis; +use namada::ledger::wallet::Wallet; use crate::wasm_loader; pub const NET_ACCOUNTS_DIR: &str = "setup"; @@ -106,7 +108,7 @@ pub async fn join_network( validator_alias_and_dir.map(|(validator_alias, pre_genesis_dir)| { ( validator_alias, - pre_genesis::ValidatorWallet::load(&pre_genesis_dir) + pre_genesis::load(&pre_genesis_dir) .unwrap_or_else(|err| { eprintln!( "Error loading validator pre-genesis wallet {err}", @@ -488,7 +490,7 @@ pub fn init_network( .unwrap_or_else(|| { let alias = format!("{}-consensus-key", name); println!("Generating validator {} consensus key...", name); - let (_alias, keypair) = wallet.gen_key( + let (_alias, keypair) = wallet.gen_key::( SchemeType::Ed25519, Some(alias), unsafe_dont_encrypt, @@ -507,7 +509,7 @@ pub fn init_network( .unwrap_or_else(|| { let alias = format!("{}-account-key", name); println!("Generating validator {} account key...", name); - let (_alias, keypair) = wallet.gen_key( + let (_alias, keypair) = wallet.gen_key::( SchemeType::Ed25519, Some(alias), unsafe_dont_encrypt, @@ -522,7 +524,7 @@ pub fn init_network( .unwrap_or_else(|| { let alias = format!("{}-protocol-key", name); println!("Generating validator {} protocol signing key...", name); - let (_alias, keypair) = wallet.gen_key( + let (_alias, keypair) = wallet.gen_key::( SchemeType::Ed25519, Some(alias), unsafe_dont_encrypt, @@ -546,8 +548,8 @@ pub fn init_network( name ); - let validator_keys = wallet - .gen_validator_keys( + let validator_keys = crate::wallet::gen_validator_keys( + &mut wallet, Some(protocol_pk.clone()), SchemeType::Ed25519, ) @@ -569,7 +571,7 @@ pub fn init_network( Some(genesis_config::HexString(dkg_pk.to_string())); // Write keypairs to wallet - wallet.add_address(name.clone(), address); + wallet.add_address::(name.clone(), address); crate::wallet::save(&wallet).unwrap(); }); @@ -592,7 +594,7 @@ pub fn init_network( if config.address.is_none() { let address = address::gen_established_address("token"); config.address = Some(address.to_string()); - wallet.add_address(name.clone(), address); + wallet.add_address::(name.clone(), address); } if config.vp.is_none() { config.vp = Some("vp_token".to_string()); @@ -606,7 +608,7 @@ pub fn init_network( "Generating implicit account {} key and address ...", name ); - let (_alias, keypair) = wallet.gen_key( + let (_alias, keypair) = wallet.gen_key::( SchemeType::Ed25519, Some(name.clone()), unsafe_dont_encrypt, @@ -642,7 +644,7 @@ pub fn init_network( genesis_config::write_genesis_config(&config_clean, &genesis_path); // Add genesis addresses and save the wallet with other account keys - wallet.add_genesis_addresses(config_clean.clone()); + crate::wallet::add_genesis_addresses(&mut wallet, config_clean.clone()); crate::wallet::save(&wallet).unwrap(); // Write the global config setting the default chain ID @@ -693,7 +695,7 @@ pub fn init_network( global_config.write(validator_dir).unwrap(); // Add genesis addresses to the validator's wallet let mut wallet = crate::wallet::load_or_new(&validator_chain_dir); - wallet.add_genesis_addresses(config_clean.clone()); + crate::wallet::add_genesis_addresses(&mut wallet, config_clean.clone()); crate::wallet::save(&wallet).unwrap(); }); @@ -852,11 +854,11 @@ fn init_established_account( if config.address.is_none() { let address = address::gen_established_address("established"); config.address = Some(address.to_string()); - wallet.add_address(&name, address); + wallet.add_address::(&name, address); } if config.public_key.is_none() { println!("Generating established account {} key...", name.as_ref()); - let (_alias, keypair) = wallet.gen_key( + let (_alias, keypair) = wallet.gen_key::( SchemeType::Ed25519, Some(format!("{}-key", name.as_ref())), unsafe_dont_encrypt, @@ -903,7 +905,7 @@ pub fn init_genesis_validator( let pre_genesis_dir = validator_pre_genesis_dir(&global_args.base_dir, &alias); println!("Generating validator keys..."); - let pre_genesis = pre_genesis::ValidatorWallet::gen_and_store( + let pre_genesis = pre_genesis::gen_and_store( key_scheme, unsafe_dont_encrypt, &pre_genesis_dir, diff --git a/apps/src/lib/node/ledger/shell/mod.rs b/apps/src/lib/node/ledger/shell/mod.rs index 078d37d931..450f051fa0 100644 --- a/apps/src/lib/node/ledger/shell/mod.rs +++ b/apps/src/lib/node/ledger/shell/mod.rs @@ -49,6 +49,7 @@ use num_derive::{FromPrimitive, ToPrimitive}; use num_traits::{FromPrimitive, ToPrimitive}; use thiserror::Error; use tokio::sync::mpsc::UnboundedSender; +use crate::wallet::CliWalletUtils; use crate::config::{genesis, TendermintMode}; #[cfg(feature = "abcipp")] @@ -275,7 +276,7 @@ where "{}", wallet_path.as_path().to_str().unwrap() ); - let wallet = crate::wallet::load_or_new_from_genesis( + let mut wallet = crate::wallet::load_or_new_from_genesis( wallet_path, genesis::genesis_config::open_genesis_config( genesis_path, @@ -284,9 +285,11 @@ where ); wallet .take_validator_data() - .map(|data| ShellMode::Validator { - data, - broadcast_sender, + .map(|data| { + ShellMode::Validator { + data: data.clone(), + broadcast_sender, + } }) .expect( "Validator data should have been stored in the \ @@ -295,11 +298,11 @@ where } #[cfg(feature = "dev")] { - let validator_keys = wallet::defaults::validator_keys(); + let validator_keys = crate::wallet::defaults::validator_keys(); ShellMode::Validator { - data: wallet::ValidatorData { - address: wallet::defaults::validator_address(), - keys: wallet::ValidatorKeys { + data: crate::wallet::ValidatorData { + address: crate::wallet::defaults::validator_address(), + keys: crate::wallet::ValidatorKeys { protocol_keypair: validator_keys.0, dkg_keypair: Some(validator_keys.1), }, @@ -651,7 +654,7 @@ where let pk = common::SecretKey::deserialize(&mut pk_bytes.as_slice()) .expect("Validator's public key should be deserializable") .ref_to(); - wallet.find_key_by_pk(&pk).expect( + wallet.find_key_by_pk::(&pk).expect( "A validator's established keypair should be stored in its \ wallet", ) diff --git a/apps/src/lib/wallet/alias.rs b/apps/src/lib/wallet/alias.rs index 13d977b852..e69de29bb2 100644 --- a/apps/src/lib/wallet/alias.rs +++ b/apps/src/lib/wallet/alias.rs @@ -1,103 +0,0 @@ -//! Wallet address and key aliases. - -use std::convert::Infallible; -use std::fmt::Display; -use std::hash::Hash; -use std::str::FromStr; - -use serde::{Deserialize, Serialize}; - -/// Aliases created from raw strings are kept in-memory as given, but their -/// `Serialize` and `Display` instance converts them to lowercase. Their -/// `PartialEq` instance is case-insensitive. -#[derive(Clone, Debug, Default, Deserialize, PartialOrd, Ord, Eq)] -#[serde(transparent)] -pub struct Alias(String); - -impl Alias { - /// Normalize an alias to lower-case - pub fn normalize(&self) -> String { - self.0.to_lowercase() - } - - /// Returns the length of the underlying `String`. - pub fn len(&self) -> usize { - self.0.len() - } - - /// Is the underlying `String` empty? - pub fn is_empty(&self) -> bool { - self.0.is_empty() - } -} - -impl Serialize for Alias { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - self.normalize().serialize(serializer) - } -} - -impl PartialEq for Alias { - fn eq(&self, other: &Self) -> bool { - self.normalize() == other.normalize() - } -} - -impl Hash for Alias { - fn hash(&self, state: &mut H) { - self.normalize().hash(state); - } -} - -impl From for Alias -where - T: AsRef, -{ - fn from(raw: T) -> Self { - Self(raw.as_ref().to_owned()) - } -} - -impl From for String { - fn from(alias: Alias) -> Self { - alias.normalize() - } -} - -impl<'a> From<&'a Alias> for String { - fn from(alias: &'a Alias) -> Self { - alias.normalize() - } -} - -impl Display for Alias { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.normalize().fmt(f) - } -} - -impl FromStr for Alias { - type Err = Infallible; - - fn from_str(s: &str) -> Result { - Ok(Self(s.into())) - } -} - -/// Default alias of a validator's account key -pub fn validator_key(validator_alias: &Alias) -> Alias { - format!("{validator_alias}-validator-key").into() -} - -/// Default alias of a validator's consensus key -pub fn validator_consensus_key(validator_alias: &Alias) -> Alias { - format!("{validator_alias}-consensus-key").into() -} - -/// Default alias of a validator's Tendermint node key -pub fn validator_tendermint_node_key(validator_alias: &Alias) -> Alias { - format!("{validator_alias}-tendermint-node-key").into() -} diff --git a/apps/src/lib/wallet/defaults.rs b/apps/src/lib/wallet/defaults.rs index b0ae08ac83..ed888beee0 100644 --- a/apps/src/lib/wallet/defaults.rs +++ b/apps/src/lib/wallet/defaults.rs @@ -11,7 +11,7 @@ use namada::types::address::Address; use namada::types::key::*; use crate::config::genesis::genesis_config::GenesisConfig; -use crate::wallet::alias::Alias; +use namada::ledger::wallet::Alias; /// The default addresses with their aliases. pub fn addresses_from_genesis(genesis: GenesisConfig) -> Vec<(Alias, Address)> { @@ -76,7 +76,7 @@ mod dev { use namada::types::key::dkg_session_keys::DkgKeypair; use namada::types::key::*; - use crate::wallet::alias::Alias; + use namada::ledger::wallet::Alias; /// Generate a new protocol signing keypair and DKG session keypair pub fn validator_keys() -> (common::SecretKey, DkgKeypair) { diff --git a/apps/src/lib/wallet/keys.rs b/apps/src/lib/wallet/keys.rs index 7627bd9b16..e69de29bb2 100644 --- a/apps/src/lib/wallet/keys.rs +++ b/apps/src/lib/wallet/keys.rs @@ -1,243 +0,0 @@ -//! Cryptographic keys for digital signatures support for the wallet. - -use std::fmt::Display; -use std::marker::PhantomData; -use std::str::FromStr; - -use borsh::{BorshDeserialize, BorshSerialize}; -use data_encoding::HEXLOWER; -use orion::{aead, kdf}; -use serde::{Deserialize, Serialize}; -use thiserror::Error; - -use super::read_password; - -const ENCRYPTED_KEY_PREFIX: &str = "encrypted:"; -const UNENCRYPTED_KEY_PREFIX: &str = "unencrypted:"; - -/// A keypair stored in a wallet -#[derive(Debug)] -pub enum StoredKeypair -where - ::Err: Display, -{ - /// An encrypted keypair - Encrypted(EncryptedKeypair), - /// An raw (unencrypted) keypair - Raw(T), -} - -impl Serialize - for StoredKeypair -where - ::Err: Display, -{ - fn serialize( - &self, - serializer: S, - ) -> std::result::Result - where - S: serde::Serializer, - { - // String encoded, because toml doesn't support enums - match self { - StoredKeypair::Encrypted(encrypted) => { - let keypair_string = - format!("{}{}", ENCRYPTED_KEY_PREFIX, encrypted); - serde::Serialize::serialize(&keypair_string, serializer) - } - StoredKeypair::Raw(raw) => { - let keypair_string = - format!("{}{}", UNENCRYPTED_KEY_PREFIX, raw); - serde::Serialize::serialize(&keypair_string, serializer) - } - } - } -} - -impl<'de, T: BorshSerialize + BorshDeserialize + Display + FromStr> - Deserialize<'de> for StoredKeypair -where - ::Err: Display, -{ - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - use serde::de::Error; - - let keypair_string: String = - serde::Deserialize::deserialize(deserializer) - .map_err(|err| { - DeserializeStoredKeypairError::InvalidStoredKeypairString( - err.to_string(), - ) - }) - .map_err(D::Error::custom)?; - if let Some(raw) = keypair_string.strip_prefix(UNENCRYPTED_KEY_PREFIX) { - FromStr::from_str(raw) - .map(|keypair| Self::Raw(keypair)) - .map_err(|err| { - DeserializeStoredKeypairError::InvalidStoredKeypairString( - err.to_string(), - ) - }) - .map_err(D::Error::custom) - } else if let Some(encrypted) = - keypair_string.strip_prefix(ENCRYPTED_KEY_PREFIX) - { - FromStr::from_str(encrypted) - .map(Self::Encrypted) - .map_err(|err| { - DeserializeStoredKeypairError::InvalidStoredKeypairString( - err.to_string(), - ) - }) - .map_err(D::Error::custom) - } else { - Err(DeserializeStoredKeypairError::MissingPrefix) - .map_err(D::Error::custom) - } - } -} - -#[allow(missing_docs)] -#[derive(Error, Debug)] -pub enum DeserializeStoredKeypairError { - #[error("The stored keypair is not valid: {0}")] - InvalidStoredKeypairString(String), - #[error("The stored keypair is missing a prefix")] - MissingPrefix, -} - -/// An encrypted keypair stored in a wallet -#[derive(Debug)] -pub struct EncryptedKeypair( - Vec, - PhantomData, -); - -impl Display for EncryptedKeypair { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", HEXLOWER.encode(self.0.as_ref())) - } -} - -impl FromStr for EncryptedKeypair { - type Err = data_encoding::DecodeError; - - fn from_str(s: &str) -> Result { - HEXLOWER.decode(s.as_ref()).map(|x| Self(x, PhantomData)) - } -} - -#[allow(missing_docs)] -#[derive(Debug, Error)] -pub enum DecryptionError { - #[error("Unexpected encryption salt")] - BadSalt, - #[error("Unable to decrypt the keypair. Is the password correct?")] - DecryptionError, - #[error("Unable to deserialize the keypair")] - DeserializingError, - #[error("Asked not to decrypt")] - NotDecrypting, -} - -impl - StoredKeypair -where - ::Err: Display, -{ - /// Construct a keypair for storage. If no password is provided, the keypair - /// will be stored raw without encryption. Returns the key for storing and a - /// reference-counting point to the raw key. - pub fn new(keypair: T, password: Option) -> (Self, T) { - match password { - Some(password) => ( - Self::Encrypted(EncryptedKeypair::new(&keypair, password)), - keypair, - ), - None => (Self::Raw(keypair.clone()), keypair), - } - } - - /// Get a raw keypair from a stored keypair. If the keypair is encrypted and - /// no password is provided in the argument, a password will be prompted - /// from stdin. - pub fn get( - &self, - decrypt: bool, - password: Option, - ) -> Result { - match self { - StoredKeypair::Encrypted(encrypted_keypair) => { - if decrypt { - let password = password.unwrap_or_else(|| { - read_password("Enter decryption password: ") - }); - let key = encrypted_keypair.decrypt(password)?; - Ok(key) - } else { - Err(DecryptionError::NotDecrypting) - } - } - StoredKeypair::Raw(keypair) => Ok(keypair.clone()), - } - } - - pub fn is_encrypted(&self) -> bool { - match self { - StoredKeypair::Encrypted(_) => true, - StoredKeypair::Raw(_) => false, - } - } -} - -impl EncryptedKeypair { - /// Encrypt a keypair and store it with its salt. - pub fn new(keypair: &T, password: String) -> Self { - let salt = encryption_salt(); - let encryption_key = encryption_key(&salt, password); - - let data = keypair - .try_to_vec() - .expect("Serializing keypair shouldn't fail"); - - let encrypted_keypair = aead::seal(&encryption_key, &data) - .expect("Encryption of data shouldn't fail"); - - let encrypted_data = [salt.as_ref(), &encrypted_keypair].concat(); - - Self(encrypted_data, PhantomData) - } - - /// Decrypt an encrypted keypair - pub fn decrypt(&self, password: String) -> Result { - let salt_len = encryption_salt().len(); - let (raw_salt, cipher) = self.0.split_at(salt_len); - - let salt = kdf::Salt::from_slice(raw_salt) - .map_err(|_| DecryptionError::BadSalt)?; - - let encryption_key = encryption_key(&salt, password); - - let decrypted_data = aead::open(&encryption_key, cipher) - .map_err(|_| DecryptionError::DecryptionError)?; - - T::try_from_slice(&decrypted_data) - .map_err(|_| DecryptionError::DeserializingError) - } -} - -/// Keypair encryption salt -fn encryption_salt() -> kdf::Salt { - kdf::Salt::default() -} - -/// Make encryption secret key from a password. -fn encryption_key(salt: &kdf::Salt, password: String) -> kdf::SecretKey { - kdf::Password::from_slice(password.as_bytes()) - .and_then(|password| kdf::derive_key(&password, salt, 3, 1 << 17, 32)) - .expect("Generation of encryption secret key shouldn't fail") -} diff --git a/apps/src/lib/wallet/mod.rs b/apps/src/lib/wallet/mod.rs index 6eca74409e..5a38b84ddb 100644 --- a/apps/src/lib/wallet/mod.rs +++ b/apps/src/lib/wallet/mod.rs @@ -19,426 +19,173 @@ use namada::types::masp::{ }; pub use store::wallet_file; use thiserror::Error; +use namada::ledger::wallet::ConfirmationResponse; -use self::alias::Alias; -pub use self::keys::{DecryptionError, StoredKeypair}; -use self::store::Store; -pub use self::store::{ValidatorData, ValidatorKeys}; +pub use namada::ledger::wallet::{DecryptionError, StoredKeypair}; +use namada::ledger::wallet::{Store, Wallet}; +pub use namada::ledger::wallet::{ValidatorData, ValidatorKeys}; use crate::cli; use crate::config::genesis::genesis_config::GenesisConfig; - -#[derive(Debug)] -pub struct Wallet { - store_dir: C, - store: Store, - decrypted_key_cache: HashMap, - decrypted_spendkey_cache: HashMap, -} - -#[derive(Error, Debug)] -pub enum FindKeyError { - #[error("No matching key found")] - KeyNotFound, - #[error("{0}")] - KeyDecryptionError(keys::DecryptionError), -} - -impl Wallet { - /// Add addresses from a genesis configuration. - pub fn add_genesis_addresses(&mut self, genesis: GenesisConfig) { - self.store.add_genesis_addresses(genesis) - } - - /// Generate a new keypair and derive an implicit address from its public - /// and insert them into the store with the provided alias, converted to - /// lower case. If none provided, the alias will be the public key hash (in - /// lowercase too). If the key is to be encrypted, will prompt for - /// password from stdin. Stores the key in decrypted key cache and - /// returns the alias of the key and a reference-counting pointer to the - /// key. - pub fn gen_key( - &mut self, - scheme: SchemeType, - alias: Option, - unsafe_dont_encrypt: bool, - ) -> (String, common::SecretKey) { - let password = read_and_confirm_pwd(unsafe_dont_encrypt); - let (alias, key) = self.store.gen_key(scheme, alias, password); - // Cache the newly added key - self.decrypted_key_cache.insert(alias.clone(), key.clone()); - (alias.into(), key) - } - - pub fn gen_spending_key( - &mut self, - alias: String, - unsafe_dont_encrypt: bool, - ) -> (String, ExtendedSpendingKey) { - let password = new_password_prompt(unsafe_dont_encrypt); - let (alias, key) = self.store.gen_spending_key(alias, password); - // Cache the newly added key - self.decrypted_spendkey_cache.insert(alias.clone(), key); - (alias.into(), key) - } - - /// Generate keypair - /// for signing protocol txs and for the DKG (which will also be stored) - /// A protocol keypair may be optionally provided, indicating that - /// we should re-use a keypair already in the wallet - pub fn gen_validator_keys( - &mut self, - protocol_pk: Option, - scheme: SchemeType, - ) -> Result { - let protocol_keypair = protocol_pk.map(|pk| { - self.find_key_by_pkh(&PublicKeyHash::from(&pk)) - .ok() - .or_else(|| { - self.store - .validator_data - .take() - .map(|data| data.keys.protocol_keypair) - }) - .ok_or(FindKeyError::KeyNotFound) - }); - match protocol_keypair { - Some(Err(err)) => Err(err), - other => Ok(Store::gen_validator_keys( - other.map(|res| res.unwrap()), - scheme, - )), - } - } - - /// Add validator data to the store - pub fn add_validator_data( - &mut self, - address: Address, - keys: ValidatorKeys, - ) { - self.store.add_validator_data(address, keys); - } - - /// Returns the validator data, if it exists. - pub fn get_validator_data(&self) -> Option<&ValidatorData> { - self.store.get_validator_data() - } - - /// Returns the validator data, if it exists. - /// [`Wallet::save`] cannot be called after using this - /// method as it involves a partial move - pub fn take_validator_data(self) -> Option { - self.store.validator_data() - } - - /// Find the stored key by an alias, a public key hash or a public key. - /// If the key is encrypted, will prompt for password from stdin. - /// Any keys that are decrypted are stored in and read from a cache to avoid - /// prompting for password multiple times. - pub fn find_key( - &mut self, - alias_pkh_or_pk: impl AsRef, - ) -> Result { - // Try cache first - if let Some(cached_key) = self - .decrypted_key_cache - .get(&alias_pkh_or_pk.as_ref().into()) - { - return Ok(cached_key.clone()); - } - // If not cached, look-up in store - let stored_key = self - .store - .find_key(alias_pkh_or_pk.as_ref()) - .ok_or(FindKeyError::KeyNotFound)?; - Self::decrypt_stored_key( - &mut self.decrypted_key_cache, - stored_key, - alias_pkh_or_pk.into(), - ) - } - - pub fn find_spending_key( - &mut self, - alias: impl AsRef, - ) -> Result { - // Try cache first - if let Some(cached_key) = - self.decrypted_spendkey_cache.get(&alias.as_ref().into()) - { - return Ok(*cached_key); +use namada::ledger::wallet::{WalletUtils, Alias}; +use std::io::prelude::*; +use std::io::{self, Write}; +use namada::ledger::wallet::FindKeyError; + +pub struct CliWalletUtils; + +impl WalletUtils for CliWalletUtils { + /// Prompt for pssword and confirm it if parameter is false + fn new_password_prompt(unsafe_dont_encrypt: bool) -> Option { + let password = if unsafe_dont_encrypt { + println!("Warning: The keypair will NOT be encrypted."); + None + } else { + Some(Self::read_password("Enter your encryption password: ")) + }; + // Bis repetita for confirmation. + let pwd = if unsafe_dont_encrypt { + None + } else { + Some(Self::read_password( + "To confirm, please enter the same encryption password once \ + more: ", + )) + }; + if pwd != password { + eprintln!("Your two inputs do not match!"); + cli::safe_exit(1) } - // If not cached, look-up in store - let stored_spendkey = self - .store - .find_spending_key(alias.as_ref()) - .ok_or(FindKeyError::KeyNotFound)?; - Self::decrypt_stored_key( - &mut self.decrypted_spendkey_cache, - stored_spendkey, - alias.into(), - ) - } - - pub fn find_viewing_key( - &mut self, - alias: impl AsRef, - ) -> Result<&ExtendedViewingKey, FindKeyError> { - self.store - .find_viewing_key(alias.as_ref()) - .ok_or(FindKeyError::KeyNotFound) - } - - pub fn find_payment_addr( - &self, - alias: impl AsRef, - ) -> Option<&PaymentAddress> { - self.store.find_payment_addr(alias.as_ref()) - } - - /// Find the stored key by a public key. - /// If the key is encrypted, will prompt for password from stdin. - /// Any keys that are decrypted are stored in and read from a cache to avoid - /// prompting for password multiple times. - pub fn find_key_by_pk( - &mut self, - pk: &common::PublicKey, - ) -> Result { - // Try to look-up alias for the given pk. Otherwise, use the PKH string. - let pkh: PublicKeyHash = pk.into(); - let alias = self - .store - .find_alias_by_pkh(&pkh) - .unwrap_or_else(|| pkh.to_string().into()); - // Try read cache - if let Some(cached_key) = self.decrypted_key_cache.get(&alias) { - return Ok(cached_key.clone()); + password + } + + /// Read the password for encryption from the file/env/stdin with confirmation. + fn read_and_confirm_pwd(unsafe_dont_encrypt: bool) -> Option { + let password = if unsafe_dont_encrypt { + println!("Warning: The keypair will NOT be encrypted."); + None + } else { + Some(Self::read_password("Enter your encryption password: ")) + }; + // Bis repetita for confirmation. + let to_confirm = if unsafe_dont_encrypt { + None + } else { + Some(Self::read_password( + "To confirm, please enter the same encryption password once more: ", + )) + }; + if to_confirm != password { + eprintln!("Your two inputs do not match!"); + cli::safe_exit(1) } - // Look-up from store - let stored_key = self - .store - .find_key_by_pk(pk) - .ok_or(FindKeyError::KeyNotFound)?; - Self::decrypt_stored_key( - &mut self.decrypted_key_cache, - stored_key, - alias, - ) - } - - /// Find the stored key by a public key hash. - /// If the key is encrypted, will prompt for password from stdin. - /// Any keys that are decrypted are stored in and read from a cache to avoid - /// prompting for password multiple times. - pub fn find_key_by_pkh( - &mut self, - pkh: &PublicKeyHash, - ) -> Result { - // Try to look-up alias for the given pk. Otherwise, use the PKH string. - let alias = self - .store - .find_alias_by_pkh(pkh) - .unwrap_or_else(|| pkh.to_string().into()); - // Try read cache - if let Some(cached_key) = self.decrypted_key_cache.get(&alias) { - return Ok(cached_key.clone()); + password + } + + /// Read the password for encryption/decryption from the file/env/stdin. Panics + /// if all options are empty/invalid. + fn read_password(prompt_msg: &str) -> String { + let pwd = match env::var("ANOMA_WALLET_PASSWORD_FILE") { + Ok(path) => fs::read_to_string(path) + .expect("Something went wrong reading the file"), + Err(_) => match env::var("ANOMA_WALLET_PASSWORD") { + Ok(password) => password, + Err(_) => rpassword::read_password_from_tty(Some(prompt_msg)) + .unwrap_or_default(), + }, + }; + if pwd.is_empty() { + eprintln!("Password cannot be empty"); + cli::safe_exit(1) } - // Look-up from store - let stored_key = self - .store - .find_key_by_pkh(pkh) - .ok_or(FindKeyError::KeyNotFound)?; - Self::decrypt_stored_key( - &mut self.decrypted_key_cache, - stored_key, - alias, - ) - } - - /// Decrypt stored key, if it's not stored un-encrypted. - /// If a given storage key needs to be decrypted, prompt for password from - /// stdin and if successfully decrypted, store it in a cache. - fn decrypt_stored_key< - T: FromStr + Display + BorshSerialize + BorshDeserialize + Clone, - >( - decrypted_key_cache: &mut HashMap, - stored_key: &StoredKeypair, - alias: Alias, - ) -> Result - where - ::Err: Display, - { - match stored_key { - StoredKeypair::Encrypted(encrypted) => { - let password = read_password("Enter decryption password: "); - let key = encrypted - .decrypt(password) - .map_err(FindKeyError::KeyDecryptionError)?; - decrypted_key_cache.insert(alias.clone(), key); - decrypted_key_cache - .get(&alias) - .cloned() - .ok_or(FindKeyError::KeyNotFound) + pwd + } + + /// The given alias has been selected but conflicts with another alias in + /// the store. Offer the user to either replace existing mapping, alter the + /// chosen alias to a name of their chosing, or cancel the aliasing. + fn show_overwrite_confirmation( + alias: &Alias, + alias_for: &str, + ) -> ConfirmationResponse { + print!( + "You're trying to create an alias \"{}\" that already exists for {} \ + in your store.\nWould you like to replace it? \ + s(k)ip/re(p)lace/re(s)elect: ", + alias, alias_for + ); + io::stdout().flush().unwrap(); + + let mut buffer = String::new(); + // Get the user to select between 3 choices + match io::stdin().read_line(&mut buffer) { + Ok(size) if size > 0 => { + // Isolate the single character representing the choice + let byte = buffer.chars().next().unwrap(); + buffer.clear(); + match byte { + 'p' | 'P' => return ConfirmationResponse::Replace, + 's' | 'S' => { + // In the case of reselection, elicit new alias + print!("Please enter a different alias: "); + io::stdout().flush().unwrap(); + if io::stdin().read_line(&mut buffer).is_ok() { + return ConfirmationResponse::Reselect( + buffer.trim().into(), + ); + } + } + 'k' | 'K' => return ConfirmationResponse::Skip, + // Input is senseless fall through to repeat prompt + _ => {} + }; } - StoredKeypair::Raw(raw) => Ok(raw.clone()), + _ => {} } + // Input is senseless fall through to repeat prompt + println!("Invalid option, try again."); + Self::show_overwrite_confirmation(alias, alias_for) } +} - /// Get all known keys by their alias, paired with PKH, if known. - pub fn get_keys( - &self, - ) -> HashMap< - String, - (&StoredKeypair, Option<&PublicKeyHash>), - > { - self.store - .get_keys() - .into_iter() - .map(|(alias, value)| (alias.into(), value)) - .collect() - } - - /// Find the stored address by an alias. - pub fn find_address(&self, alias: impl AsRef) -> Option<&Address> { - self.store.find_address(alias) - } - - /// Find an alias by the address if it's in the wallet. - pub fn find_alias(&self, address: &Address) -> Option<&Alias> { - self.store.find_alias(address) - } - - /// Get all known addresses by their alias, paired with PKH, if known. - pub fn get_addresses(&self) -> HashMap { - self.store - .get_addresses() - .iter() - .map(|(alias, value)| (alias.into(), value.clone())) - .collect() - } - - /// Get all known payment addresses by their alias - pub fn get_payment_addrs(&self) -> HashMap { - self.store - .get_payment_addrs() - .iter() - .map(|(alias, value)| (alias.into(), *value)) - .collect() - } - - /// Get all known viewing keys by their alias - pub fn get_viewing_keys(&self) -> HashMap { - self.store - .get_viewing_keys() - .iter() - .map(|(alias, value)| (alias.into(), *value)) - .collect() - } - - /// Get all known viewing keys by their alias - pub fn get_spending_keys( - &self, - ) -> HashMap> { - self.store - .get_spending_keys() - .iter() - .map(|(alias, value)| (alias.into(), value)) - .collect() - } - - /// Add a new address with the given alias. If the alias is already used, - /// will ask whether the existing alias should be replaced, a different - /// alias is desired, or the alias creation should be cancelled. Return - /// the chosen alias if the address has been added, otherwise return - /// nothing. - pub fn add_address( - &mut self, - alias: impl AsRef, - address: Address, - ) -> Option { - self.store - .insert_address(alias.into(), address) - .map(Into::into) - } - - /// Insert a new key with the given alias. If the alias is already used, - /// will prompt for overwrite confirmation. - pub fn insert_keypair( - &mut self, - alias: String, - keypair: StoredKeypair, - pkh: PublicKeyHash, - ) -> Option { - self.store - .insert_keypair(alias.into(), keypair, pkh) - .map(Into::into) - } - - pub fn insert_viewing_key( - &mut self, - alias: String, - view_key: ExtendedViewingKey, - ) -> Option { - self.store - .insert_viewing_key(alias.into(), view_key) - .map(Into::into) - } - - pub fn insert_spending_key( - &mut self, - alias: String, - spend_key: StoredKeypair, - viewkey: ExtendedViewingKey, - ) -> Option { - self.store - .insert_spending_key(alias.into(), spend_key, viewkey) - .map(Into::into) - } - - pub fn encrypt_insert_spending_key( - &mut self, - alias: String, - spend_key: ExtendedSpendingKey, - unsafe_dont_encrypt: bool, - ) -> Option { - let password = new_password_prompt(unsafe_dont_encrypt); - self.store - .insert_spending_key( - alias.into(), - StoredKeypair::new(spend_key, password).0, - ExtendedFullViewingKey::from(&spend_key.into()).into(), - ) - .map(Into::into) - } - - pub fn insert_payment_addr( - &mut self, - alias: String, - payment_addr: PaymentAddress, - ) -> Option { - self.store - .insert_payment_addr(alias.into(), payment_addr) - .map(Into::into) +/// Generate keypair +/// for signing protocol txs and for the DKG (which will also be stored) +/// A protocol keypair may be optionally provided, indicating that +/// we should re-use a keypair already in the wallet +pub fn gen_validator_keys( + wallet: &mut Wallet, + protocol_pk: Option, + scheme: SchemeType, +) -> Result { + let protocol_keypair = protocol_pk.map(|pk| { + wallet.find_key_by_pkh::(&PublicKeyHash::from(&pk)) + .ok() + .or_else(|| { + wallet.store_mut() + .validator_data() + .take() + .map(|data| data.keys.protocol_keypair.clone()) + }) + .ok_or(FindKeyError::KeyNotFound) + }); + match protocol_keypair { + Some(Err(err)) => Err(err), + other => Ok(store::gen_validator_keys( + other.map(|res| res.unwrap()), + scheme, + )), } +} - /// Extend this wallet from pre-genesis validator wallet. - pub fn extend_from_pre_genesis_validator( - &mut self, - validator_address: Address, - validator_alias: Alias, - other: pre_genesis::ValidatorWallet, - ) { - self.store.extend_from_pre_genesis_validator( - validator_address, - validator_alias, - other, - ) +/// Add addresses from a genesis configuration. +pub fn add_genesis_addresses(wallet: &mut Wallet, genesis: GenesisConfig) { + for (alias, addr) in defaults::addresses_from_genesis(genesis) { + wallet.add_address::(alias.normalize(), addr); } } /// Save the wallet store to a file. pub fn save(wallet: &Wallet) -> std::io::Result<()> { - self::store::save(&wallet.store, &wallet.store_dir) + self::store::save(&wallet.store(), &wallet.store_dir()) } /// Load a wallet from the store file. @@ -447,12 +194,7 @@ pub fn load(store_dir: &Path) -> Option> { eprintln!("Unable to load the wallet: {}", err); cli::safe_exit(1) }); - Some(Wallet:: { - store_dir: store_dir.to_path_buf(), - store, - decrypted_key_cache: HashMap::default(), - decrypted_spendkey_cache: HashMap::default(), - }) + Some(Wallet::::new(store_dir.to_path_buf(), store)) } /// Load a wallet from the store file or create a new wallet without any @@ -462,12 +204,7 @@ pub fn load_or_new(store_dir: &Path) -> Wallet { eprintln!("Unable to load the wallet: {}", err); cli::safe_exit(1) }); - Wallet:: { - store_dir: store_dir.to_path_buf(), - store, - decrypted_key_cache: HashMap::default(), - decrypted_spendkey_cache: HashMap::default(), - } + Wallet::::new(store_dir.to_path_buf(), store) } /// Load a wallet from the store file or create a new one with the default @@ -481,76 +218,5 @@ pub fn load_or_new_from_genesis( eprintln!("Unable to load the wallet: {}", err); cli::safe_exit(1) }); - Wallet:: { - store_dir: store_dir.to_path_buf(), - store, - decrypted_key_cache: HashMap::default(), - decrypted_spendkey_cache: HashMap::default(), - } -} - -/// Prompt for pssword and confirm it if parameter is false -fn new_password_prompt(unsafe_dont_encrypt: bool) -> Option { - let password = if unsafe_dont_encrypt { - println!("Warning: The keypair will NOT be encrypted."); - None - } else { - Some(read_password("Enter your encryption password: ")) - }; - // Bis repetita for confirmation. - let pwd = if unsafe_dont_encrypt { - None - } else { - Some(read_password( - "To confirm, please enter the same encryption password once \ - more: ", - )) - }; - if pwd != password { - eprintln!("Your two inputs do not match!"); - cli::safe_exit(1) - } - password -} - -/// Read the password for encryption from the file/env/stdin with confirmation. -pub fn read_and_confirm_pwd(unsafe_dont_encrypt: bool) -> Option { - let password = if unsafe_dont_encrypt { - println!("Warning: The keypair will NOT be encrypted."); - None - } else { - Some(read_password("Enter your encryption password: ")) - }; - // Bis repetita for confirmation. - let to_confirm = if unsafe_dont_encrypt { - None - } else { - Some(read_password( - "To confirm, please enter the same encryption password once more: ", - )) - }; - if to_confirm != password { - eprintln!("Your two inputs do not match!"); - cli::safe_exit(1) - } - password -} - -/// Read the password for encryption/decryption from the file/env/stdin. Panics -/// if all options are empty/invalid. -pub fn read_password(prompt_msg: &str) -> String { - let pwd = match env::var("ANOMA_WALLET_PASSWORD_FILE") { - Ok(path) => fs::read_to_string(path) - .expect("Something went wrong reading the file"), - Err(_) => match env::var("ANOMA_WALLET_PASSWORD") { - Ok(password) => password, - Err(_) => rpassword::read_password_from_tty(Some(prompt_msg)) - .unwrap_or_default(), - }, - }; - if pwd.is_empty() { - eprintln!("Password cannot be empty"); - cli::safe_exit(1) - } - pwd + Wallet::::new(store_dir.to_path_buf(), store) } diff --git a/apps/src/lib/wallet/pre_genesis.rs b/apps/src/lib/wallet/pre_genesis.rs index d3c5fa14c5..1a6ec9bc25 100644 --- a/apps/src/lib/wallet/pre_genesis.rs +++ b/apps/src/lib/wallet/pre_genesis.rs @@ -6,6 +6,13 @@ use file_lock::{FileLock, FileOptions}; use namada::types::key::{common, SchemeType}; use serde::{Deserialize, Serialize}; use thiserror::Error; +use namada::ledger::wallet::pre_genesis::ValidatorWallet; +use namada::ledger::wallet::pre_genesis::ReadError; +use namada::ledger::wallet::pre_genesis::ValidatorStore; +use namada::ledger::wallet::gen_key_to_store; +use namada::ledger::wallet::WalletUtils; +use crate::wallet::store::gen_validator_keys; +use crate::wallet::CliWalletUtils; use crate::wallet; use crate::wallet::{store, StoredKeypair}; @@ -13,178 +20,109 @@ use crate::wallet::{store, StoredKeypair}; /// Validator pre-genesis wallet file name const VALIDATOR_FILE_NAME: &str = "wallet.toml"; -#[derive(Error, Debug)] -pub enum ReadError { - #[error("Failed decoding the wallet store: {0}")] - Decode(toml::de::Error), - #[error("Failed to read the wallet store from {0}: {1}")] - ReadWallet(String, String), - #[error("Failed to write the wallet store: {0}")] - StoreNewWallet(String), - #[error("Failed to decode a key: {0}")] - Decryption(wallet::keys::DecryptionError), -} - /// Get the path to the validator pre-genesis wallet store. pub fn validator_file_name(store_dir: impl AsRef) -> PathBuf { store_dir.as_ref().join(VALIDATOR_FILE_NAME) } -/// Validator pre-genesis wallet includes all the required keys for genesis -/// setup and a cache of decrypted keys. -pub struct ValidatorWallet { - /// The wallet store that can be written/read to/from TOML - pub store: ValidatorStore, - /// Cryptographic keypair for validator account key - pub account_key: common::SecretKey, - /// Cryptographic keypair for consensus key - pub consensus_key: common::SecretKey, - /// Cryptographic keypair for Tendermint node key - pub tendermint_node_key: common::SecretKey, -} - -/// Validator pre-genesis wallet store includes all the required keys for -/// genesis setup. -#[derive(Serialize, Deserialize, Debug)] -pub struct ValidatorStore { - /// Cryptographic keypair for validator account key - pub account_key: wallet::StoredKeypair, - /// Cryptographic keypair for consensus key - pub consensus_key: wallet::StoredKeypair, - /// Cryptographic keypair for Tendermint node key - pub tendermint_node_key: wallet::StoredKeypair, - /// Special validator keys - pub validator_keys: wallet::ValidatorKeys, +/// Generate a new [`ValidatorWallet`] with required pre-genesis keys and +/// store it as TOML at the given path. +pub fn gen_and_store( + scheme: SchemeType, + unsafe_dont_encrypt: bool, + store_dir: &Path, +) -> std::io::Result { + let validator = gen(scheme, unsafe_dont_encrypt); + let data = validator.store.encode(); + let wallet_path = validator_file_name(store_dir); + // Make sure the dir exists + let wallet_dir = wallet_path.parent().unwrap(); + fs::create_dir_all(wallet_dir)?; + // Write the file + let options = + FileOptions::new().create(true).write(true).truncate(true); + let mut filelock = + FileLock::lock(wallet_path.to_str().unwrap(), true, options)?; + filelock.file.write_all(&data)?; + Ok(validator) } -impl ValidatorWallet { - /// Generate a new [`ValidatorWallet`] with required pre-genesis keys and - /// store it as TOML at the given path. - pub fn gen_and_store( - scheme: SchemeType, - unsafe_dont_encrypt: bool, - store_dir: &Path, - ) -> std::io::Result { - let validator = Self::gen(scheme, unsafe_dont_encrypt); - let data = validator.store.encode(); - let wallet_path = validator_file_name(store_dir); - // Make sure the dir exists - let wallet_dir = wallet_path.parent().unwrap(); - fs::create_dir_all(wallet_dir)?; - // Write the file - let options = - FileOptions::new().create(true).write(true).truncate(true); - let mut filelock = - FileLock::lock(wallet_path.to_str().unwrap(), true, options)?; - filelock.file.write_all(&data)?; - Ok(validator) - } - - /// Try to load and decrypt keys, if encrypted, in a [`ValidatorWallet`] - /// from a TOML file. - pub fn load(store_dir: &Path) -> Result { - let wallet_file = validator_file_name(store_dir); - match FileLock::lock( - wallet_file.to_str().unwrap(), - true, - FileOptions::new().read(true).write(false), - ) { - Ok(mut filelock) => { - let mut store = Vec::::new(); - filelock.file.read_to_end(&mut store).map_err(|err| { - ReadError::ReadWallet( - store_dir.to_str().unwrap().into(), - err.to_string(), - ) - })?; - let store = - ValidatorStore::decode(store).map_err(ReadError::Decode)?; - - let password = if store.account_key.is_encrypted() - || store.consensus_key.is_encrypted() - || store.account_key.is_encrypted() - { - Some(wallet::read_password("Enter decryption password: ")) - } else { - None - }; - - let account_key = - store.account_key.get(true, password.clone())?; - let consensus_key = - store.consensus_key.get(true, password.clone())?; - let tendermint_node_key = - store.tendermint_node_key.get(true, password)?; - - Ok(Self { - store, - account_key, - consensus_key, - tendermint_node_key, - }) - } - Err(err) => Err(ReadError::ReadWallet( - wallet_file.to_string_lossy().into_owned(), - err.to_string(), - )), +/// Try to load and decrypt keys, if encrypted, in a [`ValidatorWallet`] +/// from a TOML file. +pub fn load(store_dir: &Path) -> Result { + let wallet_file = validator_file_name(store_dir); + match FileLock::lock( + wallet_file.to_str().unwrap(), + true, + FileOptions::new().read(true).write(false), + ) { + Ok(mut filelock) => { + let mut store = Vec::::new(); + filelock.file.read_to_end(&mut store).map_err(|err| { + ReadError::ReadWallet( + store_dir.to_str().unwrap().into(), + err.to_string(), + ) + })?; + let store = + ValidatorStore::decode(store).map_err(ReadError::Decode)?; + + let password = if store.account_key.is_encrypted() + || store.consensus_key.is_encrypted() + || store.account_key.is_encrypted() + { + Some(CliWalletUtils::read_password("Enter decryption password: ")) + } else { + None + }; + + let account_key = + store.account_key.get::(true, password.clone())?; + let consensus_key = + store.consensus_key.get::(true, password.clone())?; + let tendermint_node_key = + store.tendermint_node_key.get::(true, password)?; + + Ok(ValidatorWallet { + store, + account_key, + consensus_key, + tendermint_node_key, + }) } + Err(err) => Err(ReadError::ReadWallet( + wallet_file.to_string_lossy().into_owned(), + err.to_string(), + )), } - - /// Generate a new [`ValidatorWallet`] with required pre-genesis keys. Will - /// prompt for password when `!unsafe_dont_encrypt`. - fn gen(scheme: SchemeType, unsafe_dont_encrypt: bool) -> Self { - let password = wallet::read_and_confirm_pwd(unsafe_dont_encrypt); - let (account_key, account_sk) = gen_key_to_store(scheme, &password); - let (consensus_key, consensus_sk) = gen_key_to_store( - // Note that TM only allows ed25519 for consensus key - SchemeType::Ed25519, - &password, - ); - let (tendermint_node_key, tendermint_node_sk) = gen_key_to_store( - // Note that TM only allows ed25519 for node IDs - SchemeType::Ed25519, - &password, - ); - let validator_keys = store::Store::gen_validator_keys(None, scheme); - let store = ValidatorStore { - account_key, - consensus_key, - tendermint_node_key, - validator_keys, - }; - Self { - store, - account_key: account_sk, - consensus_key: consensus_sk, - tendermint_node_key: tendermint_node_sk, - } - } -} - -impl ValidatorStore { - /// Decode from TOML string bytes - pub fn decode(data: Vec) -> Result { - toml::from_slice(&data) - } - - /// Encode in TOML string bytes - pub fn encode(&self) -> Vec { - toml::to_vec(self).expect( - "Serializing of validator pre-genesis wallet shouldn't fail", - ) - } -} - -fn gen_key_to_store( - scheme: SchemeType, - password: &Option, -) -> (StoredKeypair, common::SecretKey) { - let sk = store::gen_sk(scheme); - StoredKeypair::new(sk, password.clone()) } -impl From for ReadError { - fn from(err: wallet::keys::DecryptionError) -> Self { - ReadError::Decryption(err) +/// Generate a new [`ValidatorWallet`] with required pre-genesis keys. Will +/// prompt for password when `!unsafe_dont_encrypt`. +fn gen(scheme: SchemeType, unsafe_dont_encrypt: bool) -> ValidatorWallet { + let password = CliWalletUtils::read_and_confirm_pwd(unsafe_dont_encrypt); + let (account_key, account_sk) = gen_key_to_store(scheme, &password); + let (consensus_key, consensus_sk) = gen_key_to_store( + // Note that TM only allows ed25519 for consensus key + SchemeType::Ed25519, + &password, + ); + let (tendermint_node_key, tendermint_node_sk) = gen_key_to_store( + // Note that TM only allows ed25519 for node IDs + SchemeType::Ed25519, + &password, + ); + let validator_keys = gen_validator_keys(None, scheme); + let store = ValidatorStore { + account_key, + consensus_key, + tendermint_node_key, + validator_keys, + }; + ValidatorWallet { + store, + account_key: account_sk, + consensus_key: consensus_sk, + tendermint_node_key: tendermint_node_sk, } } diff --git a/apps/src/lib/wallet/store.rs b/apps/src/lib/wallet/store.rs index 9193ff5ef8..4a4ac0dbcb 100644 --- a/apps/src/lib/wallet/store.rs +++ b/apps/src/lib/wallet/store.rs @@ -19,57 +19,14 @@ use namada::types::masp::{ use namada::types::transaction::EllipticCurve; use serde::{Deserialize, Serialize}; use thiserror::Error; +use namada::ledger::wallet::ConfirmationResponse; +use namada::ledger::wallet::Store; -use super::alias::{self, Alias}; -use super::keys::StoredKeypair; +use namada::ledger::wallet::{Alias, StoredKeypair, ValidatorKeys, gen_sk}; use super::pre_genesis; use crate::cli; use crate::config::genesis::genesis_config::GenesisConfig; - -/// Special keys for a validator -#[derive(Serialize, Deserialize, Debug)] -pub struct ValidatorKeys { - /// Special keypair for signing protocol txs - pub protocol_keypair: common::SecretKey, - /// Special session keypair needed by validators for participating - /// in the DKG protocol - pub dkg_keypair: Option, -} - -impl ValidatorKeys { - /// Get the protocol keypair - pub fn get_protocol_keypair(&self) -> &common::SecretKey { - &self.protocol_keypair - } -} - -/// Special data associated with a validator -#[derive(Serialize, Deserialize, Debug)] -pub struct ValidatorData { - /// The address associated to a validator - pub address: Address, - /// special keys for a validator - pub keys: ValidatorKeys, -} - -#[derive(Serialize, Deserialize, Debug, Default)] -pub struct Store { - /// Known viewing keys - view_keys: HashMap, - /// Known spending keys - spend_keys: HashMap>, - /// Known payment addresses - payment_addrs: HashMap, - /// Cryptographic keypairs - keys: HashMap>, - /// Anoma address book - addresses: BiHashMap, - /// Known mappings of public key hashes to their aliases in the `keys` - /// field. Used for look-up by a public key. - pkhs: HashMap, - /// Special keys if the wallet belongs to a validator - pub(crate) validator_data: Option, -} +use crate::wallet::CliWalletUtils; #[derive(Error, Debug)] pub enum LoadStoreError { @@ -81,583 +38,6 @@ pub enum LoadStoreError { StoreNewWallet(String), } -impl Store { - #[cfg(not(feature = "dev"))] - fn new(genesis: GenesisConfig) -> Self { - let mut store = Self::default(); - store.add_genesis_addresses(genesis); - store - } - - #[cfg(feature = "dev")] - fn new() -> Self { - let mut store = Self::default(); - // Pre-load the default keys without encryption - let no_password = None; - for (alias, keypair) in super::defaults::keys() { - let pkh: PublicKeyHash = (&keypair.ref_to()).into(); - store.keys.insert( - alias.clone(), - StoredKeypair::new(keypair, no_password.clone()).0, - ); - store.pkhs.insert(pkh, alias); - } - store - .addresses - .extend(super::defaults::addresses().into_iter()); - store - } - - /// Add addresses from a genesis configuration. - pub fn add_genesis_addresses(&mut self, genesis: GenesisConfig) { - self.addresses.extend( - super::defaults::addresses_from_genesis(genesis).into_iter(), - ); - } - - /// Find the stored key by an alias, a public key hash or a public key. - pub fn find_key( - &self, - alias_pkh_or_pk: impl AsRef, - ) -> Option<&StoredKeypair> { - let alias_pkh_or_pk = alias_pkh_or_pk.as_ref(); - // Try to find by alias - self.keys - .get(&alias_pkh_or_pk.into()) - // Try to find by PKH - .or_else(|| { - let pkh = PublicKeyHash::from_str(alias_pkh_or_pk).ok()?; - self.find_key_by_pkh(&pkh) - }) - // Try to find by PK - .or_else(|| { - let pk = common::PublicKey::from_str(alias_pkh_or_pk).ok()?; - self.find_key_by_pk(&pk) - }) - } - - pub fn find_spending_key( - &self, - alias: impl AsRef, - ) -> Option<&StoredKeypair> { - self.spend_keys.get(&alias.into()) - } - - pub fn find_viewing_key( - &self, - alias: impl AsRef, - ) -> Option<&ExtendedViewingKey> { - self.view_keys.get(&alias.into()) - } - - pub fn find_payment_addr( - &self, - alias: impl AsRef, - ) -> Option<&PaymentAddress> { - self.payment_addrs.get(&alias.into()) - } - - /// Find the stored key by a public key. - pub fn find_key_by_pk( - &self, - pk: &common::PublicKey, - ) -> Option<&StoredKeypair> { - let pkh = PublicKeyHash::from(pk); - self.find_key_by_pkh(&pkh) - } - - /// Find the stored key by a public key hash. - pub fn find_key_by_pkh( - &self, - pkh: &PublicKeyHash, - ) -> Option<&StoredKeypair> { - let alias = self.pkhs.get(pkh)?; - self.keys.get(alias) - } - - /// Find the stored alias for a public key hash. - pub fn find_alias_by_pkh(&self, pkh: &PublicKeyHash) -> Option { - self.pkhs.get(pkh).cloned() - } - - /// Find the stored address by an alias. - pub fn find_address(&self, alias: impl AsRef) -> Option<&Address> { - self.addresses.get_by_left(&alias.into()) - } - - /// Find an alias by the address if it's in the wallet. - pub fn find_alias(&self, address: &Address) -> Option<&Alias> { - self.addresses.get_by_right(address) - } - - /// Get all known keys by their alias, paired with PKH, if known. - pub fn get_keys( - &self, - ) -> HashMap< - Alias, - (&StoredKeypair, Option<&PublicKeyHash>), - > { - let mut keys: HashMap< - Alias, - (&StoredKeypair, Option<&PublicKeyHash>), - > = self - .pkhs - .iter() - .filter_map(|(pkh, alias)| { - let key = &self.keys.get(alias)?; - Some((alias.clone(), (*key, Some(pkh)))) - }) - .collect(); - self.keys.iter().for_each(|(alias, key)| { - if !keys.contains_key(alias) { - keys.insert(alias.clone(), (key, None)); - } - }); - keys - } - - /// Get all known addresses by their alias, paired with PKH, if known. - pub fn get_addresses(&self) -> &BiHashMap { - &self.addresses - } - - /// Get all known payment addresses by their alias. - pub fn get_payment_addrs(&self) -> &HashMap { - &self.payment_addrs - } - - /// Get all known viewing keys by their alias. - pub fn get_viewing_keys(&self) -> &HashMap { - &self.view_keys - } - - /// Get all known spending keys by their alias. - pub fn get_spending_keys( - &self, - ) -> &HashMap> { - &self.spend_keys - } - - fn generate_spending_key() -> ExtendedSpendingKey { - use rand::rngs::OsRng; - let mut spend_key = [0; 32]; - OsRng.fill_bytes(&mut spend_key); - masp_primitives::zip32::ExtendedSpendingKey::master(spend_key.as_ref()) - .into() - } - - /// Generate a new keypair and insert it into the store with the provided - /// alias. If none provided, the alias will be the public key hash. - /// If no password is provided, the keypair will be stored raw without - /// encryption. Returns the alias of the key and a reference-counting - /// pointer to the key. - pub fn gen_key( - &mut self, - scheme: SchemeType, - alias: Option, - password: Option, - ) -> (Alias, common::SecretKey) { - let sk = gen_sk(scheme); - let pkh: PublicKeyHash = PublicKeyHash::from(&sk.ref_to()); - let (keypair_to_store, raw_keypair) = StoredKeypair::new(sk, password); - let address = Address::Implicit(ImplicitAddress(pkh.clone())); - let alias: Alias = alias.unwrap_or_else(|| pkh.clone().into()).into(); - if self - .insert_keypair(alias.clone(), keypair_to_store, pkh) - .is_none() - { - eprintln!("Action cancelled, no changes persisted."); - cli::safe_exit(1); - } - if self.insert_address(alias.clone(), address).is_none() { - eprintln!("Action cancelled, no changes persisted."); - cli::safe_exit(1); - } - (alias, raw_keypair) - } - - /// Generate a spending key similarly to how it's done for keypairs - pub fn gen_spending_key( - &mut self, - alias: String, - password: Option, - ) -> (Alias, ExtendedSpendingKey) { - let spendkey = Self::generate_spending_key(); - let viewkey = ExtendedFullViewingKey::from(&spendkey.into()).into(); - let (spendkey_to_store, _raw_spendkey) = - StoredKeypair::new(spendkey, password); - let alias = Alias::from(alias); - if self - .insert_spending_key(alias.clone(), spendkey_to_store, viewkey) - .is_none() - { - eprintln!("Action cancelled, no changes persisted."); - cli::safe_exit(1); - } - (alias, spendkey) - } - - /// Generate keypair for signing protocol txs and for the DKG - /// A protocol keypair may be optionally provided - /// - /// Note that this removes the validator data. - pub fn gen_validator_keys( - protocol_keypair: Option, - scheme: SchemeType, - ) -> ValidatorKeys { - let protocol_keypair = - protocol_keypair.unwrap_or_else(|| gen_sk(scheme)); - let dkg_keypair = ferveo_common::Keypair::::new( - &mut StdRng::from_entropy(), - ); - ValidatorKeys { - protocol_keypair, - dkg_keypair: Some(dkg_keypair.into()), - } - } - - /// Add validator data to the store - pub fn add_validator_data( - &mut self, - address: Address, - keys: ValidatorKeys, - ) { - self.validator_data = Some(ValidatorData { address, keys }); - } - - /// Returns the validator data, if it exists - pub fn get_validator_data(&self) -> Option<&ValidatorData> { - self.validator_data.as_ref() - } - - /// Returns the validator data, if it exists - pub fn validator_data(self) -> Option { - self.validator_data - } - - /// Insert a new key with the given alias. If the alias is already used, - /// will prompt for overwrite/reselection confirmation. If declined, then - /// keypair is not inserted and nothing is returned, otherwise selected - /// alias is returned. - pub(super) fn insert_keypair( - &mut self, - alias: Alias, - keypair: StoredKeypair, - pkh: PublicKeyHash, - ) -> Option { - if alias.is_empty() { - println!( - "Empty alias given, defaulting to {}.", - Into::::into(pkh.to_string()) - ); - } - // Addresses and keypairs can share aliases, so first remove any - // addresses sharing the same namesake before checking if alias has been - // used. - let counterpart_address = self.addresses.remove_by_left(&alias); - if self.contains_alias(&alias) { - match show_overwrite_confirmation(&alias, "a key") { - ConfirmationResponse::Replace => {} - ConfirmationResponse::Reselect(new_alias) => { - // Restore the removed address in case the recursive prompt - // terminates with a cancellation - counterpart_address - .map(|x| self.addresses.insert(alias.clone(), x.1)); - return self.insert_keypair(new_alias, keypair, pkh); - } - ConfirmationResponse::Skip => { - // Restore the removed address since this insertion action - // has now been cancelled - counterpart_address - .map(|x| self.addresses.insert(alias.clone(), x.1)); - return None; - } - } - } - self.remove_alias(&alias); - self.keys.insert(alias.clone(), keypair); - self.pkhs.insert(pkh, alias.clone()); - // Since it is intended for the inserted keypair to share its namesake - // with the pre-existing address - counterpart_address.map(|x| self.addresses.insert(alias.clone(), x.1)); - Some(alias) - } - - /// Insert spending keys similarly to how it's done for keypairs - pub fn insert_spending_key( - &mut self, - alias: Alias, - spendkey: StoredKeypair, - viewkey: ExtendedViewingKey, - ) -> Option { - if alias.is_empty() { - eprintln!("Empty alias given."); - return None; - } - if self.contains_alias(&alias) { - match show_overwrite_confirmation(&alias, "a spending key") { - ConfirmationResponse::Replace => {} - ConfirmationResponse::Reselect(new_alias) => { - return self - .insert_spending_key(new_alias, spendkey, viewkey); - } - ConfirmationResponse::Skip => return None, - } - } - self.remove_alias(&alias); - self.spend_keys.insert(alias.clone(), spendkey); - // Simultaneously add the derived viewing key to ease balance viewing - self.view_keys.insert(alias.clone(), viewkey); - Some(alias) - } - - /// Insert viewing keys similarly to how it's done for keypairs - pub fn insert_viewing_key( - &mut self, - alias: Alias, - viewkey: ExtendedViewingKey, - ) -> Option { - if alias.is_empty() { - eprintln!("Empty alias given."); - return None; - } - if self.contains_alias(&alias) { - match show_overwrite_confirmation(&alias, "a viewing key") { - ConfirmationResponse::Replace => {} - ConfirmationResponse::Reselect(new_alias) => { - return self.insert_viewing_key(new_alias, viewkey); - } - ConfirmationResponse::Skip => return None, - } - } - self.remove_alias(&alias); - self.view_keys.insert(alias.clone(), viewkey); - Some(alias) - } - - /// Check if any map of the wallet contains the given alias - fn contains_alias(&self, alias: &Alias) -> bool { - self.payment_addrs.contains_key(alias) - || self.view_keys.contains_key(alias) - || self.spend_keys.contains_key(alias) - || self.keys.contains_key(alias) - || self.addresses.contains_left(alias) - } - - /// Completely remove the given alias from all maps in the wallet - fn remove_alias(&mut self, alias: &Alias) { - self.payment_addrs.remove(alias); - self.view_keys.remove(alias); - self.spend_keys.remove(alias); - self.keys.remove(alias); - self.addresses.remove_by_left(alias); - self.pkhs.retain(|_key, val| val != alias); - } - - /// Insert payment addresses similarly to how it's done for keypairs - pub fn insert_payment_addr( - &mut self, - alias: Alias, - payment_addr: PaymentAddress, - ) -> Option { - if alias.is_empty() { - eprintln!("Empty alias given."); - return None; - } - if self.contains_alias(&alias) { - match show_overwrite_confirmation(&alias, "a payment address") { - ConfirmationResponse::Replace => {} - ConfirmationResponse::Reselect(new_alias) => { - return self.insert_payment_addr(new_alias, payment_addr); - } - ConfirmationResponse::Skip => return None, - } - } - self.remove_alias(&alias); - self.payment_addrs.insert(alias.clone(), payment_addr); - Some(alias) - } - - /// Helper function to restore keypair given alias-keypair mapping and the - /// pkhs-alias mapping. - fn restore_keypair( - &mut self, - alias: Alias, - key: Option>, - pkh: Option, - ) { - key.map(|x| self.keys.insert(alias.clone(), x)); - pkh.map(|x| self.pkhs.insert(x, alias.clone())); - } - - /// Insert a new address with the given alias. If the alias is already used, - /// will prompt for overwrite/reselection confirmation, which when declined, - /// the address won't be added. Return the selected alias if the address has - /// been added. - pub fn insert_address( - &mut self, - alias: Alias, - address: Address, - ) -> Option { - if alias.is_empty() { - println!("Empty alias given, defaulting to {}.", address.encode()); - } - // Addresses and keypairs can share aliases, so first remove any keys - // sharing the same namesake before checking if alias has been used. - let counterpart_key = self.keys.remove(&alias); - let mut counterpart_pkh = None; - self.pkhs.retain(|k, v| { - if v == &alias { - counterpart_pkh = Some(k.clone()); - false - } else { - true - } - }); - if self.addresses.contains_left(&alias) { - match show_overwrite_confirmation(&alias, "an address") { - ConfirmationResponse::Replace => {} - ConfirmationResponse::Reselect(new_alias) => { - // Restore the removed keypair in case the recursive prompt - // terminates with a cancellation - self.restore_keypair( - alias, - counterpart_key, - counterpart_pkh, - ); - return self.insert_address(new_alias, address); - } - ConfirmationResponse::Skip => { - // Restore the removed keypair since this insertion action - // has now been cancelled - self.restore_keypair( - alias, - counterpart_key, - counterpart_pkh, - ); - return None; - } - } - } - self.remove_alias(&alias); - self.addresses.insert(alias.clone(), address); - // Since it is intended for the inserted address to share its namesake - // with the pre-existing keypair - self.restore_keypair(alias.clone(), counterpart_key, counterpart_pkh); - Some(alias) - } - - /// Extend this store from pre-genesis validator wallet. - pub fn extend_from_pre_genesis_validator( - &mut self, - validator_address: Address, - validator_alias: Alias, - other: pre_genesis::ValidatorWallet, - ) { - let account_key_alias = alias::validator_key(&validator_alias); - let consensus_key_alias = - alias::validator_consensus_key(&validator_alias); - let tendermint_node_key_alias = - alias::validator_tendermint_node_key(&validator_alias); - - let keys = [ - (account_key_alias.clone(), other.store.account_key), - (consensus_key_alias.clone(), other.store.consensus_key), - ( - tendermint_node_key_alias.clone(), - other.store.tendermint_node_key, - ), - ]; - self.keys.extend(keys.into_iter()); - - let account_pk = other.account_key.ref_to(); - let consensus_pk = other.consensus_key.ref_to(); - let tendermint_node_pk = other.tendermint_node_key.ref_to(); - let addresses = [ - (account_key_alias.clone(), (&account_pk).into()), - (consensus_key_alias.clone(), (&consensus_pk).into()), - ( - tendermint_node_key_alias.clone(), - (&tendermint_node_pk).into(), - ), - ]; - self.addresses.extend(addresses.into_iter()); - - let pkhs = [ - ((&account_pk).into(), account_key_alias), - ((&consensus_pk).into(), consensus_key_alias), - ((&tendermint_node_pk).into(), tendermint_node_key_alias), - ]; - self.pkhs.extend(pkhs.into_iter()); - - self.validator_data = Some(ValidatorData { - address: validator_address, - keys: other.store.validator_keys, - }); - } - - fn decode(data: Vec) -> Result { - toml::from_slice(&data) - } - - fn encode(&self) -> Vec { - toml::to_vec(self).expect("Serializing of store shouldn't fail") - } -} - -enum ConfirmationResponse { - Replace, - Reselect(Alias), - Skip, -} - -/// The given alias has been selected but conflicts with another alias in -/// the store. Offer the user to either replace existing mapping, alter the -/// chosen alias to a name of their chosing, or cancel the aliasing. - -fn show_overwrite_confirmation( - alias: &Alias, - alias_for: &str, -) -> ConfirmationResponse { - print!( - "You're trying to create an alias \"{}\" that already exists for {} \ - in your store.\nWould you like to replace it? \ - s(k)ip/re(p)lace/re(s)elect: ", - alias, alias_for - ); - io::stdout().flush().unwrap(); - - let mut buffer = String::new(); - // Get the user to select between 3 choices - match io::stdin().read_line(&mut buffer) { - Ok(size) if size > 0 => { - // Isolate the single character representing the choice - let byte = buffer.chars().next().unwrap(); - buffer.clear(); - match byte { - 'p' | 'P' => return ConfirmationResponse::Replace, - 's' | 'S' => { - // In the case of reselection, elicit new alias - print!("Please enter a different alias: "); - io::stdout().flush().unwrap(); - if io::stdin().read_line(&mut buffer).is_ok() { - return ConfirmationResponse::Reselect( - buffer.trim().into(), - ); - } - } - 'k' | 'K' => return ConfirmationResponse::Skip, - // Input is senseless fall through to repeat prompt - _ => {} - }; - } - _ => {} - } - // Input is senseless fall through to repeat prompt - println!("Invalid option, try again."); - show_overwrite_confirmation(alias, alias_for) -} - /// Wallet file name const FILE_NAME: &str = "wallet.toml"; @@ -666,23 +46,6 @@ pub fn wallet_file(store_dir: impl AsRef) -> PathBuf { store_dir.as_ref().join(FILE_NAME) } -/// Generate a new secret key. -pub fn gen_sk(scheme: SchemeType) -> common::SecretKey { - use rand::rngs::OsRng; - let mut csprng = OsRng {}; - match scheme { - SchemeType::Ed25519 => ed25519::SigScheme::generate(&mut csprng) - .try_to_sk() - .unwrap(), - SchemeType::Secp256k1 => secp256k1::SigScheme::generate(&mut csprng) - .try_to_sk() - .unwrap(), - SchemeType::Common => common::SigScheme::generate(&mut csprng) - .try_to_sk() - .unwrap(), - } -} - /// Save the wallet store to a file. pub fn save(store: &Store, store_dir: &Path) -> std::io::Result<()> { let data = store.encode(); @@ -717,12 +80,12 @@ pub fn load_or_new_from_genesis( ) -> Result { load(store_dir).or_else(|_| { #[cfg(not(feature = "dev"))] - let store = Store::new(genesis_cfg); + let store = new(genesis_cfg); #[cfg(feature = "dev")] let store = { // The function is unused in dev let _ = genesis_cfg; - Store::new() + new() }; save(&store, store_dir).map_err(|err| { LoadStoreError::StoreNewWallet(err.to_string()) @@ -756,15 +119,67 @@ pub fn load(store_dir: &Path) -> Result { } } +/// Add addresses from a genesis configuration. +pub fn add_genesis_addresses(store: &mut Store, genesis: GenesisConfig) { + for (alias, addr) in super::defaults::addresses_from_genesis(genesis) { + store.insert_address::(alias, addr); + } +} + +#[cfg(not(feature = "dev"))] +fn new(genesis: GenesisConfig) -> Store { + let mut store = Store::default(); + add_genesis_addresses(&mut store, genesis); + store +} + +#[cfg(feature = "dev")] +fn new() -> Store { + let mut store = Store::default(); + // Pre-load the default keys without encryption + let no_password = None; + for (alias, keypair) in super::defaults::keys() { + let pkh: PublicKeyHash = (&keypair.ref_to()).into(); + store.insert_keypair::( + alias, + StoredKeypair::new(keypair, no_password.clone()).0, + pkh, + ); + } + for (alias, addr) in super::defaults::addresses() { + store.insert_address::(alias, addr); + } + store +} + +/// Generate keypair for signing protocol txs and for the DKG +/// A protocol keypair may be optionally provided +/// +/// Note that this removes the validator data. +pub fn gen_validator_keys( + protocol_keypair: Option, + scheme: SchemeType, +) -> ValidatorKeys { + let protocol_keypair = + protocol_keypair.unwrap_or_else(|| gen_sk(scheme)); + let dkg_keypair = ferveo_common::Keypair::::new( + &mut StdRng::from_entropy(), + ); + ValidatorKeys { + protocol_keypair, + dkg_keypair: Some(dkg_keypair.into()), + } +} + #[cfg(all(test, feature = "dev"))] mod test_wallet { use super::*; #[test] fn test_toml_roundtrip_ed25519() { - let mut store = Store::new(); + let mut store = new(); let validator_keys = - Store::gen_validator_keys(None, SchemeType::Ed25519); + gen_validator_keys(None, SchemeType::Ed25519); store.add_validator_data( Address::decode("atest1v4ehgw36x3prswzxggunzv6pxqmnvdj9xvcyzvpsggeyvs3cg9qnywf589qnwvfsg5erg3fkl09rg5").unwrap(), validator_keys @@ -775,9 +190,9 @@ mod test_wallet { #[test] fn test_toml_roundtrip_secp256k1() { - let mut store = Store::new(); + let mut store = new(); let validator_keys = - Store::gen_validator_keys(None, SchemeType::Secp256k1); + gen_validator_keys(None, SchemeType::Secp256k1); store.add_validator_data( Address::decode("atest1v4ehgw36x3prswzxggunzv6pxqmnvdj9xvcyzvpsggeyvs3cg9qnywf589qnwvfsg5erg3fkl09rg5").unwrap(), validator_keys diff --git a/shared/Cargo.toml b/shared/Cargo.toml index 3112c314e6..00676aacf6 100644 --- a/shared/Cargo.toml +++ b/shared/Cargo.toml @@ -107,6 +107,7 @@ prost = "0.9.0" pwasm-utils = {version = "0.18.0", optional = true} rayon = {version = "=1.5.3", optional = true} rust_decimal = "1.26.1" +serde = {version = "1.0.125", features = ["derive"]} serde_json = "1.0.62" sha2 = "0.9.3" # We switch off "blake2b" because it cannot be compiled to wasm @@ -132,6 +133,9 @@ masp_proofs = { git = "https://github.com/anoma/masp", rev = "bee40fc465f6afbd10 rand = {version = "0.8", default-features = false, optional = true} rand_core = {version = "0.6", default-features = false, optional = true} zeroize = "1.5.5" +toml = "0.5.8" +bimap = {version = "0.6.2", features = ["serde"]} +orion = "0.16.0" [dev-dependencies] assert_matches = "1.5.0" diff --git a/shared/src/ledger/mod.rs b/shared/src/ledger/mod.rs index 73f39dda05..31c72a04c6 100644 --- a/shared/src/ledger/mod.rs +++ b/shared/src/ledger/mod.rs @@ -11,6 +11,7 @@ pub mod protocol; pub mod queries; pub mod storage; pub mod vp_host_fns; +pub mod wallet; pub use namada_core::ledger::{ gas, governance, parameters, storage_api, tx_env, vp_env, diff --git a/shared/src/ledger/wallet/alias.rs b/shared/src/ledger/wallet/alias.rs new file mode 100644 index 0000000000..13d977b852 --- /dev/null +++ b/shared/src/ledger/wallet/alias.rs @@ -0,0 +1,103 @@ +//! Wallet address and key aliases. + +use std::convert::Infallible; +use std::fmt::Display; +use std::hash::Hash; +use std::str::FromStr; + +use serde::{Deserialize, Serialize}; + +/// Aliases created from raw strings are kept in-memory as given, but their +/// `Serialize` and `Display` instance converts them to lowercase. Their +/// `PartialEq` instance is case-insensitive. +#[derive(Clone, Debug, Default, Deserialize, PartialOrd, Ord, Eq)] +#[serde(transparent)] +pub struct Alias(String); + +impl Alias { + /// Normalize an alias to lower-case + pub fn normalize(&self) -> String { + self.0.to_lowercase() + } + + /// Returns the length of the underlying `String`. + pub fn len(&self) -> usize { + self.0.len() + } + + /// Is the underlying `String` empty? + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } +} + +impl Serialize for Alias { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + self.normalize().serialize(serializer) + } +} + +impl PartialEq for Alias { + fn eq(&self, other: &Self) -> bool { + self.normalize() == other.normalize() + } +} + +impl Hash for Alias { + fn hash(&self, state: &mut H) { + self.normalize().hash(state); + } +} + +impl From for Alias +where + T: AsRef, +{ + fn from(raw: T) -> Self { + Self(raw.as_ref().to_owned()) + } +} + +impl From for String { + fn from(alias: Alias) -> Self { + alias.normalize() + } +} + +impl<'a> From<&'a Alias> for String { + fn from(alias: &'a Alias) -> Self { + alias.normalize() + } +} + +impl Display for Alias { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.normalize().fmt(f) + } +} + +impl FromStr for Alias { + type Err = Infallible; + + fn from_str(s: &str) -> Result { + Ok(Self(s.into())) + } +} + +/// Default alias of a validator's account key +pub fn validator_key(validator_alias: &Alias) -> Alias { + format!("{validator_alias}-validator-key").into() +} + +/// Default alias of a validator's consensus key +pub fn validator_consensus_key(validator_alias: &Alias) -> Alias { + format!("{validator_alias}-consensus-key").into() +} + +/// Default alias of a validator's Tendermint node key +pub fn validator_tendermint_node_key(validator_alias: &Alias) -> Alias { + format!("{validator_alias}-tendermint-node-key").into() +} diff --git a/shared/src/ledger/wallet/keys.rs b/shared/src/ledger/wallet/keys.rs new file mode 100644 index 0000000000..71e2b59865 --- /dev/null +++ b/shared/src/ledger/wallet/keys.rs @@ -0,0 +1,242 @@ +//! Cryptographic keys for digital signatures support for the wallet. + +use std::fmt::Display; +use std::marker::PhantomData; +use std::str::FromStr; + +use borsh::{BorshDeserialize, BorshSerialize}; +use data_encoding::HEXLOWER; +use orion::{aead, kdf}; +use serde::{Deserialize, Serialize}; +use thiserror::Error; +use crate::ledger::wallet::WalletUtils; + +const ENCRYPTED_KEY_PREFIX: &str = "encrypted:"; +const UNENCRYPTED_KEY_PREFIX: &str = "unencrypted:"; + +/// A keypair stored in a wallet +#[derive(Debug)] +pub enum StoredKeypair +where + ::Err: Display, +{ + /// An encrypted keypair + Encrypted(EncryptedKeypair), + /// An raw (unencrypted) keypair + Raw(T), +} + +impl Serialize + for StoredKeypair +where + ::Err: Display, +{ + fn serialize( + &self, + serializer: S, + ) -> std::result::Result + where + S: serde::Serializer, + { + // String encoded, because toml doesn't support enums + match self { + StoredKeypair::Encrypted(encrypted) => { + let keypair_string = + format!("{}{}", ENCRYPTED_KEY_PREFIX, encrypted); + serde::Serialize::serialize(&keypair_string, serializer) + } + StoredKeypair::Raw(raw) => { + let keypair_string = + format!("{}{}", UNENCRYPTED_KEY_PREFIX, raw); + serde::Serialize::serialize(&keypair_string, serializer) + } + } + } +} + +impl<'de, T: BorshSerialize + BorshDeserialize + Display + FromStr> + Deserialize<'de> for StoredKeypair +where + ::Err: Display, +{ + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + use serde::de::Error; + + let keypair_string: String = + serde::Deserialize::deserialize(deserializer) + .map_err(|err| { + DeserializeStoredKeypairError::InvalidStoredKeypairString( + err.to_string(), + ) + }) + .map_err(D::Error::custom)?; + if let Some(raw) = keypair_string.strip_prefix(UNENCRYPTED_KEY_PREFIX) { + FromStr::from_str(raw) + .map(|keypair| Self::Raw(keypair)) + .map_err(|err| { + DeserializeStoredKeypairError::InvalidStoredKeypairString( + err.to_string(), + ) + }) + .map_err(D::Error::custom) + } else if let Some(encrypted) = + keypair_string.strip_prefix(ENCRYPTED_KEY_PREFIX) + { + FromStr::from_str(encrypted) + .map(Self::Encrypted) + .map_err(|err| { + DeserializeStoredKeypairError::InvalidStoredKeypairString( + err.to_string(), + ) + }) + .map_err(D::Error::custom) + } else { + Err(DeserializeStoredKeypairError::MissingPrefix) + .map_err(D::Error::custom) + } + } +} + +#[allow(missing_docs)] +#[derive(Error, Debug)] +pub enum DeserializeStoredKeypairError { + #[error("The stored keypair is not valid: {0}")] + InvalidStoredKeypairString(String), + #[error("The stored keypair is missing a prefix")] + MissingPrefix, +} + +/// An encrypted keypair stored in a wallet +#[derive(Debug)] +pub struct EncryptedKeypair( + Vec, + PhantomData, +); + +impl Display for EncryptedKeypair { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", HEXLOWER.encode(self.0.as_ref())) + } +} + +impl FromStr for EncryptedKeypair { + type Err = data_encoding::DecodeError; + + fn from_str(s: &str) -> Result { + HEXLOWER.decode(s.as_ref()).map(|x| Self(x, PhantomData)) + } +} + +#[allow(missing_docs)] +#[derive(Debug, Error)] +pub enum DecryptionError { + #[error("Unexpected encryption salt")] + BadSalt, + #[error("Unable to decrypt the keypair. Is the password correct?")] + DecryptionError, + #[error("Unable to deserialize the keypair")] + DeserializingError, + #[error("Asked not to decrypt")] + NotDecrypting, +} + +impl + StoredKeypair +where + ::Err: Display, +{ + /// Construct a keypair for storage. If no password is provided, the keypair + /// will be stored raw without encryption. Returns the key for storing and a + /// reference-counting point to the raw key. + pub fn new(keypair: T, password: Option) -> (Self, T) { + match password { + Some(password) => ( + Self::Encrypted(EncryptedKeypair::new(&keypair, password)), + keypair, + ), + None => (Self::Raw(keypair.clone()), keypair), + } + } + + /// Get a raw keypair from a stored keypair. If the keypair is encrypted and + /// no password is provided in the argument, a password will be prompted + /// from stdin. + pub fn get( + &self, + decrypt: bool, + password: Option, + ) -> Result { + match self { + StoredKeypair::Encrypted(encrypted_keypair) => { + if decrypt { + let password = password.unwrap_or_else(|| { + U::read_password("Enter decryption password: ") + }); + let key = encrypted_keypair.decrypt(password)?; + Ok(key) + } else { + Err(DecryptionError::NotDecrypting) + } + } + StoredKeypair::Raw(keypair) => Ok(keypair.clone()), + } + } + + pub fn is_encrypted(&self) -> bool { + match self { + StoredKeypair::Encrypted(_) => true, + StoredKeypair::Raw(_) => false, + } + } +} + +impl EncryptedKeypair { + /// Encrypt a keypair and store it with its salt. + pub fn new(keypair: &T, password: String) -> Self { + let salt = encryption_salt(); + let encryption_key = encryption_key(&salt, password); + + let data = keypair + .try_to_vec() + .expect("Serializing keypair shouldn't fail"); + + let encrypted_keypair = aead::seal(&encryption_key, &data) + .expect("Encryption of data shouldn't fail"); + + let encrypted_data = [salt.as_ref(), &encrypted_keypair].concat(); + + Self(encrypted_data, PhantomData) + } + + /// Decrypt an encrypted keypair + pub fn decrypt(&self, password: String) -> Result { + let salt_len = encryption_salt().len(); + let (raw_salt, cipher) = self.0.split_at(salt_len); + + let salt = kdf::Salt::from_slice(raw_salt) + .map_err(|_| DecryptionError::BadSalt)?; + + let encryption_key = encryption_key(&salt, password); + + let decrypted_data = aead::open(&encryption_key, cipher) + .map_err(|_| DecryptionError::DecryptionError)?; + + T::try_from_slice(&decrypted_data) + .map_err(|_| DecryptionError::DeserializingError) + } +} + +/// Keypair encryption salt +fn encryption_salt() -> kdf::Salt { + kdf::Salt::default() +} + +/// Make encryption secret key from a password. +fn encryption_key(salt: &kdf::Salt, password: String) -> kdf::SecretKey { + kdf::Password::from_slice(password.as_bytes()) + .and_then(|password| kdf::derive_key(&password, salt, 3, 1 << 17, 32)) + .expect("Generation of encryption secret key shouldn't fail") +} diff --git a/shared/src/ledger/wallet/mod.rs b/shared/src/ledger/wallet/mod.rs new file mode 100644 index 0000000000..0ad78b8b2c --- /dev/null +++ b/shared/src/ledger/wallet/mod.rs @@ -0,0 +1,446 @@ +mod store; +mod alias; +mod keys; +pub mod pre_genesis; + +use std::collections::HashMap; +use std::fmt::Display; +use std::path::{Path, PathBuf}; +use std::str::FromStr; +use std::{env, fs}; +pub use self::store::{ValidatorData, ValidatorKeys}; +use std::marker::PhantomData; + +use borsh::{BorshDeserialize, BorshSerialize}; +use masp_primitives::zip32::ExtendedFullViewingKey; +use crate::types::address::Address; +use crate::types::key::*; +use crate::types::masp::{ + ExtendedSpendingKey, ExtendedViewingKey, PaymentAddress, +}; +use thiserror::Error; + +pub use self::keys::{DecryptionError, StoredKeypair}; +pub use self::store::ConfirmationResponse; + +pub use store::{Store, gen_sk}; +pub use alias::Alias; +pub use pre_genesis::gen_key_to_store; + +/// Captures the interactive parts of the wallet's functioning +pub trait WalletUtils { + /// Read the password for encryption from the file/env/stdin with confirmation. + fn read_and_confirm_pwd(unsafe_dont_encrypt: bool) -> Option; + + /// Read the password for encryption/decryption from the file/env/stdin. Panics + /// if all options are empty/invalid. + fn read_password(prompt_msg: &str) -> String; + + /// The given alias has been selected but conflicts with another alias in + /// the store. Offer the user to either replace existing mapping, alter the + /// chosen alias to a name of their chosing, or cancel the aliasing. + fn show_overwrite_confirmation( + alias: &Alias, + alias_for: &str, + ) -> store::ConfirmationResponse; + + /// Prompt for pssword and confirm it if parameter is false + fn new_password_prompt(unsafe_dont_encrypt: bool) -> Option; +} + +#[derive(Error, Debug)] +pub enum FindKeyError { + #[error("No matching key found")] + KeyNotFound, + #[error("{0}")] + KeyDecryptionError(keys::DecryptionError), +} + +#[derive(Debug)] +pub struct Wallet { + store_dir: C, + store: Store, + decrypted_key_cache: HashMap, + decrypted_spendkey_cache: HashMap, +} + +impl Wallet { + pub fn new(store_dir: C, store: Store) -> Self { + Self { + store_dir, + store, + decrypted_key_cache: HashMap::default(), + decrypted_spendkey_cache: HashMap::default(), + } + } + + /// Generate a new keypair and derive an implicit address from its public + /// and insert them into the store with the provided alias, converted to + /// lower case. If none provided, the alias will be the public key hash (in + /// lowercase too). If the key is to be encrypted, will prompt for + /// password from stdin. Stores the key in decrypted key cache and + /// returns the alias of the key and a reference-counting pointer to the + /// key. + pub fn gen_key( + &mut self, + scheme: SchemeType, + alias: Option, + unsafe_dont_encrypt: bool, + ) -> (String, common::SecretKey) { + let password = U::read_and_confirm_pwd(unsafe_dont_encrypt); + let (alias, key) = self.store.gen_key::(scheme, alias, password); + // Cache the newly added key + self.decrypted_key_cache.insert(alias.clone(), key.clone()); + (alias.into(), key) + } + + pub fn gen_spending_key( + &mut self, + alias: String, + unsafe_dont_encrypt: bool, + ) -> (String, ExtendedSpendingKey) { + let password = U::new_password_prompt(unsafe_dont_encrypt); + let (alias, key) = self.store.gen_spending_key::(alias, password); + // Cache the newly added key + self.decrypted_spendkey_cache.insert(alias.clone(), key); + (alias.into(), key) + } + + /// Add validator data to the store + pub fn add_validator_data( + &mut self, + address: Address, + keys: ValidatorKeys, + ) { + self.store.add_validator_data(address, keys); + } + + /// Returns the validator data, if it exists. + pub fn get_validator_data(&self) -> Option<&ValidatorData> { + self.store.get_validator_data() + } + + /// Returns the validator data, if it exists. + /// [`Wallet::save`] cannot be called after using this + /// method as it involves a partial move + pub fn take_validator_data(&mut self) -> Option<&mut ValidatorData> { + self.store.validator_data() + } + + /// Find the stored key by an alias, a public key hash or a public key. + /// If the key is encrypted, will prompt for password from stdin. + /// Any keys that are decrypted are stored in and read from a cache to avoid + /// prompting for password multiple times. + pub fn find_key( + &mut self, + alias_pkh_or_pk: impl AsRef, + ) -> Result { + // Try cache first + if let Some(cached_key) = self + .decrypted_key_cache + .get(&alias_pkh_or_pk.as_ref().into()) + { + return Ok(cached_key.clone()); + } + // If not cached, look-up in store + let stored_key = self + .store + .find_key(alias_pkh_or_pk.as_ref()) + .ok_or(FindKeyError::KeyNotFound)?; + Self::decrypt_stored_key::<_, U>( + &mut self.decrypted_key_cache, + stored_key, + alias_pkh_or_pk.into(), + ) + } + + pub fn find_spending_key( + &mut self, + alias: impl AsRef, + ) -> Result { + // Try cache first + if let Some(cached_key) = + self.decrypted_spendkey_cache.get(&alias.as_ref().into()) + { + return Ok(*cached_key); + } + // If not cached, look-up in store + let stored_spendkey = self + .store + .find_spending_key(alias.as_ref()) + .ok_or(FindKeyError::KeyNotFound)?; + Self::decrypt_stored_key::<_, U>( + &mut self.decrypted_spendkey_cache, + stored_spendkey, + alias.into(), + ) + } + + pub fn find_viewing_key( + &mut self, + alias: impl AsRef, + ) -> Result<&ExtendedViewingKey, FindKeyError> { + self.store + .find_viewing_key(alias.as_ref()) + .ok_or(FindKeyError::KeyNotFound) + } + + pub fn find_payment_addr( + &self, + alias: impl AsRef, + ) -> Option<&PaymentAddress> { + self.store.find_payment_addr(alias.as_ref()) + } + + /// Find the stored key by a public key. + /// If the key is encrypted, will prompt for password from stdin. + /// Any keys that are decrypted are stored in and read from a cache to avoid + /// prompting for password multiple times. + pub fn find_key_by_pk( + &mut self, + pk: &common::PublicKey, + ) -> Result { + // Try to look-up alias for the given pk. Otherwise, use the PKH string. + let pkh: PublicKeyHash = pk.into(); + let alias = self + .store + .find_alias_by_pkh(&pkh) + .unwrap_or_else(|| pkh.to_string().into()); + // Try read cache + if let Some(cached_key) = self.decrypted_key_cache.get(&alias) { + return Ok(cached_key.clone()); + } + // Look-up from store + let stored_key = self + .store + .find_key_by_pk(pk) + .ok_or(FindKeyError::KeyNotFound)?; + Self::decrypt_stored_key::<_, U>( + &mut self.decrypted_key_cache, + stored_key, + alias, + ) + } + + /// Find the stored key by a public key hash. + /// If the key is encrypted, will prompt for password from stdin. + /// Any keys that are decrypted are stored in and read from a cache to avoid + /// prompting for password multiple times. + pub fn find_key_by_pkh( + &mut self, + pkh: &PublicKeyHash, + ) -> Result { + // Try to look-up alias for the given pk. Otherwise, use the PKH string. + let alias = self + .store + .find_alias_by_pkh(pkh) + .unwrap_or_else(|| pkh.to_string().into()); + // Try read cache + if let Some(cached_key) = self.decrypted_key_cache.get(&alias) { + return Ok(cached_key.clone()); + } + // Look-up from store + let stored_key = self + .store + .find_key_by_pkh(pkh) + .ok_or(FindKeyError::KeyNotFound)?; + Self::decrypt_stored_key::<_, U>( + &mut self.decrypted_key_cache, + stored_key, + alias, + ) + } + + /// Decrypt stored key, if it's not stored un-encrypted. + /// If a given storage key needs to be decrypted, prompt for password from + /// stdin and if successfully decrypted, store it in a cache. + fn decrypt_stored_key< + T: FromStr + Display + BorshSerialize + BorshDeserialize + Clone, + U: WalletUtils, + >( + decrypted_key_cache: &mut HashMap, + stored_key: &StoredKeypair, + alias: Alias, + ) -> Result + where + ::Err: Display, + { + match stored_key { + StoredKeypair::Encrypted(encrypted) => { + let password = U::read_password("Enter decryption password: "); + let key = encrypted + .decrypt(password) + .map_err(FindKeyError::KeyDecryptionError)?; + decrypted_key_cache.insert(alias.clone(), key); + decrypted_key_cache + .get(&alias) + .cloned() + .ok_or(FindKeyError::KeyNotFound) + } + StoredKeypair::Raw(raw) => Ok(raw.clone()), + } + } + + /// Get all known keys by their alias, paired with PKH, if known. + pub fn get_keys( + &self, + ) -> HashMap< + String, + (&StoredKeypair, Option<&PublicKeyHash>), + > { + self.store + .get_keys() + .into_iter() + .map(|(alias, value)| (alias.into(), value)) + .collect() + } + + /// Find the stored address by an alias. + pub fn find_address(&self, alias: impl AsRef) -> Option<&Address> { + self.store.find_address(alias) + } + + /// Find an alias by the address if it's in the wallet. + pub fn find_alias(&self, address: &Address) -> Option<&Alias> { + self.store.find_alias(address) + } + + /// Get all known addresses by their alias, paired with PKH, if known. + pub fn get_addresses(&self) -> HashMap { + self.store + .get_addresses() + .iter() + .map(|(alias, value)| (alias.into(), value.clone())) + .collect() + } + + /// Get all known payment addresses by their alias + pub fn get_payment_addrs(&self) -> HashMap { + self.store + .get_payment_addrs() + .iter() + .map(|(alias, value)| (alias.into(), *value)) + .collect() + } + + /// Get all known viewing keys by their alias + pub fn get_viewing_keys(&self) -> HashMap { + self.store + .get_viewing_keys() + .iter() + .map(|(alias, value)| (alias.into(), *value)) + .collect() + } + + /// Get all known viewing keys by their alias + pub fn get_spending_keys( + &self, + ) -> HashMap> { + self.store + .get_spending_keys() + .iter() + .map(|(alias, value)| (alias.into(), value)) + .collect() + } + + /// Add a new address with the given alias. If the alias is already used, + /// will ask whether the existing alias should be replaced, a different + /// alias is desired, or the alias creation should be cancelled. Return + /// the chosen alias if the address has been added, otherwise return + /// nothing. + pub fn add_address( + &mut self, + alias: impl AsRef, + address: Address, + ) -> Option { + self.store + .insert_address::(alias.into(), address) + .map(Into::into) + } + + /// Insert a new key with the given alias. If the alias is already used, + /// will prompt for overwrite confirmation. + pub fn insert_keypair( + &mut self, + alias: String, + keypair: StoredKeypair, + pkh: PublicKeyHash, + ) -> Option { + self.store + .insert_keypair::(alias.into(), keypair, pkh) + .map(Into::into) + } + + pub fn insert_viewing_key( + &mut self, + alias: String, + view_key: ExtendedViewingKey, + ) -> Option { + self.store + .insert_viewing_key::(alias.into(), view_key) + .map(Into::into) + } + + pub fn insert_spending_key( + &mut self, + alias: String, + spend_key: StoredKeypair, + viewkey: ExtendedViewingKey, + ) -> Option { + self.store + .insert_spending_key::(alias.into(), spend_key, viewkey) + .map(Into::into) + } + + pub fn encrypt_insert_spending_key( + &mut self, + alias: String, + spend_key: ExtendedSpendingKey, + unsafe_dont_encrypt: bool, + ) -> Option { + let password = U::new_password_prompt(unsafe_dont_encrypt); + self.store + .insert_spending_key::( + alias.into(), + StoredKeypair::new(spend_key, password).0, + ExtendedFullViewingKey::from(&spend_key.into()).into(), + ) + .map(Into::into) + } + + pub fn insert_payment_addr( + &mut self, + alias: String, + payment_addr: PaymentAddress, + ) -> Option { + self.store + .insert_payment_addr::(alias.into(), payment_addr) + .map(Into::into) + } + + /// Extend this wallet from pre-genesis validator wallet. + pub fn extend_from_pre_genesis_validator( + &mut self, + validator_address: Address, + validator_alias: Alias, + other: pre_genesis::ValidatorWallet, + ) { + self.store.extend_from_pre_genesis_validator( + validator_address, + validator_alias, + other, + ) + } + + pub fn store(&self) -> &Store { + &self.store + } + + pub fn store_mut(&mut self) -> &mut Store { + &mut self.store + } + + pub fn store_dir(&self) -> &C { + &self.store_dir + } +} diff --git a/shared/src/ledger/wallet/pre_genesis.rs b/shared/src/ledger/wallet/pre_genesis.rs new file mode 100644 index 0000000000..8dc2cca3e3 --- /dev/null +++ b/shared/src/ledger/wallet/pre_genesis.rs @@ -0,0 +1,73 @@ +use thiserror::Error; +use crate::types::key::{common, SchemeType}; +use serde::{Deserialize, Serialize}; +use crate::ledger::wallet; +use crate::ledger::wallet::{store, StoredKeypair}; + +#[derive(Error, Debug)] +pub enum ReadError { + #[error("Failed decoding the wallet store: {0}")] + Decode(toml::de::Error), + #[error("Failed to read the wallet store from {0}: {1}")] + ReadWallet(String, String), + #[error("Failed to write the wallet store: {0}")] + StoreNewWallet(String), + #[error("Failed to decode a key: {0}")] + Decryption(wallet::keys::DecryptionError), +} + +/// Validator pre-genesis wallet includes all the required keys for genesis +/// setup and a cache of decrypted keys. +pub struct ValidatorWallet { + /// The wallet store that can be written/read to/from TOML + pub store: ValidatorStore, + /// Cryptographic keypair for validator account key + pub account_key: common::SecretKey, + /// Cryptographic keypair for consensus key + pub consensus_key: common::SecretKey, + /// Cryptographic keypair for Tendermint node key + pub tendermint_node_key: common::SecretKey, +} + +/// Validator pre-genesis wallet store includes all the required keys for +/// genesis setup. +#[derive(Serialize, Deserialize, Debug)] +pub struct ValidatorStore { + /// Cryptographic keypair for validator account key + pub account_key: wallet::StoredKeypair, + /// Cryptographic keypair for consensus key + pub consensus_key: wallet::StoredKeypair, + /// Cryptographic keypair for Tendermint node key + pub tendermint_node_key: wallet::StoredKeypair, + /// Special validator keys + pub validator_keys: wallet::ValidatorKeys, +} + +impl ValidatorStore { + /// Decode from TOML string bytes + pub fn decode(data: Vec) -> Result { + toml::from_slice(&data) + } + + /// Encode in TOML string bytes + pub fn encode(&self) -> Vec { + toml::to_vec(self).expect( + "Serializing of validator pre-genesis wallet shouldn't fail", + ) + } +} + +pub fn gen_key_to_store( + scheme: SchemeType, + password: &Option, +) -> (StoredKeypair, common::SecretKey) { + let sk = store::gen_sk(scheme); + StoredKeypair::new(sk, password.clone()) +} + +impl From for ReadError { + fn from(err: wallet::keys::DecryptionError) -> Self { + ReadError::Decryption(err) + } +} + diff --git a/shared/src/ledger/wallet/store.rs b/shared/src/ledger/wallet/store.rs new file mode 100644 index 0000000000..9f1b54c5ff --- /dev/null +++ b/shared/src/ledger/wallet/store.rs @@ -0,0 +1,557 @@ +use serde::{Deserialize, Serialize}; +use crate::types::key::*; +use crate::types::masp::{ + ExtendedSpendingKey, ExtendedViewingKey, PaymentAddress, +}; +use std::collections::HashMap; +use super::alias::{self, Alias}; +use crate::types::address::{Address, ImplicitAddress}; +use bimap::BiHashMap; +use crate::types::key::dkg_session_keys::DkgKeypair; +use masp_primitives::zip32::ExtendedFullViewingKey; +use std::str::FromStr; +use crate::ledger::wallet::WalletUtils; +use std::marker::PhantomData; +#[cfg(feature = "masp-tx-gen")] +use rand_core::RngCore; + +use super::pre_genesis; +use crate::ledger::wallet::{store, StoredKeypair}; + +pub enum ConfirmationResponse { + Replace, + Reselect(Alias), + Skip, +} + +/// Special keys for a validator +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct ValidatorKeys { + /// Special keypair for signing protocol txs + pub protocol_keypair: common::SecretKey, + /// Special session keypair needed by validators for participating + /// in the DKG protocol + pub dkg_keypair: Option, +} + +impl ValidatorKeys { + /// Get the protocol keypair + pub fn get_protocol_keypair(&self) -> &common::SecretKey { + &self.protocol_keypair + } +} + +/// Special data associated with a validator +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct ValidatorData { + /// The address associated to a validator + pub address: Address, + /// special keys for a validator + pub keys: ValidatorKeys, +} + +#[derive(Serialize, Deserialize, Debug, Default)] +pub struct Store { + /// Known viewing keys + view_keys: HashMap, + /// Known spending keys + spend_keys: HashMap>, + /// Known payment addresses + payment_addrs: HashMap, + /// Cryptographic keypairs + keys: HashMap>, + /// Anoma address book + addresses: BiHashMap, + /// Known mappings of public key hashes to their aliases in the `keys` + /// field. Used for look-up by a public key. + pkhs: HashMap, + /// Special keys if the wallet belongs to a validator + pub(crate) validator_data: Option, +} + +impl Store { + /// Find the stored key by an alias, a public key hash or a public key. + pub fn find_key( + &self, + alias_pkh_or_pk: impl AsRef, + ) -> Option<&StoredKeypair> { + let alias_pkh_or_pk = alias_pkh_or_pk.as_ref(); + // Try to find by alias + self.keys + .get(&alias_pkh_or_pk.into()) + // Try to find by PKH + .or_else(|| { + let pkh = PublicKeyHash::from_str(alias_pkh_or_pk).ok()?; + self.find_key_by_pkh(&pkh) + }) + // Try to find by PK + .or_else(|| { + let pk = common::PublicKey::from_str(alias_pkh_or_pk).ok()?; + self.find_key_by_pk(&pk) + }) + } + + pub fn find_spending_key( + &self, + alias: impl AsRef, + ) -> Option<&StoredKeypair> { + self.spend_keys.get(&alias.into()) + } + + pub fn find_viewing_key( + &self, + alias: impl AsRef, + ) -> Option<&ExtendedViewingKey> { + self.view_keys.get(&alias.into()) + } + + pub fn find_payment_addr( + &self, + alias: impl AsRef, + ) -> Option<&PaymentAddress> { + self.payment_addrs.get(&alias.into()) + } + + /// Find the stored key by a public key. + pub fn find_key_by_pk( + &self, + pk: &common::PublicKey, + ) -> Option<&StoredKeypair> { + let pkh = PublicKeyHash::from(pk); + self.find_key_by_pkh(&pkh) + } + + /// Find the stored key by a public key hash. + pub fn find_key_by_pkh( + &self, + pkh: &PublicKeyHash, + ) -> Option<&StoredKeypair> { + let alias = self.pkhs.get(pkh)?; + self.keys.get(alias) + } + + /// Find the stored alias for a public key hash. + pub fn find_alias_by_pkh(&self, pkh: &PublicKeyHash) -> Option { + self.pkhs.get(pkh).cloned() + } + + /// Find the stored address by an alias. + pub fn find_address(&self, alias: impl AsRef) -> Option<&Address> { + self.addresses.get_by_left(&alias.into()) + } + + /// Find an alias by the address if it's in the wallet. + pub fn find_alias(&self, address: &Address) -> Option<&Alias> { + self.addresses.get_by_right(address) + } + + /// Get all known keys by their alias, paired with PKH, if known. + pub fn get_keys( + &self, + ) -> HashMap< + Alias, + (&StoredKeypair, Option<&PublicKeyHash>), + > { + let mut keys: HashMap< + Alias, + (&StoredKeypair, Option<&PublicKeyHash>), + > = self + .pkhs + .iter() + .filter_map(|(pkh, alias)| { + let key = &self.keys.get(alias)?; + Some((alias.clone(), (*key, Some(pkh)))) + }) + .collect(); + self.keys.iter().for_each(|(alias, key)| { + if !keys.contains_key(alias) { + keys.insert(alias.clone(), (key, None)); + } + }); + keys + } + + /// Get all known addresses by their alias, paired with PKH, if known. + pub fn get_addresses(&self) -> &BiHashMap { + &self.addresses + } + + /// Get all known payment addresses by their alias. + pub fn get_payment_addrs(&self) -> &HashMap { + &self.payment_addrs + } + + /// Get all known viewing keys by their alias. + pub fn get_viewing_keys(&self) -> &HashMap { + &self.view_keys + } + + /// Get all known spending keys by their alias. + pub fn get_spending_keys( + &self, + ) -> &HashMap> { + &self.spend_keys + } + + #[cfg(feature = "masp-tx-gen")] + fn generate_spending_key() -> ExtendedSpendingKey { + use rand::rngs::OsRng; + let mut spend_key = [0; 32]; + OsRng.fill_bytes(&mut spend_key); + masp_primitives::zip32::ExtendedSpendingKey::master(spend_key.as_ref()) + .into() + } + + /// Generate a new keypair and insert it into the store with the provided + /// alias. If none provided, the alias will be the public key hash. + /// If no password is provided, the keypair will be stored raw without + /// encryption. Returns the alias of the key and a reference-counting + /// pointer to the key. + pub fn gen_key( + &mut self, + scheme: SchemeType, + alias: Option, + password: Option, + ) -> (Alias, common::SecretKey) { + let sk = gen_sk(scheme); + let pkh: PublicKeyHash = PublicKeyHash::from(&sk.ref_to()); + let (keypair_to_store, raw_keypair) = StoredKeypair::new(sk, password); + let address = Address::Implicit(ImplicitAddress(pkh.clone())); + let alias: Alias = alias.unwrap_or_else(|| pkh.clone().into()).into(); + if self + .insert_keypair::(alias.clone(), keypair_to_store, pkh) + .is_none() + { + panic!("Action cancelled, no changes persisted."); + } + if self.insert_address::(alias.clone(), address).is_none() { + panic!("Action cancelled, no changes persisted."); + } + (alias, raw_keypair) + } + + /// Generate a spending key similarly to how it's done for keypairs + pub fn gen_spending_key( + &mut self, + alias: String, + password: Option, + ) -> (Alias, ExtendedSpendingKey) { + let spendkey = Self::generate_spending_key(); + let viewkey = ExtendedFullViewingKey::from(&spendkey.into()).into(); + let (spendkey_to_store, _raw_spendkey) = + StoredKeypair::new(spendkey, password); + let alias = Alias::from(alias); + if self + .insert_spending_key::(alias.clone(), spendkey_to_store, viewkey) + .is_none() + { + panic!("Action cancelled, no changes persisted."); + } + (alias, spendkey) + } + + /// Add validator data to the store + pub fn add_validator_data( + &mut self, + address: Address, + keys: ValidatorKeys, + ) { + self.validator_data = Some(ValidatorData { address, keys }); + } + + /// Returns the validator data, if it exists + pub fn get_validator_data(&self) -> Option<&ValidatorData> { + self.validator_data.as_ref() + } + + /// Returns the validator data, if it exists + pub fn validator_data(&mut self) -> Option<&mut ValidatorData> { + self.validator_data.as_mut() + } + + /// Insert a new key with the given alias. If the alias is already used, + /// will prompt for overwrite/reselection confirmation. If declined, then + /// keypair is not inserted and nothing is returned, otherwise selected + /// alias is returned. + pub fn insert_keypair( + &mut self, + alias: Alias, + keypair: StoredKeypair, + pkh: PublicKeyHash, + ) -> Option { + if alias.is_empty() { + println!( + "Empty alias given, defaulting to {}.", + Into::::into(pkh.to_string()) + ); + } + // Addresses and keypairs can share aliases, so first remove any + // addresses sharing the same namesake before checking if alias has been + // used. + let counterpart_address = self.addresses.remove_by_left(&alias); + if self.contains_alias(&alias) { + match U::show_overwrite_confirmation(&alias, "a key") { + ConfirmationResponse::Replace => {} + ConfirmationResponse::Reselect(new_alias) => { + // Restore the removed address in case the recursive prompt + // terminates with a cancellation + counterpart_address + .map(|x| self.addresses.insert(alias.clone(), x.1)); + return self.insert_keypair::(new_alias, keypair, pkh); + } + ConfirmationResponse::Skip => { + // Restore the removed address since this insertion action + // has now been cancelled + counterpart_address + .map(|x| self.addresses.insert(alias.clone(), x.1)); + return None; + } + } + } + self.remove_alias(&alias); + self.keys.insert(alias.clone(), keypair); + self.pkhs.insert(pkh, alias.clone()); + // Since it is intended for the inserted keypair to share its namesake + // with the pre-existing address + counterpart_address.map(|x| self.addresses.insert(alias.clone(), x.1)); + Some(alias) + } + + /// Insert spending keys similarly to how it's done for keypairs + pub fn insert_spending_key( + &mut self, + alias: Alias, + spendkey: StoredKeypair, + viewkey: ExtendedViewingKey, + ) -> Option { + if alias.is_empty() { + eprintln!("Empty alias given."); + return None; + } + if self.contains_alias(&alias) { + match U::show_overwrite_confirmation(&alias, "a spending key") { + ConfirmationResponse::Replace => {} + ConfirmationResponse::Reselect(new_alias) => { + return self + .insert_spending_key::(new_alias, spendkey, viewkey); + } + ConfirmationResponse::Skip => return None, + } + } + self.remove_alias(&alias); + self.spend_keys.insert(alias.clone(), spendkey); + // Simultaneously add the derived viewing key to ease balance viewing + self.view_keys.insert(alias.clone(), viewkey); + Some(alias) + } + + /// Insert viewing keys similarly to how it's done for keypairs + pub fn insert_viewing_key( + &mut self, + alias: Alias, + viewkey: ExtendedViewingKey, + ) -> Option { + if alias.is_empty() { + eprintln!("Empty alias given."); + return None; + } + if self.contains_alias(&alias) { + match U::show_overwrite_confirmation(&alias, "a viewing key") { + ConfirmationResponse::Replace => {} + ConfirmationResponse::Reselect(new_alias) => { + return self.insert_viewing_key::(new_alias, viewkey); + } + ConfirmationResponse::Skip => return None, + } + } + self.remove_alias(&alias); + self.view_keys.insert(alias.clone(), viewkey); + Some(alias) + } + + /// Check if any map of the wallet contains the given alias + fn contains_alias(&self, alias: &Alias) -> bool { + self.payment_addrs.contains_key(alias) + || self.view_keys.contains_key(alias) + || self.spend_keys.contains_key(alias) + || self.keys.contains_key(alias) + || self.addresses.contains_left(alias) + } + + /// Completely remove the given alias from all maps in the wallet + fn remove_alias(&mut self, alias: &Alias) { + self.payment_addrs.remove(alias); + self.view_keys.remove(alias); + self.spend_keys.remove(alias); + self.keys.remove(alias); + self.addresses.remove_by_left(alias); + self.pkhs.retain(|_key, val| val != alias); + } + + /// Insert payment addresses similarly to how it's done for keypairs + pub fn insert_payment_addr( + &mut self, + alias: Alias, + payment_addr: PaymentAddress, + ) -> Option { + if alias.is_empty() { + eprintln!("Empty alias given."); + return None; + } + if self.contains_alias(&alias) { + match U::show_overwrite_confirmation(&alias, "a payment address") { + ConfirmationResponse::Replace => {} + ConfirmationResponse::Reselect(new_alias) => { + return self.insert_payment_addr::(new_alias, payment_addr); + } + ConfirmationResponse::Skip => return None, + } + } + self.remove_alias(&alias); + self.payment_addrs.insert(alias.clone(), payment_addr); + Some(alias) + } + + /// Helper function to restore keypair given alias-keypair mapping and the + /// pkhs-alias mapping. + fn restore_keypair( + &mut self, + alias: Alias, + key: Option>, + pkh: Option, + ) { + key.map(|x| self.keys.insert(alias.clone(), x)); + pkh.map(|x| self.pkhs.insert(x, alias.clone())); + } + + /// Insert a new address with the given alias. If the alias is already used, + /// will prompt for overwrite/reselection confirmation, which when declined, + /// the address won't be added. Return the selected alias if the address has + /// been added. + pub fn insert_address( + &mut self, + alias: Alias, + address: Address, + ) -> Option { + if alias.is_empty() { + println!("Empty alias given, defaulting to {}.", address.encode()); + } + // Addresses and keypairs can share aliases, so first remove any keys + // sharing the same namesake before checking if alias has been used. + let counterpart_key = self.keys.remove(&alias); + let mut counterpart_pkh = None; + self.pkhs.retain(|k, v| { + if v == &alias { + counterpart_pkh = Some(k.clone()); + false + } else { + true + } + }); + if self.addresses.contains_left(&alias) { + match U::show_overwrite_confirmation(&alias, "an address") { + ConfirmationResponse::Replace => {} + ConfirmationResponse::Reselect(new_alias) => { + // Restore the removed keypair in case the recursive prompt + // terminates with a cancellation + self.restore_keypair( + alias, + counterpart_key, + counterpart_pkh, + ); + return self.insert_address::(new_alias, address); + } + ConfirmationResponse::Skip => { + // Restore the removed keypair since this insertion action + // has now been cancelled + self.restore_keypair( + alias, + counterpart_key, + counterpart_pkh, + ); + return None; + } + } + } + self.remove_alias(&alias); + self.addresses.insert(alias.clone(), address); + // Since it is intended for the inserted address to share its namesake + // with the pre-existing keypair + self.restore_keypair(alias.clone(), counterpart_key, counterpart_pkh); + Some(alias) + } + + /// Extend this store from pre-genesis validator wallet. + pub fn extend_from_pre_genesis_validator( + &mut self, + validator_address: Address, + validator_alias: Alias, + other: pre_genesis::ValidatorWallet, + ) { + let account_key_alias = alias::validator_key(&validator_alias); + let consensus_key_alias = + alias::validator_consensus_key(&validator_alias); + let tendermint_node_key_alias = + alias::validator_tendermint_node_key(&validator_alias); + + let keys = [ + (account_key_alias.clone(), other.store.account_key), + (consensus_key_alias.clone(), other.store.consensus_key), + ( + tendermint_node_key_alias.clone(), + other.store.tendermint_node_key, + ), + ]; + self.keys.extend(keys.into_iter()); + + let account_pk = other.account_key.ref_to(); + let consensus_pk = other.consensus_key.ref_to(); + let tendermint_node_pk = other.tendermint_node_key.ref_to(); + let addresses = [ + (account_key_alias.clone(), (&account_pk).into()), + (consensus_key_alias.clone(), (&consensus_pk).into()), + ( + tendermint_node_key_alias.clone(), + (&tendermint_node_pk).into(), + ), + ]; + self.addresses.extend(addresses.into_iter()); + + let pkhs = [ + ((&account_pk).into(), account_key_alias), + ((&consensus_pk).into(), consensus_key_alias), + ((&tendermint_node_pk).into(), tendermint_node_key_alias), + ]; + self.pkhs.extend(pkhs.into_iter()); + + self.validator_data = Some(ValidatorData { + address: validator_address, + keys: other.store.validator_keys, + }); + } + + pub fn decode(data: Vec) -> Result { + toml::from_slice(&data) + } + + pub fn encode(&self) -> Vec { + toml::to_vec(self).expect("Serializing of store shouldn't fail") + } +} + +/// Generate a new secret key. +pub fn gen_sk(scheme: SchemeType) -> common::SecretKey { + use rand::rngs::OsRng; + let mut csprng = OsRng {}; + match scheme { + SchemeType::Ed25519 => ed25519::SigScheme::generate(&mut csprng) + .try_to_sk() + .unwrap(), + SchemeType::Secp256k1 => secp256k1::SigScheme::generate(&mut csprng) + .try_to_sk() + .unwrap(), + SchemeType::Common => common::SigScheme::generate(&mut csprng) + .try_to_sk() + .unwrap(), + } +} From 9d4a9b23bc14b52e06443e53593d8d3910685959 Mon Sep 17 00:00:00 2001 From: Murisi Tarusenga Date: Sat, 17 Dec 2022 12:56:54 +0200 Subject: [PATCH 21/51] Added some documentation. --- apps/src/lib/wallet/mod.rs | 13 +------------ apps/src/lib/wallet/pre_genesis.rs | 7 +------ apps/src/lib/wallet/store.rs | 18 +++--------------- shared/src/ledger/wallet/keys.rs | 1 + shared/src/ledger/wallet/mod.rs | 22 +++++++++++++++++++--- shared/src/ledger/wallet/pre_genesis.rs | 7 +++++++ shared/src/ledger/wallet/store.rs | 13 +++++++++++-- 7 files changed, 43 insertions(+), 38 deletions(-) diff --git a/apps/src/lib/wallet/mod.rs b/apps/src/lib/wallet/mod.rs index 5a38b84ddb..758a7ff78d 100644 --- a/apps/src/lib/wallet/mod.rs +++ b/apps/src/lib/wallet/mod.rs @@ -4,30 +4,19 @@ mod keys; pub mod pre_genesis; mod store; -use std::collections::HashMap; -use std::fmt::Display; use std::path::{Path, PathBuf}; -use std::str::FromStr; use std::{env, fs}; -use borsh::{BorshDeserialize, BorshSerialize}; -use masp_primitives::zip32::ExtendedFullViewingKey; -use namada::types::address::Address; use namada::types::key::*; -use namada::types::masp::{ - ExtendedSpendingKey, ExtendedViewingKey, PaymentAddress, -}; pub use store::wallet_file; -use thiserror::Error; use namada::ledger::wallet::ConfirmationResponse; pub use namada::ledger::wallet::{DecryptionError, StoredKeypair}; -use namada::ledger::wallet::{Store, Wallet}; +use namada::ledger::wallet::Wallet; pub use namada::ledger::wallet::{ValidatorData, ValidatorKeys}; use crate::cli; use crate::config::genesis::genesis_config::GenesisConfig; use namada::ledger::wallet::{WalletUtils, Alias}; -use std::io::prelude::*; use std::io::{self, Write}; use namada::ledger::wallet::FindKeyError; diff --git a/apps/src/lib/wallet/pre_genesis.rs b/apps/src/lib/wallet/pre_genesis.rs index 1a6ec9bc25..9984dd182d 100644 --- a/apps/src/lib/wallet/pre_genesis.rs +++ b/apps/src/lib/wallet/pre_genesis.rs @@ -3,9 +3,7 @@ use std::path::{Path, PathBuf}; use ark_serialize::{Read, Write}; use file_lock::{FileLock, FileOptions}; -use namada::types::key::{common, SchemeType}; -use serde::{Deserialize, Serialize}; -use thiserror::Error; +use namada::types::key::SchemeType; use namada::ledger::wallet::pre_genesis::ValidatorWallet; use namada::ledger::wallet::pre_genesis::ReadError; use namada::ledger::wallet::pre_genesis::ValidatorStore; @@ -14,9 +12,6 @@ use namada::ledger::wallet::WalletUtils; use crate::wallet::store::gen_validator_keys; use crate::wallet::CliWalletUtils; -use crate::wallet; -use crate::wallet::{store, StoredKeypair}; - /// Validator pre-genesis wallet file name const VALIDATOR_FILE_NAME: &str = "wallet.toml"; diff --git a/apps/src/lib/wallet/store.rs b/apps/src/lib/wallet/store.rs index 4a4ac0dbcb..77436c62fa 100644 --- a/apps/src/lib/wallet/store.rs +++ b/apps/src/lib/wallet/store.rs @@ -1,30 +1,18 @@ -use std::collections::HashMap; use std::fs; use std::io::prelude::*; -use std::io::{self, Write}; +use std::io::Write; use std::path::{Path, PathBuf}; -use std::str::FromStr; use ark_std::rand::prelude::*; use ark_std::rand::SeedableRng; -use bimap::BiHashMap; use file_lock::{FileLock, FileOptions}; -use masp_primitives::zip32::ExtendedFullViewingKey; -use namada::types::address::{Address, ImplicitAddress}; -use namada::types::key::dkg_session_keys::DkgKeypair; +use namada::types::address::Address; use namada::types::key::*; -use namada::types::masp::{ - ExtendedSpendingKey, ExtendedViewingKey, PaymentAddress, -}; use namada::types::transaction::EllipticCurve; -use serde::{Deserialize, Serialize}; use thiserror::Error; -use namada::ledger::wallet::ConfirmationResponse; use namada::ledger::wallet::Store; -use namada::ledger::wallet::{Alias, StoredKeypair, ValidatorKeys, gen_sk}; -use super::pre_genesis; -use crate::cli; +use namada::ledger::wallet::{StoredKeypair, ValidatorKeys, gen_sk}; use crate::config::genesis::genesis_config::GenesisConfig; use crate::wallet::CliWalletUtils; diff --git a/shared/src/ledger/wallet/keys.rs b/shared/src/ledger/wallet/keys.rs index 71e2b59865..f2b7cfa619 100644 --- a/shared/src/ledger/wallet/keys.rs +++ b/shared/src/ledger/wallet/keys.rs @@ -185,6 +185,7 @@ where } } + /// Indicates whether this key has been encrypted or not pub fn is_encrypted(&self) -> bool { match self { StoredKeypair::Encrypted(_) => true, diff --git a/shared/src/ledger/wallet/mod.rs b/shared/src/ledger/wallet/mod.rs index 0ad78b8b2c..06e167b5ae 100644 --- a/shared/src/ledger/wallet/mod.rs +++ b/shared/src/ledger/wallet/mod.rs @@ -1,3 +1,4 @@ +//! Provides functionality for managing keys and addresses for a user mod store; mod alias; mod keys; @@ -5,11 +6,8 @@ pub mod pre_genesis; use std::collections::HashMap; use std::fmt::Display; -use std::path::{Path, PathBuf}; use std::str::FromStr; -use std::{env, fs}; pub use self::store::{ValidatorData, ValidatorKeys}; -use std::marker::PhantomData; use borsh::{BorshDeserialize, BorshSerialize}; use masp_primitives::zip32::ExtendedFullViewingKey; @@ -48,14 +46,18 @@ pub trait WalletUtils { fn new_password_prompt(unsafe_dont_encrypt: bool) -> Option; } +/// The error that is produced when a given key cannot be obtained #[derive(Error, Debug)] pub enum FindKeyError { + /// Could not find a given key in the wallet #[error("No matching key found")] KeyNotFound, + /// Could not decrypt a given key in the wallet #[error("{0}")] KeyDecryptionError(keys::DecryptionError), } +/// Represents a collection of keys and addresses while caching key decryptions #[derive(Debug)] pub struct Wallet { store_dir: C, @@ -65,6 +67,7 @@ pub struct Wallet { } impl Wallet { + /// Create a new wallet from the given backing store and storage location pub fn new(store_dir: C, store: Store) -> Self { Self { store_dir, @@ -94,6 +97,7 @@ impl Wallet { (alias.into(), key) } + /// Generate a spending key and store it under the given alias in the wallet pub fn gen_spending_key( &mut self, alias: String, @@ -154,6 +158,7 @@ impl Wallet { ) } + /// Find the spending key with the given alias in the wallet and return it pub fn find_spending_key( &mut self, alias: impl AsRef, @@ -176,6 +181,7 @@ impl Wallet { ) } + /// Find the viewing key with the given alias in the wallet and return it pub fn find_viewing_key( &mut self, alias: impl AsRef, @@ -185,6 +191,8 @@ impl Wallet { .ok_or(FindKeyError::KeyNotFound) } + /// Find the payment address with the given alias in the wallet and return + /// it pub fn find_payment_addr( &self, alias: impl AsRef, @@ -371,6 +379,7 @@ impl Wallet { .map(Into::into) } + /// Insert a viewing key into the wallet under the given alias pub fn insert_viewing_key( &mut self, alias: String, @@ -381,6 +390,7 @@ impl Wallet { .map(Into::into) } + /// Insert a spending key into the wallet under the given alias pub fn insert_spending_key( &mut self, alias: String, @@ -392,6 +402,8 @@ impl Wallet { .map(Into::into) } + /// Encrypt the given spending key and insert it into the wallet under the + /// given alias pub fn encrypt_insert_spending_key( &mut self, alias: String, @@ -408,6 +420,7 @@ impl Wallet { .map(Into::into) } + /// Insert a payment address into the wallet under the given alias pub fn insert_payment_addr( &mut self, alias: String, @@ -432,14 +445,17 @@ impl Wallet { ) } + /// Provide immutable access to the backing store pub fn store(&self) -> &Store { &self.store } + /// Provide mutable access to the backing store pub fn store_mut(&mut self) -> &mut Store { &mut self.store } + /// Access storage location data pub fn store_dir(&self) -> &C { &self.store_dir } diff --git a/shared/src/ledger/wallet/pre_genesis.rs b/shared/src/ledger/wallet/pre_genesis.rs index 8dc2cca3e3..c01b14902b 100644 --- a/shared/src/ledger/wallet/pre_genesis.rs +++ b/shared/src/ledger/wallet/pre_genesis.rs @@ -1,17 +1,23 @@ +//! Provides functionality for managing validator keys use thiserror::Error; use crate::types::key::{common, SchemeType}; use serde::{Deserialize, Serialize}; use crate::ledger::wallet; use crate::ledger::wallet::{store, StoredKeypair}; +/// Ways in which wallet store operations can fail #[derive(Error, Debug)] pub enum ReadError { + /// Failed decoding the wallet store #[error("Failed decoding the wallet store: {0}")] Decode(toml::de::Error), + /// Failed to read the wallet store #[error("Failed to read the wallet store from {0}: {1}")] ReadWallet(String, String), + /// Failed to write the wallet store #[error("Failed to write the wallet store: {0}")] StoreNewWallet(String), + /// Failed to decode a key #[error("Failed to decode a key: {0}")] Decryption(wallet::keys::DecryptionError), } @@ -57,6 +63,7 @@ impl ValidatorStore { } } +/// Generate a key and then encrypt it pub fn gen_key_to_store( scheme: SchemeType, password: &Option, diff --git a/shared/src/ledger/wallet/store.rs b/shared/src/ledger/wallet/store.rs index 9f1b54c5ff..4381390f3e 100644 --- a/shared/src/ledger/wallet/store.rs +++ b/shared/src/ledger/wallet/store.rs @@ -11,16 +11,19 @@ use crate::types::key::dkg_session_keys::DkgKeypair; use masp_primitives::zip32::ExtendedFullViewingKey; use std::str::FromStr; use crate::ledger::wallet::WalletUtils; -use std::marker::PhantomData; #[cfg(feature = "masp-tx-gen")] use rand_core::RngCore; use super::pre_genesis; -use crate::ledger::wallet::{store, StoredKeypair}; +use crate::ledger::wallet::StoredKeypair; +/// Actions that can be taken when there is an alias conflict pub enum ConfirmationResponse { + /// Replace the existing alias Replace, + /// Reselect the alias that is ascribed to a given entity Reselect(Alias), + /// Skip assigning the given entity an alias Skip, } @@ -50,6 +53,7 @@ pub struct ValidatorData { pub keys: ValidatorKeys, } +/// A Storage area for keys and addresses #[derive(Serialize, Deserialize, Debug, Default)] pub struct Store { /// Known viewing keys @@ -91,6 +95,7 @@ impl Store { }) } + /// Find the spending key with the given alias and return it pub fn find_spending_key( &self, alias: impl AsRef, @@ -98,6 +103,7 @@ impl Store { self.spend_keys.get(&alias.into()) } + /// Find the viewing key with the given alias and return it pub fn find_viewing_key( &self, alias: impl AsRef, @@ -105,6 +111,7 @@ impl Store { self.view_keys.get(&alias.into()) } + /// Find the payment address with the given alias and return it pub fn find_payment_addr( &self, alias: impl AsRef, @@ -530,10 +537,12 @@ impl Store { }); } + /// Decode a Store from the given bytes pub fn decode(data: Vec) -> Result { toml::from_slice(&data) } + /// Encode a store into a string of bytes pub fn encode(&self) -> Vec { toml::to_vec(self).expect("Serializing of store shouldn't fail") } From dd351ec28a0a71b1ae4e23cbff6634d1810ed1c3 Mon Sep 17 00:00:00 2001 From: Murisi Tarusenga Date: Sat, 17 Dec 2022 14:35:53 +0200 Subject: [PATCH 22/51] Moved CLI arguments into the shared crate. --- apps/src/bin/anoma-client/cli.rs | 1 + apps/src/bin/anoma-wallet/cli.rs | 1 + apps/src/lib/cli.rs | 563 ++++--------------------------- shared/src/ledger/args.rs | 471 ++++++++++++++++++++++++++ shared/src/ledger/mod.rs | 1 + 5 files changed, 533 insertions(+), 504 deletions(-) create mode 100644 shared/src/ledger/args.rs diff --git a/apps/src/bin/anoma-client/cli.rs b/apps/src/bin/anoma-client/cli.rs index bc42d88dfe..8c1d0f348b 100644 --- a/apps/src/bin/anoma-client/cli.rs +++ b/apps/src/bin/anoma-client/cli.rs @@ -6,6 +6,7 @@ use namada_apps::cli::cmds::*; use namada_apps::client::{rpc, tx, utils}; use tendermint_rpc::{HttpClient, WebSocketClient, SubscriptionClient}; use namada_apps::wallet::CliWalletUtils; +use namada_apps::cli::args::CliToSdk; pub async fn main() -> Result<()> { match cli::anoma_client_cli()? { diff --git a/apps/src/bin/anoma-wallet/cli.rs b/apps/src/bin/anoma-wallet/cli.rs index ce4398ffc0..9445495c85 100644 --- a/apps/src/bin/anoma-wallet/cli.rs +++ b/apps/src/bin/anoma-wallet/cli.rs @@ -16,6 +16,7 @@ use namada_apps::wallet::DecryptionError; use rand_core::OsRng; use namada_apps::wallet::CliWalletUtils; use namada::ledger::wallet::FindKeyError; +use namada_apps::cli::args::CliToSdk; pub fn main() -> Result<()> { let (cmd, mut ctx) = cli::anoma_wallet_cli()?; diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index dc68bb288a..30e6580517 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -1520,8 +1520,8 @@ pub mod args { use namada::types::masp::MaspValue; use namada::types::storage::{self, Epoch}; use namada::types::token; - use namada::types::transaction::GasLimit; use rust_decimal::Decimal; + pub use namada::ledger::args::*; use super::context::*; use super::utils::*; @@ -1699,17 +1699,12 @@ pub mod args { } } - /// Transaction associated results arguments - #[derive(Clone, Debug)] - pub struct QueryResult { - /// Common query args - pub query: Query, - /// Hash of transaction to lookup - pub tx_hash: String, + pub trait CliToSdk: Args { + fn to_sdk(self, ctx: &mut Context) -> X; } - impl QueryResult { - pub fn to_sdk(self, ctx: &mut Context) -> QueryResult { + impl CliToSdk> for QueryResult { + fn to_sdk(self, ctx: &mut Context) -> QueryResult { QueryResult:: { query: self.query.to_sdk(ctx), tx_hash: self.tx_hash, @@ -1733,19 +1728,8 @@ pub mod args { } } - /// Custom transaction arguments - #[derive(Clone, Debug)] - pub struct TxCustom { - /// Common tx arguments - pub tx: Tx, - /// Path to the tx WASM code file - pub code_path: C::Data, - /// Path to the data file - pub data_path: Option, - } - - impl TxCustom { - pub fn to_sdk(self, ctx: &mut Context) -> TxCustom { + impl CliToSdk> for TxCustom { + fn to_sdk(self, ctx: &mut Context) -> TxCustom { TxCustom:: { tx: self.tx.to_sdk(ctx), code_path: ctx.read_wasm(self.code_path), @@ -1783,29 +1767,8 @@ pub mod args { } } - /// Transfer transaction arguments - #[derive(Clone, Debug)] - pub struct TxTransfer { - /// Common tx arguments - pub tx: Tx, - /// Transfer source address - pub source: C::TransferSource, - /// Transfer target address - pub target: C::TransferTarget, - /// Transferred token address - pub token: C::Address, - /// Transferred token address - pub sub_prefix: Option, - /// Transferred token amount - pub amount: token::Amount, - /// Native token address - pub native_token: C::NativeAddress, - /// Path to the TX WASM code file - pub tx_code_path: C::Data, - } - - impl TxTransfer { - pub fn to_sdk(self, ctx: &mut Context) -> TxTransfer { + impl CliToSdk> for TxTransfer { + fn to_sdk(self, ctx: &mut Context) -> TxTransfer { TxTransfer:: { tx: self.tx.to_sdk(ctx), source: ctx.get_cached(&self.source), @@ -1857,35 +1820,8 @@ pub mod args { } } - /// IBC transfer transaction arguments - #[derive(Clone, Debug)] - pub struct TxIbcTransfer { - /// Common tx arguments - pub tx: Tx, - /// Transfer source address - pub source: C::Address, - /// Transfer target address - pub receiver: String, - /// Transferred token address - pub token: C::Address, - /// Transferred token address - pub sub_prefix: Option, - /// Transferred token amount - pub amount: token::Amount, - /// Port ID - pub port_id: PortId, - /// Channel ID - pub channel_id: ChannelId, - /// Timeout height of the destination chain - pub timeout_height: Option, - /// Timeout timestamp offset - pub timeout_sec_offset: Option, - /// Path to the TX WASM code file - pub tx_code_path: C::Data, - } - - impl TxIbcTransfer { - pub fn to_sdk(self, ctx: &mut Context) -> TxIbcTransfer { + impl CliToSdk> for TxIbcTransfer { + fn to_sdk(self, ctx: &mut Context) -> TxIbcTransfer { TxIbcTransfer:: { tx: self.tx.to_sdk(ctx), source: ctx.get(&self.source), @@ -1953,23 +1889,8 @@ pub mod args { } } - /// Transaction to initialize a new account - #[derive(Clone, Debug)] - pub struct TxInitAccount { - /// Common tx arguments - pub tx: Tx, - /// Address of the source account - pub source: C::Address, - /// Path to the VP WASM code file for the new account - pub vp_code_path: C::Data, - /// Path to the TX WASM code file - pub tx_code_path: C::Data, - /// Public key for the new account - pub public_key: C::PublicKey, - } - - impl TxInitAccount { - pub fn to_sdk(self, ctx: &mut Context) -> TxInitAccount { + impl CliToSdk> for TxInitAccount { + fn to_sdk(self, ctx: &mut Context) -> TxInitAccount { TxInitAccount:: { tx: self.tx.to_sdk(ctx), source: ctx.get(&self.source), @@ -2014,24 +1935,8 @@ pub mod args { } } - /// Transaction to initialize a new account - #[derive(Clone, Debug)] - pub struct TxInitValidator { - pub tx: Tx, - pub source: C::Address, - pub scheme: SchemeType, - pub account_key: Option, - pub consensus_key: Option, - pub protocol_key: Option, - pub commission_rate: Decimal, - pub max_commission_rate_change: Decimal, - pub validator_vp_code_path: C::Data, - pub tx_code_path: C::Data, - pub unsafe_dont_encrypt: bool, - } - - impl TxInitValidator { - pub fn to_sdk(self, ctx: &mut Context) -> TxInitValidator { + impl CliToSdk> for TxInitValidator { + fn to_sdk(self, ctx: &mut Context) -> TxInitValidator { TxInitValidator:: { tx: self.tx.to_sdk(ctx), source: ctx.get(&self.source), @@ -2122,21 +2027,8 @@ pub mod args { } } - /// Transaction to update a VP arguments - #[derive(Clone, Debug)] - pub struct TxUpdateVp { - /// Common tx arguments - pub tx: Tx, - /// Path to the VP WASM code file - pub vp_code_path: C::Data, - /// Path to the TX WASM code file - pub tx_code_path: C::Data, - /// Address of the account whose VP is to be updated - pub addr: C::Address, - } - - impl TxUpdateVp { - pub fn to_sdk(self, ctx: &mut Context) -> TxUpdateVp { + impl CliToSdk> for TxUpdateVp { + fn to_sdk(self, ctx: &mut Context) -> TxUpdateVp { TxUpdateVp:: { tx: self.tx.to_sdk(ctx), vp_code_path: ctx.read_wasm(self.vp_code_path), @@ -2174,26 +2066,8 @@ pub mod args { } } - /// Bond arguments - #[derive(Clone, Debug)] - pub struct Bond { - /// Common tx arguments - pub tx: Tx, - /// Validator address - pub validator: C::Address, - /// Amount of tokens to stake in a bond - pub amount: token::Amount, - /// Source address for delegations. For self-bonds, the validator is - /// also the source. - pub source: Option, - /// Native token address - pub native_token: C::NativeAddress, - /// Path to the TX WASM code file - pub tx_code_path: C::Data, - } - - impl Bond { - pub fn to_sdk(self, ctx: &mut Context) -> Bond { + impl CliToSdk> for Bond { + fn to_sdk(self, ctx: &mut Context) -> Bond { Bond:: { tx: self.tx.to_sdk(ctx), validator: ctx.get(&self.validator), @@ -2234,24 +2108,8 @@ pub mod args { } } - /// Unbond arguments - #[derive(Clone, Debug)] - pub struct Unbond { - /// Common tx arguments - pub tx: Tx, - /// Validator address - pub validator: C::Address, - /// Amount of tokens to unbond from a bond - pub amount: token::Amount, - /// Source address for unbonding from delegations. For unbonding from - /// self-bonds, the validator is also the source - pub source: Option, - /// Path to the TX WASM code file - pub tx_code_path: C::Data, - } - - impl Unbond { - pub fn to_sdk(self, ctx: &mut Context) -> Unbond { + impl CliToSdk> for Unbond { + fn to_sdk(self, ctx: &mut Context) -> Unbond { Unbond:: { tx: self.tx.to_sdk(ctx), validator: ctx.get(&self.validator), @@ -2308,8 +2166,8 @@ pub mod args { pub tx_code_path: C::Data, } - impl InitProposal { - pub fn to_sdk(self, ctx: &mut Context) -> InitProposal { + impl CliToSdk> for InitProposal { + fn to_sdk(self, ctx: &mut Context) -> InitProposal { InitProposal:: { tx: self.tx.to_sdk(ctx), proposal_data: self.proposal_data, @@ -2366,8 +2224,8 @@ pub mod args { pub tx_code_path: C::Data, } - impl VoteProposal { - pub fn to_sdk(self, ctx: &mut Context) -> VoteProposal { + impl CliToSdk> for VoteProposal { + fn to_sdk(self, ctx: &mut Context) -> VoteProposal { VoteProposal:: { tx: self.tx.to_sdk(ctx), proposal_id: self.proposal_id, @@ -2432,16 +2290,8 @@ pub mod args { } } - #[derive(Clone, Debug)] - pub struct RevealPk { - /// Common tx arguments - pub tx: Tx, - /// A public key to be revealed on-chain - pub public_key: C::PublicKey, - } - - impl RevealPk { - pub fn to_sdk(self, ctx: &mut Context) -> RevealPk { + impl CliToSdk> for RevealPk { + fn to_sdk(self, ctx: &mut Context) -> RevealPk { RevealPk:: { tx: self.tx.to_sdk(ctx), public_key: ctx.get_cached(&self.public_key), @@ -2463,16 +2313,8 @@ pub mod args { } } - #[derive(Clone, Debug)] - pub struct QueryProposal { - /// Common query args - pub query: Query, - /// Proposal id - pub proposal_id: Option, - } - - impl QueryProposal { - pub fn to_sdk(self, ctx: &mut Context) -> QueryProposal { + impl CliToSdk> for QueryProposal { + fn to_sdk(self, ctx: &mut Context) -> QueryProposal { QueryProposal:: { query: self.query.to_sdk(ctx), proposal_id: self.proposal_id, @@ -2506,8 +2348,8 @@ pub mod args { pub proposal_folder: Option, } - impl QueryProposalResult { - pub fn to_sdk(self, ctx: &mut Context) -> QueryProposalResult { + impl CliToSdk> for QueryProposalResult { + fn to_sdk(self, ctx: &mut Context) -> QueryProposalResult { QueryProposalResult:: { query: self.query.to_sdk(ctx), proposal_id: self.proposal_id, @@ -2556,14 +2398,8 @@ pub mod args { } } - #[derive(Clone, Debug)] - pub struct QueryProtocolParameters { - /// Common query args - pub query: Query, - } - - impl QueryProtocolParameters { - pub fn to_sdk(self, ctx: &mut Context) -> QueryProtocolParameters { + impl CliToSdk> for QueryProtocolParameters { + fn to_sdk(self, ctx: &mut Context) -> QueryProtocolParameters { QueryProtocolParameters:: { query: self.query.to_sdk(ctx), } @@ -2582,22 +2418,8 @@ pub mod args { } } - /// Withdraw arguments - #[derive(Clone, Debug)] - pub struct Withdraw { - /// Common tx arguments - pub tx: Tx, - /// Validator address - pub validator: C::Address, - /// Source address for withdrawing from delegations. For withdrawing - /// from self-bonds, the validator is also the source - pub source: Option, - /// Path to the TX WASM code file - pub tx_code_path: C::Data, - } - - impl Withdraw { - pub fn to_sdk(self, ctx: &mut Context) -> Withdraw { + impl CliToSdk> for Withdraw { + fn to_sdk(self, ctx: &mut Context) -> Withdraw { Withdraw:: { tx: self.tx.to_sdk(ctx), validator: ctx.get(&self.validator), @@ -2632,19 +2454,8 @@ pub mod args { } } - /// Query asset conversions - #[derive(Clone, Debug)] - pub struct QueryConversions { - /// Common query args - pub query: Query, - /// Address of a token - pub token: Option, - /// Epoch of the asset - pub epoch: Option, - } - - impl QueryConversions { - pub fn to_sdk(self, ctx: &mut Context) -> QueryConversions { + impl CliToSdk> for QueryConversions { + fn to_sdk(self, ctx: &mut Context) -> QueryConversions { QueryConversions:: { query: self.query.to_sdk(ctx), token: self.token.map(|x| ctx.get(&x)), @@ -2680,23 +2491,8 @@ pub mod args { } } - /// Query token balance(s) - #[derive(Clone, Debug)] - pub struct QueryBalance { - /// Common query args - pub query: Query, - /// Address of an owner - pub owner: Option, - /// Address of a token - pub token: Option, - /// Whether not to convert balances - pub no_conversions: bool, - /// Sub prefix of an account - pub sub_prefix: Option, - } - - impl QueryBalance { - pub fn to_sdk(self, ctx: &mut Context) -> QueryBalance { + impl CliToSdk> for QueryBalance { + fn to_sdk(self, ctx: &mut Context) -> QueryBalance { QueryBalance:: { query: self.query.to_sdk(ctx), owner: self.owner.map(|x| ctx.get_cached(&x)), @@ -2748,19 +2544,8 @@ pub mod args { } } - /// Query historical transfer(s) - #[derive(Clone, Debug)] - pub struct QueryTransfers { - /// Common query args - pub query: Query, - /// Address of an owner - pub owner: Option, - /// Address of a token - pub token: Option, - } - - impl QueryTransfers { - pub fn to_sdk(self, ctx: &mut Context) -> QueryTransfers { + impl CliToSdk> for QueryTransfers { + fn to_sdk(self, ctx: &mut Context) -> QueryTransfers { QueryTransfers:: { query: self.query.to_sdk(ctx), owner: self.owner.map(|x| ctx.get_cached(&x)), @@ -2792,19 +2577,8 @@ pub mod args { } } - /// Query PoS bond(s) - #[derive(Clone, Debug)] - pub struct QueryBonds { - /// Common query args - pub query: Query, - /// Address of an owner - pub owner: Option, - /// Address of a validator - pub validator: Option, - } - - impl QueryBonds { - pub fn to_sdk(self, ctx: &mut Context) -> QueryBonds { + impl CliToSdk> for QueryBonds { + fn to_sdk(self, ctx: &mut Context) -> QueryBonds { QueryBonds:: { query: self.query.to_sdk(ctx), owner: self.owner.map(|x| ctx.get(&x)), @@ -2840,19 +2614,8 @@ pub mod args { } } - /// Query PoS bonded stake - #[derive(Clone, Debug)] - pub struct QueryBondedStake { - /// Common query args - pub query: Query, - /// Address of a validator - pub validator: Option, - /// Epoch in which to find bonded stake - pub epoch: Option, - } - - impl QueryBondedStake { - pub fn to_sdk(self, ctx: &mut Context) -> QueryBondedStake { + impl CliToSdk> for QueryBondedStake { + fn to_sdk(self, ctx: &mut Context) -> QueryBondedStake { QueryBondedStake:: { query: self.query.to_sdk(ctx), validator: self.validator.map(|x| ctx.get(&x)), @@ -2885,21 +2648,8 @@ pub mod args { } } - #[derive(Clone, Debug)] - /// Commission rate change args - pub struct TxCommissionRateChange { - /// Common tx arguments - pub tx: Tx, - /// Validator address (should be self) - pub validator: C::Address, - /// Value to which the tx changes the commission rate - pub rate: Decimal, - /// Path to the TX WASM code file - pub tx_code_path: C::Data, - } - - impl TxCommissionRateChange { - pub fn to_sdk(self, ctx: &mut Context) -> TxCommissionRateChange { + impl CliToSdk> for TxCommissionRateChange { + fn to_sdk(self, ctx: &mut Context) -> TxCommissionRateChange { TxCommissionRateChange:: { tx: self.tx.to_sdk(ctx), validator: ctx.get(&self.validator), @@ -2936,19 +2686,8 @@ pub mod args { } } - /// Query PoS commission rate - #[derive(Clone, Debug)] - pub struct QueryCommissionRate { - /// Common query args - pub query: Query, - /// Address of a validator - pub validator: C::Address, - /// Epoch in which to find commission rate - pub epoch: Option, - } - - impl QueryCommissionRate { - pub fn to_sdk(self, ctx: &mut Context) -> QueryCommissionRate { + impl CliToSdk> for QueryCommissionRate { + fn to_sdk(self, ctx: &mut Context) -> QueryCommissionRate { QueryCommissionRate:: { query: self.query.to_sdk(ctx), validator: ctx.get(&self.validator), @@ -2981,17 +2720,8 @@ pub mod args { } } - /// Query PoS slashes - #[derive(Clone, Debug)] - pub struct QuerySlashes { - /// Common query args - pub query: Query, - /// Address of a validator - pub validator: Option, - } - - impl QuerySlashes { - pub fn to_sdk(self, ctx: &mut Context) -> QuerySlashes { + impl CliToSdk> for QuerySlashes { + fn to_sdk(self, ctx: &mut Context) -> QuerySlashes { QuerySlashes:: { query: self.query.to_sdk(ctx), validator: self.validator.map(|x| ctx.get(&x)), @@ -3014,17 +2744,9 @@ pub mod args { ) } } - /// Query the raw bytes of given storage key - #[derive(Clone, Debug)] - pub struct QueryRawBytes { - /// The storage key to query - pub storage_key: storage::Key, - /// Common query args - pub query: Query, - } - impl QueryRawBytes { - pub fn to_sdk(self, ctx: &mut Context) -> QueryRawBytes { + impl CliToSdk> for QueryRawBytes { + fn to_sdk(self, ctx: &mut Context) -> QueryRawBytes { QueryRawBytes:: { query: self.query.to_sdk(ctx), storage_key: self.storage_key, @@ -3045,46 +2767,6 @@ pub mod args { } } - /// Abstraction of types being used in Namada - pub trait NamadaTypes: Clone + std::fmt::Debug { - type Address: Clone + std::fmt::Debug; - type NativeAddress: Clone + std::fmt::Debug; - type Keypair: Clone + std::fmt::Debug; - type TendermintAddress: Clone + std::fmt::Debug; - type ViewingKey: Clone + std::fmt::Debug; - type BalanceOwner: Clone + std::fmt::Debug; - type PublicKey: Clone + std::fmt::Debug; - type TransferSource: Clone + std::fmt::Debug; - type TransferTarget: Clone + std::fmt::Debug; - type Data: Clone + std::fmt::Debug; - } - - /// The concrete types being used in Namada SDK - #[derive(Clone, Debug)] - pub struct SdkTypes; - - impl NamadaTypes for SdkTypes { - type Address = Address; - - type NativeAddress = Address; - - type Keypair = common::SecretKey; - - type TendermintAddress = (); - - type ViewingKey = namada::types::masp::ExtendedViewingKey; - - type BalanceOwner = namada::types::masp::BalanceOwner; - - type PublicKey = common::PublicKey; - - type TransferSource = namada::types::masp::TransferSource; - - type TransferTarget = namada::types::masp::TransferTarget; - - type Data = Vec; - } - /// The concrete types being used in the CLI #[derive(Clone, Debug)] pub struct CliTypes; @@ -3110,37 +2792,9 @@ pub mod args { type Data = PathBuf; } - - /// Common transaction arguments - #[derive(Clone, Debug)] - pub struct Tx { - /// Simulate applying the transaction - pub dry_run: bool, - /// Submit the transaction even if it doesn't pass client checks - pub force: bool, - /// Do not wait for the transaction to be added to the blockchain - pub broadcast_only: bool, - /// The address of the ledger node as host:port - pub ledger_address: C::TendermintAddress, - /// If any new account is initialized by the tx, use the given alias to - /// save it in the wallet. - pub initialized_account_alias: Option, - /// The amount being payed to include the transaction - pub fee_amount: token::Amount, - /// The token in which the fee is being paid - pub fee_token: C::Address, - /// The max amount of gas used to process tx - pub gas_limit: GasLimit, - /// Sign the tx with the key for the given alias from your wallet - pub signing_key: Option, - /// Sign the tx with the keypair of the public key of the given address - pub signer: Option, - /// Path to the TX WASM code file - pub tx_code_path: C::Data, - } - impl Tx { - pub fn to_sdk(self, ctx: &mut Context) -> Tx { + impl CliToSdk> for Tx { + fn to_sdk(self, ctx: &mut Context) -> Tx { Tx:: { dry_run: self.dry_run, force: self.force, @@ -3237,15 +2891,8 @@ pub mod args { } } - /// Common query arguments - #[derive(Clone, Debug)] - pub struct Query { - /// The address of the ledger node as host:port - pub ledger_address: C::TendermintAddress, - } - - impl Query { - pub fn to_sdk(self, _ctx: &mut Context) -> Query { + impl CliToSdk> for Query { + fn to_sdk(self, _ctx: &mut Context) -> Query { Query:: { ledger_address: (), } @@ -3263,17 +2910,6 @@ pub mod args { } } - /// MASP add key or address arguments - #[derive(Clone, Debug)] - pub struct MaspAddrKeyAdd { - /// Key alias - pub alias: String, - /// Any MASP value - pub value: MaspValue, - /// Don't encrypt the keypair - pub unsafe_dont_encrypt: bool, - } - impl Args for MaspAddrKeyAdd { fn parse(matches: &ArgMatches) -> Self { let alias = ALIAS.parse(matches); @@ -3304,15 +2940,6 @@ pub mod args { } } - /// MASP generate spending key arguments - #[derive(Clone, Debug)] - pub struct MaspSpendKeyGen { - /// Key alias - pub alias: String, - /// Don't encrypt the keypair - pub unsafe_dont_encrypt: bool, - } - impl Args for MaspSpendKeyGen { fn parse(matches: &ArgMatches) -> Self { let alias = ALIAS.parse(matches); @@ -3336,19 +2963,8 @@ pub mod args { } } - /// MASP generate payment address arguments - #[derive(Clone, Debug)] - pub struct MaspPayAddrGen { - /// Key alias - pub alias: String, - /// Viewing key - pub viewing_key: C::ViewingKey, - /// Pin - pub pin: bool, - } - - impl MaspPayAddrGen { - pub fn to_sdk(self, ctx: &mut Context) -> MaspPayAddrGen { + impl CliToSdk> for MaspPayAddrGen { + fn to_sdk(self, ctx: &mut Context) -> MaspPayAddrGen { MaspPayAddrGen:: { alias: self.alias, viewing_key: ctx.get_cached(&self.viewing_key), @@ -3383,17 +2999,6 @@ pub mod args { } } - /// Wallet generate key and implicit address arguments - #[derive(Clone, Debug)] - pub struct KeyAndAddressGen { - /// Scheme type - pub scheme: SchemeType, - /// Key alias - pub alias: Option, - /// Don't encrypt the keypair - pub unsafe_dont_encrypt: bool, - } - impl Args for KeyAndAddressGen { fn parse(matches: &ArgMatches) -> Self { let scheme = SCHEME.parse(matches); @@ -3423,15 +3028,6 @@ pub mod args { } } - /// Wallet key lookup arguments - #[derive(Clone, Debug)] - pub struct KeyFind { - pub public_key: Option, - pub alias: Option, - pub value: Option, - pub unsafe_show_secret: bool, - } - impl Args for KeyFind { fn parse(matches: &ArgMatches) -> Self { let public_key = RAW_PUBLIC_KEY_OPT.parse(matches); @@ -3473,13 +3069,6 @@ pub mod args { } } - /// Wallet find shielded address or key arguments - #[derive(Clone, Debug)] - pub struct AddrKeyFind { - pub alias: String, - pub unsafe_show_secret: bool, - } - impl Args for AddrKeyFind { fn parse(matches: &ArgMatches) -> Self { let alias = ALIAS.parse(matches); @@ -3500,13 +3089,6 @@ pub mod args { } } - /// Wallet list shielded keys arguments - #[derive(Clone, Debug)] - pub struct MaspKeysList { - pub decrypt: bool, - pub unsafe_show_secret: bool, - } - impl Args for MaspKeysList { fn parse(matches: &ArgMatches) -> Self { let decrypt = DECRYPT.parse(matches); @@ -3527,13 +3109,6 @@ pub mod args { } } - /// Wallet list keys arguments - #[derive(Clone, Debug)] - pub struct KeyList { - pub decrypt: bool, - pub unsafe_show_secret: bool, - } - impl Args for KeyList { fn parse(matches: &ArgMatches) -> Self { let decrypt = DECRYPT.parse(matches); @@ -3554,12 +3129,6 @@ pub mod args { } } - /// Wallet key export arguments - #[derive(Clone, Debug)] - pub struct KeyExport { - pub alias: String, - } - impl Args for KeyExport { fn parse(matches: &ArgMatches) -> Self { let alias = ALIAS.parse(matches); @@ -3576,13 +3145,6 @@ pub mod args { } } - /// Wallet address lookup arguments - #[derive(Clone, Debug)] - pub struct AddressOrAliasFind { - pub alias: Option, - pub address: Option
, - } - impl Args for AddressOrAliasFind { fn parse(matches: &ArgMatches) -> Self { let alias = ALIAS_OPT.parse(matches); @@ -3609,13 +3171,6 @@ pub mod args { } } - /// Wallet address add arguments - #[derive(Clone, Debug)] - pub struct AddressAdd { - pub alias: String, - pub address: Address, - } - impl Args for AddressAdd { fn parse(matches: &ArgMatches) -> Self { let alias = ALIAS.parse(matches); diff --git a/shared/src/ledger/args.rs b/shared/src/ledger/args.rs new file mode 100644 index 0000000000..79476dc7eb --- /dev/null +++ b/shared/src/ledger/args.rs @@ -0,0 +1,471 @@ +use crate::types::address::Address; +use crate::types::transaction::GasLimit; +use crate::types::masp::MaspValue; +use crate::types::storage::Epoch; +use crate::types::token; +use crate::types::storage; +use crate::types::key::common; +use rust_decimal::Decimal; +use crate::types::key::SchemeType; +use crate::ibc::core::ics24_host::identifier::ChannelId; +use crate::ibc::core::ics24_host::identifier::PortId; + +/// Abstraction of types being used in Namada +pub trait NamadaTypes: Clone + std::fmt::Debug { + /// Represents an address on the ledger + type Address: Clone + std::fmt::Debug; + /// Represents the address of a native token + type NativeAddress: Clone + std::fmt::Debug; + /// Represents a key pair + type Keypair: Clone + std::fmt::Debug; + /// Represents the address of a Tendermint endpoint + type TendermintAddress: Clone + std::fmt::Debug; + /// Represents a viewing key + type ViewingKey: Clone + std::fmt::Debug; + /// Represents the owner of a balance + type BalanceOwner: Clone + std::fmt::Debug; + /// Represents a public key + type PublicKey: Clone + std::fmt::Debug; + /// Represents the source of a Transfer + type TransferSource: Clone + std::fmt::Debug; + /// Represents the target of a Transfer + type TransferTarget: Clone + std::fmt::Debug; + /// Represents some data that is used in a transaction + type Data: Clone + std::fmt::Debug; +} + +/// The concrete types being used in Namada SDK +#[derive(Clone, Debug)] +pub struct SdkTypes; + +impl NamadaTypes for SdkTypes { + type Address = Address; + + type NativeAddress = Address; + + type Keypair = namada_core::types::key::common::SecretKey; + + type TendermintAddress = (); + + type ViewingKey = namada_core::types::masp::ExtendedViewingKey; + + type BalanceOwner = namada_core::types::masp::BalanceOwner; + + type PublicKey = namada_core::types::key::common::PublicKey; + + type TransferSource = namada_core::types::masp::TransferSource; + + type TransferTarget = namada_core::types::masp::TransferTarget; + + type Data = Vec; +} + +/// Common query arguments +#[derive(Clone, Debug)] +pub struct Query { + /// The address of the ledger node as host:port + pub ledger_address: C::TendermintAddress, +} + +/// Transaction associated results arguments +#[derive(Clone, Debug)] +pub struct QueryResult { + /// Common query args + pub query: Query, + /// Hash of transaction to lookup + pub tx_hash: String, +} + +/// Custom transaction arguments +#[derive(Clone, Debug)] +pub struct TxCustom { + /// Common tx arguments + pub tx: Tx, + /// Path to the tx WASM code file + pub code_path: C::Data, + /// Path to the data file + pub data_path: Option, +} + +/// Transfer transaction arguments +#[derive(Clone, Debug)] +pub struct TxTransfer { + /// Common tx arguments + pub tx: Tx, + /// Transfer source address + pub source: C::TransferSource, + /// Transfer target address + pub target: C::TransferTarget, + /// Transferred token address + pub token: C::Address, + /// Transferred token address + pub sub_prefix: Option, + /// Transferred token amount + pub amount: token::Amount, + /// Native token address + pub native_token: C::NativeAddress, + /// Path to the TX WASM code file + pub tx_code_path: C::Data, +} + +/// IBC transfer transaction arguments +#[derive(Clone, Debug)] +pub struct TxIbcTransfer { + /// Common tx arguments + pub tx: Tx, + /// Transfer source address + pub source: C::Address, + /// Transfer target address + pub receiver: String, + /// Transferred token address + pub token: C::Address, + /// Transferred token address + pub sub_prefix: Option, + /// Transferred token amount + pub amount: token::Amount, + /// Port ID + pub port_id: PortId, + /// Channel ID + pub channel_id: ChannelId, + /// Timeout height of the destination chain + pub timeout_height: Option, + /// Timeout timestamp offset + pub timeout_sec_offset: Option, + /// Path to the TX WASM code file + pub tx_code_path: C::Data, +} + +/// Transaction to initialize a new account +#[derive(Clone, Debug)] +pub struct TxInitAccount { + /// Common tx arguments + pub tx: Tx, + /// Address of the source account + pub source: C::Address, + /// Path to the VP WASM code file for the new account + pub vp_code_path: C::Data, + /// Path to the TX WASM code file + pub tx_code_path: C::Data, + /// Public key for the new account + pub public_key: C::PublicKey, +} + +/// Transaction to initialize a new account +#[derive(Clone, Debug)] +pub struct TxInitValidator { + pub tx: Tx, + pub source: C::Address, + pub scheme: SchemeType, + pub account_key: Option, + pub consensus_key: Option, + pub protocol_key: Option, + pub commission_rate: Decimal, + pub max_commission_rate_change: Decimal, + pub validator_vp_code_path: C::Data, + pub tx_code_path: C::Data, + pub unsafe_dont_encrypt: bool, +} + +/// Transaction to update a VP arguments +#[derive(Clone, Debug)] +pub struct TxUpdateVp { + /// Common tx arguments + pub tx: Tx, + /// Path to the VP WASM code file + pub vp_code_path: C::Data, + /// Path to the TX WASM code file + pub tx_code_path: C::Data, + /// Address of the account whose VP is to be updated + pub addr: C::Address, +} + +/// Bond arguments +#[derive(Clone, Debug)] +pub struct Bond { + /// Common tx arguments + pub tx: Tx, + /// Validator address + pub validator: C::Address, + /// Amount of tokens to stake in a bond + pub amount: token::Amount, + /// Source address for delegations. For self-bonds, the validator is + /// also the source. + pub source: Option, + /// Native token address + pub native_token: C::NativeAddress, + /// Path to the TX WASM code file + pub tx_code_path: C::Data, +} + +/// Unbond arguments +#[derive(Clone, Debug)] +pub struct Unbond { + /// Common tx arguments + pub tx: Tx, + /// Validator address + pub validator: C::Address, + /// Amount of tokens to unbond from a bond + pub amount: token::Amount, + /// Source address for unbonding from delegations. For unbonding from + /// self-bonds, the validator is also the source + pub source: Option, + /// Path to the TX WASM code file + pub tx_code_path: C::Data, +} + +#[derive(Clone, Debug)] +pub struct RevealPk { + /// Common tx arguments + pub tx: Tx, + /// A public key to be revealed on-chain + pub public_key: C::PublicKey, +} + +#[derive(Clone, Debug)] +pub struct QueryProposal { + /// Common query args + pub query: Query, + /// Proposal id + pub proposal_id: Option, +} + +#[derive(Clone, Debug)] +pub struct QueryProtocolParameters { + /// Common query args + pub query: Query, +} + +/// Withdraw arguments +#[derive(Clone, Debug)] +pub struct Withdraw { + /// Common tx arguments + pub tx: Tx, + /// Validator address + pub validator: C::Address, + /// Source address for withdrawing from delegations. For withdrawing + /// from self-bonds, the validator is also the source + pub source: Option, + /// Path to the TX WASM code file + pub tx_code_path: C::Data, +} + +/// Query asset conversions +#[derive(Clone, Debug)] +pub struct QueryConversions { + /// Common query args + pub query: Query, + /// Address of a token + pub token: Option, + /// Epoch of the asset + pub epoch: Option, +} + +/// Query token balance(s) +#[derive(Clone, Debug)] +pub struct QueryBalance { + /// Common query args + pub query: Query, + /// Address of an owner + pub owner: Option, + /// Address of a token + pub token: Option, + /// Whether not to convert balances + pub no_conversions: bool, + /// Sub prefix of an account + pub sub_prefix: Option, +} + +/// Query historical transfer(s) +#[derive(Clone, Debug)] +pub struct QueryTransfers { + /// Common query args + pub query: Query, + /// Address of an owner + pub owner: Option, + /// Address of a token + pub token: Option, +} + +/// Query PoS bond(s) +#[derive(Clone, Debug)] +pub struct QueryBonds { + /// Common query args + pub query: Query, + /// Address of an owner + pub owner: Option, + /// Address of a validator + pub validator: Option, +} + +/// Query PoS bonded stake +#[derive(Clone, Debug)] +pub struct QueryBondedStake { + /// Common query args + pub query: Query, + /// Address of a validator + pub validator: Option, + /// Epoch in which to find bonded stake + pub epoch: Option, +} + +#[derive(Clone, Debug)] +/// Commission rate change args +pub struct TxCommissionRateChange { + /// Common tx arguments + pub tx: Tx, + /// Validator address (should be self) + pub validator: C::Address, + /// Value to which the tx changes the commission rate + pub rate: Decimal, + /// Path to the TX WASM code file + pub tx_code_path: C::Data, +} + +/// Query PoS commission rate +#[derive(Clone, Debug)] +pub struct QueryCommissionRate { + /// Common query args + pub query: Query, + /// Address of a validator + pub validator: C::Address, + /// Epoch in which to find commission rate + pub epoch: Option, +} + +/// Query PoS slashes +#[derive(Clone, Debug)] +pub struct QuerySlashes { + /// Common query args + pub query: Query, + /// Address of a validator + pub validator: Option, +} + +/// Query the raw bytes of given storage key +#[derive(Clone, Debug)] +pub struct QueryRawBytes { + /// The storage key to query + pub storage_key: storage::Key, + /// Common query args + pub query: Query, +} + +/// Common transaction arguments +#[derive(Clone, Debug)] +pub struct Tx { + /// Simulate applying the transaction + pub dry_run: bool, + /// Submit the transaction even if it doesn't pass client checks + pub force: bool, + /// Do not wait for the transaction to be added to the blockchain + pub broadcast_only: bool, + /// The address of the ledger node as host:port + pub ledger_address: C::TendermintAddress, + /// If any new account is initialized by the tx, use the given alias to + /// save it in the wallet. + pub initialized_account_alias: Option, + /// The amount being payed to include the transaction + pub fee_amount: token::Amount, + /// The token in which the fee is being paid + pub fee_token: C::Address, + /// The max amount of gas used to process tx + pub gas_limit: GasLimit, + /// Sign the tx with the key for the given alias from your wallet + pub signing_key: Option, + /// Sign the tx with the keypair of the public key of the given address + pub signer: Option, + /// Path to the TX WASM code file + pub tx_code_path: C::Data, +} + +/// MASP add key or address arguments +#[derive(Clone, Debug)] +pub struct MaspAddrKeyAdd { + /// Key alias + pub alias: String, + /// Any MASP value + pub value: MaspValue, + /// Don't encrypt the keypair + pub unsafe_dont_encrypt: bool, +} + +/// MASP generate spending key arguments +#[derive(Clone, Debug)] +pub struct MaspSpendKeyGen { + /// Key alias + pub alias: String, + /// Don't encrypt the keypair + pub unsafe_dont_encrypt: bool, +} + +/// MASP generate payment address arguments +#[derive(Clone, Debug)] +pub struct MaspPayAddrGen { + /// Key alias + pub alias: String, + /// Viewing key + pub viewing_key: C::ViewingKey, + /// Pin + pub pin: bool, +} + +/// Wallet generate key and implicit address arguments +#[derive(Clone, Debug)] +pub struct KeyAndAddressGen { + /// Scheme type + pub scheme: SchemeType, + /// Key alias + pub alias: Option, + /// Don't encrypt the keypair + pub unsafe_dont_encrypt: bool, +} + +/// Wallet key lookup arguments +#[derive(Clone, Debug)] +pub struct KeyFind { + pub public_key: Option, + pub alias: Option, + pub value: Option, + pub unsafe_show_secret: bool, +} + +/// Wallet find shielded address or key arguments +#[derive(Clone, Debug)] +pub struct AddrKeyFind { + pub alias: String, + pub unsafe_show_secret: bool, +} + +/// Wallet list shielded keys arguments +#[derive(Clone, Debug)] +pub struct MaspKeysList { + pub decrypt: bool, + pub unsafe_show_secret: bool, +} + +/// Wallet list keys arguments +#[derive(Clone, Debug)] +pub struct KeyList { + pub decrypt: bool, + pub unsafe_show_secret: bool, +} + +/// Wallet key export arguments +#[derive(Clone, Debug)] +pub struct KeyExport { + pub alias: String, +} + +/// Wallet address lookup arguments +#[derive(Clone, Debug)] +pub struct AddressOrAliasFind { + pub alias: Option, + pub address: Option
, +} + +/// Wallet address add arguments +#[derive(Clone, Debug)] +pub struct AddressAdd { + pub alias: String, + pub address: Address, +} diff --git a/shared/src/ledger/mod.rs b/shared/src/ledger/mod.rs index 31c72a04c6..8d553721a1 100644 --- a/shared/src/ledger/mod.rs +++ b/shared/src/ledger/mod.rs @@ -12,6 +12,7 @@ pub mod queries; pub mod storage; pub mod vp_host_fns; pub mod wallet; +pub mod args; pub use namada_core::ledger::{ gas, governance, parameters, storage_api, tx_env, vp_env, From 88223d831d8f5cbc7e6c2ddc5b8393f9c94cdaad Mon Sep 17 00:00:00 2001 From: Murisi Tarusenga Date: Sat, 17 Dec 2022 19:19:11 +0200 Subject: [PATCH 23/51] Generalized functions to depend on Client instead of HttpClient. --- apps/src/bin/anoma-client/cli.rs | 24 +-- apps/src/lib/client/rpc.rs | 330 +++++++++++++++---------------- apps/src/lib/client/signing.rs | 21 +- apps/src/lib/client/tx.rs | 178 ++++++++--------- 4 files changed, 277 insertions(+), 276 deletions(-) diff --git a/apps/src/bin/anoma-client/cli.rs b/apps/src/bin/anoma-client/cli.rs index 8c1d0f348b..66e3fe3187 100644 --- a/apps/src/bin/anoma-client/cli.rs +++ b/apps/src/bin/anoma-client/cli.rs @@ -19,7 +19,7 @@ pub async fn main() -> Result<()> { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); let dry_run = args.tx.dry_run; - tx::submit_custom::(&client, &mut ctx.wallet, args).await; + tx::submit_custom::(&client, &mut ctx.wallet, args).await; if !dry_run { namada_apps::wallet::save(&ctx.wallet).unwrap_or_else(|err| eprintln!("{}", err)); } else { @@ -29,23 +29,23 @@ pub async fn main() -> Result<()> { Sub::TxTransfer(TxTransfer(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_transfer::<_, CliWalletUtils>(&client, &mut ctx.wallet, &mut ctx.shielded, args).await; + tx::submit_transfer::(&client, &mut ctx.wallet, &mut ctx.shielded, args).await; } Sub::TxIbcTransfer(TxIbcTransfer(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_ibc_transfer::(&client, &mut ctx.wallet, args).await; + tx::submit_ibc_transfer::(&client, &mut ctx.wallet, args).await; } Sub::TxUpdateVp(TxUpdateVp(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_update_vp::(&client, &mut ctx.wallet, args).await; + tx::submit_update_vp::(&client, &mut ctx.wallet, args).await; } Sub::TxInitAccount(TxInitAccount(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); let dry_run = args.tx.dry_run; - tx::submit_init_account::(&client, &mut ctx.wallet, args).await; + tx::submit_init_account::(&client, &mut ctx.wallet, args).await; if !dry_run { namada_apps::wallet::save(&ctx.wallet).unwrap_or_else(|err| eprintln!("{}", err)); } else { @@ -55,37 +55,37 @@ pub async fn main() -> Result<()> { Sub::TxInitValidator(TxInitValidator(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_init_validator::(&client, ctx, args).await; + tx::submit_init_validator::(&client, ctx, args).await; } Sub::TxInitProposal(TxInitProposal(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_init_proposal::(&client, ctx, args).await; + tx::submit_init_proposal::(&client, ctx, args).await; } Sub::TxVoteProposal(TxVoteProposal(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_vote_proposal::(&client, &mut ctx.wallet, args).await; + tx::submit_vote_proposal::(&client, &mut ctx.wallet, args).await; } Sub::TxRevealPk(TxRevealPk(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_reveal_pk::(&client, &mut ctx.wallet, args).await; + tx::submit_reveal_pk::(&client, &mut ctx.wallet, args).await; } Sub::Bond(Bond(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_bond::(&client, &mut ctx.wallet, args).await; + tx::submit_bond::(&client, &mut ctx.wallet, args).await; } Sub::Unbond(Unbond(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_unbond::(&client, &mut ctx.wallet, args).await; + tx::submit_unbond::(&client, &mut ctx.wallet, args).await; } Sub::Withdraw(Withdraw(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_withdraw::(&client, &mut ctx.wallet, args).await; + tx::submit_withdraw::(&client, &mut ctx.wallet, args).await; } // Ledger queries Sub::QueryEpoch(QueryEpoch(args)) => { diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index d87d044bca..203f951999 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -66,8 +66,8 @@ use crate::facade::tendermint_rpc::{ /// /// If a response is not delivered until `deadline`, we exit the cli with an /// error. -pub async fn query_tx_status( - client: &HttpClient, +pub async fn query_tx_status( + client: &C, status: TxEventQuery<'_>, deadline: Instant, ) -> Event { @@ -90,10 +90,10 @@ pub async fn query_tx_status( loop { tracing::debug!(query = ?status, "Querying tx status"); - let maybe_event = match query_tx_events(&client, status).await { + let maybe_event = match query_tx_events(client, status).await { Ok(response) => response, Err(err) => { - tracing::debug!(%err, "ABCI query failed"); + //tracing::debug!(%err, "ABCI query failed"); sleep_update(status, &mut backoff).await; continue; } @@ -113,15 +113,15 @@ pub async fn query_tx_status( } /// Query the epoch of the last committed block -pub async fn query_epoch(client: &HttpClient) -> Epoch { - let epoch = unwrap_client_response(RPC.shell().epoch(&client.clone()).await); +pub async fn query_epoch(client: &C) -> Epoch { + let epoch = unwrap_client_response::(RPC.shell().epoch(client).await); println!("Last committed epoch: {}", epoch); epoch } /// Query the last committed block -pub async fn query_block( - client: &HttpClient, +pub async fn query_block( + client: &C, ) -> crate::facade::tendermint_rpc::endpoint::block::Response { let response = client.latest_block().await.unwrap(); println!( @@ -134,13 +134,13 @@ pub async fn query_block( } /// Query the results of the last committed block -pub async fn query_results(client: &HttpClient) -> Vec { - unwrap_client_response(RPC.shell().read_results(&client.clone()).await) +pub async fn query_results(client: &C) -> Vec { + unwrap_client_response::(RPC.shell().read_results(client).await) } /// Query the specified accepted transfers from the ledger -pub async fn query_transfers>( - client: &HttpClient, +pub async fn query_transfers>( + client: &C, wallet: &mut Wallet, shielded: &mut ShieldedContext, args: args::QueryTransfers @@ -262,8 +262,8 @@ pub async fn query_transfers>( } /// Query the raw bytes of given storage key -pub async fn query_raw_bytes(client: &HttpClient, args: args::QueryRawBytes) { - let response = unwrap_client_response( +pub async fn query_raw_bytes(client: &C, args: args::QueryRawBytes) { + let response = unwrap_client_response::( RPC.shell() .storage_value(client, None, None, false, &args.storage_key) .await, @@ -276,8 +276,8 @@ pub async fn query_raw_bytes(client: &HttpClient, args: args::QueryRawBytes) { } /// Query token balance(s) -pub async fn query_balance>( - client: &HttpClient, +pub async fn query_balance>( + client: &C, wallet: &mut Wallet, shielded: &mut ShieldedContext, args: args::QueryBalance, @@ -306,8 +306,8 @@ pub async fn query_balance>( } /// Query token balance(s) -pub async fn query_transparent_balance( - client: &HttpClient, +pub async fn query_transparent_balance( + client: &C, wallet: &mut Wallet, args: args::QueryBalance, ) { @@ -330,7 +330,7 @@ pub async fn query_transparent_balance( .get(&token) .map(|c| Cow::Borrowed(*c)) .unwrap_or_else(|| Cow::Owned(token.to_string())); - match query_storage_value::(&client, &key).await { + match query_storage_value::(&client, &key).await { Some(balance) => match &args.sub_prefix { Some(sub_prefix) => { println!( @@ -349,7 +349,7 @@ pub async fn query_transparent_balance( for (token, _) in tokens { let prefix = token.to_db_key().into(); let balances = - query_storage_prefix::(&client, &prefix) + query_storage_prefix::(&client, &prefix) .await; if let Some(balances) = balances { print_balances( @@ -364,7 +364,7 @@ pub async fn query_transparent_balance( (Some(token), None) => { let prefix = token.to_db_key().into(); let balances = - query_storage_prefix::(&client, &prefix).await; + query_storage_prefix::(client, &prefix).await; if let Some(balances) = balances { print_balances(wallet, balances, &token, None); } @@ -373,7 +373,7 @@ pub async fn query_transparent_balance( for (token, _) in tokens { let key = token::balance_prefix(&token); let balances = - query_storage_prefix::(&client, &key).await; + query_storage_prefix::(client, &key).await; if let Some(balances) = balances { print_balances(wallet, balances, &token, None); } @@ -383,8 +383,8 @@ pub async fn query_transparent_balance( } /// Query the token pinned balance(s) -pub async fn query_pinned_balance>( - client: &HttpClient, +pub async fn query_pinned_balance>( + client: &C, wallet: &mut Wallet, shielded: &mut ShieldedContext, args: args::QueryBalance, @@ -582,9 +582,9 @@ fn print_balances( } /// Query Proposals -pub async fn query_proposal(client: &HttpClient, args: args::QueryProposal) { - async fn print_proposal( - client: &HttpClient, +pub async fn query_proposal(client: &C, args: args::QueryProposal) { + async fn print_proposal( + client: &C, id: u64, current_epoch: Epoch, details: bool, @@ -594,22 +594,22 @@ pub async fn query_proposal(client: &HttpClient, args: args::QueryProposal) { let end_epoch_key = gov_storage::get_voting_end_epoch_key(id); let author = - query_storage_value::
(client, &author_key).await?; + query_storage_value::(client, &author_key).await?; let start_epoch = - query_storage_value::(client, &start_epoch_key).await?; + query_storage_value::(client, &start_epoch_key).await?; let end_epoch = - query_storage_value::(client, &end_epoch_key).await?; + query_storage_value::(client, &end_epoch_key).await?; if details { let content_key = gov_storage::get_content_key(id); let grace_epoch_key = gov_storage::get_grace_epoch_key(id); - let content = query_storage_value::>( + let content = query_storage_value::>( client, &content_key, ) .await?; let grace_epoch = - query_storage_value::(client, &grace_epoch_key).await?; + query_storage_value::(client, &grace_epoch_key).await?; println!("Proposal: {}", id); println!("{:4}Author: {}", "", author); @@ -664,7 +664,7 @@ pub async fn query_proposal(client: &HttpClient, args: args::QueryProposal) { let current_epoch = query_epoch(client).await; match args.proposal_id { Some(id) => { - if print_proposal(&client, id, current_epoch, true) + if print_proposal::(&client, id, current_epoch, true) .await .is_none() { @@ -674,12 +674,12 @@ pub async fn query_proposal(client: &HttpClient, args: args::QueryProposal) { None => { let last_proposal_id_key = gov_storage::get_counter_key(); let last_proposal_id = - query_storage_value::(&client, &last_proposal_id_key) + query_storage_value::(&client, &last_proposal_id_key) .await .unwrap(); for id in 0..last_proposal_id { - if print_proposal(&client, id, current_epoch, false) + if print_proposal::(&client, id, current_epoch, false) .await .is_none() { @@ -708,8 +708,8 @@ pub fn value_by_address( } /// Query token shielded balance(s) -pub async fn query_shielded_balance>( - client: &HttpClient, +pub async fn query_shielded_balance>( + client: &C, wallet: &mut Wallet, shielded: &mut ShieldedContext, args: args::QueryBalance, @@ -983,8 +983,8 @@ pub fn print_decoded_balance_with_epoch( } /// Query token amount of owner. -pub async fn get_token_balance( - client: &HttpClient, +pub async fn get_token_balance( + client: &C, token: &Address, owner: &Address, ) -> Option { @@ -992,8 +992,8 @@ pub async fn get_token_balance( query_storage_value(client, &balance_key).await } -pub async fn query_proposal_result( - client: &HttpClient, +pub async fn query_proposal_result( + client: &C, args: args::QueryProposalResult, ) { let current_epoch = query_epoch(client).await; @@ -1002,15 +1002,15 @@ pub async fn query_proposal_result( Some(id) => { let end_epoch_key = gov_storage::get_voting_end_epoch_key(id); let end_epoch = - query_storage_value::(&client, &end_epoch_key).await; + query_storage_value::(&client, &end_epoch_key).await; match end_epoch { Some(end_epoch) => { if current_epoch > end_epoch { let votes = - get_proposal_votes(&client, end_epoch, id).await; + get_proposal_votes(client, end_epoch, id).await; let proposal_result = - compute_tally(&client, end_epoch, votes).await; + compute_tally(client, end_epoch, votes).await; println!("Proposal: {}", id); println!("{:4}Result: {}", "", proposal_result); } else { @@ -1097,13 +1097,13 @@ pub async fn query_proposal_result( } let votes = get_proposal_offline_votes( - &client, + client, proposal.clone(), files, ) .await; let proposal_result = - compute_tally(&client, proposal.tally_epoch, votes) + compute_tally(client, proposal.tally_epoch, votes) .await; println!("{:4}Result: {}", "", proposal_result); @@ -1126,16 +1126,16 @@ pub async fn query_proposal_result( } } -pub async fn query_protocol_parameters( - client: &HttpClient, +pub async fn query_protocol_parameters( + client: &C, _args: args::QueryProtocolParameters, ) { - let gov_parameters = get_governance_parameters(&client).await; + let gov_parameters = get_governance_parameters(client).await; println!("Governance Parameters\n {:4}", gov_parameters); println!("Protocol parameters"); let key = param_storage::get_epoch_duration_storage_key(); - let epoch_duration = query_storage_value::(&client, &key) + let epoch_duration = query_storage_value::(&client, &key) .await .expect("Parameter should be definied."); println!( @@ -1148,26 +1148,26 @@ pub async fn query_protocol_parameters( ); let key = param_storage::get_max_expected_time_per_block_key(); - let max_block_duration = query_storage_value::(&client, &key) + let max_block_duration = query_storage_value::(&client, &key) .await .expect("Parameter should be definied."); println!("{:4}Max. block duration: {}", "", max_block_duration); let key = param_storage::get_tx_whitelist_storage_key(); - let vp_whitelist = query_storage_value::>(&client, &key) + let vp_whitelist = query_storage_value::>(&client, &key) .await .expect("Parameter should be definied."); println!("{:4}VP whitelist: {:?}", "", vp_whitelist); let key = param_storage::get_tx_whitelist_storage_key(); - let tx_whitelist = query_storage_value::>(&client, &key) + let tx_whitelist = query_storage_value::>(&client, &key) .await .expect("Parameter should be definied."); println!("{:4}Transactions whitelist: {:?}", "", tx_whitelist); println!("PoS parameters"); let key = pos::params_key(); - let pos_params = query_storage_value::(&client, &key) + let pos_params = query_storage_value::(&client, &key) .await .expect("Parameter should be definied."); println!( @@ -1196,7 +1196,7 @@ pub async fn query_protocol_parameters( } /// Query PoS bond(s) -pub async fn query_bonds(client: &HttpClient, args: args::QueryBonds) { +pub async fn query_bonds(client: &C, args: args::QueryBonds) { let epoch = query_epoch(client).await; match (args.owner, args.validator) { (Some(owner), Some(validator)) => { @@ -1206,16 +1206,16 @@ pub async fn query_bonds(client: &HttpClient, args: args::QueryBonds) { let bond_id = pos::BondId { source, validator }; let bond_key = pos::bond_key(&bond_id); let bonds = - query_storage_value::(&client, &bond_key).await; + query_storage_value::(client, &bond_key).await; // Find owner's unbonded delegations from the given // validator let unbond_key = pos::unbond_key(&bond_id); let unbonds = - query_storage_value::(&client, &unbond_key).await; + query_storage_value::(client, &unbond_key).await; // Find validator's slashes, if any let slashes_key = pos::validator_slashes_key(&bond_id.validator); let slashes = - query_storage_value::(&client, &slashes_key) + query_storage_value::(client, &slashes_key) .await .unwrap_or_default(); @@ -1265,15 +1265,15 @@ pub async fn query_bonds(client: &HttpClient, args: args::QueryBonds) { }; let bond_key = pos::bond_key(&bond_id); let bonds = - query_storage_value::(&client, &bond_key).await; + query_storage_value::(client, &bond_key).await; // Find validator's unbonded self-bonds let unbond_key = pos::unbond_key(&bond_id); let unbonds = - query_storage_value::(&client, &unbond_key).await; + query_storage_value::(client, &unbond_key).await; // Find validator's slashes, if any let slashes_key = pos::validator_slashes_key(&bond_id.validator); let slashes = - query_storage_value::(&client, &slashes_key) + query_storage_value::(client, &slashes_key) .await .unwrap_or_default(); @@ -1308,12 +1308,12 @@ pub async fn query_bonds(client: &HttpClient, args: args::QueryBonds) { // Find owner's bonds to any validator let bonds_prefix = pos::bonds_for_source_prefix(&owner); let bonds = - query_storage_prefix::(&client, &bonds_prefix) + query_storage_prefix::(client, &bonds_prefix) .await; // Find owner's unbonds to any validator let unbonds_prefix = pos::unbonds_for_source_prefix(&owner); let unbonds = - query_storage_prefix::(&client, &unbonds_prefix) + query_storage_prefix::(client, &unbonds_prefix) .await; let mut total: token::Amount = 0.into(); @@ -1326,8 +1326,8 @@ pub async fn query_bonds(client: &HttpClient, args: args::QueryBonds) { // Find validator's slashes, if any let slashes_key = pos::validator_slashes_key(&validator); - let slashes = query_storage_value::( - &client, + let slashes = query_storage_value::( + client, &slashes_key, ) .await @@ -1377,8 +1377,8 @@ pub async fn query_bonds(client: &HttpClient, args: args::QueryBonds) { // Find validator's slashes, if any let slashes_key = pos::validator_slashes_key(&validator); - let slashes = query_storage_value::( - &client, + let slashes = query_storage_value::( + client, &slashes_key, ) .await @@ -1424,12 +1424,12 @@ pub async fn query_bonds(client: &HttpClient, args: args::QueryBonds) { // Find all the bonds let bonds_prefix = pos::bonds_prefix(); let bonds = - query_storage_prefix::(&client, &bonds_prefix) + query_storage_prefix::(client, &bonds_prefix) .await; // Find all the unbonds let unbonds_prefix = pos::unbonds_prefix(); let unbonds = - query_storage_prefix::(&client, &unbonds_prefix) + query_storage_prefix::(client, &unbonds_prefix) .await; let mut total: token::Amount = 0.into(); @@ -1441,8 +1441,8 @@ pub async fn query_bonds(client: &HttpClient, args: args::QueryBonds) { // Find validator's slashes, if any let slashes_key = pos::validator_slashes_key(&validator); - let slashes = query_storage_value::( - &client, + let slashes = query_storage_value::( + client, &slashes_key, ) .await @@ -1492,8 +1492,8 @@ pub async fn query_bonds(client: &HttpClient, args: args::QueryBonds) { // Find validator's slashes, if any let slashes_key = pos::validator_slashes_key(&validator); - let slashes = query_storage_value::( - &client, + let slashes = query_storage_value::( + client, &slashes_key, ) .await @@ -1542,7 +1542,7 @@ pub async fn query_bonds(client: &HttpClient, args: args::QueryBonds) { } /// Query PoS bonded stake -pub async fn query_bonded_stake(client: &HttpClient, args: args::QueryBondedStake) { +pub async fn query_bonded_stake(client: &C, args: args::QueryBondedStake) { let epoch = match args.epoch { Some(epoch) => epoch, None => query_epoch(client).await, @@ -1551,7 +1551,7 @@ pub async fn query_bonded_stake(client: &HttpClient, args: args::QueryBondedStak // Find the validator set let validator_set_key = pos::validator_set_key(); let validator_sets = - query_storage_value::(&client, &validator_set_key) + query_storage_value::(client, &validator_set_key) .await .expect("Validator set should always be set"); let validator_set = validator_sets @@ -1563,8 +1563,8 @@ pub async fn query_bonded_stake(client: &HttpClient, args: args::QueryBondedStak let validator = validator; // Find bonded stake for the given validator let validator_deltas_key = pos::validator_deltas_key(&validator); - let validator_deltas = query_storage_value::( - &client, + let validator_deltas = query_storage_value::( + client, &validator_deltas_key, ) .await; @@ -1627,7 +1627,7 @@ pub async fn query_bonded_stake(client: &HttpClient, args: args::QueryBondedStak } let total_deltas_key = pos::total_deltas_key(); let total_deltas = - query_storage_value::(&client, &total_deltas_key) + query_storage_value::(client, &total_deltas_key) .await .expect("Total bonded stake should always be set"); let total_bonded_stake = total_deltas @@ -1641,8 +1641,8 @@ pub async fn query_bonded_stake(client: &HttpClient, args: args::QueryBondedStak } /// Query PoS validator's commission rate -pub async fn query_commission_rate( - client: &HttpClient, +pub async fn query_commission_rate( + client: &C, args: args::QueryCommissionRate, ) { let epoch = match args.epoch { @@ -1658,13 +1658,13 @@ pub async fn query_commission_rate( pos::validator_commission_rate_key(&validator); let validator_max_commission_change_key = pos::validator_max_commission_rate_change_key(&validator); - let commission_rates = query_storage_value::( - &client, + let commission_rates = query_storage_value::( + client, &validator_commission_key, ) .await; - let max_rate_change = query_storage_value::( - &client, + let max_rate_change = query_storage_value::( + client, &validator_max_commission_change_key, ) .await; @@ -1696,14 +1696,14 @@ pub async fn query_commission_rate( } /// Query PoS slashes -pub async fn query_slashes(client: &HttpClient, args: args::QuerySlashes) { +pub async fn query_slashes(client: &C, args: args::QuerySlashes) { match args.validator { Some(validator) => { let validator = validator; // Find slashes for the given validator let slashes_key = pos::validator_slashes_key(&validator); let slashes = - query_storage_value::(&client, &slashes_key) + query_storage_value::(&client, &slashes_key) .await; match slashes { Some(slashes) => { @@ -1727,7 +1727,7 @@ pub async fn query_slashes(client: &HttpClient, args: args::QuerySlashes) { // Iterate slashes for all validators let slashes_prefix = pos::slashes_prefix(); let slashes = - query_storage_prefix::(&client, &slashes_prefix) + query_storage_prefix::(&client, &slashes_prefix) .await; match slashes { @@ -1765,9 +1765,9 @@ pub async fn query_slashes(client: &HttpClient, args: args::QuerySlashes) { } /// Dry run a transaction -pub async fn dry_run_tx(client: &HttpClient, tx_bytes: Vec) { +pub async fn dry_run_tx(client: &C, tx_bytes: Vec) { let (data, height, prove) = (Some(tx_bytes), None, false); - let result = unwrap_client_response( + let result = unwrap_client_response::( RPC.shell().dry_run_tx(client, data, height, prove).await, ) .data; @@ -1775,40 +1775,40 @@ pub async fn dry_run_tx(client: &HttpClient, tx_bytes: Vec) { } /// Get account's public key stored in its storage sub-space -pub async fn get_public_key( - client: &HttpClient, +pub async fn get_public_key( + client: &C, address: &Address, ) -> Option { let key = pk_key(address); - query_storage_value(&client, &key).await + query_storage_value(client, &key).await } /// Check if the given address is a known validator. -pub async fn is_validator( - client: &HttpClient, +pub async fn is_validator( + client: &C, address: &Address, ) -> bool { - unwrap_client_response(RPC.vp().pos().is_validator(client, address).await) + unwrap_client_response::(RPC.vp().pos().is_validator(client, address).await) } /// Check if a given address is a known delegator -pub async fn is_delegator( - client: &HttpClient, +pub async fn is_delegator( + client: &C, address: &Address, ) -> bool { let bonds_prefix = pos::bonds_for_source_prefix(address); let bonds = - query_storage_prefix::(&client, &bonds_prefix).await; + query_storage_prefix::(&client, &bonds_prefix).await; bonds.is_some() && bonds.unwrap().count() > 0 } -pub async fn is_delegator_at( - client: &HttpClient, +pub async fn is_delegator_at( + client: &C, address: &Address, epoch: Epoch, ) -> bool { let key = pos::bonds_for_source_prefix(address); - let bonds_iter = query_storage_prefix::(client, &key).await; + let bonds_iter = query_storage_prefix::(client, &key).await; if let Some(mut bonds) = bonds_iter { bonds.any(|(_, bond)| bond.get(epoch).is_some()) } else { @@ -1819,15 +1819,15 @@ pub async fn is_delegator_at( /// Check if the address exists on chain. Established address exists if it has a /// stored validity predicate. Implicit and internal addresses always return /// true. -pub async fn known_address( - client: &HttpClient, +pub async fn known_address( + client: &C, address: &Address, ) -> bool { match address { Address::Established(_) => { // Established account exists if it has a VP let key = storage::Key::validity_predicate(address); - query_has_storage_key(&client, &key).await + query_has_storage_key(client, &key).await } Address::Implicit(_) | Address::Internal(_) => true, } @@ -1968,7 +1968,7 @@ fn process_unbonds_query( } /// Query for all conversions. -pub async fn query_conversions(client: &HttpClient, args: args::QueryConversions) { +pub async fn query_conversions(client: &C, args: args::QueryConversions) { // The chosen token type of the conversions let target_token = args.token; // To facilitate human readable token addresses @@ -1979,7 +1979,7 @@ pub async fn query_conversions(client: &HttpClient, args: args::QueryConversions .push(&(token::CONVERSION_KEY_PREFIX.to_owned())) .unwrap(); let conv_state = - query_storage_value::(&client, &state_key) + query_storage_value::(&client, &state_key) .await .expect("Conversions should be defined"); // Track whether any non-sentinel conversions are found @@ -2030,8 +2030,8 @@ pub async fn query_conversions(client: &HttpClient, args: args::QueryConversions } /// Query a conversion. -pub async fn query_conversion( - client: HttpClient, +pub async fn query_conversion( + client: &C, asset_type: AssetType, ) -> Option<( Address, @@ -2039,14 +2039,14 @@ pub async fn query_conversion( masp_primitives::transaction::components::Amount, MerklePath, )> { - Some(unwrap_client_response( - RPC.shell().read_conversion(&client, &asset_type).await, + Some(unwrap_client_response::( + RPC.shell().read_conversion(client, &asset_type).await, )) } /// Query a storage value and decode it with [`BorshDeserialize`]. -pub async fn query_storage_value( - client: &HttpClient, +pub async fn query_storage_value( + client: &C, key: &storage::Key, ) -> Option where @@ -2057,7 +2057,7 @@ where // returns 0 bytes when the key is not found. let maybe_unit = T::try_from_slice(&[]); if let Ok(unit) = maybe_unit { - return if unwrap_client_response( + return if unwrap_client_response::( RPC.shell().storage_has_key(client, key).await, ) { Some(unit) @@ -2066,7 +2066,7 @@ where }; } - let response = unwrap_client_response( + let response = unwrap_client_response::( RPC.shell() .storage_value(client, None, None, false, key) .await, @@ -2083,14 +2083,14 @@ where } /// Query a storage value and the proof without decoding. -pub async fn query_storage_value_bytes( - client: &HttpClient, +pub async fn query_storage_value_bytes( + client: &C, key: &storage::Key, height: Option, prove: bool, ) -> (Option>, Option) { let data = None; - let response = unwrap_client_response( + let response = unwrap_client_response::( RPC.shell() .storage_value(client, data, height, prove, key) .await, @@ -2105,14 +2105,14 @@ pub async fn query_storage_value_bytes( /// Query a range of storage values with a matching prefix and decode them with /// [`BorshDeserialize`]. Returns an iterator of the storage keys paired with /// their associated values. -pub async fn query_storage_prefix( - client: &HttpClient, +pub async fn query_storage_prefix( + client: &C, key: &storage::Key, ) -> Option> where T: BorshDeserialize, { - let values = unwrap_client_response( + let values = unwrap_client_response::( RPC.shell() .storage_prefix(client, None, None, false, key) .await, @@ -2138,11 +2138,11 @@ where } /// Query to check if the given storage key exists. -pub async fn query_has_storage_key( - client: &HttpClient, +pub async fn query_has_storage_key( + client: &C, key: &storage::Key, ) -> bool { - unwrap_client_response(RPC.shell().storage_has_key(client, key).await) + unwrap_client_response::(RPC.shell().storage_has_key(client, key).await) } /// Represents a query for an event pertaining to the specified transaction @@ -2186,26 +2186,26 @@ impl<'a> From> for Query { /// Call the corresponding `tx_event_query` RPC method, to fetch /// the current status of a transation. -pub async fn query_tx_events( - client: &HttpClient, +pub async fn query_tx_events( + client: &C, tx_event_query: TxEventQuery<'_>, -) -> eyre::Result> { - let tx_hash: Hash = tx_event_query.tx_hash().try_into()?; +) -> std::result::Result, ::Error> { + let tx_hash: Hash = tx_event_query.tx_hash().try_into().unwrap(); match tx_event_query { TxEventQuery::Accepted(_) => RPC .shell() .accepted(client, &tx_hash) .await - .wrap_err_with(|| { + /*.wrap_err_with(|| { eyre!("Failed querying whether a transaction was accepted") - }), + })*/, TxEventQuery::Applied(_) => RPC .shell() .applied(client, &tx_hash) .await - .wrap_err_with(|| { + /*.wrap_err_with(|| { eyre!("Error querying whether a transaction was applied") - }), + })*/, } } @@ -2318,8 +2318,8 @@ pub async fn query_result(client: &WebSocketClient, args: args::QueryResult) { } } -pub async fn get_proposal_votes( - client: &HttpClient, +pub async fn get_proposal_votes( + client: &C, epoch: Epoch, proposal_id: u64, ) -> Votes { @@ -2328,7 +2328,7 @@ pub async fn get_proposal_votes( let vote_prefix_key = gov_storage::get_proposal_vote_prefix_key(proposal_id); let vote_iter = - query_storage_prefix::(client, &vote_prefix_key).await; + query_storage_prefix::(client, &vote_prefix_key).await; let mut yay_validators: HashMap = HashMap::new(); let mut yay_delegators: HashMap> = @@ -2385,8 +2385,8 @@ pub async fn get_proposal_votes( } } -pub async fn get_proposal_offline_votes( - client: &HttpClient, +pub async fn get_proposal_offline_votes( + client: &C, proposal: OfflineProposal, files: HashSet, ) -> Votes { @@ -2436,7 +2436,7 @@ pub async fn get_proposal_offline_votes( { let key = pos::bonds_for_source_prefix(&proposal_vote.address); let bonds_iter = - query_storage_prefix::(client, &key).await; + query_storage_prefix::(client, &key).await; if let Some(bonds) = bonds_iter { for (key, epoched_bonds) in bonds { // Look-up slashes for the validator in this key and @@ -2446,7 +2446,7 @@ pub async fn get_proposal_offline_votes( "Delegation key should contain validator address.", ); let slashes_key = pos::validator_slashes_key(&validator); - let slashes = query_storage_value::( + let slashes = query_storage_value::( client, &slashes_key, ) @@ -2515,8 +2515,8 @@ pub async fn get_proposal_offline_votes( } // Compute the result of a proposal -pub async fn compute_tally( - client: &HttpClient, +pub async fn compute_tally( + client: &C, epoch: Epoch, votes: Votes, ) -> ProposalResult { @@ -2569,21 +2569,21 @@ pub async fn compute_tally( } } -pub async fn get_bond_amount_at( - client: &HttpClient, +pub async fn get_bond_amount_at( + client: &C, delegator: &Address, validator: &Address, epoch: Epoch, ) -> Option { let slashes_key = pos::validator_slashes_key(validator); - let slashes = query_storage_value::(client, &slashes_key) + let slashes = query_storage_value::(client, &slashes_key) .await .unwrap_or_default(); let bond_key = pos::bond_key(&BondId { source: delegator.clone(), validator: validator.clone(), }); - let epoched_bonds = query_storage_value::(client, &bond_key).await; + let epoched_bonds = query_storage_value::(client, &bond_key).await; match epoched_bonds { Some(epoched_bonds) => { let mut delegated_amount: token::Amount = 0.into(); @@ -2621,11 +2621,11 @@ pub async fn get_bond_amount_at( } } -pub async fn get_all_validators( - client: &HttpClient, +pub async fn get_all_validators( + client: &C, epoch: Epoch, ) -> HashSet
{ - unwrap_client_response( + unwrap_client_response::( RPC.vp() .pos() .validator_addresses(client, &Some(epoch)) @@ -2633,21 +2633,21 @@ pub async fn get_all_validators( ) } -pub async fn get_total_staked_tokens( - client: &HttpClient, +pub async fn get_total_staked_tokens( + client: &C, epoch: Epoch, ) -> token::Amount { - unwrap_client_response( + unwrap_client_response::( RPC.vp().pos().total_stake(client, &Some(epoch)).await, ) } -async fn get_validator_stake( - client: &HttpClient, +async fn get_validator_stake( + client: &C, epoch: Epoch, validator: &Address, ) -> token::Amount { - unwrap_client_response( + unwrap_client_response::( RPC.vp() .pos() .validator_stake(client, validator, &Some(epoch)) @@ -2655,42 +2655,42 @@ async fn get_validator_stake( ) } -pub async fn get_delegators_delegation( - client: &HttpClient, +pub async fn get_delegators_delegation( + client: &C, address: &Address, ) -> HashSet
{ - unwrap_client_response(RPC.vp().pos().delegations(client, address).await) + unwrap_client_response::(RPC.vp().pos().delegations(client, address).await) } -pub async fn get_governance_parameters(client: &HttpClient) -> GovParams { +pub async fn get_governance_parameters(client: &C) -> GovParams { use namada::types::token::Amount; let key = gov_storage::get_max_proposal_code_size_key(); - let max_proposal_code_size = query_storage_value::(client, &key) + let max_proposal_code_size = query_storage_value::(client, &key) .await .expect("Parameter should be definied."); let key = gov_storage::get_max_proposal_content_key(); - let max_proposal_content_size = query_storage_value::(client, &key) + let max_proposal_content_size = query_storage_value::(client, &key) .await .expect("Parameter should be definied."); let key = gov_storage::get_min_proposal_fund_key(); - let min_proposal_fund = query_storage_value::(client, &key) + let min_proposal_fund = query_storage_value::(client, &key) .await .expect("Parameter should be definied."); let key = gov_storage::get_min_proposal_grace_epoch_key(); - let min_proposal_grace_epochs = query_storage_value::(client, &key) + let min_proposal_grace_epochs = query_storage_value::(client, &key) .await .expect("Parameter should be definied."); let key = gov_storage::get_min_proposal_period_key(); - let min_proposal_period = query_storage_value::(client, &key) + let min_proposal_period = query_storage_value::(client, &key) .await .expect("Parameter should be definied."); let key = gov_storage::get_max_proposal_period_key(); - let max_proposal_period = query_storage_value::(client, &key) + let max_proposal_period = query_storage_value::(client, &key) .await .expect("Parameter should be definied."); @@ -2714,9 +2714,9 @@ fn lookup_alias(wallet: &Wallet, addr: &Address) -> String { } /// A helper to unwrap client's response. Will shut down process on error. -fn unwrap_client_response(response: Result) -> T { +fn unwrap_client_response(response: Result) -> T { response.unwrap_or_else(|err| { - eprintln!("Error in the query {}", err); + eprintln!("Error in the query"); cli::safe_exit(1) }) } diff --git a/apps/src/lib/client/signing.rs b/apps/src/lib/client/signing.rs index 8146dc249c..bdaf808e1d 100644 --- a/apps/src/lib/client/signing.rs +++ b/apps/src/lib/client/signing.rs @@ -15,11 +15,12 @@ use crate::cli::{self, args}; use crate::client::tendermint_rpc_types::TxBroadcastData; use namada::ledger::wallet::Wallet; use namada::ledger::wallet::WalletUtils; +use crate::facade::tendermint_rpc::Client; /// Find the public key for the given address and try to load the keypair /// for it from the wallet. Panics if the key cannot be found or loaded. -pub async fn find_keypair( - client: &HttpClient, +pub async fn find_keypair( + client: &C, wallet: &mut Wallet, addr: &Address, ) -> common::SecretKey { @@ -86,8 +87,8 @@ pub enum TxSigningKey { /// signer. Return the given signing key or public key of the given signer if /// possible. If no explicit signer given, use the `default`. If no `default` /// is given, panics. -pub async fn tx_signer( - client: &HttpClient, +pub async fn tx_signer( + client: &C, wallet: &mut Wallet, args: &args::Tx, mut default: TxSigningKey, @@ -105,7 +106,7 @@ pub async fn tx_signer( } TxSigningKey::WalletAddress(signer) => { let signer = signer; - let signing_key = find_keypair::( + let signing_key = find_keypair::( client, wallet, &signer, @@ -115,14 +116,14 @@ pub async fn tx_signer( // PK first if matches!(signer, Address::Implicit(_)) { let pk: common::PublicKey = signing_key.ref_to(); - super::tx::reveal_pk_if_needed::(client, wallet, &pk, args).await; + super::tx::reveal_pk_if_needed::(client, wallet, &pk, args).await; } signing_key } TxSigningKey::SecretKey(signing_key) => { // Check if the signing key needs to reveal its PK first let pk: common::PublicKey = signing_key.ref_to(); - super::tx::reveal_pk_if_needed::(client, wallet, &pk, args).await; + super::tx::reveal_pk_if_needed::(client, wallet, &pk, args).await; signing_key } TxSigningKey::None => { @@ -142,14 +143,14 @@ pub async fn tx_signer( /// hashes needed for monitoring the tx on chain. /// /// If it is a dry run, it is not put in a wrapper, but returned as is. -pub async fn sign_tx( - client: &HttpClient, +pub async fn sign_tx( + client: &C, wallet: &mut Wallet, tx: Tx, args: &args::Tx, default: TxSigningKey, ) -> TxBroadcastData { - let keypair = tx_signer::(client, wallet, args, default).await; + let keypair = tx_signer::(client, wallet, args, default).await; let tx = tx.sign(&keypair); let epoch = rpc::query_epoch(client) diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index f3248d277a..b0cdffef2e 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -71,8 +71,8 @@ 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( - client: &HttpClient, +pub async fn submit_custom( + client: &C, wallet: &mut Wallet, args: args::TxCustom, ) { @@ -80,12 +80,12 @@ pub async fn submit_custom( let data = args.data_path; let tx = Tx::new(tx_code, data); let initialized_accounts = - process_tx::(client, wallet, &args.tx, tx, TxSigningKey::None).await; + process_tx::(client, wallet, &args.tx, tx, TxSigningKey::None).await; save_initialized_accounts::(wallet, &args.tx, initialized_accounts).await; } -pub async fn submit_update_vp( - client: &HttpClient, +pub async fn submit_update_vp( + client: &C, wallet: &mut Wallet, args: args::TxUpdateVp, ) { @@ -95,7 +95,7 @@ pub async fn submit_update_vp( match &addr { Address::Established(_) => { let exists = - rpc::known_address(client, &addr).await; + rpc::known_address::(client, &addr).await; if !exists { eprintln!("The address {} doesn't exist on chain.", addr); if !args.tx.force { @@ -139,11 +139,11 @@ pub async fn submit_update_vp( let data = data.try_to_vec().expect("Encoding tx data shouldn't fail"); let tx = Tx::new(tx_code, Some(data)); - process_tx::(client, wallet, &args.tx, tx, TxSigningKey::WalletAddress(args.addr)).await; + process_tx::(client, wallet, &args.tx, tx, TxSigningKey::WalletAddress(args.addr)).await; } -pub async fn submit_init_account( - client: &HttpClient, +pub async fn submit_init_account( + client: &C, wallet: &mut Wallet, args: args::TxInitAccount, ) { @@ -166,13 +166,13 @@ pub async fn submit_init_account( let tx = Tx::new(tx_code, Some(data)); let initialized_accounts = - process_tx::(client, wallet, &args.tx, tx, TxSigningKey::WalletAddress(args.source)) + process_tx::(client, wallet, &args.tx, tx, TxSigningKey::WalletAddress(args.source)) .await; save_initialized_accounts::(wallet, &args.tx, initialized_accounts).await; } -pub async fn submit_init_validator( - client: &HttpClient, +pub async fn submit_init_validator( + client: &C, mut ctx: Context, args::TxInitValidator { tx: tx_args, @@ -292,7 +292,7 @@ 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 initialized_accounts = - process_tx::(client, &mut ctx.wallet, &tx_args, tx, TxSigningKey::WalletAddress(source)) + process_tx::(client, &mut ctx.wallet, &tx_args, tx, TxSigningKey::WalletAddress(source)) .await; if !tx_args.dry_run { let (validator_address_alias, validator_address) = @@ -413,14 +413,14 @@ impl masp::ShieldedUtils for CLIShieldedUtils { type C = HttpClient; async fn query_storage_value( - client: &HttpClient, + client: &Self::C, key: &storage::Key, ) -> Option where T: BorshDeserialize { - query_storage_value::(client, &key).await + query_storage_value::(client, &key).await } - async fn query_epoch(client: &HttpClient) -> Epoch { + async fn query_epoch(client: &Self::C) -> Epoch { rpc::query_epoch(client).await } @@ -483,7 +483,7 @@ impl masp::ShieldedUtils for CLIShieldedUtils { /// Query a conversion. async fn query_conversion( - client: &HttpClient, + client: &Self::C, asset_type: AssetType, ) -> Option<( Address, @@ -491,18 +491,18 @@ impl masp::ShieldedUtils for CLIShieldedUtils { masp_primitives::transaction::components::Amount, MerklePath, )> { - query_conversion(client.clone(), asset_type).await + query_conversion(client, asset_type).await } - async fn query_results(client: &HttpClient) -> Vec { + async fn query_results(client: &Self::C) -> Vec { rpc::query_results(client).await } } -pub async fn submit_transfer, V: WalletUtils>( - client: &HttpClient, +pub async fn submit_transfer>( + client: &C, wallet: &mut Wallet, shielded: &mut ShieldedContext, args: args::TxTransfer, @@ -513,7 +513,7 @@ pub async fn submit_transfer, V: WalletUtils>( let target = transfer_target.effective_address(); // Check that the source address exists on chain let source_exists = - rpc::known_address(client, &source).await; + rpc::known_address::(client, &source).await; if !source_exists { eprintln!("The source address {} doesn't exist on chain.", source); if !args.tx.force { @@ -522,7 +522,7 @@ pub async fn submit_transfer, V: WalletUtils>( } // Check that the target address exists on chain let target_exists = - rpc::known_address(client, &target).await; + rpc::known_address::(client, &target).await; if !target_exists { eprintln!("The target address {} doesn't exist on chain.", target); if !args.tx.force { @@ -532,7 +532,7 @@ pub async fn submit_transfer, V: WalletUtils>( let token = &args.token; // Check that the token address exists on chain let token_exists = - rpc::known_address(client, &token) + rpc::known_address::(client, &token) .await; if !token_exists { eprintln!( @@ -558,7 +558,7 @@ pub async fn submit_transfer, V: WalletUtils>( } None => (None, token::balance_key(&token, &source)), }; - match rpc::query_storage_value::(&client, &balance_key).await + match rpc::query_storage_value::(&client, &balance_key).await { Some(balance) => { if balance < args.amount { @@ -613,7 +613,7 @@ pub async fn submit_transfer, V: WalletUtils>( }; // 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::(client, wallet, &args.tx, default_signer.clone()) + let chosen_signer = tx_signer::(client, wallet, &args.tx, default_signer.clone()) .await .ref_to(); let shielded_gas = masp_tx_key().ref_to() == chosen_signer; @@ -669,18 +669,18 @@ pub async fn submit_transfer, V: WalletUtils>( let tx = Tx::new(tx_code, Some(data)); let signing_address = TxSigningKey::WalletAddress(source); - process_tx::(client, wallet, &args.tx, tx, signing_address).await; + process_tx::(client, wallet, &args.tx, tx, signing_address).await; } -pub async fn submit_ibc_transfer( - client: &HttpClient, +pub async fn submit_ibc_transfer( + client: &C, wallet: &mut Wallet, args: args::TxIbcTransfer, ) { let source = args.source.clone(); // Check that the source address exists on chain let source_exists = - rpc::known_address(client, &source).await; + rpc::known_address::(client, &source).await; if !source_exists { eprintln!("The source address {} doesn't exist on chain.", source); if !args.tx.force { @@ -693,7 +693,7 @@ pub async fn submit_ibc_transfer( let token = args.token; // Check that the token address exists on chain let token_exists = - rpc::known_address(client, &token).await; + rpc::known_address::(client, &token).await; if !token_exists { eprintln!("The token address {} doesn't exist on chain.", token); if !args.tx.force { @@ -712,7 +712,7 @@ pub async fn submit_ibc_transfer( } None => (None, token::balance_key(&token, &source)), }; - match rpc::query_storage_value::(&client, &balance_key).await + match rpc::query_storage_value::(&client, &balance_key).await { Some(balance) => { if balance < args.amount { @@ -782,12 +782,12 @@ pub async fn submit_ibc_transfer( .expect("Encoding tx data shouldn't fail"); let tx = Tx::new(tx_code, Some(data)); - process_tx::(client, wallet, &args.tx, tx, TxSigningKey::WalletAddress(args.source)) + process_tx::(client, wallet, &args.tx, tx, TxSigningKey::WalletAddress(args.source)) .await; } -pub async fn submit_init_proposal( - client: &HttpClient, +pub async fn submit_init_proposal( + client: &C, mut ctx: Context, args: args::InitProposal, ) { @@ -796,7 +796,7 @@ pub async fn submit_init_proposal( serde_json::from_reader(file).expect("JSON was not well-formatted"); let signer = WalletAddress::new(proposal.clone().author.to_string()); - let governance_parameters = rpc::get_governance_parameters(&client).await; + let governance_parameters = rpc::get_governance_parameters(client).await; let current_epoch = rpc::query_epoch(client) .await; @@ -856,7 +856,7 @@ pub async fn submit_init_proposal( if args.offline { let signer = ctx.get(&signer); - let signing_key = find_keypair::( + let signing_key = find_keypair::( client, &mut ctx.wallet, &signer, @@ -893,7 +893,7 @@ pub async fn submit_init_proposal( }; let balance = rpc::get_token_balance( - &client, + client, &args.native_token, &proposal.author, ) @@ -922,13 +922,13 @@ pub async fn submit_init_proposal( let tx_code = args.tx_code_path; let tx = Tx::new(tx_code, Some(data)); - process_tx::(client, &mut ctx.wallet, &args.tx, tx, TxSigningKey::WalletAddress(signer)) + process_tx::(client, &mut ctx.wallet, &args.tx, tx, TxSigningKey::WalletAddress(signer)) .await; } } -pub async fn submit_vote_proposal( - client: &HttpClient, +pub async fn submit_vote_proposal( + client: &C, wallet: &mut Wallet, args: args::VoteProposal, ) { @@ -958,7 +958,7 @@ pub async fn submit_vote_proposal( safe_exit(1) } - let signing_key = find_keypair::( + let signing_key = find_keypair::( client, wallet, &signer, @@ -996,7 +996,7 @@ pub async fn submit_vote_proposal( let proposal_id = args.proposal_id.unwrap(); let proposal_start_epoch_key = gov_storage::get_voting_start_epoch_key(proposal_id); - let proposal_start_epoch = rpc::query_storage_value::( + let proposal_start_epoch = rpc::query_storage_value::( &client, &proposal_start_epoch_key, ) @@ -1016,7 +1016,7 @@ pub async fn submit_vote_proposal( } } let mut delegations = - rpc::get_delegators_delegation(&client, &voter_address) + rpc::get_delegators_delegation(client, &voter_address) .await; // Optimize by quering if a vote from a validator @@ -1034,7 +1034,7 @@ pub async fn submit_vote_proposal( .await { delegations = filter_delegations( - &client, + client, delegations, proposal_id, &args.vote, @@ -1055,7 +1055,7 @@ pub async fn submit_vote_proposal( let tx_code = args.tx_code_path; let tx = Tx::new(tx_code, Some(data)); - process_tx::( + process_tx::( client, wallet, &args.tx, @@ -1077,8 +1077,8 @@ pub async fn submit_vote_proposal( } } -pub async fn submit_reveal_pk( - client: &HttpClient, +pub async fn submit_reveal_pk( + client: &C, wallet: &mut Wallet, args: args::RevealPk, ) { @@ -1087,14 +1087,14 @@ pub async fn submit_reveal_pk( public_key, } = args; let public_key = public_key; - if !reveal_pk_if_needed::(client, wallet, &public_key, &args).await { + if !reveal_pk_if_needed::(client, wallet, &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( - client: &HttpClient, +pub async fn reveal_pk_if_needed( + client: &C, wallet: &mut Wallet, public_key: &common::PublicKey, args: &args::Tx, @@ -1104,22 +1104,22 @@ pub async fn reveal_pk_if_needed( if args.force || !has_revealed_pk(client, &addr).await { // If not, submit it - submit_reveal_pk_aux::(client, wallet, public_key, args).await; + submit_reveal_pk_aux::(client, wallet, public_key, args).await; true } else { false } } -pub async fn has_revealed_pk( - client: &HttpClient, +pub async fn has_revealed_pk( + client: &C, addr: &Address, ) -> bool { rpc::get_public_key(client, addr).await.is_some() } -pub async fn submit_reveal_pk_aux( - client: &HttpClient, +pub async fn submit_reveal_pk_aux( + client: &C, wallet: &mut Wallet, public_key: &common::PublicKey, args: &args::Tx, @@ -1137,10 +1137,10 @@ pub async fn submit_reveal_pk_aux( signing_key.clone() } else if let Some(signer) = args.signer.as_ref() { let signer = signer; - find_keypair::(client, wallet, &signer) + find_keypair::(client, wallet, &signer) .await } else { - find_keypair::(client, wallet, &addr).await + find_keypair::(client, wallet, &addr).await }; let epoch = rpc::query_epoch(client) .await; @@ -1192,8 +1192,8 @@ pub async fn submit_reveal_pk_aux( /// Check if current epoch is in the last third of the voting period of the /// proposal. This ensures that it is safe to optimize the vote writing to /// storage. -async fn is_safe_voting_window( - client: &HttpClient, +async fn is_safe_voting_window( + client: &C, proposal_id: u64, proposal_start_epoch: Epoch, ) -> bool { @@ -1202,7 +1202,7 @@ async fn is_safe_voting_window( let proposal_end_epoch_key = gov_storage::get_voting_end_epoch_key(proposal_id); let proposal_end_epoch = - rpc::query_storage_value::(client, &proposal_end_epoch_key) + rpc::query_storage_value::(client, &proposal_end_epoch_key) .await; match proposal_end_epoch { @@ -1222,8 +1222,8 @@ async fn is_safe_voting_window( /// Removes validators whose vote corresponds to that of the delegator (needless /// vote) -async fn filter_delegations( - client: &HttpClient, +async fn filter_delegations( + client: &C, delegations: HashSet
, proposal_id: u64, delegator_vote: &ProposalVote, @@ -1242,7 +1242,7 @@ async fn filter_delegations( ); if let Some(validator_vote) = - rpc::query_storage_value::(client, &vote_key) + rpc::query_storage_value::(client, &vote_key) .await { if &validator_vote == delegator_vote { @@ -1257,8 +1257,8 @@ async fn filter_delegations( delegations.into_iter().flatten().collect() } -pub async fn submit_bond( - client: &HttpClient, +pub async fn submit_bond( + client: &C, wallet: &mut Wallet, args: args::Bond, ) { @@ -1279,7 +1279,7 @@ pub async fn submit_bond( // Check that the source address exists on chain if let Some(source) = &source { let source_exists = - rpc::known_address(client, source).await; + rpc::known_address::(client, source).await; if !source_exists { eprintln!("The source address {} doesn't exist on chain.", source); if !args.tx.force { @@ -1291,7 +1291,7 @@ pub async fn submit_bond( // balance let bond_source = source.as_ref().unwrap_or(&validator); let balance_key = token::balance_key(&args.native_token, bond_source); - match rpc::query_storage_value::(&client, &balance_key).await + match rpc::query_storage_value::(&client, &balance_key).await { Some(balance) => { if balance < args.amount { @@ -1323,7 +1323,7 @@ pub async fn submit_bond( let tx = Tx::new(tx_code, Some(data)); let default_signer = args.source.unwrap_or(args.validator); - process_tx::( + process_tx::( client, wallet, &args.tx, @@ -1333,8 +1333,8 @@ pub async fn submit_bond( .await; } -pub async fn submit_unbond( - client: &HttpClient, +pub async fn submit_unbond( + client: &C, wallet: &mut Wallet, args: args::Unbond, ) { @@ -1362,7 +1362,7 @@ pub async fn submit_unbond( validator: validator.clone(), }; let bond_key = ledger::pos::bond_key(&bond_id); - let bonds = rpc::query_storage_value::(&client, &bond_key).await; + let bonds = rpc::query_storage_value::(&client, &bond_key).await; match bonds { Some(bonds) => { let mut bond_amount: token::Amount = 0.into(); @@ -1400,7 +1400,7 @@ pub async fn submit_unbond( let tx = Tx::new(tx_code, Some(data)); let default_signer = args.source.unwrap_or(args.validator); - process_tx::( + process_tx::( client, wallet, &args.tx, @@ -1410,8 +1410,8 @@ pub async fn submit_unbond( .await; } -pub async fn submit_withdraw( - client: &HttpClient, +pub async fn submit_withdraw( + client: &C, wallet: &mut Wallet, args: args::Withdraw, ) { @@ -1442,7 +1442,7 @@ pub async fn submit_withdraw( validator: validator.clone(), }; let bond_key = ledger::pos::unbond_key(&bond_id); - let unbonds = rpc::query_storage_value::(&client, &bond_key).await; + let unbonds = rpc::query_storage_value::(&client, &bond_key).await; match unbonds { Some(unbonds) => { let mut unbonded_amount: token::Amount = 0.into(); @@ -1475,7 +1475,7 @@ pub async fn submit_withdraw( let tx = Tx::new(tx_code, Some(data)); let default_signer = args.source.unwrap_or(args.validator); - process_tx::( + process_tx::( client, wallet, &args.tx, @@ -1485,8 +1485,8 @@ pub async fn submit_withdraw( .await; } -pub async fn submit_validator_commission_change( - client: &HttpClient, +pub async fn submit_validator_commission_change( + client: &C, wallet: &mut Wallet, args: args::TxCommissionRateChange, ) { @@ -1508,12 +1508,12 @@ pub async fn submit_validator_commission_change( ledger::pos::validator_commission_rate_key(&validator); let max_commission_rate_change_key = ledger::pos::validator_max_commission_rate_change_key(&validator); - let commission_rates = rpc::query_storage_value::( + let commission_rates = rpc::query_storage_value::( &client, &commission_rate_key, ) .await; - let max_change = rpc::query_storage_value::( + let max_change = rpc::query_storage_value::( &client, &max_commission_rate_change_key, ) @@ -1556,7 +1556,7 @@ pub async fn submit_validator_commission_change( let tx = Tx::new(tx_code, Some(data)); let default_signer = args.validator.clone(); - process_tx::( + process_tx::( client, wallet, &args.tx, @@ -1568,14 +1568,14 @@ 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( - client: &HttpClient, +async fn process_tx( + client: &C, wallet: &mut Wallet, args: &args::Tx, tx: Tx, default_signer: TxSigningKey, ) -> Vec
{ - let to_broadcast = sign_tx::(client, wallet, tx, args, default_signer).await; + let to_broadcast = sign_tx::(client, wallet, tx, args, default_signer).await; // NOTE: use this to print the request JSON body: // let request = @@ -1684,8 +1684,8 @@ async fn save_initialized_accounts( /// the tx has been successfully included into the mempool of a validator /// /// In the case of errors in any of those stages, an error message is returned -pub async fn broadcast_tx( - rpc_cli: &HttpClient, +pub async fn broadcast_tx( + rpc_cli: &C, to_broadcast: &TxBroadcastData, ) -> Result { let (tx, wrapper_tx_hash, decrypted_tx_hash) = match to_broadcast { @@ -1729,8 +1729,8 @@ pub async fn broadcast_tx( /// 3. The decrypted payload of the tx has been included on the blockchain. /// /// In the case of errors in any of those stages, an error message is returned -pub async fn submit_tx( - client: &HttpClient, +pub async fn submit_tx( + client: &C, to_broadcast: TxBroadcastData, ) -> Result { let (_, wrapper_hash, decrypted_hash) = match &to_broadcast { From 0ca277f345010561a4df88850697431370ffc53d Mon Sep 17 00:00:00 2001 From: Murisi Tarusenga Date: Sun, 18 Dec 2022 10:44:06 +0200 Subject: [PATCH 24/51] Moved functions from rpc.rs into the shared crate. --- apps/src/lib/client/rpc.rs | 474 +----------- apps/src/lib/client/signing.rs | 2 +- apps/src/lib/client/tendermint_rpc_types.rs | 91 --- apps/src/lib/client/tx.rs | 6 +- shared/src/ledger/mod.rs | 1 + shared/src/ledger/rpc.rs | 774 ++++++++++++++++++++ 6 files changed, 816 insertions(+), 532 deletions(-) create mode 100644 shared/src/ledger/rpc.rs diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index 203f951999..177cd79c4b 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -53,7 +53,7 @@ use tokio::time::{Duration, Instant}; use namada::ledger::wallet::Wallet; use crate::cli::{self, args}; -use crate::client::tendermint_rpc_types::TxResponse; +use namada::ledger::rpc::TxResponse; use namada::ledger::masp::{Conversions, PinnedBalanceError}; use crate::facade::tendermint::merkle::proof::Proof; use crate::facade::tendermint_rpc::error::Error as TError; @@ -68,13 +68,13 @@ use crate::facade::tendermint_rpc::{ /// error. pub async fn query_tx_status( client: &C, - status: TxEventQuery<'_>, + status: namada::ledger::rpc::TxEventQuery<'_>, deadline: Instant, ) -> Event { const ONE_SECOND: Duration = Duration::from_secs(1); // sleep for the duration of `backoff`, // and update the underlying value - async fn sleep_update(query: TxEventQuery<'_>, backoff: &mut Duration) { + async fn sleep_update(query: namada::ledger::rpc::TxEventQuery<'_>, backoff: &mut Duration) { tracing::debug!( ?query, duration = ?backoff, @@ -114,28 +114,19 @@ pub async fn query_tx_status /// Query the epoch of the last committed block pub async fn query_epoch(client: &C) -> Epoch { - let epoch = unwrap_client_response::(RPC.shell().epoch(client).await); - println!("Last committed epoch: {}", epoch); - epoch + namada::ledger::rpc::query_epoch(client).await } /// Query the last committed block pub async fn query_block( client: &C, ) -> crate::facade::tendermint_rpc::endpoint::block::Response { - let response = client.latest_block().await.unwrap(); - println!( - "Last committed block ID: {}, height: {}, time: {}", - response.block_id, - response.block.header.height, - response.block.header.time - ); - response + namada::ledger::rpc::query_block(client).await } /// Query the results of the last committed block pub async fn query_results(client: &C) -> Vec { - unwrap_client_response::(RPC.shell().read_results(client).await) + namada::ledger::rpc::query_results(client).await } /// Query the specified accepted transfers from the ledger @@ -988,8 +979,7 @@ pub async fn get_token_balance Option { - let balance_key = balance_key(token, owner); - query_storage_value(client, &balance_key).await + namada::ledger::rpc::get_token_balance(client, token, owner).await } pub async fn query_proposal_result( @@ -1779,8 +1769,7 @@ pub async fn get_public_key( client: &C, address: &Address, ) -> Option { - let key = pk_key(address); - query_storage_value(client, &key).await + namada::ledger::rpc::get_public_key(client, address).await } /// Check if the given address is a known validator. @@ -1788,7 +1777,7 @@ pub async fn is_validator( client: &C, address: &Address, ) -> bool { - unwrap_client_response::(RPC.vp().pos().is_validator(client, address).await) + namada::ledger::rpc::is_validator(client, address).await } /// Check if a given address is a known delegator @@ -1796,10 +1785,7 @@ pub async fn is_delegator( client: &C, address: &Address, ) -> bool { - let bonds_prefix = pos::bonds_for_source_prefix(address); - let bonds = - query_storage_prefix::(&client, &bonds_prefix).await; - bonds.is_some() && bonds.unwrap().count() > 0 + namada::ledger::rpc::is_delegator(client, address).await } pub async fn is_delegator_at( @@ -1807,13 +1793,7 @@ pub async fn is_delegator_at address: &Address, epoch: Epoch, ) -> bool { - let key = pos::bonds_for_source_prefix(address); - let bonds_iter = query_storage_prefix::(client, &key).await; - if let Some(mut bonds) = bonds_iter { - bonds.any(|(_, bond)| bond.get(epoch).is_some()) - } else { - false - } + namada::ledger::rpc::is_delegator_at(client, address, epoch).await } /// Check if the address exists on chain. Established address exists if it has a @@ -1823,14 +1803,7 @@ pub async fn known_address( client: &C, address: &Address, ) -> bool { - match address { - Address::Established(_) => { - // Established account exists if it has a VP - let key = storage::Key::validity_predicate(address); - query_has_storage_key(client, &key).await - } - Address::Implicit(_) | Address::Internal(_) => true, - } + namada::ledger::rpc::known_address(client, address).await } /// Accumulate slashes starting from `epoch_start` until (optionally) @@ -2039,9 +2012,7 @@ pub async fn query_conversion, )> { - Some(unwrap_client_response::( - RPC.shell().read_conversion(client, &asset_type).await, - )) + namada::ledger::rpc::query_conversion(client, asset_type).await } /// Query a storage value and decode it with [`BorshDeserialize`]. @@ -2052,34 +2023,7 @@ pub async fn query_storage_value( - RPC.shell().storage_has_key(client, key).await, - ) { - Some(unit) - } else { - None - }; - } - - let response = unwrap_client_response::( - RPC.shell() - .storage_value(client, None, None, false, key) - .await, - ); - if response.data.is_empty() { - return None; - } - T::try_from_slice(&response.data[..]) - .map(Some) - .unwrap_or_else(|err| { - eprintln!("Error decoding the value: {}", err); - cli::safe_exit(1) - }) + namada::ledger::rpc::query_storage_value(client, key).await } /// Query a storage value and the proof without decoding. @@ -2089,17 +2033,7 @@ pub async fn query_storage_value_bytes, prove: bool, ) -> (Option>, Option) { - let data = None; - let response = unwrap_client_response::( - RPC.shell() - .storage_value(client, data, height, prove, key) - .await, - ); - if response.data.is_empty() { - (None, response.proof) - } else { - (Some(response.data), response.proof) - } + namada::ledger::rpc::query_storage_value_bytes(client, key, height, prove).await } /// Query a range of storage values with a matching prefix and decode them with @@ -2112,29 +2046,7 @@ pub async fn query_storage_prefix( - RPC.shell() - .storage_prefix(client, None, None, false, key) - .await, - ); - let decode = - |PrefixValue { key, value }: PrefixValue| match T::try_from_slice( - &value[..], - ) { - Err(err) => { - eprintln!( - "Skipping a value for key {}. Error in decoding: {}", - key, err - ); - None - } - Ok(value) => Some((key, value)), - }; - if values.data.is_empty() { - None - } else { - Some(values.data.into_iter().filter_map(decode)) - } + namada::ledger::rpc::query_storage_prefix(client, key).await } /// Query to check if the given storage key exists. @@ -2142,151 +2054,37 @@ pub async fn query_has_storage_key bool { - unwrap_client_response::(RPC.shell().storage_has_key(client, key).await) -} - -/// Represents a query for an event pertaining to the specified transaction -#[derive(Debug, Copy, Clone)] -pub enum TxEventQuery<'a> { - Accepted(&'a str), - Applied(&'a str), -} - -impl<'a> TxEventQuery<'a> { - /// The event type to which this event query pertains - fn event_type(self) -> &'static str { - match self { - TxEventQuery::Accepted(_) => "accepted", - TxEventQuery::Applied(_) => "applied", - } - } - - /// The transaction to which this event query pertains - fn tx_hash(self) -> &'a str { - match self { - TxEventQuery::Accepted(tx_hash) => tx_hash, - TxEventQuery::Applied(tx_hash) => tx_hash, - } - } -} - -/// Transaction event queries are semantically a subset of general queries -impl<'a> From> for Query { - fn from(tx_query: TxEventQuery<'a>) -> Self { - match tx_query { - TxEventQuery::Accepted(tx_hash) => { - Query::default().and_eq("accepted.hash", tx_hash) - } - TxEventQuery::Applied(tx_hash) => { - Query::default().and_eq("applied.hash", tx_hash) - } - } - } + namada::ledger::rpc::query_has_storage_key(client, key).await } /// Call the corresponding `tx_event_query` RPC method, to fetch /// the current status of a transation. pub async fn query_tx_events( client: &C, - tx_event_query: TxEventQuery<'_>, + tx_event_query: namada::ledger::rpc::TxEventQuery<'_>, ) -> std::result::Result, ::Error> { - let tx_hash: Hash = tx_event_query.tx_hash().try_into().unwrap(); - match tx_event_query { - TxEventQuery::Accepted(_) => RPC - .shell() - .accepted(client, &tx_hash) - .await - /*.wrap_err_with(|| { - eyre!("Failed querying whether a transaction was accepted") - })*/, - TxEventQuery::Applied(_) => RPC - .shell() - .applied(client, &tx_hash) - .await - /*.wrap_err_with(|| { - eyre!("Error querying whether a transaction was applied") - })*/, - } + namada::ledger::rpc::query_tx_events(client, tx_event_query).await } /// Lookup the full response accompanying the specified transaction event // TODO: maybe remove this in favor of `query_tx_status` -pub async fn query_tx_response( - client: &WebSocketClient, - tx_query: TxEventQuery<'_>, +pub async fn query_tx_response( + client: &C, + tx_query: namada::ledger::rpc::TxEventQuery<'_>, ) -> Result { - // Find all blocks that apply a transaction with the specified hash - let blocks = &client - .block_search(tx_query.into(), 1, 255, Order::Ascending) - .await - .expect("Unable to query for transaction with given hash") - .blocks; - // Get the block results corresponding to a block to which - // the specified transaction belongs - let block = &blocks - .get(0) - .ok_or_else(|| { - TError::server( - "Unable to find a block applying the given transaction" - .to_string(), - ) - })? - .block; - let response_block_results = client - .block_results(block.header.height) - .await - .expect("Unable to retrieve block containing transaction"); - // Search for the event where the specified transaction is - // applied to the blockchain - let query_event_opt = - response_block_results.end_block_events.and_then(|events| { - events - .iter() - .find(|event| { - event.type_str == tx_query.event_type() - && event.attributes.iter().any(|tag| { - tag.key.as_ref() == "hash" - && tag.value.as_ref() == tx_query.tx_hash() - }) - }) - .cloned() - }); - let query_event = query_event_opt.ok_or_else(|| { - TError::server( - "Unable to find the event corresponding to the specified \ - transaction" - .to_string(), - ) - })?; - // Reformat the event attributes so as to ease value extraction - let event_map: std::collections::HashMap<&str, &str> = query_event - .attributes - .iter() - .map(|tag| (tag.key.as_ref(), tag.value.as_ref())) - .collect(); - // Summarize the transaction results that we were searching for - let result = TxResponse { - info: event_map["info"].to_string(), - log: event_map["log"].to_string(), - height: event_map["height"].to_string(), - hash: event_map["hash"].to_string(), - code: event_map["code"].to_string(), - gas_used: event_map["gas_used"].to_string(), - initialized_accounts: serde_json::from_str( - event_map["initialized_accounts"], - ) - .unwrap_or_default(), - }; - Ok(result) + namada::ledger::rpc::query_tx_response(client, tx_query).await } /// Lookup the results of applying the specified transaction to the /// blockchain. -pub async fn query_result(client: &WebSocketClient, args: args::QueryResult) { +pub async fn query_result( + client: &C, + args: args::QueryResult, +) { // First try looking up application event pertaining to given hash. let tx_response = query_tx_response( client, - TxEventQuery::Applied(&args.tx_hash), + namada::ledger::rpc::TxEventQuery::Applied(&args.tx_hash), ) .await; match tx_response { @@ -2300,7 +2098,7 @@ pub async fn query_result(client: &WebSocketClient, args: args::QueryResult) { // If this fails then instead look for an acceptance event. let tx_response = query_tx_response( client, - TxEventQuery::Accepted(&args.tx_hash), + namada::ledger::rpc::TxEventQuery::Accepted(&args.tx_hash), ) .await; match tx_response { @@ -2323,66 +2121,7 @@ pub async fn get_proposal_votes Votes { - let validators = get_all_validators(client, epoch).await; - - let vote_prefix_key = - gov_storage::get_proposal_vote_prefix_key(proposal_id); - let vote_iter = - query_storage_prefix::(client, &vote_prefix_key).await; - - let mut yay_validators: HashMap = HashMap::new(); - let mut yay_delegators: HashMap> = - HashMap::new(); - let mut nay_delegators: HashMap> = - HashMap::new(); - - if let Some(vote_iter) = vote_iter { - for (key, vote) in vote_iter { - let voter_address = gov_storage::get_voter_address(&key) - .expect("Vote key should contain the voting address.") - .clone(); - if vote.is_yay() && validators.contains(&voter_address) { - let amount: VotePower = - get_validator_stake(client, epoch, &voter_address) - .await - .into(); - yay_validators.insert(voter_address, amount); - } else if !validators.contains(&voter_address) { - let validator_address = - gov_storage::get_vote_delegation_address(&key) - .expect( - "Vote key should contain the delegation address.", - ) - .clone(); - let delegator_token_amount = get_bond_amount_at( - client, - &voter_address, - &validator_address, - epoch, - ) - .await; - if let Some(amount) = delegator_token_amount { - if vote.is_yay() { - let entry = - yay_delegators.entry(voter_address).or_default(); - entry - .insert(validator_address, VotePower::from(amount)); - } else { - let entry = - nay_delegators.entry(voter_address).or_default(); - entry - .insert(validator_address, VotePower::from(amount)); - } - } - } - } - } - - Votes { - yay_validators, - yay_delegators, - nay_delegators, - } + namada::ledger::rpc::get_proposal_votes(client, epoch, proposal_id).await } pub async fn get_proposal_offline_votes( @@ -2520,53 +2259,7 @@ pub async fn compute_tally( epoch: Epoch, votes: Votes, ) -> ProposalResult { - let total_staked_tokens: VotePower = - get_total_staked_tokens(client, epoch).await.into(); - - let Votes { - yay_validators, - yay_delegators, - nay_delegators, - } = votes; - - let mut total_yay_staked_tokens = VotePower::from(0_u64); - for (_, amount) in yay_validators.clone().into_iter() { - total_yay_staked_tokens += amount; - } - - // YAY: Add delegator amount whose validator didn't vote / voted nay - for (_, vote_map) in yay_delegators.iter() { - for (validator_address, vote_power) in vote_map.iter() { - if !yay_validators.contains_key(validator_address) { - total_yay_staked_tokens += vote_power; - } - } - } - - // NAY: Remove delegator amount whose validator validator vote yay - for (_, vote_map) in nay_delegators.iter() { - for (validator_address, vote_power) in vote_map.iter() { - if yay_validators.contains_key(validator_address) { - total_yay_staked_tokens -= vote_power; - } - } - } - - if total_yay_staked_tokens >= (total_staked_tokens / 3) * 2 { - ProposalResult { - result: TallyResult::Passed, - total_voting_power: total_staked_tokens, - total_yay_power: total_yay_staked_tokens, - total_nay_power: 0, - } - } else { - ProposalResult { - result: TallyResult::Rejected, - total_voting_power: total_staked_tokens, - total_yay_power: total_yay_staked_tokens, - total_nay_power: 0, - } - } + namada::ledger::rpc::compute_tally(client, epoch, votes).await } pub async fn get_bond_amount_at( @@ -2575,71 +2268,21 @@ pub async fn get_bond_amount_at Option { - let slashes_key = pos::validator_slashes_key(validator); - let slashes = query_storage_value::(client, &slashes_key) - .await - .unwrap_or_default(); - let bond_key = pos::bond_key(&BondId { - source: delegator.clone(), - validator: validator.clone(), - }); - let epoched_bonds = query_storage_value::(client, &bond_key).await; - match epoched_bonds { - Some(epoched_bonds) => { - let mut delegated_amount: token::Amount = 0.into(); - for bond in epoched_bonds.iter() { - let mut to_deduct = bond.neg_deltas; - for (epoch_start, &(mut delta)) in - bond.pos_deltas.iter().sorted() - { - // deduct bond's neg_deltas - if to_deduct > delta { - to_deduct -= delta; - // If the whole bond was deducted, continue to - // the next one - continue; - } else { - delta -= to_deduct; - to_deduct = token::Amount::default(); - } - - delta = apply_slashes( - &slashes, - delta, - *epoch_start, - None, - None, - ); - if epoch >= *epoch_start { - delegated_amount += delta; - } - } - } - Some(delegated_amount) - } - None => None, - } + namada::ledger::rpc::get_bond_amount_at(client, delegator, validator, epoch).await } pub async fn get_all_validators( client: &C, epoch: Epoch, ) -> HashSet
{ - unwrap_client_response::( - RPC.vp() - .pos() - .validator_addresses(client, &Some(epoch)) - .await, - ) + namada::ledger::rpc::get_all_validators(client, epoch).await } pub async fn get_total_staked_tokens( client: &C, epoch: Epoch, ) -> token::Amount { - unwrap_client_response::( - RPC.vp().pos().total_stake(client, &Some(epoch)).await, - ) + namada::ledger::rpc::get_total_staked_tokens(client, epoch).await } async fn get_validator_stake( @@ -2647,61 +2290,18 @@ async fn get_validator_stake epoch: Epoch, validator: &Address, ) -> token::Amount { - unwrap_client_response::( - RPC.vp() - .pos() - .validator_stake(client, validator, &Some(epoch)) - .await, - ) + namada::ledger::rpc::get_validator_stake(client, epoch, validator).await } pub async fn get_delegators_delegation( client: &C, address: &Address, ) -> HashSet
{ - unwrap_client_response::(RPC.vp().pos().delegations(client, address).await) + namada::ledger::rpc::get_delegators_delegation(client, address).await } pub async fn get_governance_parameters(client: &C) -> GovParams { - use namada::types::token::Amount; - let key = gov_storage::get_max_proposal_code_size_key(); - let max_proposal_code_size = query_storage_value::(client, &key) - .await - .expect("Parameter should be definied."); - - let key = gov_storage::get_max_proposal_content_key(); - let max_proposal_content_size = query_storage_value::(client, &key) - .await - .expect("Parameter should be definied."); - - let key = gov_storage::get_min_proposal_fund_key(); - let min_proposal_fund = query_storage_value::(client, &key) - .await - .expect("Parameter should be definied."); - - let key = gov_storage::get_min_proposal_grace_epoch_key(); - let min_proposal_grace_epochs = query_storage_value::(client, &key) - .await - .expect("Parameter should be definied."); - - let key = gov_storage::get_min_proposal_period_key(); - let min_proposal_period = query_storage_value::(client, &key) - .await - .expect("Parameter should be definied."); - - let key = gov_storage::get_max_proposal_period_key(); - let max_proposal_period = query_storage_value::(client, &key) - .await - .expect("Parameter should be definied."); - - GovParams { - min_proposal_fund: u64::from(min_proposal_fund), - max_proposal_code_size, - min_proposal_period, - max_proposal_period, - max_proposal_content_size, - min_proposal_grace_epochs, - } + namada::ledger::rpc::get_governance_parameters(client).await } /// Try to find an alias for a given address from the wallet. If not found, diff --git a/apps/src/lib/client/signing.rs b/apps/src/lib/client/signing.rs index bdaf808e1d..c90f9964b9 100644 --- a/apps/src/lib/client/signing.rs +++ b/apps/src/lib/client/signing.rs @@ -12,7 +12,7 @@ use std::path::PathBuf; use super::rpc; use crate::cli::{self, args}; -use crate::client::tendermint_rpc_types::TxBroadcastData; +use namada::ledger::rpc::TxBroadcastData; use namada::ledger::wallet::Wallet; use namada::ledger::wallet::WalletUtils; use crate::facade::tendermint_rpc::Client; diff --git a/apps/src/lib/client/tendermint_rpc_types.rs b/apps/src/lib/client/tendermint_rpc_types.rs index 537cca243f..db5dcf0c8e 100644 --- a/apps/src/lib/client/tendermint_rpc_types.rs +++ b/apps/src/lib/client/tendermint_rpc_types.rs @@ -7,94 +7,3 @@ use serde::Serialize; use crate::cli::safe_exit; -/// Data needed for broadcasting a tx and -/// monitoring its progress on chain -/// -/// Txs may be either a dry run or else -/// they should be encrypted and included -/// in a wrapper. -#[derive(Debug, Clone)] -pub enum TxBroadcastData { - DryRun(Tx), - Wrapper { - tx: Tx, - wrapper_hash: String, - decrypted_hash: String, - }, -} - -/// A parsed event from tendermint relating to a transaction -#[derive(Debug, Serialize)] -pub struct TxResponse { - pub info: String, - pub log: String, - pub height: String, - pub hash: String, - pub code: String, - pub gas_used: String, - pub initialized_accounts: Vec
, -} - -impl TryFrom for TxResponse { - type Error = String; - - fn try_from(event: Event) -> Result { - fn missing_field_err(field: &str) -> String { - format!("Field \"{field}\" not present in event") - } - - let hash = event - .get("hash") - .ok_or_else(|| missing_field_err("hash"))? - .clone(); - let info = event - .get("info") - .ok_or_else(|| missing_field_err("info"))? - .clone(); - let log = event - .get("log") - .ok_or_else(|| missing_field_err("log"))? - .clone(); - let height = event - .get("height") - .ok_or_else(|| missing_field_err("height"))? - .clone(); - let code = event - .get("code") - .ok_or_else(|| missing_field_err("code"))? - .clone(); - let gas_used = event - .get("gas_used") - .ok_or_else(|| missing_field_err("gas_used"))? - .clone(); - let initialized_accounts = event - .get("initialized_accounts") - .map(String::as_str) - // TODO: fix finalize block, to return initialized accounts, - // even when we reject a tx? - .map_or(Ok(vec![]), |initialized_accounts| { - serde_json::from_str(initialized_accounts) - .map_err(|err| format!("JSON decode error: {err}")) - })?; - - Ok(TxResponse { - hash, - info, - log, - height, - code, - gas_used, - initialized_accounts, - }) - } -} - -impl TxResponse { - /// Convert an [`Event`] to a [`TxResponse`], or error out. - pub fn from_event(event: Event) -> Self { - event.try_into().unwrap_or_else(|err| { - eprintln!("Error fetching TxResponse: {err}"); - safe_exit(1); - }) - } -} diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index b0cdffef2e..79417c6510 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -54,7 +54,7 @@ use crate::cli::context::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}; -use crate::client::tendermint_rpc_types::{TxBroadcastData, TxResponse}; +use namada::ledger::rpc::{TxBroadcastData, TxResponse}; use crate::facade::tendermint_config::net::Address as TendermintAddress; use crate::facade::tendermint_rpc::endpoint::broadcast::tx_sync::Response; use crate::facade::tendermint_rpc::error::Error as RpcError; @@ -1760,7 +1760,7 @@ pub async fn submit_tx( ); let parsed = { - let wrapper_query = rpc::TxEventQuery::Accepted(wrapper_hash.as_str()); + let wrapper_query = namada::ledger::rpc::TxEventQuery::Accepted(wrapper_hash.as_str()); let event = rpc::query_tx_status(client, wrapper_query, deadline) .await; @@ -1776,7 +1776,7 @@ pub async fn submit_tx( // We also listen to the event emitted when the encrypted // payload makes its way onto the blockchain let decrypted_query = - rpc::TxEventQuery::Applied(decrypted_hash.as_str()); + namada::ledger::rpc::TxEventQuery::Applied(decrypted_hash.as_str()); let event = rpc::query_tx_status(client, decrypted_query, deadline).await; let parsed = TxResponse::from_event(event); diff --git a/shared/src/ledger/mod.rs b/shared/src/ledger/mod.rs index 8d553721a1..c9a85fdf19 100644 --- a/shared/src/ledger/mod.rs +++ b/shared/src/ledger/mod.rs @@ -13,6 +13,7 @@ pub mod storage; pub mod vp_host_fns; pub mod wallet; pub mod args; +pub mod rpc; pub use namada_core::ledger::{ gas, governance, parameters, storage_api, tx_env, vp_env, diff --git a/shared/src/ledger/rpc.rs b/shared/src/ledger/rpc.rs new file mode 100644 index 0000000000..d194e729cb --- /dev/null +++ b/shared/src/ledger/rpc.rs @@ -0,0 +1,774 @@ +use crate::tendermint_rpc::Client; +use crate::types::storage::Epoch; +use crate::ledger::queries::RPC; +use crate::types::storage::BlockResults; +use crate::ledger::masp::Conversions; +use namada_core::types::address::masp; +use crate::ledger::masp::ShieldedContext; +use namada_core::types::address::tokens; +use std::collections::HashMap; +use itertools::Either; +use crate::ledger::wallet::Wallet; +use crate::types::token; +use crate::ledger::masp::ShieldedUtils; +use crate::types::masp::{BalanceOwner, ExtendedViewingKey, PaymentAddress}; +use std::cmp::Ordering; +use masp_primitives::zip32::ExtendedFullViewingKey; +use crate::ledger::args; +use data_encoding::HEXLOWER; +use namada_core::types::address::Address; +use borsh::BorshDeserialize; +use masp_primitives::asset_type::AssetType; +use masp_primitives::merkle_tree::MerklePath; +use crate::types::storage::{ + BlockHeight, Key, KeySeg, PrefixValue, +}; +use crate::tendermint::merkle::proof::Proof; +use crate::ledger::pos::{ + self, is_validator_slashes_key, BondId, Bonds, PosParams, Slash, Unbonds, +}; +use crate::types::{address, storage}; +use masp_primitives::sapling::Node; +use crate::types::token::balance_key; +use crate::types::key::*; +use crate::ledger::events::Event; +use crate::types::hash::Hash; +use crate::tendermint_rpc::query::Query; +use crate::proto::Tx; +use serde::Serialize; +use crate::tendermint_rpc::error::Error as TError; +use crate::tendermint_rpc::Order; +use crate::types::governance::VotePower; +use crate::types::governance::ProposalVote; +use crate::ledger::native_vp::governance::utils::Votes; +use crate::ledger::governance::storage as gov_storage; +use std::collections::HashSet; +use crate::ledger::governance::parameters::GovParams; +use crate::types::governance::ProposalResult; +use crate::types::governance::{ + OfflineProposal, OfflineVote, TallyResult, +}; +use itertools::{Itertools}; +use crate::ledger::pos::types::decimal_mult_u64; + +/// Query the epoch of the last committed block +pub async fn query_epoch(client: &C) -> Epoch { + let epoch = unwrap_client_response::(RPC.shell().epoch(client).await); + println!("Last committed epoch: {}", epoch); + epoch +} + +/// Query the last committed block +pub async fn query_block( + client: &C, +) -> crate::tendermint_rpc::endpoint::block::Response { + let response = client.latest_block().await.unwrap(); + println!( + "Last committed block ID: {}, height: {}, time: {}", + response.block_id, + response.block.header.height, + response.block.header.time + ); + response +} + +/// A helper to unwrap client's response. Will shut down process on error. +fn unwrap_client_response(response: Result) -> T { + response.unwrap_or_else(|err| { + panic!("Error in the query"); + }) +} + +/// Query the results of the last committed block +pub async fn query_results(client: &C) -> Vec { + unwrap_client_response::(RPC.shell().read_results(client).await) +} + +/// Query token amount of owner. +pub async fn get_token_balance( + client: &C, + token: &Address, + owner: &Address, +) -> Option { + let balance_key = balance_key(token, owner); + query_storage_value(client, &balance_key).await +} + +/// Get account's public key stored in its storage sub-space +pub async fn get_public_key( + client: &C, + address: &Address, +) -> Option { + let key = pk_key(address); + query_storage_value(client, &key).await +} + +/// Check if the given address is a known validator. +pub async fn is_validator( + client: &C, + address: &Address, +) -> bool { + unwrap_client_response::(RPC.vp().pos().is_validator(client, address).await) +} + +/// Check if a given address is a known delegator +pub async fn is_delegator( + client: &C, + address: &Address, +) -> bool { + let bonds_prefix = pos::bonds_for_source_prefix(address); + let bonds = + query_storage_prefix::(&client, &bonds_prefix).await; + bonds.is_some() && bonds.unwrap().count() > 0 +} + +pub async fn is_delegator_at( + client: &C, + address: &Address, + epoch: Epoch, +) -> bool { + let key = pos::bonds_for_source_prefix(address); + let bonds_iter = query_storage_prefix::(client, &key).await; + if let Some(mut bonds) = bonds_iter { + bonds.any(|(_, bond)| bond.get(epoch).is_some()) + } else { + false + } +} + +/// Check if the address exists on chain. Established address exists if it has a +/// stored validity predicate. Implicit and internal addresses always return +/// true. +pub async fn known_address( + client: &C, + address: &Address, +) -> bool { + match address { + Address::Established(_) => { + // Established account exists if it has a VP + let key = storage::Key::validity_predicate(address); + query_has_storage_key(client, &key).await + } + Address::Implicit(_) | Address::Internal(_) => true, + } +} + +/// Query a conversion. +pub async fn query_conversion( + client: &C, + asset_type: AssetType, +) -> Option<( + Address, + Epoch, + masp_primitives::transaction::components::Amount, + MerklePath, +)> { + Some(unwrap_client_response::( + RPC.shell().read_conversion(client, &asset_type).await, + )) +} + +/// Query a storage value and decode it with [`BorshDeserialize`]. +pub async fn query_storage_value( + client: &C, + key: &storage::Key, +) -> Option +where + T: BorshDeserialize, +{ + // In case `T` is a unit (only thing that encodes to 0 bytes), we have to + // use `storage_has_key` instead of `storage_value`, because `storage_value` + // returns 0 bytes when the key is not found. + let maybe_unit = T::try_from_slice(&[]); + if let Ok(unit) = maybe_unit { + return if unwrap_client_response::( + RPC.shell().storage_has_key(client, key).await, + ) { + Some(unit) + } else { + None + }; + } + + let response = unwrap_client_response::( + RPC.shell() + .storage_value(client, None, None, false, key) + .await, + ); + if response.data.is_empty() { + return None; + } + T::try_from_slice(&response.data[..]) + .map(Some) + .unwrap_or_else(|err| { + panic!("Error decoding the value: {}", err); + }) +} + +/// Query a storage value and the proof without decoding. +pub async fn query_storage_value_bytes( + client: &C, + key: &storage::Key, + height: Option, + prove: bool, +) -> (Option>, Option) { + let data = None; + let response = unwrap_client_response::( + RPC.shell() + .storage_value(client, data, height, prove, key) + .await, + ); + if response.data.is_empty() { + (None, response.proof) + } else { + (Some(response.data), response.proof) + } +} + +/// Query a range of storage values with a matching prefix and decode them with +/// [`BorshDeserialize`]. Returns an iterator of the storage keys paired with +/// their associated values. +pub async fn query_storage_prefix( + client: &C, + key: &storage::Key, +) -> Option> +where + T: BorshDeserialize, +{ + let values = unwrap_client_response::( + RPC.shell() + .storage_prefix(client, None, None, false, key) + .await, + ); + let decode = + |PrefixValue { key, value }: PrefixValue| match T::try_from_slice( + &value[..], + ) { + Err(err) => { + eprintln!( + "Skipping a value for key {}. Error in decoding: {}", + key, err + ); + None + } + Ok(value) => Some((key, value)), + }; + if values.data.is_empty() { + None + } else { + Some(values.data.into_iter().filter_map(decode)) + } +} + +/// Query to check if the given storage key exists. +pub async fn query_has_storage_key( + client: &C, + key: &storage::Key, +) -> bool { + unwrap_client_response::(RPC.shell().storage_has_key(client, key).await) +} + +/// Represents a query for an event pertaining to the specified transaction +#[derive(Debug, Copy, Clone)] +pub enum TxEventQuery<'a> { + Accepted(&'a str), + Applied(&'a str), +} + +impl<'a> TxEventQuery<'a> { + /// The event type to which this event query pertains + pub fn event_type(self) -> &'static str { + match self { + TxEventQuery::Accepted(_) => "accepted", + TxEventQuery::Applied(_) => "applied", + } + } + + /// The transaction to which this event query pertains + pub fn tx_hash(self) -> &'a str { + match self { + TxEventQuery::Accepted(tx_hash) => tx_hash, + TxEventQuery::Applied(tx_hash) => tx_hash, + } + } +} + +/// Transaction event queries are semantically a subset of general queries +impl<'a> From> for Query { + fn from(tx_query: TxEventQuery<'a>) -> Self { + match tx_query { + TxEventQuery::Accepted(tx_hash) => { + Query::default().and_eq("accepted.hash", tx_hash) + } + TxEventQuery::Applied(tx_hash) => { + Query::default().and_eq("applied.hash", tx_hash) + } + } + } +} + +/// Call the corresponding `tx_event_query` RPC method, to fetch +/// the current status of a transation. +pub async fn query_tx_events( + client: &C, + tx_event_query: TxEventQuery<'_>, +) -> std::result::Result, ::Error> { + let tx_hash: Hash = tx_event_query.tx_hash().try_into().unwrap(); + match tx_event_query { + TxEventQuery::Accepted(_) => RPC + .shell() + .accepted(client, &tx_hash) + .await + /*.wrap_err_with(|| { + eyre!("Failed querying whether a transaction was accepted") + })*/, + TxEventQuery::Applied(_) => RPC + .shell() + .applied(client, &tx_hash) + .await + /*.wrap_err_with(|| { + eyre!("Error querying whether a transaction was applied") + })*/, + } +} + +/// Data needed for broadcasting a tx and +/// monitoring its progress on chain +/// +/// Txs may be either a dry run or else +/// they should be encrypted and included +/// in a wrapper. +#[derive(Debug, Clone)] +pub enum TxBroadcastData { + DryRun(Tx), + Wrapper { + tx: Tx, + wrapper_hash: String, + decrypted_hash: String, + }, +} + +/// A parsed event from tendermint relating to a transaction +#[derive(Debug, Serialize)] +pub struct TxResponse { + pub info: String, + pub log: String, + pub height: String, + pub hash: String, + pub code: String, + pub gas_used: String, + pub initialized_accounts: Vec
, +} + +impl TryFrom for TxResponse { + type Error = String; + + fn try_from(event: Event) -> Result { + fn missing_field_err(field: &str) -> String { + format!("Field \"{field}\" not present in event") + } + + let hash = event + .get("hash") + .ok_or_else(|| missing_field_err("hash"))? + .clone(); + let info = event + .get("info") + .ok_or_else(|| missing_field_err("info"))? + .clone(); + let log = event + .get("log") + .ok_or_else(|| missing_field_err("log"))? + .clone(); + let height = event + .get("height") + .ok_or_else(|| missing_field_err("height"))? + .clone(); + let code = event + .get("code") + .ok_or_else(|| missing_field_err("code"))? + .clone(); + let gas_used = event + .get("gas_used") + .ok_or_else(|| missing_field_err("gas_used"))? + .clone(); + let initialized_accounts = event + .get("initialized_accounts") + .map(String::as_str) + // TODO: fix finalize block, to return initialized accounts, + // even when we reject a tx? + .map_or(Ok(vec![]), |initialized_accounts| { + serde_json::from_str(initialized_accounts) + .map_err(|err| format!("JSON decode error: {err}")) + })?; + + Ok(TxResponse { + hash, + info, + log, + height, + code, + gas_used, + initialized_accounts, + }) + } +} + +impl TxResponse { + /// Convert an [`Event`] to a [`TxResponse`], or error out. + pub fn from_event(event: Event) -> Self { + event.try_into().unwrap_or_else(|err| { + panic!("Error fetching TxResponse: {err}"); + }) + } +} + +/// Lookup the full response accompanying the specified transaction event +// TODO: maybe remove this in favor of `query_tx_status` +pub async fn query_tx_response( + client: &C, + tx_query: TxEventQuery<'_>, +) -> Result { + // Find all blocks that apply a transaction with the specified hash + let blocks = &client + .block_search(tx_query.into(), 1, 255, Order::Ascending) + .await + .expect("Unable to query for transaction with given hash") + .blocks; + // Get the block results corresponding to a block to which + // the specified transaction belongs + let block = &blocks + .get(0) + .ok_or_else(|| { + TError::server( + "Unable to find a block applying the given transaction" + .to_string(), + ) + })? + .block; + let response_block_results = client + .block_results(block.header.height) + .await + .expect("Unable to retrieve block containing transaction"); + // Search for the event where the specified transaction is + // applied to the blockchain + let query_event_opt = + response_block_results.end_block_events.and_then(|events| { + events + .iter() + .find(|event| { + event.type_str == tx_query.event_type() + && event.attributes.iter().any(|tag| { + tag.key.as_ref() == "hash" + && tag.value.as_ref() == tx_query.tx_hash() + }) + }) + .cloned() + }); + let query_event = query_event_opt.ok_or_else(|| { + TError::server( + "Unable to find the event corresponding to the specified \ + transaction" + .to_string(), + ) + })?; + // Reformat the event attributes so as to ease value extraction + let event_map: std::collections::HashMap<&str, &str> = query_event + .attributes + .iter() + .map(|tag| (tag.key.as_ref(), tag.value.as_ref())) + .collect(); + // Summarize the transaction results that we were searching for + let result = TxResponse { + info: event_map["info"].to_string(), + log: event_map["log"].to_string(), + height: event_map["height"].to_string(), + hash: event_map["hash"].to_string(), + code: event_map["code"].to_string(), + gas_used: event_map["gas_used"].to_string(), + initialized_accounts: serde_json::from_str( + event_map["initialized_accounts"], + ) + .unwrap_or_default(), + }; + Ok(result) +} + +pub async fn get_proposal_votes( + client: &C, + epoch: Epoch, + proposal_id: u64, +) -> Votes { + let validators = get_all_validators(client, epoch).await; + + let vote_prefix_key = + gov_storage::get_proposal_vote_prefix_key(proposal_id); + let vote_iter = + query_storage_prefix::(client, &vote_prefix_key).await; + + let mut yay_validators: HashMap = HashMap::new(); + let mut yay_delegators: HashMap> = + HashMap::new(); + let mut nay_delegators: HashMap> = + HashMap::new(); + + if let Some(vote_iter) = vote_iter { + for (key, vote) in vote_iter { + let voter_address = gov_storage::get_voter_address(&key) + .expect("Vote key should contain the voting address.") + .clone(); + if vote.is_yay() && validators.contains(&voter_address) { + let amount: VotePower = + get_validator_stake(client, epoch, &voter_address) + .await + .into(); + yay_validators.insert(voter_address, amount); + } else if !validators.contains(&voter_address) { + let validator_address = + gov_storage::get_vote_delegation_address(&key) + .expect( + "Vote key should contain the delegation address.", + ) + .clone(); + let delegator_token_amount = get_bond_amount_at( + client, + &voter_address, + &validator_address, + epoch, + ) + .await; + if let Some(amount) = delegator_token_amount { + if vote.is_yay() { + let entry = + yay_delegators.entry(voter_address).or_default(); + entry + .insert(validator_address, VotePower::from(amount)); + } else { + let entry = + nay_delegators.entry(voter_address).or_default(); + entry + .insert(validator_address, VotePower::from(amount)); + } + } + } + } + } + + Votes { + yay_validators, + yay_delegators, + nay_delegators, + } +} + +pub async fn get_all_validators( + client: &C, + epoch: Epoch, +) -> HashSet
{ + unwrap_client_response::( + RPC.vp() + .pos() + .validator_addresses(client, &Some(epoch)) + .await, + ) +} + +pub async fn get_total_staked_tokens( + client: &C, + epoch: Epoch, +) -> token::Amount { + unwrap_client_response::( + RPC.vp().pos().total_stake(client, &Some(epoch)).await, + ) +} + +pub async fn get_validator_stake( + client: &C, + epoch: Epoch, + validator: &Address, +) -> token::Amount { + unwrap_client_response::( + RPC.vp() + .pos() + .validator_stake(client, validator, &Some(epoch)) + .await, + ) +} + +pub async fn get_delegators_delegation( + client: &C, + address: &Address, +) -> HashSet
{ + unwrap_client_response::(RPC.vp().pos().delegations(client, address).await) +} + +pub async fn get_governance_parameters(client: &C) -> GovParams { + use crate::types::token::Amount; + let key = gov_storage::get_max_proposal_code_size_key(); + let max_proposal_code_size = query_storage_value::(client, &key) + .await + .expect("Parameter should be definied."); + + let key = gov_storage::get_max_proposal_content_key(); + let max_proposal_content_size = query_storage_value::(client, &key) + .await + .expect("Parameter should be definied."); + + let key = gov_storage::get_min_proposal_fund_key(); + let min_proposal_fund = query_storage_value::(client, &key) + .await + .expect("Parameter should be definied."); + + let key = gov_storage::get_min_proposal_grace_epoch_key(); + let min_proposal_grace_epochs = query_storage_value::(client, &key) + .await + .expect("Parameter should be definied."); + + let key = gov_storage::get_min_proposal_period_key(); + let min_proposal_period = query_storage_value::(client, &key) + .await + .expect("Parameter should be definied."); + + let key = gov_storage::get_max_proposal_period_key(); + let max_proposal_period = query_storage_value::(client, &key) + .await + .expect("Parameter should be definied."); + + GovParams { + min_proposal_fund: u64::from(min_proposal_fund), + max_proposal_code_size, + min_proposal_period, + max_proposal_period, + max_proposal_content_size, + min_proposal_grace_epochs, + } +} + +// Compute the result of a proposal +pub async fn compute_tally( + client: &C, + epoch: Epoch, + votes: Votes, +) -> ProposalResult { + let total_staked_tokens: VotePower = + get_total_staked_tokens(client, epoch).await.into(); + + let Votes { + yay_validators, + yay_delegators, + nay_delegators, + } = votes; + + let mut total_yay_staked_tokens = VotePower::from(0_u64); + for (_, amount) in yay_validators.clone().into_iter() { + total_yay_staked_tokens += amount; + } + + // YAY: Add delegator amount whose validator didn't vote / voted nay + for (_, vote_map) in yay_delegators.iter() { + for (validator_address, vote_power) in vote_map.iter() { + if !yay_validators.contains_key(validator_address) { + total_yay_staked_tokens += vote_power; + } + } + } + + // NAY: Remove delegator amount whose validator validator vote yay + for (_, vote_map) in nay_delegators.iter() { + for (validator_address, vote_power) in vote_map.iter() { + if yay_validators.contains_key(validator_address) { + total_yay_staked_tokens -= vote_power; + } + } + } + + if total_yay_staked_tokens >= (total_staked_tokens / 3) * 2 { + ProposalResult { + result: TallyResult::Passed, + total_voting_power: total_staked_tokens, + total_yay_power: total_yay_staked_tokens, + total_nay_power: 0, + } + } else { + ProposalResult { + result: TallyResult::Rejected, + total_voting_power: total_staked_tokens, + total_yay_power: total_yay_staked_tokens, + total_nay_power: 0, + } + } +} + +pub async fn get_bond_amount_at( + client: &C, + delegator: &Address, + validator: &Address, + epoch: Epoch, +) -> Option { + let slashes_key = pos::validator_slashes_key(validator); + let slashes = query_storage_value::(client, &slashes_key) + .await + .unwrap_or_default(); + let bond_key = pos::bond_key(&BondId { + source: delegator.clone(), + validator: validator.clone(), + }); + let epoched_bonds = query_storage_value::(client, &bond_key).await; + match epoched_bonds { + Some(epoched_bonds) => { + let mut delegated_amount: token::Amount = 0.into(); + for bond in epoched_bonds.iter() { + let mut to_deduct = bond.neg_deltas; + for (epoch_start, &(mut delta)) in + bond.pos_deltas.iter().sorted() + { + // deduct bond's neg_deltas + if to_deduct > delta { + to_deduct -= delta; + // If the whole bond was deducted, continue to + // the next one + continue; + } else { + delta -= to_deduct; + to_deduct = token::Amount::default(); + } + + delta = apply_slashes( + &slashes, + delta, + *epoch_start, + None, + ); + if epoch >= *epoch_start { + delegated_amount += delta; + } + } + } + Some(delegated_amount) + } + None => None, + } +} + +/// Accumulate slashes starting from `epoch_start` until (optionally) +/// `withdraw_epoch` and apply them to the token amount `delta`. +fn apply_slashes( + slashes: &[Slash], + mut delta: token::Amount, + epoch_start: Epoch, + withdraw_epoch: Option, +) -> token::Amount { + let mut slashed = token::Amount::default(); + for slash in slashes { + if slash.epoch >= epoch_start + && slash.epoch < withdraw_epoch.unwrap_or_else(|| u64::MAX.into()) + { + let raw_delta: u64 = delta.into(); + let current_slashed = + token::Amount::from(decimal_mult_u64(slash.rate, raw_delta)); + slashed += current_slashed; + delta -= current_slashed; + } + } + delta +} From 84d9340447756d054c5fc23791fa16756d282f6e Mon Sep 17 00:00:00 2001 From: Murisi Tarusenga Date: Sun, 18 Dec 2022 11:19:12 +0200 Subject: [PATCH 25/51] Parameterized tx functions by storage location. --- apps/src/bin/anoma-client/cli.rs | 21 ++++--- apps/src/lib/client/signing.rs | 20 +++--- apps/src/lib/client/tx.rs | 104 +++++++++++++++---------------- 3 files changed, 73 insertions(+), 72 deletions(-) diff --git a/apps/src/bin/anoma-client/cli.rs b/apps/src/bin/anoma-client/cli.rs index 66e3fe3187..adb72e9bee 100644 --- a/apps/src/bin/anoma-client/cli.rs +++ b/apps/src/bin/anoma-client/cli.rs @@ -7,6 +7,7 @@ use namada_apps::client::{rpc, tx, utils}; use tendermint_rpc::{HttpClient, WebSocketClient, SubscriptionClient}; use namada_apps::wallet::CliWalletUtils; use namada_apps::cli::args::CliToSdk; +use std::path::PathBuf; pub async fn main() -> Result<()> { match cli::anoma_client_cli()? { @@ -19,7 +20,7 @@ pub async fn main() -> Result<()> { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); let dry_run = args.tx.dry_run; - tx::submit_custom::(&client, &mut ctx.wallet, args).await; + tx::submit_custom::(&client, &mut ctx.wallet, args).await; if !dry_run { namada_apps::wallet::save(&ctx.wallet).unwrap_or_else(|err| eprintln!("{}", err)); } else { @@ -29,23 +30,23 @@ pub async fn main() -> Result<()> { Sub::TxTransfer(TxTransfer(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_transfer::(&client, &mut ctx.wallet, &mut ctx.shielded, args).await; + tx::submit_transfer::(&client, &mut ctx.wallet, &mut ctx.shielded, args).await; } Sub::TxIbcTransfer(TxIbcTransfer(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_ibc_transfer::(&client, &mut ctx.wallet, args).await; + tx::submit_ibc_transfer::(&client, &mut ctx.wallet, args).await; } Sub::TxUpdateVp(TxUpdateVp(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_update_vp::(&client, &mut ctx.wallet, args).await; + tx::submit_update_vp::(&client, &mut ctx.wallet, args).await; } Sub::TxInitAccount(TxInitAccount(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); let dry_run = args.tx.dry_run; - tx::submit_init_account::(&client, &mut ctx.wallet, args).await; + tx::submit_init_account::(&client, &mut ctx.wallet, args).await; if !dry_run { namada_apps::wallet::save(&ctx.wallet).unwrap_or_else(|err| eprintln!("{}", err)); } else { @@ -65,27 +66,27 @@ pub async fn main() -> Result<()> { Sub::TxVoteProposal(TxVoteProposal(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_vote_proposal::(&client, &mut ctx.wallet, args).await; + tx::submit_vote_proposal::(&client, &mut ctx.wallet, args).await; } Sub::TxRevealPk(TxRevealPk(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_reveal_pk::(&client, &mut ctx.wallet, args).await; + tx::submit_reveal_pk::(&client, &mut ctx.wallet, args).await; } Sub::Bond(Bond(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_bond::(&client, &mut ctx.wallet, args).await; + tx::submit_bond::(&client, &mut ctx.wallet, args).await; } Sub::Unbond(Unbond(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_unbond::(&client, &mut ctx.wallet, args).await; + tx::submit_unbond::(&client, &mut ctx.wallet, args).await; } Sub::Withdraw(Withdraw(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_withdraw::(&client, &mut ctx.wallet, args).await; + tx::submit_withdraw::(&client, &mut ctx.wallet, args).await; } // Ledger queries Sub::QueryEpoch(QueryEpoch(args)) => { diff --git a/apps/src/lib/client/signing.rs b/apps/src/lib/client/signing.rs index c90f9964b9..569cc59552 100644 --- a/apps/src/lib/client/signing.rs +++ b/apps/src/lib/client/signing.rs @@ -19,9 +19,9 @@ use crate::facade::tendermint_rpc::Client; /// Find the public key for the given address and try to load the keypair /// for it from the wallet. Panics if the key cannot be found or loaded. -pub async fn find_keypair( +pub async fn find_keypair( client: &C, - wallet: &mut Wallet, + wallet: &mut Wallet

, addr: &Address, ) -> common::SecretKey { match addr { @@ -87,9 +87,9 @@ pub enum TxSigningKey { /// signer. Return the given signing key or public key of the given signer if /// possible. If no explicit signer given, use the `default`. If no `default` /// is given, panics. -pub async fn tx_signer( +pub async fn tx_signer( client: &C, - wallet: &mut Wallet, + wallet: &mut Wallet

, args: &args::Tx, mut default: TxSigningKey, ) -> common::SecretKey { @@ -106,7 +106,7 @@ pub async fn tx_signer { let signer = signer; - let signing_key = find_keypair::( + let signing_key = find_keypair::( client, wallet, &signer, @@ -116,14 +116,14 @@ pub async fn tx_signer(client, wallet, &pk, args).await; + super::tx::reveal_pk_if_needed::(client, wallet, &pk, args).await; } signing_key } TxSigningKey::SecretKey(signing_key) => { // Check if the signing key needs to reveal its PK first let pk: common::PublicKey = signing_key.ref_to(); - super::tx::reveal_pk_if_needed::(client, wallet, &pk, args).await; + super::tx::reveal_pk_if_needed::(client, wallet, &pk, args).await; signing_key } TxSigningKey::None => { @@ -143,14 +143,14 @@ pub async fn tx_signer( +pub async fn sign_tx( client: &C, - wallet: &mut Wallet, + wallet: &mut Wallet

, tx: Tx, args: &args::Tx, default: TxSigningKey, ) -> TxBroadcastData { - let keypair = tx_signer::(client, wallet, args, default).await; + let keypair = tx_signer::(client, wallet, args, default).await; let tx = tx.sign(&keypair); let epoch = rpc::query_epoch(client) diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index 79417c6510..d929e725a9 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -71,22 +71,22 @@ 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( +pub async fn submit_custom( client: &C, - wallet: &mut Wallet, + wallet: &mut Wallet

, args: args::TxCustom, ) { let tx_code = args.code_path; let data = args.data_path; let tx = Tx::new(tx_code, data); let initialized_accounts = - process_tx::(client, wallet, &args.tx, tx, TxSigningKey::None).await; - save_initialized_accounts::(wallet, &args.tx, initialized_accounts).await; + process_tx::(client, wallet, &args.tx, tx, TxSigningKey::None).await; + save_initialized_accounts::(wallet, &args.tx, initialized_accounts).await; } -pub async fn submit_update_vp( +pub async fn submit_update_vp( client: &C, - wallet: &mut Wallet, + wallet: &mut Wallet

, args: args::TxUpdateVp, ) { let addr = args.addr.clone(); @@ -139,12 +139,12 @@ pub async fn submit_update_vp(client, wallet, &args.tx, tx, TxSigningKey::WalletAddress(args.addr)).await; + process_tx::(client, wallet, &args.tx, tx, TxSigningKey::WalletAddress(args.addr)).await; } -pub async fn submit_init_account( +pub async fn submit_init_account( client: &C, - wallet: &mut Wallet, + wallet: &mut Wallet

, args: args::TxInitAccount, ) { let public_key = args.public_key; @@ -166,9 +166,9 @@ pub async fn submit_init_account(client, wallet, &args.tx, tx, TxSigningKey::WalletAddress(args.source)) + process_tx::(client, wallet, &args.tx, tx, TxSigningKey::WalletAddress(args.source)) .await; - save_initialized_accounts::(wallet, &args.tx, initialized_accounts).await; + save_initialized_accounts::(wallet, &args.tx, initialized_accounts).await; } pub async fn submit_init_validator( @@ -292,7 +292,7 @@ pub async fn submit_init_validator(client, &mut ctx.wallet, &tx_args, tx, TxSigningKey::WalletAddress(source)) + process_tx::(client, &mut ctx.wallet, &tx_args, tx, TxSigningKey::WalletAddress(source)) .await; if !tx_args.dry_run { let (validator_address_alias, validator_address) = @@ -501,9 +501,9 @@ impl masp::ShieldedUtils for CLIShieldedUtils { -pub async fn submit_transfer>( +pub async fn submit_transfer, P>( client: &C, - wallet: &mut Wallet, + wallet: &mut Wallet

, shielded: &mut ShieldedContext, args: args::TxTransfer, ) { @@ -613,7 +613,7 @@ pub async fn submit_transfer(client, wallet, &args.tx, default_signer.clone()) + let chosen_signer = tx_signer::(client, wallet, &args.tx, default_signer.clone()) .await .ref_to(); let shielded_gas = masp_tx_key().ref_to() == chosen_signer; @@ -669,12 +669,12 @@ pub async fn submit_transfer(client, wallet, &args.tx, tx, signing_address).await; + process_tx::(client, wallet, &args.tx, tx, signing_address).await; } -pub async fn submit_ibc_transfer( +pub async fn submit_ibc_transfer( client: &C, - wallet: &mut Wallet, + wallet: &mut Wallet

, args: args::TxIbcTransfer, ) { let source = args.source.clone(); @@ -782,7 +782,7 @@ pub async fn submit_ibc_transfer(client, wallet, &args.tx, tx, TxSigningKey::WalletAddress(args.source)) + process_tx::(client, wallet, &args.tx, tx, TxSigningKey::WalletAddress(args.source)) .await; } @@ -856,7 +856,7 @@ pub async fn submit_init_proposal( + let signing_key = find_keypair::( client, &mut ctx.wallet, &signer, @@ -922,14 +922,14 @@ pub async fn submit_init_proposal(client, &mut ctx.wallet, &args.tx, tx, TxSigningKey::WalletAddress(signer)) + process_tx::(client, &mut ctx.wallet, &args.tx, tx, TxSigningKey::WalletAddress(signer)) .await; } } -pub async fn submit_vote_proposal( +pub async fn submit_vote_proposal( client: &C, - wallet: &mut Wallet, + wallet: &mut Wallet

, args: args::VoteProposal, ) { let signer = if let Some(addr) = &args.tx.signer { @@ -958,7 +958,7 @@ pub async fn submit_vote_proposal( + let signing_key = find_keypair::( client, wallet, &signer, @@ -1055,7 +1055,7 @@ pub async fn submit_vote_proposal( + process_tx::( client, wallet, &args.tx, @@ -1077,9 +1077,9 @@ pub async fn submit_vote_proposal( +pub async fn submit_reveal_pk( client: &C, - wallet: &mut Wallet, + wallet: &mut Wallet

, args: args::RevealPk, ) { let args::RevealPk { @@ -1087,15 +1087,15 @@ pub async fn submit_reveal_pk(client, wallet, &public_key, &args).await { + if !reveal_pk_if_needed::(client, wallet, &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( +pub async fn reveal_pk_if_needed( client: &C, - wallet: &mut Wallet, + wallet: &mut Wallet

, public_key: &common::PublicKey, args: &args::Tx, ) -> bool { @@ -1104,7 +1104,7 @@ pub async fn reveal_pk_if_needed(client, wallet, public_key, args).await; + submit_reveal_pk_aux::(client, wallet, public_key, args).await; true } else { false @@ -1118,9 +1118,9 @@ pub async fn has_revealed_pk rpc::get_public_key(client, addr).await.is_some() } -pub async fn submit_reveal_pk_aux( +pub async fn submit_reveal_pk_aux( client: &C, - wallet: &mut Wallet, + wallet: &mut Wallet

, public_key: &common::PublicKey, args: &args::Tx, ) { @@ -1137,10 +1137,10 @@ pub async fn submit_reveal_pk_aux(client, wallet, &signer) + find_keypair::(client, wallet, &signer) .await } else { - find_keypair::(client, wallet, &addr).await + find_keypair::(client, wallet, &addr).await }; let epoch = rpc::query_epoch(client) .await; @@ -1257,9 +1257,9 @@ async fn filter_delegations( delegations.into_iter().flatten().collect() } -pub async fn submit_bond( +pub async fn submit_bond( client: &C, - wallet: &mut Wallet, + wallet: &mut Wallet

, args: args::Bond, ) { let validator = args.validator.clone(); @@ -1323,7 +1323,7 @@ pub async fn submit_bond( + process_tx::( client, wallet, &args.tx, @@ -1333,9 +1333,9 @@ pub async fn submit_bond( +pub async fn submit_unbond( client: &C, - wallet: &mut Wallet, + wallet: &mut Wallet

, args: args::Unbond, ) { let validator = args.validator.clone(); @@ -1400,7 +1400,7 @@ pub async fn submit_unbond( + process_tx::( client, wallet, &args.tx, @@ -1410,9 +1410,9 @@ pub async fn submit_unbond( +pub async fn submit_withdraw( client: &C, - wallet: &mut Wallet, + wallet: &mut Wallet

, args: args::Withdraw, ) { let epoch = rpc::query_epoch(client) @@ -1475,7 +1475,7 @@ pub async fn submit_withdraw( + process_tx::( client, wallet, &args.tx, @@ -1485,9 +1485,9 @@ pub async fn submit_withdraw( +pub async fn submit_validator_commission_change( client: &C, - wallet: &mut Wallet, + wallet: &mut Wallet

, args: args::TxCommissionRateChange, ) { let epoch = rpc::query_epoch(client) @@ -1556,7 +1556,7 @@ pub async fn submit_validator_commission_change( + process_tx::( client, wallet, &args.tx, @@ -1568,14 +1568,14 @@ pub async fn submit_validator_commission_change( +async fn process_tx( client: &C, - wallet: &mut Wallet, + wallet: &mut Wallet

, args: &args::Tx, tx: Tx, default_signer: TxSigningKey, ) -> Vec

{ - let to_broadcast = sign_tx::(client, wallet, tx, args, default_signer).await; + let to_broadcast = sign_tx::(client, wallet, tx, args, default_signer).await; // NOTE: use this to print the request JSON body: // let request = @@ -1628,8 +1628,8 @@ async fn process_tx( - wallet: &mut Wallet, +async fn save_initialized_accounts( + wallet: &mut Wallet

, args: &args::Tx, initialized_accounts: Vec

, ) { From e77794708f32de0ec47330b5541e540dc52936c1 Mon Sep 17 00:00:00 2001 From: Murisi Tarusenga Date: Sun, 18 Dec 2022 12:33:43 +0200 Subject: [PATCH 26/51] Started moving functions from tx.rs and rpc.rs. --- apps/src/lib/client/rpc.rs | 47 +---- apps/src/lib/client/signing.rs | 140 +-------------- apps/src/lib/client/tx.rs | 237 +------------------------ shared/Cargo.toml | 1 + shared/src/ledger/mod.rs | 2 + shared/src/ledger/rpc.rs | 60 +++++++ shared/src/ledger/signing.rs | 193 +++++++++++++++++++++ shared/src/ledger/tx.rs | 305 +++++++++++++++++++++++++++++++++ 8 files changed, 577 insertions(+), 408 deletions(-) create mode 100644 shared/src/ledger/signing.rs create mode 100644 shared/src/ledger/tx.rs diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index 177cd79c4b..c7dc6024f1 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -71,45 +71,7 @@ pub async fn query_tx_status status: namada::ledger::rpc::TxEventQuery<'_>, deadline: Instant, ) -> Event { - const ONE_SECOND: Duration = Duration::from_secs(1); - // sleep for the duration of `backoff`, - // and update the underlying value - async fn sleep_update(query: namada::ledger::rpc::TxEventQuery<'_>, backoff: &mut Duration) { - tracing::debug!( - ?query, - duration = ?backoff, - "Retrying tx status query after timeout", - ); - // simple linear backoff - if an event is not available, - // increase the backoff duration by one second - tokio::time::sleep(*backoff).await; - *backoff += ONE_SECOND; - } - tokio::time::timeout_at(deadline, async move { - let mut backoff = ONE_SECOND; - - loop { - tracing::debug!(query = ?status, "Querying tx status"); - let maybe_event = match query_tx_events(client, status).await { - Ok(response) => response, - Err(err) => { - //tracing::debug!(%err, "ABCI query failed"); - sleep_update(status, &mut backoff).await; - continue; - } - }; - if let Some(e) = maybe_event { - break Ok(e); - } - sleep_update(status, &mut backoff).await; - } - }) - .await - .map_err(|_| { - eprintln!("Transaction status query deadline of {deadline:?} exceeded"); - }) - .and_then(|result| result) - .unwrap_or_else(|_| cli::safe_exit(1)) + namada::ledger::rpc::query_tx_status(client, status, deadline).await } /// Query the epoch of the last committed block @@ -1756,12 +1718,7 @@ pub async fn query_slashes(c /// Dry run a transaction pub async fn dry_run_tx(client: &C, tx_bytes: Vec) { - let (data, height, prove) = (Some(tx_bytes), None, false); - let result = unwrap_client_response::( - RPC.shell().dry_run_tx(client, data, height, prove).await, - ) - .data; - println!("Dry-run result: {}", result); + println!("Dry-run result: {}", namada::ledger::rpc::dry_run_tx(client, tx_bytes).await); } /// Get account's public key stored in its storage sub-space diff --git a/apps/src/lib/client/signing.rs b/apps/src/lib/client/signing.rs index 569cc59552..9e2b579b06 100644 --- a/apps/src/lib/client/signing.rs +++ b/apps/src/lib/client/signing.rs @@ -16,6 +16,7 @@ use namada::ledger::rpc::TxBroadcastData; use namada::ledger::wallet::Wallet; use namada::ledger::wallet::WalletUtils; use crate::facade::tendermint_rpc::Client; +use namada::ledger::signing::TxSigningKey; /// Find the public key for the given address and try to load the keypair /// for it from the wallet. Panics if the key cannot be found or loaded. @@ -24,63 +25,7 @@ pub async fn find_keypair, addr: &Address, ) -> common::SecretKey { - match addr { - Address::Established(_) => { - println!( - "Looking-up public key of {} from the ledger...", - addr.encode() - ); - let public_key = rpc::get_public_key(client, addr) - .await - .unwrap_or_else(|| { - eprintln!( - "No public key found for the address {}", - addr.encode() - ); - cli::safe_exit(1); - }); - wallet.find_key_by_pk::(&public_key).unwrap_or_else(|err| { - eprintln!( - "Unable to load the keypair from the wallet for public \ - key {}. Failed with: {}", - public_key, err - ); - cli::safe_exit(1) - }) - } - Address::Implicit(ImplicitAddress(pkh)) => { - wallet.find_key_by_pkh::(pkh).unwrap_or_else(|err| { - eprintln!( - "Unable to load the keypair from the wallet for the \ - implicit address {}. Failed with: {}", - addr.encode(), - err - ); - cli::safe_exit(1) - }) - } - Address::Internal(_) => { - eprintln!( - "Internal address {} doesn't have any signing keys.", - addr - ); - cli::safe_exit(1) - } - } -} - -/// Carries types that can be directly/indirectly used to sign a transaction. -#[allow(clippy::large_enum_variant)] -#[derive(Clone)] -pub enum TxSigningKey { - // Do not sign any transaction - None, - // Obtain the actual keypair from wallet and use that to sign - WalletKeypair(common::SecretKey), - // Obtain the keypair corresponding to given address from wallet and sign - WalletAddress(Address), - // Directly use the given secret key to sign transactions - SecretKey(common::SecretKey), + namada::ledger::signing::find_keypair::(client, wallet, addr).await } /// Given CLI arguments and some defaults, determine the rightful transaction @@ -93,46 +38,7 @@ pub async fn tx_signer common::SecretKey { - // Override the default signing key source if possible - if let Some(signing_key) = &args.signing_key { - default = TxSigningKey::WalletKeypair(signing_key.clone()); - } else if let Some(signer) = &args.signer { - default = TxSigningKey::WalletAddress(signer.clone()); - } - // Now actually fetch the signing key and apply it - match default { - TxSigningKey::WalletKeypair(signing_key) => { - signing_key - } - TxSigningKey::WalletAddress(signer) => { - let signer = signer; - let signing_key = find_keypair::( - client, - wallet, - &signer, - ) - .await; - // Check if the signer is implicit account that needs to reveal its - // PK first - if matches!(signer, Address::Implicit(_)) { - let pk: common::PublicKey = signing_key.ref_to(); - super::tx::reveal_pk_if_needed::(client, wallet, &pk, args).await; - } - signing_key - } - TxSigningKey::SecretKey(signing_key) => { - // Check if the signing key needs to reveal its PK first - let pk: common::PublicKey = signing_key.ref_to(); - super::tx::reveal_pk_if_needed::(client, wallet, &pk, args).await; - signing_key - } - TxSigningKey::None => { - panic!( - "All transactions must be signed; please either specify the \ - key or the address from which to look up the signing key." - ); - } - } + namada::ledger::signing::tx_signer::(client, wallet, args, default).await } /// Sign a transaction with a given signing key or public key of a given signer. @@ -150,17 +56,7 @@ pub async fn sign_tx TxBroadcastData { - let keypair = tx_signer::(client, wallet, args, default).await; - let tx = tx.sign(&keypair); - - let epoch = rpc::query_epoch(client) - .await; - let broadcast_data = if args.dry_run { - TxBroadcastData::DryRun(tx) - } else { - sign_wrapper(args, epoch, tx, &keypair).await - }; - broadcast_data + namada::ledger::signing::sign_tx::(client, wallet, tx, args, default).await } /// Create a wrapper tx from a normal tx. Get the hash of the @@ -172,31 +68,5 @@ pub async fn sign_wrapper( tx: Tx, keypair: &common::SecretKey, ) -> TxBroadcastData { - let tx = { - WrapperTx::new( - Fee { - amount: args.fee_amount, - token: args.fee_token.clone(), - }, - keypair, - epoch, - args.gas_limit.clone(), - tx, - // TODO: Actually use the fetched encryption key - Default::default(), - ) - }; - - // We use this to determine when the wrapper tx makes it on-chain - let wrapper_hash = hash_tx(&tx.try_to_vec().unwrap()).to_string(); - // We use this to determine when the decrypted inner tx makes it - // on-chain - let decrypted_hash = tx.tx_hash.to_string(); - TxBroadcastData::Wrapper { - tx: tx - .sign(keypair) - .expect("Wrapper tx signing keypair should be correct"), - wrapper_hash, - decrypted_hash, - } + namada::ledger::signing::sign_wrapper(args, epoch, tx, keypair).await } diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index d929e725a9..fe99fb7880 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -53,7 +53,8 @@ use super::rpc; use crate::cli::context::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}; +use crate::client::signing::{find_keypair, sign_tx, tx_signer}; +use namada::ledger::signing::TxSigningKey; use namada::ledger::rpc::{TxBroadcastData, TxResponse}; use crate::facade::tendermint_config::net::Address as TendermintAddress; use crate::facade::tendermint_rpc::endpoint::broadcast::tx_sync::Response; @@ -1082,15 +1083,7 @@ pub async fn submit_reveal_pk, args: args::RevealPk, ) { - let args::RevealPk { - tx: args, - public_key, - } = args; - let public_key = public_key; - if !reveal_pk_if_needed::(client, wallet, &public_key, &args).await { - let addr: Address = (&public_key).into(); - println!("PK for {addr} is already revealed, nothing to do."); - } + namada::ledger::tx::submit_reveal_pk::(client, wallet, args).await; } pub async fn reveal_pk_if_needed( @@ -1099,23 +1092,14 @@ pub async fn reveal_pk_if_needed bool { - let addr: Address = public_key.into(); - // Check if PK revealed - if args.force || !has_revealed_pk(client, &addr).await - { - // If not, submit it - submit_reveal_pk_aux::(client, wallet, public_key, args).await; - true - } else { - false - } + namada::ledger::tx::reveal_pk_if_needed::(client, wallet, public_key, args).await } pub async fn has_revealed_pk( client: &C, addr: &Address, ) -> bool { - rpc::get_public_key(client, addr).await.is_some() + namada::ledger::tx::has_revealed_pk(client, addr).await } pub async fn submit_reveal_pk_aux( @@ -1124,69 +1108,7 @@ pub async fn submit_reveal_pk_aux(client, wallet, &signer) - .await - } else { - find_keypair::(client, wallet, &addr).await - }; - let epoch = rpc::query_epoch(client) - .await; - let to_broadcast = if args.dry_run { - TxBroadcastData::DryRun(tx) - } else { - super::signing::sign_wrapper(args, epoch, tx, &keypair).await - }; - - if args.dry_run { - if let TxBroadcastData::DryRun(tx) = to_broadcast { - rpc::dry_run_tx(client, tx.to_bytes()).await; - } else { - panic!( - "Expected a dry-run transaction, received a wrapper \ - transaction instead" - ); - } - } else { - // Either broadcast or submit transaction and collect result into - // sum type - let result = if args.broadcast_only { - Left(broadcast_tx(client, &to_broadcast).await) - } else { - Right(submit_tx(client, to_broadcast).await) - }; - // Return result based on executed operation, otherwise deal with - // the encountered errors uniformly - match result { - Right(Err(err)) => { - eprintln!( - "Encountered error while broadcasting transaction: {}", - err - ); - safe_exit(1) - } - Left(Err(err)) => { - eprintln!( - "Encountered error while broadcasting transaction: {}", - err - ); - safe_exit(1) - } - _ => {} - } - } + namada::ledger::tx::submit_reveal_pk_aux::(client, wallet, public_key, args); } /// Check if current epoch is in the last third of the voting period of the @@ -1575,56 +1497,7 @@ async fn process_tx Vec
{ - let to_broadcast = sign_tx::(client, wallet, tx, args, default_signer).await; - // NOTE: use this to print the request JSON body: - - // let request = - // tendermint_rpc::endpoint::broadcast::tx_commit::Request::new( - // tx_bytes.clone().into(), - // ); - // use tendermint_rpc::Request; - // let request_body = request.into_json(); - // println!("HTTP request body: {}", request_body); - - if args.dry_run { - if let TxBroadcastData::DryRun(tx) = to_broadcast { - rpc::dry_run_tx(client, tx.to_bytes()).await; - vec![] - } else { - panic!( - "Expected a dry-run transaction, received a wrapper \ - transaction instead" - ); - } - } else { - // Either broadcast or submit transaction and collect result into - // sum type - let result = if args.broadcast_only { - Left(broadcast_tx(client, &to_broadcast).await) - } else { - Right(submit_tx(client, to_broadcast).await) - }; - // Return result based on executed operation, otherwise deal with - // the encountered errors uniformly - match result { - Right(Ok(result)) => result.initialized_accounts, - Left(Ok(_)) => Vec::default(), - Right(Err(err)) => { - eprintln!( - "Encountered error while broadcasting transaction: {}", - err - ); - safe_exit(1) - } - Left(Err(err)) => { - eprintln!( - "Encountered error while broadcasting transaction: {}", - err - ); - safe_exit(1) - } - } - } + namada::ledger::tx::process_tx::(client, wallet, args, tx, default_signer).await } /// Save accounts initialized from a tx into the wallet, if any. @@ -1688,37 +1561,7 @@ pub async fn broadcast_tx( rpc_cli: &C, to_broadcast: &TxBroadcastData, ) -> Result { - let (tx, wrapper_tx_hash, decrypted_tx_hash) = match to_broadcast { - TxBroadcastData::Wrapper { - tx, - wrapper_hash, - decrypted_hash, - } => (tx, wrapper_hash, decrypted_hash), - _ => panic!("Cannot broadcast a dry-run transaction"), - }; - - tracing::debug!( - transaction = ?to_broadcast, - "Broadcasting transaction", - ); - - // TODO: configure an explicit timeout value? we need to hack away at - // `tendermint-rs` for this, which is currently using a hard-coded 30s - // timeout. - let response = rpc_cli.broadcast_tx_sync(tx.to_bytes().into()).await?; - - if response.code == 0.into() { - println!("Transaction added to mempool: {:?}", response); - // Print the transaction identifiers to enable the extraction of - // acceptance/application results later - { - println!("Wrapper transaction hash: {:?}", wrapper_tx_hash); - println!("Inner transaction hash: {:?}", decrypted_tx_hash); - } - Ok(response) - } else { - Err(RpcError::server(serde_json::to_string(&response).unwrap())) - } + namada::ledger::tx::broadcast_tx(rpc_cli, to_broadcast).await } /// Broadcast a transaction to be included in the blockchain. @@ -1733,67 +1576,5 @@ pub async fn submit_tx( client: &C, to_broadcast: TxBroadcastData, ) -> Result { - let (_, wrapper_hash, decrypted_hash) = match &to_broadcast { - TxBroadcastData::Wrapper { - tx, - wrapper_hash, - decrypted_hash, - } => (tx, wrapper_hash, decrypted_hash), - _ => panic!("Cannot broadcast a dry-run transaction"), - }; - - // Broadcast the supplied transaction - broadcast_tx(client, &to_broadcast).await?; - - let max_wait_time = Duration::from_secs( - env::var(ENV_VAR_NAMADA_EVENTS_MAX_WAIT_TIME_SECONDS) - .ok() - .and_then(|val| val.parse().ok()) - .unwrap_or(DEFAULT_NAMADA_EVENTS_MAX_WAIT_TIME_SECONDS), - ); - let deadline = Instant::now() + max_wait_time; - - tracing::debug!( - transaction = ?to_broadcast, - ?deadline, - "Awaiting transaction approval", - ); - - let parsed = { - let wrapper_query = namada::ledger::rpc::TxEventQuery::Accepted(wrapper_hash.as_str()); - let event = - rpc::query_tx_status(client, wrapper_query, deadline) - .await; - let parsed = TxResponse::from_event(event); - - println!( - "Transaction accepted with result: {}", - serde_json::to_string_pretty(&parsed).unwrap() - ); - // The transaction is now on chain. We wait for it to be decrypted - // and applied - if parsed.code == 0.to_string() { - // We also listen to the event emitted when the encrypted - // payload makes its way onto the blockchain - let decrypted_query = - namada::ledger::rpc::TxEventQuery::Applied(decrypted_hash.as_str()); - let event = - rpc::query_tx_status(client, decrypted_query, deadline).await; - let parsed = TxResponse::from_event(event); - println!( - "Transaction applied with result: {}", - serde_json::to_string_pretty(&parsed).unwrap() - ); - Ok(parsed) - } else { - Ok(parsed) - } - }; - - tracing::debug!( - transaction = ?to_broadcast, - "Transaction approved", - ); - - parsed + namada::ledger::tx::submit_tx(client, to_broadcast).await } diff --git a/shared/Cargo.toml b/shared/Cargo.toml index 00676aacf6..73e6eddd1c 100644 --- a/shared/Cargo.toml +++ b/shared/Cargo.toml @@ -136,6 +136,7 @@ zeroize = "1.5.5" toml = "0.5.8" bimap = {version = "0.6.2", features = ["serde"]} orion = "0.16.0" +tokio = {version = "1.8.2"} [dev-dependencies] assert_matches = "1.5.0" diff --git a/shared/src/ledger/mod.rs b/shared/src/ledger/mod.rs index c9a85fdf19..36ad2dd022 100644 --- a/shared/src/ledger/mod.rs +++ b/shared/src/ledger/mod.rs @@ -14,6 +14,8 @@ pub mod vp_host_fns; pub mod wallet; pub mod args; pub mod rpc; +pub mod tx; +pub mod signing; pub use namada_core::ledger::{ gas, governance, parameters, storage_api, tx_env, vp_env, diff --git a/shared/src/ledger/rpc.rs b/shared/src/ledger/rpc.rs index d194e729cb..c65b4198d2 100644 --- a/shared/src/ledger/rpc.rs +++ b/shared/src/ledger/rpc.rs @@ -50,6 +50,57 @@ use crate::types::governance::{ }; use itertools::{Itertools}; use crate::ledger::pos::types::decimal_mult_u64; +use tokio::time::{Duration, Instant}; + +/// Query the status of a given transaction. +/// +/// If a response is not delivered until `deadline`, we exit the cli with an +/// error. +pub async fn query_tx_status( + client: &C, + status: TxEventQuery<'_>, + deadline: Instant, +) -> Event { + const ONE_SECOND: Duration = Duration::from_secs(1); + // sleep for the duration of `backoff`, + // and update the underlying value + async fn sleep_update(query: TxEventQuery<'_>, backoff: &mut Duration) { + tracing::debug!( + ?query, + duration = ?backoff, + "Retrying tx status query after timeout", + ); + // simple linear backoff - if an event is not available, + // increase the backoff duration by one second + tokio::time::sleep(*backoff).await; + *backoff += ONE_SECOND; + } + tokio::time::timeout_at(deadline, async move { + let mut backoff = ONE_SECOND; + + loop { + tracing::debug!(query = ?status, "Querying tx status"); + let maybe_event = match query_tx_events(client, status).await { + Ok(response) => response, + Err(err) => { + //tracing::debug!(%err, "ABCI query failed"); + sleep_update(status, &mut backoff).await; + continue; + } + }; + if let Some(e) = maybe_event { + break Ok(e); + } + sleep_update(status, &mut backoff).await; + } + }) + .await + .map_err(|_| { + eprintln!("Transaction status query deadline of {deadline:?} exceeded"); + }) + .and_then(|result| result) + .unwrap_or_else(|_| panic!()) +} /// Query the epoch of the last committed block pub async fn query_epoch(client: &C) -> Epoch { @@ -332,6 +383,15 @@ pub async fn query_tx_events( } } +/// Dry run a transaction +pub async fn dry_run_tx(client: &C, tx_bytes: Vec) -> namada_core::types::transaction::TxResult { + let (data, height, prove) = (Some(tx_bytes), None, false); + unwrap_client_response::( + RPC.shell().dry_run_tx(client, data, height, prove).await, + ) + .data +} + /// Data needed for broadcasting a tx and /// monitoring its progress on chain /// diff --git a/shared/src/ledger/signing.rs b/shared/src/ledger/signing.rs new file mode 100644 index 0000000000..9a2ebf161d --- /dev/null +++ b/shared/src/ledger/signing.rs @@ -0,0 +1,193 @@ +use crate::types::transaction::hash_tx; +use crate::types::transaction::{Fee, WrapperTx}; +use crate::tendermint_rpc::Client; +use crate::ledger::wallet::{Wallet, WalletUtils}; +use crate::types::key::*; +use namada_core::types::address::Address; +use crate::ledger::rpc; +use namada_core::types::address::ImplicitAddress; +use crate::types::storage::Epoch; +use crate::ledger::rpc::TxBroadcastData; +use crate::proto::Tx; +use crate::ledger::args; +use borsh::BorshSerialize; + +/// Find the public key for the given address and try to load the keypair +/// for it from the wallet. Panics if the key cannot be found or loaded. +pub async fn find_keypair( + client: &C, + wallet: &mut Wallet

, + addr: &Address, +) -> common::SecretKey { + match addr { + Address::Established(_) => { + println!( + "Looking-up public key of {} from the ledger...", + addr.encode() + ); + let public_key = rpc::get_public_key(client, addr) + .await + .unwrap_or_else(|| { + panic!( + "No public key found for the address {}", + addr.encode() + ); + }); + wallet.find_key_by_pk::(&public_key).unwrap_or_else(|err| { + panic!( + "Unable to load the keypair from the wallet for public \ + key {}. Failed with: {}", + public_key, err + ); + }) + } + Address::Implicit(ImplicitAddress(pkh)) => { + wallet.find_key_by_pkh::(pkh).unwrap_or_else(|err| { + panic!( + "Unable to load the keypair from the wallet for the \ + implicit address {}. Failed with: {}", + addr.encode(), + err + ); + }) + } + Address::Internal(_) => { + panic!( + "Internal address {} doesn't have any signing keys.", + addr + ); + } + } +} + +/// Carries types that can be directly/indirectly used to sign a transaction. +#[allow(clippy::large_enum_variant)] +#[derive(Clone)] +pub enum TxSigningKey { + // Do not sign any transaction + None, + // Obtain the actual keypair from wallet and use that to sign + WalletKeypair(common::SecretKey), + // Obtain the keypair corresponding to given address from wallet and sign + WalletAddress(Address), + // Directly use the given secret key to sign transactions + SecretKey(common::SecretKey), +} + +/// Given CLI arguments and some defaults, determine the rightful transaction +/// signer. Return the given signing key or public key of the given signer if +/// possible. If no explicit signer given, use the `default`. If no `default` +/// is given, panics. +pub async fn tx_signer( + client: &C, + wallet: &mut Wallet

, + args: &args::Tx, + mut default: TxSigningKey, +) -> common::SecretKey { + // Override the default signing key source if possible + if let Some(signing_key) = &args.signing_key { + default = TxSigningKey::WalletKeypair(signing_key.clone()); + } else if let Some(signer) = &args.signer { + default = TxSigningKey::WalletAddress(signer.clone()); + } + // Now actually fetch the signing key and apply it + match default { + TxSigningKey::WalletKeypair(signing_key) => { + signing_key + } + TxSigningKey::WalletAddress(signer) => { + let signer = signer; + let signing_key = find_keypair::( + client, + wallet, + &signer, + ) + .await; + // Check if the signer is implicit account that needs to reveal its + // PK first + if matches!(signer, Address::Implicit(_)) { + let pk: common::PublicKey = signing_key.ref_to(); + super::tx::reveal_pk_if_needed::(client, wallet, &pk, args).await; + } + signing_key + } + TxSigningKey::SecretKey(signing_key) => { + // Check if the signing key needs to reveal its PK first + let pk: common::PublicKey = signing_key.ref_to(); + super::tx::reveal_pk_if_needed::(client, wallet, &pk, args).await; + signing_key + } + TxSigningKey::None => { + panic!( + "All transactions must be signed; please either specify the \ + key or the address from which to look up the signing key." + ); + } + } +} + +/// Sign a transaction with a given signing key or public key of a given signer. +/// If no explicit signer given, use the `default`. If no `default` is given, +/// panics. +/// +/// If this is not a dry run, the tx is put in a wrapper and returned along with +/// hashes needed for monitoring the tx on chain. +/// +/// If it is a dry run, it is not put in a wrapper, but returned as is. +pub async fn sign_tx( + client: &C, + wallet: &mut Wallet

, + tx: Tx, + args: &args::Tx, + default: TxSigningKey, +) -> TxBroadcastData { + let keypair = tx_signer::(client, wallet, args, default).await; + let tx = tx.sign(&keypair); + + let epoch = rpc::query_epoch(client) + .await; + let broadcast_data = if args.dry_run { + TxBroadcastData::DryRun(tx) + } else { + sign_wrapper(args, epoch, tx, &keypair).await + }; + 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( + args: &args::Tx, + epoch: Epoch, + tx: Tx, + keypair: &common::SecretKey, +) -> TxBroadcastData { + let tx = { + WrapperTx::new( + Fee { + amount: args.fee_amount, + token: args.fee_token.clone(), + }, + keypair, + epoch, + args.gas_limit.clone(), + tx, + // TODO: Actually use the fetched encryption key + Default::default(), + ) + }; + + // We use this to determine when the wrapper tx makes it on-chain + let wrapper_hash = hash_tx(&tx.try_to_vec().unwrap()).to_string(); + // We use this to determine when the decrypted inner tx makes it + // on-chain + let decrypted_hash = tx.tx_hash.to_string(); + TxBroadcastData::Wrapper { + tx: tx + .sign(keypair) + .expect("Wrapper tx signing keypair should be correct"), + wrapper_hash, + decrypted_hash, + } +} diff --git a/shared/src/ledger/tx.rs b/shared/src/ledger/tx.rs new file mode 100644 index 0000000000..811ca7bcd6 --- /dev/null +++ b/shared/src/ledger/tx.rs @@ -0,0 +1,305 @@ +use itertools::Either::*; +use crate::ledger::args; +use crate::ledger::wallet::{Wallet, WalletUtils}; +use crate::tendermint_rpc::Client; +use crate::proto::Tx; +use namada_core::types::address::Address; +use crate::ledger::signing::sign_tx; +use crate::ledger::signing::TxSigningKey; +use crate::ledger::rpc::{self, TxBroadcastData}; +use crate::ledger::signing::find_keypair; +use crate::types::key::*; +use borsh::BorshSerialize; +use crate::tendermint_rpc::error::Error as RpcError; +use crate::ledger::rpc::TxResponse; +use tokio::time::{Duration, Instant}; +use crate::tendermint_rpc::endpoint::broadcast::tx_sync::Response; + +/// Default timeout in seconds for requests to the `/accepted` +/// and `/applied` ABCI query endpoints. +const DEFAULT_NAMADA_EVENTS_MAX_WAIT_TIME_SECONDS: u64 = 60; + +/// Submit transaction and wait for result. Returns a list of addresses +/// initialized in the transaction if any. In dry run, this is always empty. +pub async fn process_tx( + client: &C, + wallet: &mut Wallet

, + args: &args::Tx, + tx: Tx, + default_signer: TxSigningKey, +) -> Vec

{ + let to_broadcast = sign_tx::(client, wallet, tx, args, default_signer).await; + // NOTE: use this to print the request JSON body: + + // let request = + // tendermint_rpc::endpoint::broadcast::tx_commit::Request::new( + // tx_bytes.clone().into(), + // ); + // use tendermint_rpc::Request; + // let request_body = request.into_json(); + // println!("HTTP request body: {}", request_body); + + if args.dry_run { + if let TxBroadcastData::DryRun(tx) = to_broadcast { + rpc::dry_run_tx(client, tx.to_bytes()).await; + vec![] + } else { + panic!( + "Expected a dry-run transaction, received a wrapper \ + transaction instead" + ); + } + } else { + // Either broadcast or submit transaction and collect result into + // sum type + let result = if args.broadcast_only { + Left(broadcast_tx(client, &to_broadcast).await) + } else { + Right(submit_tx(client, to_broadcast).await) + }; + // Return result based on executed operation, otherwise deal with + // the encountered errors uniformly + match result { + Right(Ok(result)) => result.initialized_accounts, + Left(Ok(_)) => Vec::default(), + Right(Err(err)) => { + panic!( + "Encountered error while broadcasting transaction: {}", + err + ); + } + Left(Err(err)) => { + panic!( + "Encountered error while broadcasting transaction: {}", + err + ); + } + } + } +} + +pub async fn submit_reveal_pk( + client: &C, + wallet: &mut Wallet

, + args: args::RevealPk, +) { + let args::RevealPk { + tx: args, + public_key, + } = args; + let public_key = public_key; + if !reveal_pk_if_needed::(client, wallet, &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( + client: &C, + wallet: &mut Wallet

, + public_key: &common::PublicKey, + args: &args::Tx, +) -> bool { + let addr: Address = public_key.into(); + // Check if PK revealed + if args.force || !has_revealed_pk(client, &addr).await + { + // If not, submit it + submit_reveal_pk_aux::(client, wallet, public_key, args).await; + true + } else { + false + } +} + +pub async fn has_revealed_pk( + client: &C, + addr: &Address, +) -> bool { + rpc::get_public_key(client, addr).await.is_some() +} + +pub async fn submit_reveal_pk_aux( + client: &C, + wallet: &mut Wallet

, + public_key: &common::PublicKey, + args: &args::Tx, +) { + let addr: Address = public_key.into(); + println!("Submitting a tx to reveal the public key for address {addr}..."); + let tx_data = public_key + .try_to_vec() + .expect("Encoding a public key shouldn't fail"); + let tx_code = args.tx_code_path.clone(); + let tx = Tx::new(tx_code, Some(tx_data)); + + // submit_tx without signing the inner tx + let keypair = if let Some(signing_key) = &args.signing_key { + signing_key.clone() + } else if let Some(signer) = args.signer.as_ref() { + let signer = signer; + find_keypair::(client, wallet, &signer) + .await + } else { + find_keypair::(client, wallet, &addr).await + }; + let epoch = rpc::query_epoch(client) + .await; + let to_broadcast = if args.dry_run { + TxBroadcastData::DryRun(tx) + } else { + super::signing::sign_wrapper(args, epoch, tx, &keypair).await + }; + + if args.dry_run { + if let TxBroadcastData::DryRun(tx) = to_broadcast { + rpc::dry_run_tx(client, tx.to_bytes()).await; + } else { + panic!( + "Expected a dry-run transaction, received a wrapper \ + transaction instead" + ); + } + } else { + // Either broadcast or submit transaction and collect result into + // sum type + let result = if args.broadcast_only { + Left(broadcast_tx(client, &to_broadcast).await) + } else { + Right(submit_tx(client, to_broadcast).await) + }; + // Return result based on executed operation, otherwise deal with + // the encountered errors uniformly + match result { + Right(Err(err)) => { + panic!( + "Encountered error while broadcasting transaction: {}", + err + ); + } + Left(Err(err)) => { + panic!( + "Encountered error while broadcasting transaction: {}", + err + ); + } + _ => {} + } + } +} + +/// Broadcast a transaction to be included in the blockchain and checks that +/// the tx has been successfully included into the mempool of a validator +/// +/// In the case of errors in any of those stages, an error message is returned +pub async fn broadcast_tx( + rpc_cli: &C, + to_broadcast: &TxBroadcastData, +) -> Result { + let (tx, wrapper_tx_hash, decrypted_tx_hash) = match to_broadcast { + TxBroadcastData::Wrapper { + tx, + wrapper_hash, + decrypted_hash, + } => (tx, wrapper_hash, decrypted_hash), + _ => panic!("Cannot broadcast a dry-run transaction"), + }; + + tracing::debug!( + transaction = ?to_broadcast, + "Broadcasting transaction", + ); + + // TODO: configure an explicit timeout value? we need to hack away at + // `tendermint-rs` for this, which is currently using a hard-coded 30s + // timeout. + let response = rpc_cli.broadcast_tx_sync(tx.to_bytes().into()).await?; + + if response.code == 0.into() { + println!("Transaction added to mempool: {:?}", response); + // Print the transaction identifiers to enable the extraction of + // acceptance/application results later + { + println!("Wrapper transaction hash: {:?}", wrapper_tx_hash); + println!("Inner transaction hash: {:?}", decrypted_tx_hash); + } + Ok(response) + } else { + Err(RpcError::server(serde_json::to_string(&response).unwrap())) + } +} + +/// Broadcast a transaction to be included in the blockchain. +/// +/// Checks that +/// 1. The tx has been successfully included into the mempool of a validator +/// 2. The tx with encrypted payload has been included on the blockchain +/// 3. The decrypted payload of the tx has been included on the blockchain. +/// +/// In the case of errors in any of those stages, an error message is returned +pub async fn submit_tx( + client: &C, + to_broadcast: TxBroadcastData, +) -> Result { + let (_, wrapper_hash, decrypted_hash) = match &to_broadcast { + TxBroadcastData::Wrapper { + tx, + wrapper_hash, + decrypted_hash, + } => (tx, wrapper_hash, decrypted_hash), + _ => panic!("Cannot broadcast a dry-run transaction"), + }; + + // Broadcast the supplied transaction + broadcast_tx(client, &to_broadcast).await?; + + let max_wait_time = Duration::from_secs( + DEFAULT_NAMADA_EVENTS_MAX_WAIT_TIME_SECONDS, + ); + let deadline = Instant::now() + max_wait_time; + + tracing::debug!( + transaction = ?to_broadcast, + ?deadline, + "Awaiting transaction approval", + ); + + let parsed = { + let wrapper_query = crate::ledger::rpc::TxEventQuery::Accepted(wrapper_hash.as_str()); + let event = + rpc::query_tx_status(client, wrapper_query, deadline) + .await; + let parsed = TxResponse::from_event(event); + + println!( + "Transaction accepted with result: {}", + serde_json::to_string_pretty(&parsed).unwrap() + ); + // The transaction is now on chain. We wait for it to be decrypted + // and applied + if parsed.code == 0.to_string() { + // We also listen to the event emitted when the encrypted + // payload makes its way onto the blockchain + let decrypted_query = + rpc::TxEventQuery::Applied(decrypted_hash.as_str()); + let event = + rpc::query_tx_status(client, decrypted_query, deadline).await; + let parsed = TxResponse::from_event(event); + println!( + "Transaction applied with result: {}", + serde_json::to_string_pretty(&parsed).unwrap() + ); + Ok(parsed) + } else { + Ok(parsed) + } + }; + + tracing::debug!( + transaction = ?to_broadcast, + "Transaction approved", + ); + + parsed +} + From 9f6baa8d30c11d3d90988558c226b94b109ac728 Mon Sep 17 00:00:00 2001 From: Murisi Tarusenga Date: Sun, 18 Dec 2022 13:00:48 +0200 Subject: [PATCH 27/51] Extended WalletUtils to allow prompting for aliases. --- apps/src/lib/client/tx.rs | 46 +------------------------------ apps/src/lib/wallet/mod.rs | 9 ++++++ shared/src/ledger/tx.rs | 49 +++++++++++++++++++++++++++++++++ shared/src/ledger/wallet/mod.rs | 4 +++ 4 files changed, 63 insertions(+), 45 deletions(-) diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index fe99fb7880..c7b3588433 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -1506,51 +1506,7 @@ async fn save_initialized_accounts( args: &args::Tx, initialized_accounts: Vec

, ) { - let len = initialized_accounts.len(); - if len != 0 { - // Store newly initialized account addresses in the wallet - println!( - "The transaction initialized {} new account{}", - len, - if len == 1 { "" } else { "s" } - ); - // Store newly initialized account addresses in the wallet - for (ix, address) in initialized_accounts.iter().enumerate() { - let encoded = address.encode(); - let alias: Cow = match &args.initialized_account_alias { - Some(initialized_account_alias) => { - if len == 1 { - // If there's only one account, use the - // alias as is - initialized_account_alias.into() - } else { - // If there're multiple accounts, use - // the alias as prefix, followed by - // index number - format!("{}{}", initialized_account_alias, ix).into() - } - } - None => { - print!("Choose an alias for {}: ", encoded); - io::stdout().flush().await.unwrap(); - let mut alias = String::new(); - io::stdin().read_line(&mut alias).await.unwrap(); - alias.trim().to_owned().into() - } - }; - let alias = alias.into_owned(); - let added = wallet.add_address::(alias.clone(), address.clone()); - match added { - Some(new_alias) if new_alias != encoded => { - println!( - "Added alias {} for address {}.", - new_alias, encoded - ); - } - _ => println!("No alias added for address {}.", encoded), - }; - } - } + namada::ledger::tx::save_initialized_accounts::(wallet, args, initialized_accounts).await } /// Broadcast a transaction to be included in the blockchain and checks that diff --git a/apps/src/lib/wallet/mod.rs b/apps/src/lib/wallet/mod.rs index 758a7ff78d..5493fb1c38 100644 --- a/apps/src/lib/wallet/mod.rs +++ b/apps/src/lib/wallet/mod.rs @@ -89,6 +89,15 @@ impl WalletUtils for CliWalletUtils { pwd } + /// Read an alias from the file/env/stdin. + fn read_alias(prompt_msg: &str) -> String { + print!("Choose an alias for {}: ", prompt_msg); + io::stdout().flush().unwrap(); + let mut alias = String::new(); + io::stdin().read_line(&mut alias).unwrap(); + alias.trim().to_owned() + } + /// The given alias has been selected but conflicts with another alias in /// the store. Offer the user to either replace existing mapping, alter the /// chosen alias to a name of their chosing, or cancel the aliasing. diff --git a/shared/src/ledger/tx.rs b/shared/src/ledger/tx.rs index 811ca7bcd6..3b4985a78f 100644 --- a/shared/src/ledger/tx.rs +++ b/shared/src/ledger/tx.rs @@ -14,6 +14,7 @@ use crate::tendermint_rpc::error::Error as RpcError; use crate::ledger::rpc::TxResponse; use tokio::time::{Duration, Instant}; use crate::tendermint_rpc::endpoint::broadcast::tx_sync::Response; +use std::borrow::Cow; /// Default timeout in seconds for requests to the `/accepted` /// and `/applied` ABCI query endpoints. @@ -303,3 +304,51 @@ pub async fn submit_tx( parsed } +/// Save accounts initialized from a tx into the wallet, if any. +pub async fn save_initialized_accounts( + wallet: &mut Wallet

, + args: &args::Tx, + initialized_accounts: Vec

, +) { + let len = initialized_accounts.len(); + if len != 0 { + // Store newly initialized account addresses in the wallet + println!( + "The transaction initialized {} new account{}", + len, + if len == 1 { "" } else { "s" } + ); + // Store newly initialized account addresses in the wallet + for (ix, address) in initialized_accounts.iter().enumerate() { + let encoded = address.encode(); + let alias: Cow = match &args.initialized_account_alias { + Some(initialized_account_alias) => { + if len == 1 { + // If there's only one account, use the + // alias as is + initialized_account_alias.into() + } else { + // If there're multiple accounts, use + // the alias as prefix, followed by + // index number + format!("{}{}", initialized_account_alias, ix).into() + } + } + None => { + U::read_alias(&encoded).into() + } + }; + let alias = alias.into_owned(); + let added = wallet.add_address::(alias.clone(), address.clone()); + match added { + Some(new_alias) if new_alias != encoded => { + println!( + "Added alias {} for address {}.", + new_alias, encoded + ); + } + _ => println!("No alias added for address {}.", encoded), + }; + } + } +} diff --git a/shared/src/ledger/wallet/mod.rs b/shared/src/ledger/wallet/mod.rs index 06e167b5ae..1e7def674a 100644 --- a/shared/src/ledger/wallet/mod.rs +++ b/shared/src/ledger/wallet/mod.rs @@ -34,6 +34,10 @@ pub trait WalletUtils { /// if all options are empty/invalid. fn read_password(prompt_msg: &str) -> String; + /// Read an alias from the file/env/stdin. Panics if all options are empty/ + /// invalid. + fn read_alias(prompt_msg: &str) -> String; + /// The given alias has been selected but conflicts with another alias in /// the store. Offer the user to either replace existing mapping, alter the /// chosen alias to a name of their chosing, or cancel the aliasing. From 11b7da9e4e28b761a5f3d978715ae0a22ffaf659 Mon Sep 17 00:00:00 2001 From: Murisi Tarusenga Date: Sun, 18 Dec 2022 13:28:18 +0200 Subject: [PATCH 28/51] Moved more tx.rs functions into shared. --- apps/src/lib/client/tx.rs | 285 +----------------------------- shared/src/ledger/tx.rs | 355 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 359 insertions(+), 281 deletions(-) diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index c7b3588433..f97150d61c 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -1184,75 +1184,7 @@ pub async fn submit_bond, args: args::Bond, ) { - let validator = args.validator.clone(); - // Check that the validator address exists on chain - let is_validator = - rpc::is_validator(client, &validator).await; - if !is_validator { - eprintln!( - "The address {} doesn't belong to any known validator account.", - validator - ); - if !args.tx.force { - safe_exit(1) - } - } - let source = args.source.clone(); - // Check that the source address exists on chain - if let Some(source) = &source { - let source_exists = - rpc::known_address::(client, source).await; - if !source_exists { - eprintln!("The source address {} doesn't exist on chain.", source); - if !args.tx.force { - safe_exit(1) - } - } - } - // 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(&args.native_token, bond_source); - match rpc::query_storage_value::(&client, &balance_key).await - { - Some(balance) => { - if balance < args.amount { - eprintln!( - "The balance of the source {} is lower than the amount to \ - be transferred. Amount to transfer is {} and the balance \ - is {}.", - bond_source, args.amount, balance - ); - if !args.tx.force { - safe_exit(1) - } - } - } - None => { - eprintln!("No balance found for the source {}", bond_source); - if !args.tx.force { - safe_exit(1) - } - } - } - let tx_code = args.tx_code_path; - let bond = pos::Bond { - validator, - amount: args.amount, - source, - }; - let data = bond.try_to_vec().expect("Encoding tx data shouldn't fail"); - - let tx = Tx::new(tx_code, Some(data)); - let default_signer = args.source.unwrap_or(args.validator); - process_tx::( - client, - wallet, - &args.tx, - tx, - TxSigningKey::WalletAddress(default_signer), - ) - .await; + namada::ledger::tx::submit_bond::(client, wallet, args).await; } pub async fn submit_unbond( @@ -1260,76 +1192,7 @@ pub async fn submit_unbond, args: args::Unbond, ) { - let validator = args.validator.clone(); - // Check that the validator address exists on chain - let is_validator = - rpc::is_validator(client, &validator).await; - if !is_validator { - eprintln!( - "The address {} doesn't belong to any known validator account.", - validator - ); - if !args.tx.force { - safe_exit(1) - } - } - - let source = args.source.clone(); - let tx_code = args.tx_code_path; - - // Check the source's current bond amount - let bond_source = source.clone().unwrap_or_else(|| validator.clone()); - let bond_id = BondId { - source: bond_source.clone(), - validator: validator.clone(), - }; - let bond_key = ledger::pos::bond_key(&bond_id); - let bonds = rpc::query_storage_value::(&client, &bond_key).await; - match bonds { - Some(bonds) => { - let mut bond_amount: token::Amount = 0.into(); - for bond in bonds.iter() { - for delta in bond.pos_deltas.values() { - bond_amount += *delta; - } - } - if args.amount > bond_amount { - eprintln!( - "The total bonds of the source {} is lower than the \ - amount to be unbonded. Amount to unbond is {} and the \ - total bonds is {}.", - bond_source, args.amount, bond_amount - ); - if !args.tx.force { - safe_exit(1) - } - } - } - None => { - eprintln!("No bonds found"); - if !args.tx.force { - safe_exit(1) - } - } - } - - let data = pos::Unbond { - validator, - amount: args.amount, - source, - }; - let data = data.try_to_vec().expect("Encoding tx data shouldn't fail"); - - let tx = Tx::new(tx_code, Some(data)); - let default_signer = args.source.unwrap_or(args.validator); - process_tx::( - client, - wallet, - &args.tx, - tx, - TxSigningKey::WalletAddress(default_signer), - ) - .await; + namada::ledger::tx::submit_unbond::(client, wallet, args).await; } pub async fn submit_withdraw( @@ -1337,74 +1200,7 @@ pub async fn submit_withdraw, args: args::Withdraw, ) { - let epoch = rpc::query_epoch(client) - .await; - - let validator = args.validator.clone(); - // Check that the validator address exists on chain - let is_validator = - rpc::is_validator(client, &validator).await; - if !is_validator { - eprintln!( - "The address {} doesn't belong to any known validator account.", - validator - ); - if !args.tx.force { - safe_exit(1) - } - } - - let source = args.source.clone(); - let tx_code = args.tx_code_path; - - // Check the source's current unbond amount - let bond_source = source.clone().unwrap_or_else(|| validator.clone()); - let bond_id = BondId { - source: bond_source.clone(), - validator: validator.clone(), - }; - let bond_key = ledger::pos::unbond_key(&bond_id); - let unbonds = rpc::query_storage_value::(&client, &bond_key).await; - match unbonds { - Some(unbonds) => { - let mut unbonded_amount: token::Amount = 0.into(); - if let Some(unbond) = unbonds.get(epoch) { - for delta in unbond.deltas.values() { - unbonded_amount += *delta; - } - } - if unbonded_amount == 0.into() { - eprintln!( - "There are no unbonded bonds ready to withdraw in the \ - current epoch {}.", - epoch - ); - if !args.tx.force { - safe_exit(1) - } - } - } - None => { - eprintln!("No unbonded bonds found"); - if !args.tx.force { - safe_exit(1) - } - } - } - - let data = pos::Withdraw { validator, source }; - let data = data.try_to_vec().expect("Encoding tx data shouldn't fail"); - - let tx = Tx::new(tx_code, Some(data)); - let default_signer = args.source.unwrap_or(args.validator); - process_tx::( - client, - wallet, - &args.tx, - tx, - TxSigningKey::WalletAddress(default_signer), - ) - .await; + namada::ledger::tx::submit_withdraw::(client, wallet, args).await; } pub async fn submit_validator_commission_change( @@ -1412,80 +1208,7 @@ pub async fn submit_validator_commission_change, args: args::TxCommissionRateChange, ) { - let epoch = rpc::query_epoch(client) - .await; - - let tx_code = args.tx_code_path; - - let validator = args.validator.clone(); - if rpc::is_validator(client, &validator).await { - if args.rate < Decimal::ZERO || args.rate > Decimal::ONE { - eprintln!("Invalid new commission rate, received {}", args.rate); - if !args.tx.force { - safe_exit(1) - } - } - - let commission_rate_key = - ledger::pos::validator_commission_rate_key(&validator); - let max_commission_rate_change_key = - ledger::pos::validator_max_commission_rate_change_key(&validator); - let commission_rates = rpc::query_storage_value::( - &client, - &commission_rate_key, - ) - .await; - let max_change = rpc::query_storage_value::( - &client, - &max_commission_rate_change_key, - ) - .await; - - match (commission_rates, max_change) { - (Some(rates), Some(max_change)) => { - // Assuming that pipeline length = 2 - let rate_next_epoch = rates.get(epoch.next()).unwrap(); - if (args.rate - rate_next_epoch).abs() > max_change { - eprintln!( - "New rate is too large of a change with respect to \ - the predecessor epoch in which the rate will take \ - effect." - ); - if !args.tx.force { - safe_exit(1) - } - } - } - _ => { - eprintln!("Error retrieving from storage"); - if !args.tx.force { - safe_exit(1) - } - } - } - } else { - eprintln!("The given address {validator} is not a validator."); - if !args.tx.force { - safe_exit(1) - } - } - - let data = pos::CommissionChange { - validator: args.validator.clone(), - new_rate: args.rate, - }; - let data = data.try_to_vec().expect("Encoding tx data shouldn't fail"); - - let tx = Tx::new(tx_code, Some(data)); - let default_signer = args.validator.clone(); - process_tx::( - client, - wallet, - &args.tx, - tx, - TxSigningKey::WalletAddress(default_signer), - ) - .await; + namada::ledger::tx::submit_validator_commission_change::(client, wallet, args).await; } /// Submit transaction and wait for result. Returns a list of addresses diff --git a/shared/src/ledger/tx.rs b/shared/src/ledger/tx.rs index 3b4985a78f..015e713f81 100644 --- a/shared/src/ledger/tx.rs +++ b/shared/src/ledger/tx.rs @@ -15,6 +15,11 @@ use crate::ledger::rpc::TxResponse; use tokio::time::{Duration, Instant}; use crate::tendermint_rpc::endpoint::broadcast::tx_sync::Response; use std::borrow::Cow; +use rust_decimal::Decimal; +use crate::ledger::pos::{BondId, Bonds, CommissionRates, Unbonds}; +use crate::ledger; +use crate::types::transaction::{pos, InitAccount, InitValidator, UpdateVp}; +use crate::types::{storage, token}; /// Default timeout in seconds for requests to the `/accepted` /// and `/applied` ABCI query endpoints. @@ -352,3 +357,353 @@ pub async fn save_initialized_accounts( } } } + +pub async fn submit_validator_commission_change( + client: &C, + wallet: &mut Wallet

, + args: args::TxCommissionRateChange, +) { + let epoch = rpc::query_epoch(client) + .await; + + let tx_code = args.tx_code_path; + + let validator = args.validator.clone(); + if rpc::is_validator(client, &validator).await { + if args.rate < Decimal::ZERO || args.rate > Decimal::ONE { + if args.tx.force { + eprintln!("Invalid new commission rate, received {}", args.rate); + } else { + panic!("Invalid new commission rate, received {}", args.rate); + } + } + + let commission_rate_key = + ledger::pos::validator_commission_rate_key(&validator); + let max_commission_rate_change_key = + ledger::pos::validator_max_commission_rate_change_key(&validator); + let commission_rates = rpc::query_storage_value::( + &client, + &commission_rate_key, + ) + .await; + let max_change = rpc::query_storage_value::( + &client, + &max_commission_rate_change_key, + ) + .await; + + match (commission_rates, max_change) { + (Some(rates), Some(max_change)) => { + // Assuming that pipeline length = 2 + let rate_next_epoch = rates.get(epoch.next()).unwrap(); + if (args.rate - rate_next_epoch).abs() > max_change { + if args.tx.force { + eprintln!( + "New rate is too large of a change with respect to \ + the predecessor epoch in which the rate will take \ + effect." + ); + } else { + panic!( + "New rate is too large of a change with respect to \ + the predecessor epoch in which the rate will take \ + effect." + ); + } + } + } + _ => { + if args.tx.force { + eprintln!("Error retrieving from storage"); + } else { + panic!("Error retrieving from storage"); + } + } + } + } else { + if args.tx.force { + eprintln!("The given address {validator} is not a validator."); + } else { + panic!("The given address {validator} is not a validator."); + } + } + + let data = pos::CommissionChange { + validator: args.validator.clone(), + new_rate: args.rate, + }; + let data = data.try_to_vec().expect("Encoding tx data shouldn't fail"); + + let tx = Tx::new(tx_code, Some(data)); + let default_signer = args.validator.clone(); + process_tx::( + client, + wallet, + &args.tx, + tx, + TxSigningKey::WalletAddress(default_signer), + ) + .await; +} + +pub async fn submit_withdraw( + client: &C, + wallet: &mut Wallet

, + args: args::Withdraw, +) { + let epoch = rpc::query_epoch(client) + .await; + + let validator = args.validator.clone(); + // Check that the validator address exists on chain + let is_validator = + rpc::is_validator(client, &validator).await; + if !is_validator { + if args.tx.force { + eprintln!( + "The address {} doesn't belong to any known validator account.", + validator + ); + } else { + panic!( + "The address {} doesn't belong to any known validator account.", + validator + ); + } + } + + let source = args.source.clone(); + let tx_code = args.tx_code_path; + + // Check the source's current unbond amount + let bond_source = source.clone().unwrap_or_else(|| validator.clone()); + let bond_id = BondId { + source: bond_source.clone(), + validator: validator.clone(), + }; + let bond_key = ledger::pos::unbond_key(&bond_id); + let unbonds = rpc::query_storage_value::(&client, &bond_key).await; + match unbonds { + Some(unbonds) => { + let mut unbonded_amount: token::Amount = 0.into(); + if let Some(unbond) = unbonds.get(epoch) { + for delta in unbond.deltas.values() { + unbonded_amount += *delta; + } + } + if unbonded_amount == 0.into() { + if args.tx.force { + eprintln!( + "There are no unbonded bonds ready to withdraw in the \ + current epoch {}.", + epoch + ); + } else { + panic!( + "There are no unbonded bonds ready to withdraw in the \ + current epoch {}.", + epoch + ); + } + } + } + None => { + if args.tx.force { + eprintln!("No unbonded bonds found"); + } else { + panic!("No unbonded bonds found"); + } + } + } + + let data = pos::Withdraw { validator, source }; + let data = data.try_to_vec().expect("Encoding tx data shouldn't fail"); + + let tx = Tx::new(tx_code, Some(data)); + let default_signer = args.source.unwrap_or(args.validator); + process_tx::( + client, + wallet, + &args.tx, + tx, + TxSigningKey::WalletAddress(default_signer), + ) + .await; +} + +pub async fn submit_unbond( + client: &C, + wallet: &mut Wallet

, + args: args::Unbond, +) { + let validator = args.validator.clone(); + // Check that the validator address exists on chain + let is_validator = + rpc::is_validator(client, &validator).await; + if !is_validator { + if args.tx.force { + eprintln!( + "The address {} doesn't belong to any known validator account.", + validator + ); + } else { + panic!( + "The address {} doesn't belong to any known validator account.", + validator + ); + } + } + + let source = args.source.clone(); + let tx_code = args.tx_code_path; + + // Check the source's current bond amount + let bond_source = source.clone().unwrap_or_else(|| validator.clone()); + let bond_id = BondId { + source: bond_source.clone(), + validator: validator.clone(), + }; + let bond_key = ledger::pos::bond_key(&bond_id); + let bonds = rpc::query_storage_value::(&client, &bond_key).await; + match bonds { + Some(bonds) => { + let mut bond_amount: token::Amount = 0.into(); + for bond in bonds.iter() { + for delta in bond.pos_deltas.values() { + bond_amount += *delta; + } + } + if args.amount > bond_amount { + if args.tx.force { + eprintln!( + "The total bonds of the source {} is lower than the \ + amount to be unbonded. Amount to unbond is {} and the \ + total bonds is {}.", + bond_source, args.amount, bond_amount + ); + } else { + panic!( + "The total bonds of the source {} is lower than the \ + amount to be unbonded. Amount to unbond is {} and the \ + total bonds is {}.", + bond_source, args.amount, bond_amount + ); + } + } + } + None => { + if args.tx.force { + eprintln!("No bonds found"); + } else { + panic!("No bonds found"); + } + } + } + + let data = pos::Unbond { + validator, + amount: args.amount, + source, + }; + let data = data.try_to_vec().expect("Encoding tx data shouldn't fail"); + + let tx = Tx::new(tx_code, Some(data)); + let default_signer = args.source.unwrap_or(args.validator); + process_tx::( + client, + wallet, + &args.tx, + tx, + TxSigningKey::WalletAddress(default_signer), + ) + .await; +} + +pub async fn submit_bond( + client: &C, + wallet: &mut Wallet

, + args: args::Bond, +) { + let validator = args.validator.clone(); + // Check that the validator address exists on chain + let is_validator = + rpc::is_validator(client, &validator).await; + if !is_validator { + if args.tx.force { + eprintln!( + "The address {} doesn't belong to any known validator account.", + validator + ); + } else { + panic!( + "The address {} doesn't belong to any known validator account.", + validator + ); + } + } + let source = args.source.clone(); + // Check that the source address exists on chain + if let Some(source) = &source { + let source_exists = + rpc::known_address::(client, source).await; + if !source_exists { + if args.tx.force { + eprintln!("The source address {} doesn't exist on chain.", source); + } else { + panic!("The source address {} doesn't exist on chain.", source); + } + } + } + // 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(&args.native_token, bond_source); + match rpc::query_storage_value::(&client, &balance_key).await + { + Some(balance) => { + if balance < args.amount { + if args.tx.force { + eprintln!( + "The balance of the source {} is lower than the amount to \ + be transferred. Amount to transfer is {} and the balance \ + is {}.", + bond_source, args.amount, balance + ); + } else { + panic!( + "The balance of the source {} is lower than the amount to \ + be transferred. Amount to transfer is {} and the balance \ + is {}.", + bond_source, args.amount, balance + ); + } + } + } + None => { + if args.tx.force { + eprintln!("No balance found for the source {}", bond_source); + } else { + panic!("No balance found for the source {}", bond_source); + } + } + } + let tx_code = args.tx_code_path; + let bond = pos::Bond { + validator, + amount: args.amount, + source, + }; + let data = bond.try_to_vec().expect("Encoding tx data shouldn't fail"); + + let tx = Tx::new(tx_code, Some(data)); + let default_signer = args.source.unwrap_or(args.validator); + process_tx::( + client, + wallet, + &args.tx, + tx, + TxSigningKey::WalletAddress(default_signer), + ) + .await; +} From 2bddb0f316b2310b812b193bacb730d085654759 Mon Sep 17 00:00:00 2001 From: Murisi Tarusenga Date: Sun, 18 Dec 2022 13:56:22 +0200 Subject: [PATCH 29/51] Moved more tx.rs functions into shared. --- apps/src/lib/client/tx.rs | 296 +------------------------------ shared/src/ledger/tx.rs | 361 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 364 insertions(+), 293 deletions(-) diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index f97150d61c..04b665919d 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -500,177 +500,13 @@ impl masp::ShieldedUtils for CLIShieldedUtils { } } - - pub async fn submit_transfer, P>( client: &C, wallet: &mut Wallet

, shielded: &mut ShieldedContext, args: args::TxTransfer, ) { - let transfer_source = args.source; - let source = transfer_source.effective_address(); - let transfer_target = args.target.clone(); - let target = transfer_target.effective_address(); - // Check that the source address exists on chain - let source_exists = - rpc::known_address::(client, &source).await; - if !source_exists { - eprintln!("The source address {} doesn't exist on chain.", source); - if !args.tx.force { - safe_exit(1) - } - } - // Check that the target address exists on chain - let target_exists = - rpc::known_address::(client, &target).await; - if !target_exists { - eprintln!("The target address {} doesn't exist on chain.", target); - if !args.tx.force { - safe_exit(1) - } - } - let token = &args.token; - // Check that the token address exists on chain - let token_exists = - rpc::known_address::(client, &token) - .await; - if !token_exists { - eprintln!( - "The token address {} doesn't exist on chain.", - token - ); - if !args.tx.force { - safe_exit(1) - } - } - // Check source balance - let (sub_prefix, balance_key) = match &args.sub_prefix { - Some(sub_prefix) => { - let sub_prefix = storage::Key::parse(sub_prefix).unwrap(); - let prefix = token::multitoken_balance_prefix( - &token, - &sub_prefix, - ); - ( - Some(sub_prefix), - token::multitoken_balance_key(&prefix, &source), - ) - } - None => (None, token::balance_key(&token, &source)), - }; - match rpc::query_storage_value::(&client, &balance_key).await - { - Some(balance) => { - if balance < args.amount { - eprintln!( - "The balance of the source {} of token {} is lower than \ - the amount to be transferred. Amount to transfer is {} \ - and the balance is {}.", - source, token, args.amount, balance - ); - if !args.tx.force { - safe_exit(1) - } - } - } - None => { - eprintln!( - "No balance found for the source {} of token {}", - source, token - ); - if !args.tx.force { - safe_exit(1) - } - } - }; - - let tx_code = args.tx_code_path; - let masp_addr = masp(); - // For MASP sources, use a special sentinel key recognized by VPs as default - // signer. Also, if the transaction is shielded, redact the amount and token - // types by setting the transparent value to 0 and token type to a constant. - // This has no side-effect because transaction is to self. - let (default_signer, amount, token) = - if source == masp_addr && target == masp_addr { - // TODO Refactor me, we shouldn't rely on any specific token here. - ( - TxSigningKey::SecretKey(masp_tx_key()), - 0.into(), - args.native_token.clone(), - ) - } else if source == masp_addr { - ( - TxSigningKey::SecretKey(masp_tx_key()), - args.amount, - token.clone(), - ) - } else { - ( - TxSigningKey::WalletAddress(source.clone()), - args.amount, - token.clone(), - ) - }; - // 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::(client, wallet, &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 &args.target { - TransferTarget::PaymentAddress(pa) if pa.is_pinned() => Some(pa.hash()), - _ => None, - }; - - let stx_result = - shielded.gen_shielded_transfer( - client, - transfer_source, - transfer_target, - args.amount, - args.token, - args.tx.fee_amount, - args.tx.fee_token.clone(), - shielded_gas, - ) - .await; - let shielded = match stx_result { - Ok(stx) => stx.map(|x| x.0), - Err(builder::Error::ChangeIsNegative(_)) => { - eprintln!( - "The balance of the source {} is lower than the \ - amount to be transferred and fees. Amount to \ - transfer is {} {} and fees are {} {}.", - source, - args.amount, - token, - args.tx.fee_amount, - &args.tx.fee_token, - ); - safe_exit(1) - } - Err(err) => panic!("{}", err), - }; - - let transfer = token::Transfer { - source: source.clone(), - target, - token, - sub_prefix, - amount, - key, - shielded, - }; - tracing::debug!("Transfer data {:?}", transfer); - let data = transfer - .try_to_vec() - .expect("Encoding tx data shouldn't fail"); - - let tx = Tx::new(tx_code, Some(data)); - let signing_address = TxSigningKey::WalletAddress(source); - process_tx::(client, wallet, &args.tx, tx, signing_address).await; + namada::ledger::tx::submit_transfer::(client, wallet, shielded, args).await; } pub async fn submit_ibc_transfer( @@ -678,113 +514,7 @@ pub async fn submit_ibc_transfer, args: args::TxIbcTransfer, ) { - let source = args.source.clone(); - // Check that the source address exists on chain - let source_exists = - rpc::known_address::(client, &source).await; - if !source_exists { - eprintln!("The source address {} doesn't exist on chain.", source); - if !args.tx.force { - safe_exit(1) - } - } - - // We cannot check the receiver - - let token = args.token; - // Check that the token address exists on chain - let token_exists = - rpc::known_address::(client, &token).await; - if !token_exists { - eprintln!("The token address {} doesn't exist on chain.", token); - if !args.tx.force { - safe_exit(1) - } - } - // Check source balance - let (sub_prefix, balance_key) = match args.sub_prefix { - Some(sub_prefix) => { - let sub_prefix = storage::Key::parse(sub_prefix).unwrap(); - let prefix = token::multitoken_balance_prefix(&token, &sub_prefix); - ( - Some(sub_prefix), - token::multitoken_balance_key(&prefix, &source), - ) - } - None => (None, token::balance_key(&token, &source)), - }; - match rpc::query_storage_value::(&client, &balance_key).await - { - Some(balance) => { - if balance < args.amount { - eprintln!( - "The balance of the source {} of token {} is lower than \ - the amount to be transferred. Amount to transfer is {} \ - and the balance is {}.", - source, token, args.amount, balance - ); - if !args.tx.force { - safe_exit(1) - } - } - } - None => { - eprintln!( - "No balance found for the source {} of token {}", - source, token - ); - if !args.tx.force { - safe_exit(1) - } - } - } - let tx_code = args.tx_code_path; - - let denom = match sub_prefix { - // To parse IbcToken address, remove the address prefix - Some(sp) => sp.to_string().replace(RESERVED_ADDRESS_PREFIX, ""), - None => token.to_string(), - }; - let token = Some(Coin { - denom, - amount: args.amount.to_string(), - }); - - // this height should be that of the destination chain, not this chain - let timeout_height = match args.timeout_height { - Some(h) => IbcHeight::new(0, h), - None => IbcHeight::zero(), - }; - - let now: namada::tendermint::Time = DateTimeUtc::now().try_into().unwrap(); - let now: IbcTimestamp = now.into(); - let timeout_timestamp = if let Some(offset) = args.timeout_sec_offset { - (now + Duration::new(offset, 0)).unwrap() - } else if timeout_height.is_zero() { - // we cannot set 0 to both the height and the timestamp - (now + Duration::new(3600, 0)).unwrap() - } else { - IbcTimestamp::none() - }; - - let msg = MsgTransfer { - source_port: args.port_id, - source_channel: args.channel_id, - token, - sender: Signer::new(source.to_string()), - receiver: Signer::new(args.receiver), - timeout_height, - timeout_timestamp, - }; - tracing::debug!("IBC transfer message {:?}", msg); - let any_msg = msg.to_any(); - let mut data = vec![]; - prost::Message::encode(&any_msg, &mut data) - .expect("Encoding tx data shouldn't fail"); - - let tx = Tx::new(tx_code, Some(data)); - process_tx::(client, wallet, &args.tx, tx, TxSigningKey::WalletAddress(args.source)) - .await; + namada::ledger::tx::submit_ibc_transfer::(client, wallet, args).await; } pub async fn submit_init_proposal( @@ -1119,27 +849,7 @@ async fn is_safe_voting_window bool { - let current_epoch = rpc::query_epoch(client).await; - - let proposal_end_epoch_key = - gov_storage::get_voting_end_epoch_key(proposal_id); - let proposal_end_epoch = - rpc::query_storage_value::(client, &proposal_end_epoch_key) - .await; - - match proposal_end_epoch { - Some(proposal_end_epoch) => { - !namada::ledger::native_vp::governance::utils::is_valid_validator_voting_period( - current_epoch, - proposal_start_epoch, - proposal_end_epoch, - ) - } - None => { - eprintln!("Proposal end epoch is not in the storage."); - safe_exit(1) - } - } + namada::ledger::tx::is_safe_voting_window(client, proposal_id, proposal_start_epoch).await } /// Removes validators whose vote corresponds to that of the delegator (needless diff --git a/shared/src/ledger/tx.rs b/shared/src/ledger/tx.rs index 015e713f81..6e298dfcf7 100644 --- a/shared/src/ledger/tx.rs +++ b/shared/src/ledger/tx.rs @@ -20,6 +20,25 @@ use crate::ledger::pos::{BondId, Bonds, CommissionRates, Unbonds}; use crate::ledger; use crate::types::transaction::{pos, InitAccount, InitValidator, UpdateVp}; use crate::types::{storage, token}; +use crate::types::storage::Epoch; +use crate::ledger::governance::storage as gov_storage; +use crate::ibc::signer::Signer; +use crate::ibc::timestamp::Timestamp as IbcTimestamp; +use crate::ibc::applications::ics20_fungible_token_transfer::msgs::transfer::MsgTransfer; +use crate::types::time::DateTimeUtc; +use crate::ibc_proto::cosmos::base::v1beta1::Coin; +use crate::types::storage::{ + BlockResults, RESERVED_ADDRESS_PREFIX, +}; +use crate::ibc::Height as IbcHeight; +use crate::ibc::tx_msg::Msg; +use crate::ledger::masp::ShieldedUtils; +use crate::ledger::masp::ShieldedContext; +use namada_core::types::address::masp; +use namada_core::types::address::masp_tx_key; +use crate::ledger::signing::tx_signer; +use masp_primitives::transaction::builder; +use crate::types::masp::TransferTarget; /// Default timeout in seconds for requests to the `/accepted` /// and `/applied` ABCI query endpoints. @@ -707,3 +726,345 @@ pub async fn submit_bond( + client: &C, + proposal_id: u64, + proposal_start_epoch: Epoch, +) -> bool { + let current_epoch = rpc::query_epoch(client).await; + + let proposal_end_epoch_key = + gov_storage::get_voting_end_epoch_key(proposal_id); + let proposal_end_epoch = + rpc::query_storage_value::(client, &proposal_end_epoch_key) + .await; + + match proposal_end_epoch { + Some(proposal_end_epoch) => { + !crate::ledger::native_vp::governance::utils::is_valid_validator_voting_period( + current_epoch, + proposal_start_epoch, + proposal_end_epoch, + ) + } + None => { + panic!("Proposal end epoch is not in the storage."); + } + } +} + +pub async fn submit_ibc_transfer( + client: &C, + wallet: &mut Wallet

, + args: args::TxIbcTransfer, +) { + let source = args.source.clone(); + // Check that the source address exists on chain + let source_exists = + rpc::known_address::(client, &source).await; + if !source_exists { + if args.tx.force { + eprintln!("The source address {} doesn't exist on chain.", source); + } else { + panic!("The source address {} doesn't exist on chain.", source); + } + } + + // We cannot check the receiver + + let token = args.token; + // Check that the token address exists on chain + let token_exists = + rpc::known_address::(client, &token).await; + if !token_exists { + if args.tx.force { + eprintln!("The token address {} doesn't exist on chain.", token); + } else { + panic!("The token address {} doesn't exist on chain.", token); + } + } + // Check source balance + let (sub_prefix, balance_key) = match args.sub_prefix { + Some(sub_prefix) => { + let sub_prefix = storage::Key::parse(sub_prefix).unwrap(); + let prefix = token::multitoken_balance_prefix(&token, &sub_prefix); + ( + Some(sub_prefix), + token::multitoken_balance_key(&prefix, &source), + ) + } + None => (None, token::balance_key(&token, &source)), + }; + match rpc::query_storage_value::(&client, &balance_key).await + { + Some(balance) => { + if balance < args.amount { + if args.tx.force { + eprintln!( + "The balance of the source {} of token {} is lower than \ + the amount to be transferred. Amount to transfer is {} \ + and the balance is {}.", + source, token, args.amount, balance + ); + } else { + panic!( + "The balance of the source {} of token {} is lower than \ + the amount to be transferred. Amount to transfer is {} \ + and the balance is {}.", + source, token, args.amount, balance + ); + } + } + } + None => { + if args.tx.force { + eprintln!( + "No balance found for the source {} of token {}", + source, token + ); + } else { + panic!( + "No balance found for the source {} of token {}", + source, token + ); + } + } + } + let tx_code = args.tx_code_path; + + let denom = match sub_prefix { + // To parse IbcToken address, remove the address prefix + Some(sp) => sp.to_string().replace(RESERVED_ADDRESS_PREFIX, ""), + None => token.to_string(), + }; + let token = Some(Coin { + denom, + amount: args.amount.to_string(), + }); + + // this height should be that of the destination chain, not this chain + let timeout_height = match args.timeout_height { + Some(h) => IbcHeight::new(0, h), + None => IbcHeight::zero(), + }; + + let now: crate::tendermint::Time = DateTimeUtc::now().try_into().unwrap(); + let now: IbcTimestamp = now.into(); + let timeout_timestamp = if let Some(offset) = args.timeout_sec_offset { + (now + Duration::new(offset, 0)).unwrap() + } else if timeout_height.is_zero() { + // we cannot set 0 to both the height and the timestamp + (now + Duration::new(3600, 0)).unwrap() + } else { + IbcTimestamp::none() + }; + + let msg = MsgTransfer { + source_port: args.port_id, + source_channel: args.channel_id, + token, + sender: Signer::new(source.to_string()), + receiver: Signer::new(args.receiver), + timeout_height, + timeout_timestamp, + }; + tracing::debug!("IBC transfer message {:?}", msg); + let any_msg = msg.to_any(); + let mut data = vec![]; + prost::Message::encode(&any_msg, &mut data) + .expect("Encoding tx data shouldn't fail"); + + let tx = Tx::new(tx_code, Some(data)); + process_tx::(client, wallet, &args.tx, tx, TxSigningKey::WalletAddress(args.source)) + .await; +} + +pub async fn submit_transfer, P>( + client: &C, + wallet: &mut Wallet

, + shielded: &mut ShieldedContext, + args: args::TxTransfer, +) { + let transfer_source = args.source; + let source = transfer_source.effective_address(); + let transfer_target = args.target.clone(); + let target = transfer_target.effective_address(); + // Check that the source address exists on chain + let source_exists = + rpc::known_address::(client, &source).await; + if !source_exists { + if args.tx.force { + eprintln!("The source address {} doesn't exist on chain.", source); + } else { + panic!("The source address {} doesn't exist on chain.", source); + } + } + // Check that the target address exists on chain + let target_exists = + rpc::known_address::(client, &target).await; + if !target_exists { + if args.tx.force { + eprintln!("The target address {} doesn't exist on chain.", target); + } else { + panic!("The target address {} doesn't exist on chain.", target); + } + } + let token = &args.token; + // Check that the token address exists on chain + let token_exists = + rpc::known_address::(client, &token) + .await; + if !token_exists { + if args.tx.force { + eprintln!( + "The token address {} doesn't exist on chain.", + token + ); + } else { + panic!( + "The token address {} doesn't exist on chain.", + token + ); + } + } + // Check source balance + let (sub_prefix, balance_key) = match &args.sub_prefix { + Some(sub_prefix) => { + let sub_prefix = storage::Key::parse(sub_prefix).unwrap(); + let prefix = token::multitoken_balance_prefix( + &token, + &sub_prefix, + ); + ( + Some(sub_prefix), + token::multitoken_balance_key(&prefix, &source), + ) + } + None => (None, token::balance_key(&token, &source)), + }; + match rpc::query_storage_value::(&client, &balance_key).await + { + Some(balance) => { + if balance < args.amount { + if args.tx.force { + eprintln!( + "The balance of the source {} of token {} is lower than \ + the amount to be transferred. Amount to transfer is {} \ + and the balance is {}.", + source, token, args.amount, balance + ); + } else { + panic!( + "The balance of the source {} of token {} is lower than \ + the amount to be transferred. Amount to transfer is {} \ + and the balance is {}.", + source, token, args.amount, balance + ); + } + } + } + None => { + if args.tx.force { + eprintln!( + "No balance found for the source {} of token {}", + source, token + ); + } else { + panic!( + "No balance found for the source {} of token {}", + source, token + ); + } + } + }; + + let tx_code = args.tx_code_path; + let masp_addr = masp(); + // For MASP sources, use a special sentinel key recognized by VPs as default + // signer. Also, if the transaction is shielded, redact the amount and token + // types by setting the transparent value to 0 and token type to a constant. + // This has no side-effect because transaction is to self. + let (default_signer, amount, token) = + if source == masp_addr && target == masp_addr { + // TODO Refactor me, we shouldn't rely on any specific token here. + ( + TxSigningKey::SecretKey(masp_tx_key()), + 0.into(), + args.native_token.clone(), + ) + } else if source == masp_addr { + ( + TxSigningKey::SecretKey(masp_tx_key()), + args.amount, + token.clone(), + ) + } else { + ( + TxSigningKey::WalletAddress(source.clone()), + args.amount, + token.clone(), + ) + }; + // 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::(client, wallet, &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 &args.target { + TransferTarget::PaymentAddress(pa) if pa.is_pinned() => Some(pa.hash()), + _ => None, + }; + + let stx_result = + shielded.gen_shielded_transfer( + client, + transfer_source, + transfer_target, + args.amount, + args.token, + args.tx.fee_amount, + args.tx.fee_token.clone(), + shielded_gas, + ) + .await; + let shielded = match stx_result { + Ok(stx) => stx.map(|x| x.0), + Err(builder::Error::ChangeIsNegative(_)) => { + panic!( + "The balance of the source {} is lower than the \ + amount to be transferred and fees. Amount to \ + transfer is {} {} and fees are {} {}.", + source, + args.amount, + token, + args.tx.fee_amount, + &args.tx.fee_token, + ); + } + Err(err) => panic!("{}", err), + }; + + let transfer = token::Transfer { + source: source.clone(), + target, + token, + sub_prefix, + amount, + key, + shielded, + }; + tracing::debug!("Transfer data {:?}", transfer); + let data = transfer + .try_to_vec() + .expect("Encoding tx data shouldn't fail"); + + let tx = Tx::new(tx_code, Some(data)); + let signing_address = TxSigningKey::WalletAddress(source); + process_tx::(client, wallet, &args.tx, tx, signing_address).await; +} From 178447e09a243b8f3b95331829e7222c7696933e Mon Sep 17 00:00:00 2001 From: Murisi Tarusenga Date: Sun, 18 Dec 2022 14:04:36 +0200 Subject: [PATCH 30/51] Moved more tx.rs functions into shared. --- apps/src/lib/client/tx.rs | 91 +----------------------------- shared/src/ledger/tx.rs | 113 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+), 88 deletions(-) diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index 04b665919d..cedbb47781 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -63,26 +63,12 @@ use crate::facade::tendermint_rpc::{Client, HttpClient}; use crate::node::ledger::tendermint_node; use namada::ledger::wallet::Wallet; -/// Timeout for requests to the `/accepted` and `/applied` -/// ABCI query endpoints. -const ENV_VAR_NAMADA_EVENTS_MAX_WAIT_TIME_SECONDS: &str = - "ANOMA_EVENTS_MAX_WAIT_TIME_SECONDS"; - -/// Default timeout in seconds for requests to the `/accepted` -/// and `/applied` ABCI query endpoints. -const DEFAULT_NAMADA_EVENTS_MAX_WAIT_TIME_SECONDS: u64 = 60; - pub async fn submit_custom( client: &C, wallet: &mut Wallet

, args: args::TxCustom, ) { - let tx_code = args.code_path; - let data = args.data_path; - let tx = Tx::new(tx_code, data); - let initialized_accounts = - process_tx::(client, wallet, &args.tx, tx, TxSigningKey::None).await; - save_initialized_accounts::(wallet, &args.tx, initialized_accounts).await; + namada::ledger::tx::submit_custom::(client, wallet, args).await; } pub async fn submit_update_vp( @@ -90,57 +76,7 @@ pub async fn submit_update_vp, args: args::TxUpdateVp, ) { - let addr = args.addr.clone(); - - // Check that the address is established and exists on chain - match &addr { - Address::Established(_) => { - let exists = - rpc::known_address::(client, &addr).await; - if !exists { - eprintln!("The address {} doesn't exist on chain.", addr); - if !args.tx.force { - safe_exit(1) - } - } - } - Address::Implicit(_) => { - eprintln!( - "A validity predicate of an implicit address cannot be \ - directly updated. You can use an established address for \ - this purpose." - ); - if !args.tx.force { - safe_exit(1) - } - } - Address::Internal(_) => { - eprintln!( - "A validity predicate of an internal address cannot be \ - directly updated." - ); - if !args.tx.force { - safe_exit(1) - } - } - } - - let vp_code = 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); - if !args.tx.force { - safe_exit(1) - } - } - - let tx_code = args.tx_code_path; - - 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::(client, wallet, &args.tx, tx, TxSigningKey::WalletAddress(args.addr)).await; + namada::ledger::tx::submit_update_vp::(client, wallet, args).await; } pub async fn submit_init_account( @@ -148,28 +84,7 @@ pub async fn submit_init_account, args: args::TxInitAccount, ) { - let public_key = args.public_key; - let vp_code = 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); - if !args.tx.force { - safe_exit(1) - } - } - - let tx_code = args.tx_code_path; - let data = InitAccount { - public_key, - vp_code, - }; - let data = data.try_to_vec().expect("Encoding tx data shouldn't fail"); - - let tx = Tx::new(tx_code, Some(data)); - let initialized_accounts = - process_tx::(client, wallet, &args.tx, tx, TxSigningKey::WalletAddress(args.source)) - .await; - save_initialized_accounts::(wallet, &args.tx, initialized_accounts).await; + namada::ledger::tx::submit_init_account::(client, wallet, args).await; } pub async fn submit_init_validator( diff --git a/shared/src/ledger/tx.rs b/shared/src/ledger/tx.rs index 6e298dfcf7..efb3d79109 100644 --- a/shared/src/ledger/tx.rs +++ b/shared/src/ledger/tx.rs @@ -39,6 +39,7 @@ use namada_core::types::address::masp_tx_key; use crate::ledger::signing::tx_signer; use masp_primitives::transaction::builder; use crate::types::masp::TransferTarget; +use crate::vm; /// Default timeout in seconds for requests to the `/accepted` /// and `/applied` ABCI query endpoints. @@ -1068,3 +1069,115 @@ pub async fn submit_transfer(client, wallet, &args.tx, tx, signing_address).await; } + +pub async fn submit_init_account( + client: &C, + wallet: &mut Wallet

, + args: args::TxInitAccount, +) { + let public_key = args.public_key; + let vp_code = args.vp_code_path; + // Validate the VP code + if let Err(err) = vm::validate_untrusted_wasm(&vp_code) { + if args.tx.force { + eprintln!("Validity predicate code validation failed with {}", err); + } else { + panic!("Validity predicate code validation failed with {}", err); + } + } + + let tx_code = args.tx_code_path; + let data = InitAccount { + public_key, + vp_code, + }; + let data = data.try_to_vec().expect("Encoding tx data shouldn't fail"); + + let tx = Tx::new(tx_code, Some(data)); + let initialized_accounts = + process_tx::(client, wallet, &args.tx, tx, TxSigningKey::WalletAddress(args.source)) + .await; + save_initialized_accounts::(wallet, &args.tx, initialized_accounts).await; +} + +pub async fn submit_update_vp( + client: &C, + wallet: &mut Wallet

, + args: args::TxUpdateVp, +) { + let addr = args.addr.clone(); + + // Check that the address is established and exists on chain + match &addr { + Address::Established(_) => { + let exists = + rpc::known_address::(client, &addr).await; + if !exists { + if args.tx.force { + eprintln!("The address {} doesn't exist on chain.", addr); + } else { + panic!("The address {} doesn't exist on chain.", addr); + } + } + } + Address::Implicit(_) => { + if args.tx.force { + eprintln!( + "A validity predicate of an implicit address cannot be \ + directly updated. You can use an established address for \ + this purpose." + ); + } else { + panic!( + "A validity predicate of an implicit address cannot be \ + directly updated. You can use an established address for \ + this purpose." + ); + } + } + Address::Internal(_) => { + if args.tx.force { + eprintln!( + "A validity predicate of an internal address cannot be \ + directly updated." + ); + } else { + panic!( + "A validity predicate of an internal address cannot be \ + directly updated." + ); + } + } + } + + let vp_code = args.vp_code_path; + // Validate the VP code + if let Err(err) = vm::validate_untrusted_wasm(&vp_code) { + if args.tx.force { + eprintln!("Validity predicate code validation failed with {}", err); + } else { + panic!("Validity predicate code validation failed with {}", err); + } + } + + let tx_code = args.tx_code_path; + + 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::(client, wallet, &args.tx, tx, TxSigningKey::WalletAddress(args.addr)).await; +} + +pub async fn submit_custom( + client: &C, + wallet: &mut Wallet

, + args: args::TxCustom, +) { + let tx_code = args.code_path; + let data = args.data_path; + let tx = Tx::new(tx_code, data); + let initialized_accounts = + process_tx::(client, wallet, &args.tx, tx, TxSigningKey::None).await; + save_initialized_accounts::(wallet, &args.tx, initialized_accounts).await; +} From b3a6aae7e72c355fe6cb03ea660fa1080b63e5d6 Mon Sep 17 00:00:00 2001 From: Murisi Tarusenga Date: Sun, 18 Dec 2022 14:49:40 +0200 Subject: [PATCH 31/51] Reduced usage of generic parameters. --- apps/src/bin/anoma-client/cli.rs | 24 +++--- apps/src/bin/anoma-wallet/cli.rs | 22 +++--- apps/src/lib/cli/context.rs | 10 +-- apps/src/lib/client/rpc.rs | 15 ++-- apps/src/lib/client/signing.rs | 19 ++--- apps/src/lib/client/tx.rs | 109 +++++++++++++------------- apps/src/lib/client/utils.rs | 18 ++--- apps/src/lib/node/ledger/shell/mod.rs | 2 +- apps/src/lib/wallet/mod.rs | 26 +++--- shared/src/ledger/signing.rs | 24 +++--- shared/src/ledger/tx.rs | 93 +++++++++++----------- shared/src/ledger/wallet/mod.rs | 48 ++++++------ 12 files changed, 209 insertions(+), 201 deletions(-) diff --git a/apps/src/bin/anoma-client/cli.rs b/apps/src/bin/anoma-client/cli.rs index adb72e9bee..98882418ed 100644 --- a/apps/src/bin/anoma-client/cli.rs +++ b/apps/src/bin/anoma-client/cli.rs @@ -20,7 +20,7 @@ pub async fn main() -> Result<()> { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); let dry_run = args.tx.dry_run; - tx::submit_custom::(&client, &mut ctx.wallet, args).await; + tx::submit_custom::(&client, &mut ctx.wallet, args).await; if !dry_run { namada_apps::wallet::save(&ctx.wallet).unwrap_or_else(|err| eprintln!("{}", err)); } else { @@ -30,23 +30,23 @@ pub async fn main() -> Result<()> { Sub::TxTransfer(TxTransfer(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_transfer::(&client, &mut ctx.wallet, &mut ctx.shielded, args).await; + tx::submit_transfer::(&client, &mut ctx.wallet, &mut ctx.shielded, args).await; } Sub::TxIbcTransfer(TxIbcTransfer(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_ibc_transfer::(&client, &mut ctx.wallet, args).await; + tx::submit_ibc_transfer::(&client, &mut ctx.wallet, args).await; } Sub::TxUpdateVp(TxUpdateVp(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_update_vp::(&client, &mut ctx.wallet, args).await; + tx::submit_update_vp::(&client, &mut ctx.wallet, args).await; } Sub::TxInitAccount(TxInitAccount(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); let dry_run = args.tx.dry_run; - tx::submit_init_account::(&client, &mut ctx.wallet, args).await; + tx::submit_init_account::(&client, &mut ctx.wallet, args).await; if !dry_run { namada_apps::wallet::save(&ctx.wallet).unwrap_or_else(|err| eprintln!("{}", err)); } else { @@ -56,37 +56,37 @@ pub async fn main() -> Result<()> { Sub::TxInitValidator(TxInitValidator(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_init_validator::(&client, ctx, args).await; + tx::submit_init_validator::(&client, ctx, args).await; } Sub::TxInitProposal(TxInitProposal(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_init_proposal::(&client, ctx, args).await; + tx::submit_init_proposal::(&client, ctx, args).await; } Sub::TxVoteProposal(TxVoteProposal(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_vote_proposal::(&client, &mut ctx.wallet, args).await; + tx::submit_vote_proposal::(&client, &mut ctx.wallet, args).await; } Sub::TxRevealPk(TxRevealPk(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_reveal_pk::(&client, &mut ctx.wallet, args).await; + tx::submit_reveal_pk::(&client, &mut ctx.wallet, args).await; } Sub::Bond(Bond(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_bond::(&client, &mut ctx.wallet, args).await; + tx::submit_bond::(&client, &mut ctx.wallet, args).await; } Sub::Unbond(Unbond(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_unbond::(&client, &mut ctx.wallet, args).await; + tx::submit_unbond::(&client, &mut ctx.wallet, args).await; } Sub::Withdraw(Withdraw(args)) => { let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_withdraw::(&client, &mut ctx.wallet, args).await; + tx::submit_withdraw::(&client, &mut ctx.wallet, args).await; } // Ledger queries Sub::QueryEpoch(QueryEpoch(args)) => { diff --git a/apps/src/bin/anoma-wallet/cli.rs b/apps/src/bin/anoma-wallet/cli.rs index 9445495c85..2a1af47483 100644 --- a/apps/src/bin/anoma-wallet/cli.rs +++ b/apps/src/bin/anoma-wallet/cli.rs @@ -83,7 +83,7 @@ fn address_key_find( println!("Viewing key: {}", viewing_key); if unsafe_show_secret { // Check if alias is also a spending key - match wallet.find_spending_key::(&alias) { + match wallet.find_spending_key(&alias) { Ok(spending_key) => println!("Spending key: {}", spending_key), Err(FindKeyError::KeyNotFound) => {} Err(err) => eprintln!("{}", err), @@ -203,7 +203,7 @@ fn spending_key_gen( ) { let mut wallet = ctx.wallet; let alias = alias.to_lowercase(); - let (alias, _key) = wallet.gen_spending_key::(alias, unsafe_dont_encrypt); + let (alias, _key) = wallet.gen_spending_key(alias, unsafe_dont_encrypt); namada_apps::wallet::save(&wallet).unwrap_or_else(|err| eprintln!("{}", err)); println!( "Successfully added a spending key with alias: \"{}\"", @@ -231,7 +231,7 @@ fn payment_address_gen( .expect("a PaymentAddress"); let mut wallet = ctx.wallet; let alias = wallet - .insert_payment_addr::( + .insert_payment_addr( alias, PaymentAddress::from(payment_addr).pinned(pin), ) @@ -260,7 +260,7 @@ fn address_key_add( MaspValue::FullViewingKey(viewing_key) => { let alias = ctx .wallet - .insert_viewing_key::(alias, viewing_key) + .insert_viewing_key(alias, viewing_key) .unwrap_or_else(|| { eprintln!("Viewing key not added"); cli::safe_exit(1); @@ -270,7 +270,7 @@ fn address_key_add( MaspValue::ExtendedSpendingKey(spending_key) => { let alias = ctx .wallet - .encrypt_insert_spending_key::( + .encrypt_insert_spending_key( alias, spending_key, unsafe_dont_encrypt, @@ -284,7 +284,7 @@ fn address_key_add( MaspValue::PaymentAddress(payment_addr) => { let alias = ctx .wallet - .insert_payment_addr::(alias, payment_addr) + .insert_payment_addr(alias, payment_addr) .unwrap_or_else(|| { eprintln!("Payment address not added"); cli::safe_exit(1); @@ -310,7 +310,7 @@ fn key_and_address_gen( }: args::KeyAndAddressGen, ) { let mut wallet = ctx.wallet; - let (alias, _key) = wallet.gen_key::(scheme, alias, unsafe_dont_encrypt); + let (alias, _key) = wallet.gen_key(scheme, alias, unsafe_dont_encrypt); namada_apps::wallet::save(&wallet).unwrap_or_else(|err| eprintln!("{}", err)); println!( "Successfully added a key and an address with alias: \"{}\"", @@ -330,7 +330,7 @@ fn key_find( ) { let mut wallet = ctx.wallet; let found_keypair = match public_key { - Some(pk) => wallet.find_key_by_pk::(&pk), + Some(pk) => wallet.find_key_by_pk(&pk), None => { let alias = alias.or(value); match alias { @@ -341,7 +341,7 @@ fn key_find( ); cli::safe_exit(1) } - Some(alias) => wallet.find_key::(alias.to_lowercase()), + Some(alias) => wallet.find_key(alias.to_lowercase()), } } }; @@ -413,7 +413,7 @@ fn key_list( fn key_export(ctx: Context, args::KeyExport { alias }: args::KeyExport) { let mut wallet = ctx.wallet; wallet - .find_key::(alias.to_lowercase()) + .find_key(alias.to_lowercase()) .map(|keypair| { let file_data = keypair .try_to_vec() @@ -486,7 +486,7 @@ fn address_or_alias_find(ctx: Context, args: args::AddressOrAliasFind) { fn address_add(ctx: Context, args: args::AddressAdd) { let mut wallet = ctx.wallet; if wallet - .add_address::(args.alias.clone().to_lowercase(), args.address) + .add_address(args.alias.clone().to_lowercase(), args.address) .is_none() { eprintln!("Address not added"); diff --git a/apps/src/lib/cli/context.rs b/apps/src/lib/cli/context.rs index 025767ceb0..de27bf7cc8 100644 --- a/apps/src/lib/cli/context.rs +++ b/apps/src/lib/cli/context.rs @@ -68,7 +68,7 @@ pub struct Context { /// Global arguments pub global_args: args::Global, /// The wallet - pub wallet: Wallet, + pub wallet: Wallet, /// The global configuration pub global_config: GlobalConfig, /// The ledger configuration for a specific chain ID @@ -345,7 +345,7 @@ impl ArgFromMutContext for common::SecretKey { FromStr::from_str(raw).or_else(|_parse_err| { // Or it can be an alias ctx.wallet - .find_key::(raw) + .find_key(raw) .map_err(|_find_err| format!("Unknown key {}", raw)) }) } @@ -362,13 +362,13 @@ impl ArgFromMutContext for common::PublicKey { // Or it can be a public key hash in hex string FromStr::from_str(raw) .map(|pkh: PublicKeyHash| { - let key = ctx.wallet.find_key_by_pkh::(&pkh).unwrap(); + let key = ctx.wallet.find_key_by_pkh(&pkh).unwrap(); key.ref_to() }) // Or it can be an alias that may be found in the wallet .or_else(|_parse_err| { ctx.wallet - .find_key::(raw) + .find_key(raw) .map(|x| x.ref_to()) .map_err(|x| x.to_string()) }) @@ -386,7 +386,7 @@ impl ArgFromMutContext for ExtendedSpendingKey { FromStr::from_str(raw).or_else(|_parse_err| { // Or it is a stored alias of one ctx.wallet - .find_spending_key::(raw) + .find_spending_key(raw) .map_err(|_find_err| format!("Unknown spending key {}", raw)) }) } diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index c7dc6024f1..6427ba9a35 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -61,6 +61,7 @@ use crate::facade::tendermint_rpc::query::Query; use crate::facade::tendermint_rpc::{ Client, HttpClient, Order, WebSocketClient, }; +use crate::wallet::CliWalletUtils; /// Query the status of a given transaction. /// @@ -94,7 +95,7 @@ pub async fn query_results(c /// Query the specified accepted transfers from the ledger pub async fn query_transfers>( client: &C, - wallet: &mut Wallet, + wallet: &mut Wallet, shielded: &mut ShieldedContext, args: args::QueryTransfers ) { @@ -231,7 +232,7 @@ pub async fn query_raw_bytes /// Query token balance(s) pub async fn query_balance>( client: &C, - wallet: &mut Wallet, + wallet: &mut Wallet, shielded: &mut ShieldedContext, args: args::QueryBalance, ) { @@ -261,7 +262,7 @@ pub async fn query_balance( client: &C, - wallet: &mut Wallet, + wallet: &mut Wallet, args: args::QueryBalance, ) { let tokens = address::tokens(); @@ -338,7 +339,7 @@ pub async fn query_transparent_balance>( client: &C, - wallet: &mut Wallet, + wallet: &mut Wallet, shielded: &mut ShieldedContext, args: args::QueryBalance, ) { @@ -471,7 +472,7 @@ pub async fn query_pinned_balance>( } fn print_balances( - wallet: &Wallet, + wallet: &Wallet, balances: impl Iterator, token: &Address, target: Option<&Address>, @@ -663,7 +664,7 @@ pub fn value_by_address( /// Query token shielded balance(s) pub async fn query_shielded_balance>( client: &C, - wallet: &mut Wallet, + wallet: &mut Wallet, shielded: &mut ShieldedContext, args: args::QueryBalance, ) { @@ -2263,7 +2264,7 @@ pub async fn get_governance_parameters, addr: &Address) -> String { +fn lookup_alias(wallet: &Wallet, addr: &Address) -> String { match 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 9e2b579b06..1da6ff6f4c 100644 --- a/apps/src/lib/client/signing.rs +++ b/apps/src/lib/client/signing.rs @@ -20,25 +20,25 @@ use namada::ledger::signing::TxSigningKey; /// Find the public key for the given address and try to load the keypair /// for it from the wallet. Panics if the key cannot be found or loaded. -pub async fn find_keypair( +pub async fn find_keypair( client: &C, - wallet: &mut Wallet

, + wallet: &mut Wallet, addr: &Address, ) -> common::SecretKey { - namada::ledger::signing::find_keypair::(client, wallet, addr).await + namada::ledger::signing::find_keypair::(client, wallet, addr).await } /// Given CLI arguments and some defaults, determine the rightful transaction /// signer. Return the given signing key or public key of the given signer if /// possible. If no explicit signer given, use the `default`. If no `default` /// is given, panics. -pub async fn tx_signer( +pub async fn tx_signer( client: &C, - wallet: &mut Wallet

, + wallet: &mut Wallet, args: &args::Tx, mut default: TxSigningKey, ) -> common::SecretKey { - namada::ledger::signing::tx_signer::(client, wallet, args, default).await + namada::ledger::signing::tx_signer::(client, wallet, args, default).await } /// Sign a transaction with a given signing key or public key of a given signer. @@ -49,14 +49,14 @@ pub async fn tx_signer( +pub async fn sign_tx( client: &C, - wallet: &mut Wallet

, + wallet: &mut Wallet, tx: Tx, args: &args::Tx, default: TxSigningKey, ) -> TxBroadcastData { - namada::ledger::signing::sign_tx::(client, wallet, tx, args, default).await + namada::ledger::signing::sign_tx::(client, wallet, tx, args, default).await } /// Create a wrapper tx from a normal tx. Get the hash of the @@ -70,3 +70,4 @@ pub async fn sign_wrapper( ) -> TxBroadcastData { namada::ledger::signing::sign_wrapper(args, epoch, tx, keypair).await } + diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index cedbb47781..23409e88d9 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -62,32 +62,33 @@ use crate::facade::tendermint_rpc::error::Error as RpcError; use crate::facade::tendermint_rpc::{Client, HttpClient}; use crate::node::ledger::tendermint_node; use namada::ledger::wallet::Wallet; +use crate::wallet::CliWalletUtils; -pub async fn submit_custom( +pub async fn submit_custom( client: &C, - wallet: &mut Wallet

, + wallet: &mut Wallet, args: args::TxCustom, ) { - namada::ledger::tx::submit_custom::(client, wallet, args).await; + namada::ledger::tx::submit_custom::(client, wallet, args).await; } -pub async fn submit_update_vp( +pub async fn submit_update_vp( client: &C, - wallet: &mut Wallet

, + wallet: &mut Wallet, args: args::TxUpdateVp, ) { - namada::ledger::tx::submit_update_vp::(client, wallet, args).await; + namada::ledger::tx::submit_update_vp::(client, wallet, args).await; } -pub async fn submit_init_account( +pub async fn submit_init_account( client: &C, - wallet: &mut Wallet

, + wallet: &mut Wallet, args: args::TxInitAccount, ) { - namada::ledger::tx::submit_init_account::(client, wallet, args).await; + namada::ledger::tx::submit_init_account::(client, wallet, args).await; } -pub async fn submit_init_validator( +pub async fn submit_init_validator( client: &C, mut ctx: Context, args::TxInitValidator { @@ -115,7 +116,7 @@ pub async fn submit_init_validator( + .gen_key( scheme, Some(validator_key_alias.clone()), unsafe_dont_encrypt, @@ -135,7 +136,7 @@ pub async fn submit_init_validator( + .gen_key( // Note that TM only allows ed25519 for consensus key SchemeType::Ed25519, Some(consensus_key_alias.clone()), @@ -208,7 +209,7 @@ pub async fn submit_init_validator(client, &mut ctx.wallet, &tx_args, tx, TxSigningKey::WalletAddress(source)) + process_tx::(client, &mut ctx.wallet, &tx_args, tx, TxSigningKey::WalletAddress(source)) .await; if !tx_args.dry_run { let (validator_address_alias, validator_address) = @@ -239,7 +240,7 @@ pub async fn submit_init_validator( + if let Some(new_alias) = ctx.wallet.add_address( validator_address_alias.clone(), validator_address.clone(), ) { @@ -415,24 +416,24 @@ impl masp::ShieldedUtils for CLIShieldedUtils { } } -pub async fn submit_transfer, P>( +pub async fn submit_transfer>( client: &C, - wallet: &mut Wallet

, + wallet: &mut Wallet, shielded: &mut ShieldedContext, args: args::TxTransfer, ) { - namada::ledger::tx::submit_transfer::(client, wallet, shielded, args).await; + namada::ledger::tx::submit_transfer::(client, wallet, shielded, args).await; } -pub async fn submit_ibc_transfer( +pub async fn submit_ibc_transfer( client: &C, - wallet: &mut Wallet

, + wallet: &mut Wallet, args: args::TxIbcTransfer, ) { - namada::ledger::tx::submit_ibc_transfer::(client, wallet, args).await; + namada::ledger::tx::submit_ibc_transfer::(client, wallet, args).await; } -pub async fn submit_init_proposal( +pub async fn submit_init_proposal( client: &C, mut ctx: Context, args: args::InitProposal, @@ -502,7 +503,7 @@ pub async fn submit_init_proposal( + let signing_key = find_keypair::( client, &mut ctx.wallet, &signer, @@ -568,14 +569,14 @@ pub async fn submit_init_proposal(client, &mut ctx.wallet, &args.tx, tx, TxSigningKey::WalletAddress(signer)) + process_tx::(client, &mut ctx.wallet, &args.tx, tx, TxSigningKey::WalletAddress(signer)) .await; } } -pub async fn submit_vote_proposal( +pub async fn submit_vote_proposal( client: &C, - wallet: &mut Wallet

, + wallet: &mut Wallet, args: args::VoteProposal, ) { let signer = if let Some(addr) = &args.tx.signer { @@ -604,7 +605,7 @@ pub async fn submit_vote_proposal( + let signing_key = find_keypair::( client, wallet, &signer, @@ -701,7 +702,7 @@ pub async fn submit_vote_proposal( + process_tx::( client, wallet, &args.tx, @@ -723,21 +724,21 @@ pub async fn submit_vote_proposal( +pub async fn submit_reveal_pk( client: &C, - wallet: &mut Wallet

, + wallet: &mut Wallet, args: args::RevealPk, ) { - namada::ledger::tx::submit_reveal_pk::(client, wallet, args).await; + namada::ledger::tx::submit_reveal_pk::(client, wallet, args).await; } -pub async fn reveal_pk_if_needed( +pub async fn reveal_pk_if_needed( client: &C, - wallet: &mut Wallet

, + wallet: &mut Wallet, public_key: &common::PublicKey, args: &args::Tx, ) -> bool { - namada::ledger::tx::reveal_pk_if_needed::(client, wallet, public_key, args).await + namada::ledger::tx::reveal_pk_if_needed::(client, wallet, public_key, args).await } pub async fn has_revealed_pk( @@ -747,13 +748,13 @@ pub async fn has_revealed_pk namada::ledger::tx::has_revealed_pk(client, addr).await } -pub async fn submit_reveal_pk_aux( +pub async fn submit_reveal_pk_aux( client: &C, - wallet: &mut Wallet

, + wallet: &mut Wallet, public_key: &common::PublicKey, args: &args::Tx, ) { - namada::ledger::tx::submit_reveal_pk_aux::(client, wallet, public_key, args); + namada::ledger::tx::submit_reveal_pk_aux::(client, wallet, public_key, args); } /// Check if current epoch is in the last third of the voting period of the @@ -804,57 +805,57 @@ async fn filter_delegations( delegations.into_iter().flatten().collect() } -pub async fn submit_bond( +pub async fn submit_bond( client: &C, - wallet: &mut Wallet

, + wallet: &mut Wallet, args: args::Bond, ) { - namada::ledger::tx::submit_bond::(client, wallet, args).await; + namada::ledger::tx::submit_bond::(client, wallet, args).await; } -pub async fn submit_unbond( +pub async fn submit_unbond( client: &C, - wallet: &mut Wallet

, + wallet: &mut Wallet, args: args::Unbond, ) { - namada::ledger::tx::submit_unbond::(client, wallet, args).await; + namada::ledger::tx::submit_unbond::(client, wallet, args).await; } -pub async fn submit_withdraw( +pub async fn submit_withdraw( client: &C, - wallet: &mut Wallet

, + wallet: &mut Wallet, args: args::Withdraw, ) { - namada::ledger::tx::submit_withdraw::(client, wallet, args).await; + namada::ledger::tx::submit_withdraw::(client, wallet, args).await; } -pub async fn submit_validator_commission_change( +pub async fn submit_validator_commission_change( client: &C, - wallet: &mut Wallet

, + wallet: &mut Wallet, args: args::TxCommissionRateChange, ) { - namada::ledger::tx::submit_validator_commission_change::(client, wallet, args).await; + namada::ledger::tx::submit_validator_commission_change::(client, wallet, args).await; } /// 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( +async fn process_tx( client: &C, - wallet: &mut Wallet

, + wallet: &mut Wallet, args: &args::Tx, tx: Tx, default_signer: TxSigningKey, ) -> Vec

{ - namada::ledger::tx::process_tx::(client, wallet, args, tx, default_signer).await + namada::ledger::tx::process_tx::(client, wallet, args, tx, default_signer).await } /// Save accounts initialized from a tx into the wallet, if any. -async fn save_initialized_accounts( - wallet: &mut Wallet

, +async fn save_initialized_accounts( + wallet: &mut Wallet, args: &args::Tx, initialized_accounts: Vec

, ) { - namada::ledger::tx::save_initialized_accounts::(wallet, args, initialized_accounts).await + namada::ledger::tx::save_initialized_accounts::(wallet, args, initialized_accounts).await } /// Broadcast a transaction to be included in the blockchain and checks that diff --git a/apps/src/lib/client/utils.rs b/apps/src/lib/client/utils.rs index 8719b9129b..59c30c53cf 100644 --- a/apps/src/lib/client/utils.rs +++ b/apps/src/lib/client/utils.rs @@ -490,7 +490,7 @@ pub fn init_network( .unwrap_or_else(|| { let alias = format!("{}-consensus-key", name); println!("Generating validator {} consensus key...", name); - let (_alias, keypair) = wallet.gen_key::( + let (_alias, keypair) = wallet.gen_key( SchemeType::Ed25519, Some(alias), unsafe_dont_encrypt, @@ -509,7 +509,7 @@ pub fn init_network( .unwrap_or_else(|| { let alias = format!("{}-account-key", name); println!("Generating validator {} account key...", name); - let (_alias, keypair) = wallet.gen_key::( + let (_alias, keypair) = wallet.gen_key( SchemeType::Ed25519, Some(alias), unsafe_dont_encrypt, @@ -524,7 +524,7 @@ pub fn init_network( .unwrap_or_else(|| { let alias = format!("{}-protocol-key", name); println!("Generating validator {} protocol signing key...", name); - let (_alias, keypair) = wallet.gen_key::( + let (_alias, keypair) = wallet.gen_key( SchemeType::Ed25519, Some(alias), unsafe_dont_encrypt, @@ -571,7 +571,7 @@ pub fn init_network( Some(genesis_config::HexString(dkg_pk.to_string())); // Write keypairs to wallet - wallet.add_address::(name.clone(), address); + wallet.add_address(name.clone(), address); crate::wallet::save(&wallet).unwrap(); }); @@ -594,7 +594,7 @@ pub fn init_network( if config.address.is_none() { let address = address::gen_established_address("token"); config.address = Some(address.to_string()); - wallet.add_address::(name.clone(), address); + wallet.add_address(name.clone(), address); } if config.vp.is_none() { config.vp = Some("vp_token".to_string()); @@ -608,7 +608,7 @@ pub fn init_network( "Generating implicit account {} key and address ...", name ); - let (_alias, keypair) = wallet.gen_key::( + let (_alias, keypair) = wallet.gen_key( SchemeType::Ed25519, Some(name.clone()), unsafe_dont_encrypt, @@ -847,18 +847,18 @@ pub fn init_network( fn init_established_account( name: impl AsRef, - wallet: &mut Wallet, + wallet: &mut Wallet, config: &mut genesis_config::EstablishedAccountConfig, unsafe_dont_encrypt: bool, ) { if config.address.is_none() { let address = address::gen_established_address("established"); config.address = Some(address.to_string()); - wallet.add_address::(&name, address); + wallet.add_address(&name, address); } if config.public_key.is_none() { println!("Generating established account {} key...", name.as_ref()); - let (_alias, keypair) = wallet.gen_key::( + let (_alias, keypair) = wallet.gen_key( SchemeType::Ed25519, Some(format!("{}-key", name.as_ref())), unsafe_dont_encrypt, diff --git a/apps/src/lib/node/ledger/shell/mod.rs b/apps/src/lib/node/ledger/shell/mod.rs index 450f051fa0..747ad6894c 100644 --- a/apps/src/lib/node/ledger/shell/mod.rs +++ b/apps/src/lib/node/ledger/shell/mod.rs @@ -654,7 +654,7 @@ where let pk = common::SecretKey::deserialize(&mut pk_bytes.as_slice()) .expect("Validator's public key should be deserializable") .ref_to(); - wallet.find_key_by_pk::(&pk).expect( + wallet.find_key_by_pk(&pk).expect( "A validator's established keypair should be stored in its \ wallet", ) diff --git a/apps/src/lib/wallet/mod.rs b/apps/src/lib/wallet/mod.rs index 5493fb1c38..53360adc97 100644 --- a/apps/src/lib/wallet/mod.rs +++ b/apps/src/lib/wallet/mod.rs @@ -20,9 +20,12 @@ use namada::ledger::wallet::{WalletUtils, Alias}; use std::io::{self, Write}; use namada::ledger::wallet::FindKeyError; +#[derive(Debug)] pub struct CliWalletUtils; impl WalletUtils for CliWalletUtils { + type Storage = PathBuf; + /// Prompt for pssword and confirm it if parameter is false fn new_password_prompt(unsafe_dont_encrypt: bool) -> Option { let password = if unsafe_dont_encrypt { @@ -150,12 +153,12 @@ impl WalletUtils for CliWalletUtils { /// A protocol keypair may be optionally provided, indicating that /// we should re-use a keypair already in the wallet pub fn gen_validator_keys( - wallet: &mut Wallet, + wallet: &mut Wallet, protocol_pk: Option, scheme: SchemeType, ) -> Result { let protocol_keypair = protocol_pk.map(|pk| { - wallet.find_key_by_pkh::(&PublicKeyHash::from(&pk)) + wallet.find_key_by_pkh(&PublicKeyHash::from(&pk)) .ok() .or_else(|| { wallet.store_mut() @@ -175,34 +178,34 @@ pub fn gen_validator_keys( } /// Add addresses from a genesis configuration. -pub fn add_genesis_addresses(wallet: &mut Wallet, genesis: GenesisConfig) { +pub fn add_genesis_addresses(wallet: &mut Wallet, genesis: GenesisConfig) { for (alias, addr) in defaults::addresses_from_genesis(genesis) { - wallet.add_address::(alias.normalize(), addr); + wallet.add_address(alias.normalize(), addr); } } /// Save the wallet store to a file. -pub fn save(wallet: &Wallet) -> std::io::Result<()> { +pub fn save(wallet: &Wallet) -> std::io::Result<()> { self::store::save(&wallet.store(), &wallet.store_dir()) } /// Load a wallet from the store file. -pub fn load(store_dir: &Path) -> Option> { +pub fn load(store_dir: &Path) -> Option> { let store = self::store::load(store_dir).unwrap_or_else(|err| { eprintln!("Unable to load the wallet: {}", err); cli::safe_exit(1) }); - Some(Wallet::::new(store_dir.to_path_buf(), store)) + Some(Wallet::::new(store_dir.to_path_buf(), store)) } /// Load a wallet from the store file or create a new wallet without any /// keys or addresses. -pub fn load_or_new(store_dir: &Path) -> Wallet { +pub fn load_or_new(store_dir: &Path) -> Wallet { let store = self::store::load_or_new(store_dir).unwrap_or_else(|err| { eprintln!("Unable to load the wallet: {}", err); cli::safe_exit(1) }); - Wallet::::new(store_dir.to_path_buf(), store) + Wallet::::new(store_dir.to_path_buf(), store) } /// Load a wallet from the store file or create a new one with the default @@ -210,11 +213,12 @@ pub fn load_or_new(store_dir: &Path) -> Wallet { pub fn load_or_new_from_genesis( store_dir: &Path, genesis_cfg: GenesisConfig, -) -> Wallet { +) -> Wallet { let store = self::store::load_or_new_from_genesis(store_dir, genesis_cfg) .unwrap_or_else(|err| { eprintln!("Unable to load the wallet: {}", err); cli::safe_exit(1) }); - Wallet::::new(store_dir.to_path_buf(), store) + Wallet::::new(store_dir.to_path_buf(), store) } + diff --git a/shared/src/ledger/signing.rs b/shared/src/ledger/signing.rs index 9a2ebf161d..24d9feed86 100644 --- a/shared/src/ledger/signing.rs +++ b/shared/src/ledger/signing.rs @@ -14,9 +14,9 @@ use borsh::BorshSerialize; /// Find the public key for the given address and try to load the keypair /// for it from the wallet. Panics if the key cannot be found or loaded. -pub async fn find_keypair( +pub async fn find_keypair( client: &C, - wallet: &mut Wallet

, + wallet: &mut Wallet, addr: &Address, ) -> common::SecretKey { match addr { @@ -33,7 +33,7 @@ pub async fn find_keypair(&public_key).unwrap_or_else(|err| { + wallet.find_key_by_pk(&public_key).unwrap_or_else(|err| { panic!( "Unable to load the keypair from the wallet for public \ key {}. Failed with: {}", @@ -42,7 +42,7 @@ pub async fn find_keypair { - wallet.find_key_by_pkh::(pkh).unwrap_or_else(|err| { + wallet.find_key_by_pkh(pkh).unwrap_or_else(|err| { panic!( "Unable to load the keypair from the wallet for the \ implicit address {}. Failed with: {}", @@ -78,9 +78,9 @@ pub enum TxSigningKey { /// signer. Return the given signing key or public key of the given signer if /// possible. If no explicit signer given, use the `default`. If no `default` /// is given, panics. -pub async fn tx_signer( +pub async fn tx_signer( client: &C, - wallet: &mut Wallet

, + wallet: &mut Wallet, args: &args::Tx, mut default: TxSigningKey, ) -> common::SecretKey { @@ -97,7 +97,7 @@ pub async fn tx_signer { let signer = signer; - let signing_key = find_keypair::( + let signing_key = find_keypair::( client, wallet, &signer, @@ -107,14 +107,14 @@ pub async fn tx_signer(client, wallet, &pk, args).await; + super::tx::reveal_pk_if_needed::(client, wallet, &pk, args).await; } signing_key } TxSigningKey::SecretKey(signing_key) => { // Check if the signing key needs to reveal its PK first let pk: common::PublicKey = signing_key.ref_to(); - super::tx::reveal_pk_if_needed::(client, wallet, &pk, args).await; + super::tx::reveal_pk_if_needed::(client, wallet, &pk, args).await; signing_key } TxSigningKey::None => { @@ -134,14 +134,14 @@ pub async fn tx_signer( +pub async fn sign_tx( client: &C, - wallet: &mut Wallet

, + wallet: &mut Wallet, tx: Tx, args: &args::Tx, default: TxSigningKey, ) -> TxBroadcastData { - let keypair = tx_signer::(client, wallet, args, default).await; + let keypair = tx_signer::(client, wallet, args, default).await; let tx = tx.sign(&keypair); let epoch = rpc::query_epoch(client) diff --git a/shared/src/ledger/tx.rs b/shared/src/ledger/tx.rs index efb3d79109..93b176302c 100644 --- a/shared/src/ledger/tx.rs +++ b/shared/src/ledger/tx.rs @@ -47,14 +47,14 @@ const DEFAULT_NAMADA_EVENTS_MAX_WAIT_TIME_SECONDS: u64 = 60; /// Submit transaction and wait for result. Returns a list of addresses /// initialized in the transaction if any. In dry run, this is always empty. -pub async fn process_tx( +pub async fn process_tx( client: &C, - wallet: &mut Wallet

, + wallet: &mut Wallet, args: &args::Tx, tx: Tx, default_signer: TxSigningKey, ) -> Vec

{ - let to_broadcast = sign_tx::(client, wallet, tx, args, default_signer).await; + let to_broadcast = sign_tx::(client, wallet, tx, args, default_signer).await; // NOTE: use this to print the request JSON body: // let request = @@ -104,9 +104,9 @@ pub async fn process_tx( +pub async fn submit_reveal_pk( client: &C, - wallet: &mut Wallet

, + wallet: &mut Wallet, args: args::RevealPk, ) { let args::RevealPk { @@ -114,15 +114,15 @@ pub async fn submit_reveal_pk(client, wallet, &public_key, &args).await { + if !reveal_pk_if_needed::(client, wallet, &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( +pub async fn reveal_pk_if_needed( client: &C, - wallet: &mut Wallet

, + wallet: &mut Wallet, public_key: &common::PublicKey, args: &args::Tx, ) -> bool { @@ -131,7 +131,7 @@ pub async fn reveal_pk_if_needed(client, wallet, public_key, args).await; + submit_reveal_pk_aux::(client, wallet, public_key, args).await; true } else { false @@ -145,9 +145,9 @@ pub async fn has_revealed_pk( rpc::get_public_key(client, addr).await.is_some() } -pub async fn submit_reveal_pk_aux( +pub async fn submit_reveal_pk_aux( client: &C, - wallet: &mut Wallet

, + wallet: &mut Wallet, public_key: &common::PublicKey, args: &args::Tx, ) { @@ -164,10 +164,10 @@ pub async fn submit_reveal_pk_aux(client, wallet, &signer) + find_keypair::(client, wallet, &signer) .await } else { - find_keypair::(client, wallet, &addr).await + find_keypair::(client, wallet, &addr).await }; let epoch = rpc::query_epoch(client) .await; @@ -330,8 +330,8 @@ pub async fn submit_tx( } /// Save accounts initialized from a tx into the wallet, if any. -pub async fn save_initialized_accounts( - wallet: &mut Wallet

, +pub async fn save_initialized_accounts( + wallet: &mut Wallet, args: &args::Tx, initialized_accounts: Vec

, ) { @@ -364,7 +364,7 @@ pub async fn save_initialized_accounts( } }; let alias = alias.into_owned(); - let added = wallet.add_address::(alias.clone(), address.clone()); + let added = wallet.add_address(alias.clone(), address.clone()); match added { Some(new_alias) if new_alias != encoded => { println!( @@ -378,9 +378,9 @@ pub async fn save_initialized_accounts( } } -pub async fn submit_validator_commission_change( +pub async fn submit_validator_commission_change( client: &C, - wallet: &mut Wallet

, + wallet: &mut Wallet, args: args::TxCommissionRateChange, ) { let epoch = rpc::query_epoch(client) @@ -457,7 +457,7 @@ pub async fn submit_validator_commission_change( + process_tx::( client, wallet, &args.tx, @@ -467,9 +467,9 @@ pub async fn submit_validator_commission_change( +pub async fn submit_withdraw( client: &C, - wallet: &mut Wallet

, + wallet: &mut Wallet, args: args::Withdraw, ) { let epoch = rpc::query_epoch(client) @@ -542,7 +542,7 @@ pub async fn submit_withdraw( + process_tx::( client, wallet, &args.tx, @@ -552,9 +552,9 @@ pub async fn submit_withdraw( +pub async fn submit_unbond( client: &C, - wallet: &mut Wallet

, + wallet: &mut Wallet, args: args::Unbond, ) { let validator = args.validator.clone(); @@ -630,7 +630,7 @@ pub async fn submit_unbond( + process_tx::( client, wallet, &args.tx, @@ -640,9 +640,9 @@ pub async fn submit_unbond( +pub async fn submit_bond( client: &C, - wallet: &mut Wallet

, + wallet: &mut Wallet, args: args::Bond, ) { let validator = args.validator.clone(); @@ -718,7 +718,7 @@ pub async fn submit_bond( + process_tx::( client, wallet, &args.tx, @@ -758,9 +758,9 @@ pub async fn is_safe_voting_window( +pub async fn submit_ibc_transfer( client: &C, - wallet: &mut Wallet

, + wallet: &mut Wallet, args: args::TxIbcTransfer, ) { let source = args.source.clone(); @@ -880,13 +880,13 @@ pub async fn submit_ibc_transfer(client, wallet, &args.tx, tx, TxSigningKey::WalletAddress(args.source)) + process_tx::(client, wallet, &args.tx, tx, TxSigningKey::WalletAddress(args.source)) .await; } -pub async fn submit_transfer, P>( +pub async fn submit_transfer>( client: &C, - wallet: &mut Wallet

, + wallet: &mut Wallet, shielded: &mut ShieldedContext, args: args::TxTransfer, ) { @@ -1012,7 +1012,7 @@ pub async fn submit_transfer(client, wallet, &args.tx, default_signer.clone()) + let chosen_signer = tx_signer::(client, wallet, &args.tx, default_signer.clone()) .await .ref_to(); let shielded_gas = masp_tx_key().ref_to() == chosen_signer; @@ -1067,12 +1067,12 @@ pub async fn submit_transfer(client, wallet, &args.tx, tx, signing_address).await; + process_tx::(client, wallet, &args.tx, tx, signing_address).await; } -pub async fn submit_init_account( +pub async fn submit_init_account( client: &C, - wallet: &mut Wallet

, + wallet: &mut Wallet, args: args::TxInitAccount, ) { let public_key = args.public_key; @@ -1095,14 +1095,14 @@ pub async fn submit_init_account(client, wallet, &args.tx, tx, TxSigningKey::WalletAddress(args.source)) + process_tx::(client, wallet, &args.tx, tx, TxSigningKey::WalletAddress(args.source)) .await; - save_initialized_accounts::(wallet, &args.tx, initialized_accounts).await; + save_initialized_accounts::(wallet, &args.tx, initialized_accounts).await; } -pub async fn submit_update_vp( +pub async fn submit_update_vp( client: &C, - wallet: &mut Wallet

, + wallet: &mut Wallet, args: args::TxUpdateVp, ) { let addr = args.addr.clone(); @@ -1166,18 +1166,19 @@ pub async fn submit_update_vp(client, wallet, &args.tx, tx, TxSigningKey::WalletAddress(args.addr)).await; + process_tx::(client, wallet, &args.tx, tx, TxSigningKey::WalletAddress(args.addr)).await; } -pub async fn submit_custom( +pub async fn submit_custom( client: &C, - wallet: &mut Wallet

, + wallet: &mut Wallet, args: args::TxCustom, ) { let tx_code = args.code_path; let data = args.data_path; let tx = Tx::new(tx_code, data); let initialized_accounts = - process_tx::(client, wallet, &args.tx, tx, TxSigningKey::None).await; - save_initialized_accounts::(wallet, &args.tx, initialized_accounts).await; + process_tx::(client, wallet, &args.tx, tx, TxSigningKey::None).await; + save_initialized_accounts::(wallet, &args.tx, initialized_accounts).await; } + diff --git a/shared/src/ledger/wallet/mod.rs b/shared/src/ledger/wallet/mod.rs index 1e7def674a..375fa89e9d 100644 --- a/shared/src/ledger/wallet/mod.rs +++ b/shared/src/ledger/wallet/mod.rs @@ -27,6 +27,8 @@ pub use pre_genesis::gen_key_to_store; /// Captures the interactive parts of the wallet's functioning pub trait WalletUtils { + /// The location where the wallet is stored + type Storage; /// Read the password for encryption from the file/env/stdin with confirmation. fn read_and_confirm_pwd(unsafe_dont_encrypt: bool) -> Option; @@ -34,8 +36,7 @@ pub trait WalletUtils { /// if all options are empty/invalid. fn read_password(prompt_msg: &str) -> String; - /// Read an alias from the file/env/stdin. Panics if all options are empty/ - /// invalid. + /// Read an alias from the file/env/stdin. fn read_alias(prompt_msg: &str) -> String; /// The given alias has been selected but conflicts with another alias in @@ -63,16 +64,16 @@ pub enum FindKeyError { /// Represents a collection of keys and addresses while caching key decryptions #[derive(Debug)] -pub struct Wallet { - store_dir: C, +pub struct Wallet { + store_dir: U::Storage, store: Store, decrypted_key_cache: HashMap, decrypted_spendkey_cache: HashMap, } -impl Wallet { +impl Wallet { /// Create a new wallet from the given backing store and storage location - pub fn new(store_dir: C, store: Store) -> Self { + pub fn new(store_dir: U::Storage, store: Store) -> Self { Self { store_dir, store, @@ -88,7 +89,7 @@ impl Wallet { /// password from stdin. Stores the key in decrypted key cache and /// returns the alias of the key and a reference-counting pointer to the /// key. - pub fn gen_key( + pub fn gen_key( &mut self, scheme: SchemeType, alias: Option, @@ -102,7 +103,7 @@ impl Wallet { } /// Generate a spending key and store it under the given alias in the wallet - pub fn gen_spending_key( + pub fn gen_spending_key( &mut self, alias: String, unsafe_dont_encrypt: bool, @@ -139,7 +140,7 @@ impl Wallet { /// If the key is encrypted, will prompt for password from stdin. /// Any keys that are decrypted are stored in and read from a cache to avoid /// prompting for password multiple times. - pub fn find_key( + pub fn find_key( &mut self, alias_pkh_or_pk: impl AsRef, ) -> Result { @@ -155,7 +156,7 @@ impl Wallet { .store .find_key(alias_pkh_or_pk.as_ref()) .ok_or(FindKeyError::KeyNotFound)?; - Self::decrypt_stored_key::<_, U>( + Self::decrypt_stored_key::<_>( &mut self.decrypted_key_cache, stored_key, alias_pkh_or_pk.into(), @@ -163,7 +164,7 @@ impl Wallet { } /// Find the spending key with the given alias in the wallet and return it - pub fn find_spending_key( + pub fn find_spending_key( &mut self, alias: impl AsRef, ) -> Result { @@ -178,7 +179,7 @@ impl Wallet { .store .find_spending_key(alias.as_ref()) .ok_or(FindKeyError::KeyNotFound)?; - Self::decrypt_stored_key::<_, U>( + Self::decrypt_stored_key::<_>( &mut self.decrypted_spendkey_cache, stored_spendkey, alias.into(), @@ -208,7 +209,7 @@ impl Wallet { /// If the key is encrypted, will prompt for password from stdin. /// Any keys that are decrypted are stored in and read from a cache to avoid /// prompting for password multiple times. - pub fn find_key_by_pk( + pub fn find_key_by_pk( &mut self, pk: &common::PublicKey, ) -> Result { @@ -227,7 +228,7 @@ impl Wallet { .store .find_key_by_pk(pk) .ok_or(FindKeyError::KeyNotFound)?; - Self::decrypt_stored_key::<_, U>( + Self::decrypt_stored_key::<_>( &mut self.decrypted_key_cache, stored_key, alias, @@ -238,7 +239,7 @@ impl Wallet { /// If the key is encrypted, will prompt for password from stdin. /// Any keys that are decrypted are stored in and read from a cache to avoid /// prompting for password multiple times. - pub fn find_key_by_pkh( + pub fn find_key_by_pkh( &mut self, pkh: &PublicKeyHash, ) -> Result { @@ -256,7 +257,7 @@ impl Wallet { .store .find_key_by_pkh(pkh) .ok_or(FindKeyError::KeyNotFound)?; - Self::decrypt_stored_key::<_, U>( + Self::decrypt_stored_key::<_>( &mut self.decrypted_key_cache, stored_key, alias, @@ -268,7 +269,6 @@ impl Wallet { /// stdin and if successfully decrypted, store it in a cache. fn decrypt_stored_key< T: FromStr + Display + BorshSerialize + BorshDeserialize + Clone, - U: WalletUtils, >( decrypted_key_cache: &mut HashMap, stored_key: &StoredKeypair, @@ -360,7 +360,7 @@ impl Wallet { /// alias is desired, or the alias creation should be cancelled. Return /// the chosen alias if the address has been added, otherwise return /// nothing. - pub fn add_address( + pub fn add_address( &mut self, alias: impl AsRef, address: Address, @@ -372,7 +372,7 @@ impl Wallet { /// Insert a new key with the given alias. If the alias is already used, /// will prompt for overwrite confirmation. - pub fn insert_keypair( + pub fn insert_keypair( &mut self, alias: String, keypair: StoredKeypair, @@ -384,7 +384,7 @@ impl Wallet { } /// Insert a viewing key into the wallet under the given alias - pub fn insert_viewing_key( + pub fn insert_viewing_key( &mut self, alias: String, view_key: ExtendedViewingKey, @@ -395,7 +395,7 @@ impl Wallet { } /// Insert a spending key into the wallet under the given alias - pub fn insert_spending_key( + pub fn insert_spending_key( &mut self, alias: String, spend_key: StoredKeypair, @@ -408,7 +408,7 @@ impl Wallet { /// Encrypt the given spending key and insert it into the wallet under the /// given alias - pub fn encrypt_insert_spending_key( + pub fn encrypt_insert_spending_key( &mut self, alias: String, spend_key: ExtendedSpendingKey, @@ -425,7 +425,7 @@ impl Wallet { } /// Insert a payment address into the wallet under the given alias - pub fn insert_payment_addr( + pub fn insert_payment_addr( &mut self, alias: String, payment_addr: PaymentAddress, @@ -460,7 +460,7 @@ impl Wallet { } /// Access storage location data - pub fn store_dir(&self) -> &C { + pub fn store_dir(&self) -> &U::Storage { &self.store_dir } } From be27cb0707b91bc38aa8c8944c73742c01a16a83 Mon Sep 17 00:00:00 2001 From: Murisi Tarusenga Date: Sun, 18 Dec 2022 15:02:41 +0200 Subject: [PATCH 32/51] Made ShieldedUtils trait smaller. --- apps/src/lib/client/tx.rs | 30 ------------------------- shared/src/ledger/masp.rs | 46 ++++++++++----------------------------- 2 files changed, 11 insertions(+), 65 deletions(-) diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index 23409e88d9..0ce10d6f4f 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -325,21 +325,8 @@ impl Default for CLIShieldedUtils { } } -#[async_trait] impl masp::ShieldedUtils for CLIShieldedUtils { type C = HttpClient; - - async fn query_storage_value( - client: &Self::C, - key: &storage::Key, - ) -> Option - where T: BorshDeserialize { - query_storage_value::(client, &key).await - } - - async fn query_epoch(client: &Self::C) -> Epoch { - rpc::query_epoch(client).await - } fn local_tx_prover(&self) -> LocalTxProver { if let Ok(params_dir) = env::var(masp::ENV_VAR_MASP_PARAMS_DIR) @@ -397,23 +384,6 @@ impl masp::ShieldedUtils for CLIShieldedUtils { std::fs::remove_file(tmp_path)?; Ok(()) } - - /// Query a conversion. - async fn query_conversion( - client: &Self::C, - asset_type: AssetType, - ) -> Option<( - Address, - Epoch, - masp_primitives::transaction::components::Amount, - MerklePath, - )> { - query_conversion(client, asset_type).await - } - - async fn query_results(client: &Self::C) -> Vec { - rpc::query_results(client).await - } } pub async fn submit_transfer>( diff --git a/shared/src/ledger/masp.rs b/shared/src/ledger/masp.rs index 6eb9f401de..a457eee009 100644 --- a/shared/src/ledger/masp.rs +++ b/shared/src/ledger/masp.rs @@ -71,6 +71,7 @@ use namada_core::types::transaction::AffineCurve; use tendermint_rpc::Client; use crate::types::masp::ExtendedViewingKey; use itertools::Either; +use crate::ledger::rpc; /// Env var to point to a dir with MASP parameters. When not specified, /// the default OS specific path is used. @@ -260,18 +261,7 @@ pub fn get_params_dir() -> PathBuf { #[async_trait] pub trait ShieldedUtils : Sized + BorshDeserialize + BorshSerialize + Default + Clone { /// The type of the Tendermint client to make queries with - type C: tendermint_rpc::Client + std::marker::Sync; - - /// Query the storage value at the given key - async fn query_storage_value( - client: &Self::C, - key: &storage::Key, - ) -> Option - where - T: BorshDeserialize; - - /// Query the current epoch - async fn query_epoch(client: &Self::C) -> Epoch; + type C: crate::ledger::queries::Client + tendermint_rpc::Client + std::marker::Sync; /// Get a MASP transaction prover fn local_tx_prover(&self) -> LocalTxProver; @@ -281,20 +271,6 @@ pub trait ShieldedUtils : Sized + BorshDeserialize + BorshSerialize + Default + /// Sace the given ShieldedContext for future loads fn save(&self, ctx: &ShieldedContext) -> std::io::Result<()>; - - /// Query the designated conversion for the given AssetType - async fn query_conversion( - client: &Self::C, - asset_type: AssetType, - ) -> Option<( - Address, - Epoch, - masp_primitives::transaction::components::Amount, - MerklePath, - )>; - - /// Query for all the accepted transactions that have occured to date - async fn query_results(client: &Self::C) -> Vec; } /// Make a ViewingKey that can view notes encrypted by given ExtendedSpendingKey @@ -539,7 +515,7 @@ impl ShieldedContext { .push(&HEAD_TX_KEY.to_owned()) .expect("Cannot obtain a storage key"); // Query for the index of the last accepted transaction - let head_txidx = U::query_storage_value::(client, &head_tx_key) + let head_txidx = rpc::query_storage_value::(client, &head_tx_key) .await .unwrap_or(0); let mut shielded_txs = BTreeMap::new(); @@ -551,7 +527,7 @@ impl ShieldedContext { .expect("Cannot obtain a storage key"); // Obtain the current transaction let (tx_epoch, tx_height, tx_index, current_tx) = - U::query_storage_value::<(Epoch, BlockHeight, TxIndex, Transfer)>( + rpc::query_storage_value::( client, ¤t_tx_key, ) @@ -722,7 +698,7 @@ impl ShieldedContext { } // Query for the ID of the last accepted transaction let (addr, ep, _conv, _path): (Address, _, Amount, MerklePath) = - U::query_conversion(client, asset_type).await?; + rpc::query_conversion(client, asset_type).await?; self.asset_types.insert(asset_type, (addr.clone(), ep)); Some((addr, ep)) } @@ -740,7 +716,7 @@ impl ShieldedContext { Entry::Vacant(conv_entry) => { // Query for the ID of the last accepted transaction let (addr, ep, conv, path): (Address, _, _, _) = - U::query_conversion(client, asset_type).await?; + rpc::query_conversion(client, asset_type).await?; self.asset_types.insert(asset_type, (addr, ep)); // If the conversion is 0, then we just have a pure decoding if conv == Amount::zero() { @@ -993,7 +969,7 @@ impl ShieldedContext { .push(&(PIN_KEY_PREFIX.to_owned() + &owner.hash())) .expect("Cannot obtain a storage key"); // Obtain the transaction pointer at the key - let txidx = U::query_storage_value::(client, &pin_key) + let txidx = rpc::query_storage_value::(client, &pin_key) .await .ok_or(PinnedBalanceError::NoTransactionPinned)?; // Construct the key for where the pinned transaction is stored @@ -1002,7 +978,7 @@ impl ShieldedContext { .expect("Cannot obtain a storage key"); // Obtain the pointed to transaction let (tx_epoch, _tx_height, _tx_index, tx) = - U::query_storage_value::<(Epoch, BlockHeight, TxIndex, Transfer)>( + rpc::query_storage_value::( client, &tx_key, ) @@ -1143,7 +1119,7 @@ impl ShieldedContext { // Save the update state so that future fetches can be short-circuited let _ = self.save(); // Determine epoch in which to submit potential shielded transaction - let epoch = U::query_epoch(client).await; + let epoch = rpc::query_epoch(client).await; // Context required for storing which notes are in the source's possesion let consensus_branch_id = BranchId::Sapling; let amt: u64 = args_amount.into(); @@ -1250,7 +1226,7 @@ impl ShieldedContext { let mut tx = builder.build(consensus_branch_id, &prover); if epoch_sensitive { - let new_epoch = U::query_epoch(client).await; + let new_epoch = rpc::query_epoch(client).await; // If epoch has changed, recalculate shielded outputs to match new epoch if new_epoch != epoch { @@ -1327,7 +1303,7 @@ impl ShieldedContext { let _ = self.save(); // Required for filtering out rejected transactions from Tendermint // responses - let block_results = U::query_results(client).await; + let block_results = rpc::query_results(client).await; let mut transfers = self.get_tx_deltas().clone(); // Construct the set of addresses relevant to user's query let relevant_addrs = match &query_owner { From d6c45464b412dbc4cfb237c7a86706e3e5ece8c5 Mon Sep 17 00:00:00 2001 From: mariari Date: Mon, 19 Dec 2022 11:55:50 +0800 Subject: [PATCH 33/51] Remove extra imports from refactoring --- apps/src/lib/client/signing.rs | 9 ++------- apps/src/lib/client/tx.rs | 35 ++++++++-------------------------- shared/src/ledger/rpc.rs | 24 +++++------------------ shared/src/ledger/tx.rs | 6 ++---- 4 files changed, 17 insertions(+), 57 deletions(-) diff --git a/apps/src/lib/client/signing.rs b/apps/src/lib/client/signing.rs index 1da6ff6f4c..0a1d712bfd 100644 --- a/apps/src/lib/client/signing.rs +++ b/apps/src/lib/client/signing.rs @@ -1,17 +1,12 @@ //! Helpers for making digital signatures using cryptographic keys from the //! wallet. -use borsh::BorshSerialize; use namada::proto::Tx; -use namada::types::address::{Address, ImplicitAddress}; +use namada::types::address::Address; use namada::types::key::*; use namada::types::storage::Epoch; -use namada::types::transaction::{hash_tx, Fee, WrapperTx}; -use tendermint_rpc::HttpClient; -use std::path::PathBuf; -use super::rpc; -use crate::cli::{self, args}; +use crate::cli::args; use namada::ledger::rpc::TxBroadcastData; use namada::ledger::wallet::Wallet; use namada::ledger::wallet::WalletUtils; diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index 0ce10d6f4f..33ffcb2ffa 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -1,4 +1,3 @@ -use std::borrow::Cow; use std::collections::HashSet; use std::env; use std::fmt::Debug; @@ -9,54 +8,35 @@ use std::path::PathBuf; use async_std::io::prelude::WriteExt; use async_std::io::{self}; use borsh::{BorshDeserialize, BorshSerialize}; -use itertools::Either::*; -use masp_primitives::asset_type::AssetType; -use masp_primitives::merkle_tree::MerklePath; -use masp_primitives::sapling::Node; -use masp_primitives::transaction::builder; use masp_proofs::prover::LocalTxProver; -use namada::ibc::applications::ics20_fungible_token_transfer::msgs::transfer::MsgTransfer; -use namada::ibc::signer::Signer; -use namada::ibc::timestamp::Timestamp as IbcTimestamp; -use namada::ibc::tx_msg::Msg; -use namada::ibc::Height as IbcHeight; -use namada::ibc_proto::cosmos::base::v1beta1::Coin; + use namada::ledger::governance::storage as gov_storage; use namada::ledger::masp; use namada::ledger::masp::ShieldedContext; -use namada::ledger::pos::{BondId, Bonds, CommissionRates, Unbonds}; use namada::proto::Tx; -use namada::types::address::{masp, masp_tx_key, Address}; +use namada::types::address::Address; use namada::types::governance::{ OfflineProposal, OfflineVote, Proposal, ProposalVote, }; use namada::types::key::*; -use namada::types::masp::TransferTarget; -use namada::types::storage::{ - Epoch, BlockResults, RESERVED_ADDRESS_PREFIX, -}; +use namada::types::storage::Epoch; use crate::wallet::gen_validator_keys; -use namada::types::time::DateTimeUtc; use namada::types::transaction::governance::{ InitProposalData, VoteProposalData, }; -use namada::types::transaction::{pos, InitAccount, InitValidator, UpdateVp}; -use namada::types::{storage, token}; -use namada::{ledger, vm}; +use namada::types::transaction::InitValidator; +use namada::types::token; +use namada::vm; use namada::ledger::masp::ShieldedUtils; use namada::ledger::wallet::WalletUtils; use rust_decimal::Decimal; -use tokio::time::{Duration, Instant}; -use async_trait::async_trait; use super::rpc; use crate::cli::context::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}; +use crate::client::signing::find_keypair; use namada::ledger::signing::TxSigningKey; use namada::ledger::rpc::{TxBroadcastData, TxResponse}; -use crate::facade::tendermint_config::net::Address as TendermintAddress; use crate::facade::tendermint_rpc::endpoint::broadcast::tx_sync::Response; use crate::facade::tendermint_rpc::error::Error as RpcError; use crate::facade::tendermint_rpc::{Client, HttpClient}; @@ -64,6 +44,7 @@ use crate::node::ledger::tendermint_node; use namada::ledger::wallet::Wallet; use crate::wallet::CliWalletUtils; + pub async fn submit_custom( client: &C, wallet: &mut Wallet, diff --git a/shared/src/ledger/rpc.rs b/shared/src/ledger/rpc.rs index c65b4198d2..44c8f7a607 100644 --- a/shared/src/ledger/rpc.rs +++ b/shared/src/ledger/rpc.rs @@ -2,32 +2,20 @@ use crate::tendermint_rpc::Client; use crate::types::storage::Epoch; use crate::ledger::queries::RPC; use crate::types::storage::BlockResults; -use crate::ledger::masp::Conversions; -use namada_core::types::address::masp; -use crate::ledger::masp::ShieldedContext; -use namada_core::types::address::tokens; use std::collections::HashMap; -use itertools::Either; -use crate::ledger::wallet::Wallet; use crate::types::token; -use crate::ledger::masp::ShieldedUtils; -use crate::types::masp::{BalanceOwner, ExtendedViewingKey, PaymentAddress}; -use std::cmp::Ordering; -use masp_primitives::zip32::ExtendedFullViewingKey; -use crate::ledger::args; -use data_encoding::HEXLOWER; use namada_core::types::address::Address; use borsh::BorshDeserialize; use masp_primitives::asset_type::AssetType; use masp_primitives::merkle_tree::MerklePath; use crate::types::storage::{ - BlockHeight, Key, KeySeg, PrefixValue, + BlockHeight, PrefixValue, }; use crate::tendermint::merkle::proof::Proof; use crate::ledger::pos::{ - self, is_validator_slashes_key, BondId, Bonds, PosParams, Slash, Unbonds, + self, BondId, Bonds, Slash, }; -use crate::types::{address, storage}; +use crate::types::storage; use masp_primitives::sapling::Node; use crate::types::token::balance_key; use crate::types::key::*; @@ -45,10 +33,8 @@ use crate::ledger::governance::storage as gov_storage; use std::collections::HashSet; use crate::ledger::governance::parameters::GovParams; use crate::types::governance::ProposalResult; -use crate::types::governance::{ - OfflineProposal, OfflineVote, TallyResult, -}; -use itertools::{Itertools}; +use crate::types::governance::TallyResult; +use itertools::Itertools; use crate::ledger::pos::types::decimal_mult_u64; use tokio::time::{Duration, Instant}; diff --git a/shared/src/ledger/tx.rs b/shared/src/ledger/tx.rs index 93b176302c..c055a350d4 100644 --- a/shared/src/ledger/tx.rs +++ b/shared/src/ledger/tx.rs @@ -18,7 +18,7 @@ use std::borrow::Cow; use rust_decimal::Decimal; use crate::ledger::pos::{BondId, Bonds, CommissionRates, Unbonds}; use crate::ledger; -use crate::types::transaction::{pos, InitAccount, InitValidator, UpdateVp}; +use crate::types::transaction::{pos, InitAccount, UpdateVp}; use crate::types::{storage, token}; use crate::types::storage::Epoch; use crate::ledger::governance::storage as gov_storage; @@ -27,9 +27,7 @@ use crate::ibc::timestamp::Timestamp as IbcTimestamp; use crate::ibc::applications::ics20_fungible_token_transfer::msgs::transfer::MsgTransfer; use crate::types::time::DateTimeUtc; use crate::ibc_proto::cosmos::base::v1beta1::Coin; -use crate::types::storage::{ - BlockResults, RESERVED_ADDRESS_PREFIX, -}; +use crate::types::storage::RESERVED_ADDRESS_PREFIX; use crate::ibc::Height as IbcHeight; use crate::ibc::tx_msg::Msg; use crate::ledger::masp::ShieldedUtils; From 817d18bc1b0781d2ad4cc0f539bd11fa5581d4f2 Mon Sep 17 00:00:00 2001 From: mariari Date: Mon, 19 Dec 2022 12:05:56 +0800 Subject: [PATCH 34/51] Alias namada::ledger::tx to just tx --- apps/src/lib/client/tx.rs | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index 33ffcb2ffa..45c8e56981 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -37,6 +37,7 @@ use crate::cli::{args, safe_exit, Context}; use crate::client::signing::find_keypair; use namada::ledger::signing::TxSigningKey; use namada::ledger::rpc::{TxBroadcastData, TxResponse}; +use namada::ledger::tx; use crate::facade::tendermint_rpc::endpoint::broadcast::tx_sync::Response; use crate::facade::tendermint_rpc::error::Error as RpcError; use crate::facade::tendermint_rpc::{Client, HttpClient}; @@ -50,7 +51,7 @@ pub async fn submit_custom, args: args::TxCustom, ) { - namada::ledger::tx::submit_custom::(client, wallet, args).await; + tx::submit_custom::(client, wallet, args).await; } pub async fn submit_update_vp( @@ -58,7 +59,7 @@ pub async fn submit_update_vp, args: args::TxUpdateVp, ) { - namada::ledger::tx::submit_update_vp::(client, wallet, args).await; + tx::submit_update_vp::(client, wallet, args).await; } pub async fn submit_init_account( @@ -66,7 +67,7 @@ pub async fn submit_init_account, args: args::TxInitAccount, ) { - namada::ledger::tx::submit_init_account::(client, wallet, args).await; + tx::submit_init_account::(client, wallet, args).await; } pub async fn submit_init_validator( @@ -373,7 +374,7 @@ pub async fn submit_transfer, args: args::TxTransfer, ) { - namada::ledger::tx::submit_transfer::(client, wallet, shielded, args).await; + tx::submit_transfer::(client, wallet, shielded, args).await; } pub async fn submit_ibc_transfer( @@ -381,7 +382,7 @@ pub async fn submit_ibc_transfer, args: args::TxIbcTransfer, ) { - namada::ledger::tx::submit_ibc_transfer::(client, wallet, args).await; + tx::submit_ibc_transfer::(client, wallet, args).await; } pub async fn submit_init_proposal( @@ -680,7 +681,7 @@ pub async fn submit_reveal_pk, args: args::RevealPk, ) { - namada::ledger::tx::submit_reveal_pk::(client, wallet, args).await; + tx::submit_reveal_pk::(client, wallet, args).await; } pub async fn reveal_pk_if_needed( @@ -689,14 +690,14 @@ pub async fn reveal_pk_if_needed bool { - namada::ledger::tx::reveal_pk_if_needed::(client, wallet, public_key, args).await + tx::reveal_pk_if_needed::(client, wallet, public_key, args).await } pub async fn has_revealed_pk( client: &C, addr: &Address, ) -> bool { - namada::ledger::tx::has_revealed_pk(client, addr).await + tx::has_revealed_pk(client, addr).await } pub async fn submit_reveal_pk_aux( @@ -705,7 +706,7 @@ pub async fn submit_reveal_pk_aux(client, wallet, public_key, args); + tx::submit_reveal_pk_aux::(client, wallet, public_key, args); } /// Check if current epoch is in the last third of the voting period of the @@ -716,7 +717,7 @@ async fn is_safe_voting_window bool { - namada::ledger::tx::is_safe_voting_window(client, proposal_id, proposal_start_epoch).await + tx::is_safe_voting_window(client, proposal_id, proposal_start_epoch).await } /// Removes validators whose vote corresponds to that of the delegator (needless @@ -761,7 +762,7 @@ pub async fn submit_bond, args: args::Bond, ) { - namada::ledger::tx::submit_bond::(client, wallet, args).await; + tx::submit_bond::(client, wallet, args).await; } pub async fn submit_unbond( @@ -769,7 +770,7 @@ pub async fn submit_unbond, args: args::Unbond, ) { - namada::ledger::tx::submit_unbond::(client, wallet, args).await; + tx::submit_unbond::(client, wallet, args).await; } pub async fn submit_withdraw( @@ -777,7 +778,7 @@ pub async fn submit_withdraw, args: args::Withdraw, ) { - namada::ledger::tx::submit_withdraw::(client, wallet, args).await; + tx::submit_withdraw::(client, wallet, args).await; } pub async fn submit_validator_commission_change( @@ -785,7 +786,7 @@ pub async fn submit_validator_commission_change, args: args::TxCommissionRateChange, ) { - namada::ledger::tx::submit_validator_commission_change::(client, wallet, args).await; + tx::submit_validator_commission_change::(client, wallet, args).await; } /// Submit transaction and wait for result. Returns a list of addresses @@ -796,8 +797,8 @@ async fn process_tx Vec

{ - namada::ledger::tx::process_tx::(client, wallet, args, tx, default_signer).await +) -> Result, tx::Error> { + tx::process_tx::(client, wallet, args, tx, default_signer).await } /// Save accounts initialized from a tx into the wallet, if any. @@ -806,7 +807,7 @@ async fn save_initialized_accounts( args: &args::Tx, initialized_accounts: Vec
, ) { - namada::ledger::tx::save_initialized_accounts::(wallet, args, initialized_accounts).await + tx::save_initialized_accounts::(wallet, args, initialized_accounts).await } /// Broadcast a transaction to be included in the blockchain and checks that @@ -817,7 +818,7 @@ pub async fn broadcast_tx( rpc_cli: &C, to_broadcast: &TxBroadcastData, ) -> Result { - namada::ledger::tx::broadcast_tx(rpc_cli, to_broadcast).await + tx::broadcast_tx(rpc_cli, to_broadcast).await } /// Broadcast a transaction to be included in the blockchain. @@ -832,5 +833,5 @@ pub async fn submit_tx( client: &C, to_broadcast: TxBroadcastData, ) -> Result { - namada::ledger::tx::submit_tx(client, to_broadcast).await + tx::submit_tx(client, to_broadcast).await } From 0df070b34f18a7a561a93b70114af412e1575b39 Mon Sep 17 00:00:00 2001 From: mariari Date: Mon, 19 Dec 2022 11:48:49 +0800 Subject: [PATCH 35/51] Turning panics in tx.rs into returning results --- apps/src/lib/client/tx.rs | 9 ++- shared/src/ledger/tx.rs | 123 +++++++++++++++++++------------------- 2 files changed, 69 insertions(+), 63 deletions(-) diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index 45c8e56981..b8956d0cca 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -192,7 +192,10 @@ pub async fn submit_init_validator(client, &mut ctx.wallet, &tx_args, tx, TxSigningKey::WalletAddress(source)) - .await; + .await.unwrap_or_else(|err| { + eprintln!("Processing transaction failed with {}", err); + safe_exit(1) + }); if !tx_args.dry_run { let (validator_address_alias, validator_address) = match &initialized_accounts[..] { @@ -817,7 +820,7 @@ async fn save_initialized_accounts( pub async fn broadcast_tx( rpc_cli: &C, to_broadcast: &TxBroadcastData, -) -> Result { +) -> Result { tx::broadcast_tx(rpc_cli, to_broadcast).await } @@ -832,6 +835,6 @@ pub async fn broadcast_tx( pub async fn submit_tx( client: &C, to_broadcast: TxBroadcastData, -) -> Result { +) -> Result { tx::submit_tx(client, to_broadcast).await } diff --git a/shared/src/ledger/tx.rs b/shared/src/ledger/tx.rs index c055a350d4..4330cc5aed 100644 --- a/shared/src/ledger/tx.rs +++ b/shared/src/ledger/tx.rs @@ -10,6 +10,7 @@ use crate::ledger::rpc::{self, TxBroadcastData}; use crate::ledger::signing::find_keypair; use crate::types::key::*; use borsh::BorshSerialize; +use thiserror::Error; use crate::tendermint_rpc::error::Error as RpcError; use crate::ledger::rpc::TxResponse; use tokio::time::{Duration, Instant}; @@ -43,6 +44,22 @@ use crate::vm; /// and `/applied` ABCI query endpoints. const DEFAULT_NAMADA_EVENTS_MAX_WAIT_TIME_SECONDS: u64 = 60; + +/// Errors to do with transaction events. +#[derive(Error, Debug)] +pub enum Error { + /// Expect a dry running transaction + #[error("Expected a dry-run transaction, received a wrapper \ + transaction instead: {0:?}")] + ExpectDryRun(Tx), + /// Expect a wrapped encrypted running transaction + #[error("Cannot broadcast a dry-run transaction")] + ExpectWrappedRun(Tx), + /// Error during broadcasting a transaction + #[error("Encountered error while broadcasting transaction: {0}")] + TxBroadcast(RpcError) +} + /// Submit transaction and wait for result. Returns a list of addresses /// initialized in the transaction if any. In dry run, this is always empty. pub async fn process_tx( @@ -51,7 +68,7 @@ pub async fn process_tx Vec
{ +) -> Result,Error> { let to_broadcast = sign_tx::(client, wallet, tx, args, default_signer).await; // NOTE: use this to print the request JSON body: @@ -64,15 +81,7 @@ pub async fn process_tx result.initialized_accounts, - Left(Ok(_)) => Vec::default(), - Right(Err(err)) => { - panic!( - "Encountered error while broadcasting transaction: {}", - err - ); - } - Left(Err(err)) => { - panic!( - "Encountered error while broadcasting transaction: {}", - err - ); - } + Right(Ok(result)) => Ok(result.initialized_accounts), + Left(Ok(_)) => Ok(Vec::default()), + Right(Err(err)) => Err(err), + Left(Err(err)) => Err(err) } } } @@ -148,7 +147,7 @@ pub async fn submit_reveal_pk_aux, public_key: &common::PublicKey, args: &args::Tx, -) { +) -> Result<(),Error> { let addr: Address = public_key.into(); println!("Submitting a tx to reveal the public key for address {addr}..."); let tx_data = public_key @@ -175,15 +174,9 @@ pub async fn submit_reveal_pk_aux { - panic!( - "Encountered error while broadcasting transaction: {}", - err - ); - } - Left(Err(err)) => { - panic!( - "Encountered error while broadcasting transaction: {}", - err - ); - } - _ => {} + Right(Err(err)) => Err(err), + Left(Err(err)) => Err(err), + _ => Ok({}) } } } @@ -219,15 +202,15 @@ pub async fn submit_reveal_pk_aux( rpc_cli: &C, to_broadcast: &TxBroadcastData, -) -> Result { +) -> Result { let (tx, wrapper_tx_hash, decrypted_tx_hash) = match to_broadcast { TxBroadcastData::Wrapper { tx, wrapper_hash, decrypted_hash, - } => (tx, wrapper_hash, decrypted_hash), - _ => panic!("Cannot broadcast a dry-run transaction"), - }; + } => Ok((tx, wrapper_hash, decrypted_hash)), + TxBroadcastData::DryRun(tx) => Err(Error::ExpectWrappedRun(tx.clone())), + }?; tracing::debug!( transaction = ?to_broadcast, @@ -237,7 +220,7 @@ pub async fn broadcast_tx( // TODO: configure an explicit timeout value? we need to hack away at // `tendermint-rs` for this, which is currently using a hard-coded 30s // timeout. - let response = rpc_cli.broadcast_tx_sync(tx.to_bytes().into()).await?; + let response = lift_rpc_error(rpc_cli.broadcast_tx_sync(tx.to_bytes().into()).await)?; if response.code == 0.into() { println!("Transaction added to mempool: {:?}", response); @@ -249,7 +232,7 @@ pub async fn broadcast_tx( } Ok(response) } else { - Err(RpcError::server(serde_json::to_string(&response).unwrap())) + Err(Error::TxBroadcast(RpcError::server(serde_json::to_string(&response).unwrap()))) } } @@ -264,15 +247,15 @@ pub async fn broadcast_tx( pub async fn submit_tx( client: &C, to_broadcast: TxBroadcastData, -) -> Result { +) -> Result { let (_, wrapper_hash, decrypted_hash) = match &to_broadcast { TxBroadcastData::Wrapper { tx, wrapper_hash, decrypted_hash, - } => (tx, wrapper_hash, decrypted_hash), - _ => panic!("Cannot broadcast a dry-run transaction"), - }; + } => Ok((tx, wrapper_hash, decrypted_hash)), + TxBroadcastData::DryRun(tx) => Err(Error::ExpectWrappedRun(tx.clone())), + }?; // Broadcast the supplied transaction broadcast_tx(client, &to_broadcast).await?; @@ -819,7 +802,7 @@ pub async fn submit_ibc_transfer { + None => { if args.tx.force { eprintln!( "No balance found for the source {} of token {}", @@ -1092,9 +1075,10 @@ pub async fn submit_init_account(client, wallet, &args.tx, tx, TxSigningKey::WalletAddress(args.source)) - .await; + .await.unwrap(); save_initialized_accounts::(wallet, &args.tx, initialized_accounts).await; } @@ -1171,12 +1155,31 @@ pub async fn submit_custom, args: args::TxCustom, -) { +) -> Result<(), Error> { let tx_code = args.code_path; let data = args.data_path; let tx = Tx::new(tx_code, data); let initialized_accounts = - process_tx::(client, wallet, &args.tx, tx, TxSigningKey::None).await; + process_tx::(client, wallet, &args.tx, tx, TxSigningKey::None).await?; save_initialized_accounts::(wallet, &args.tx, initialized_accounts).await; + Ok(()) } +async fn expect_dry_broadcast( + to_broadcast: TxBroadcastData, + client: &C, + ret:T +) -> Result { + match to_broadcast { + TxBroadcastData::DryRun(tx) => { + rpc::dry_run_tx(client, tx.to_bytes()).await; + Ok(ret) + } + TxBroadcastData::Wrapper { tx, wrapper_hash: _, decrypted_hash: _ } => + Err(Error::ExpectDryRun(tx)) + } +} + +fn lift_rpc_error(res: Result) -> Result { + res.map_err(Error::TxBroadcast) +} From 5d59cfa92d973816d3708e786be8f12b657477b7 Mon Sep 17 00:00:00 2001 From: mariari Date: Mon, 19 Dec 2022 15:38:58 +0800 Subject: [PATCH 36/51] Converted over more uses of panic, also abstracted known validator --- shared/src/ledger/tx.rs | 160 +++++++++++++++++++++++----------------- 1 file changed, 92 insertions(+), 68 deletions(-) diff --git a/shared/src/ledger/tx.rs b/shared/src/ledger/tx.rs index 4330cc5aed..403925bb62 100644 --- a/shared/src/ledger/tx.rs +++ b/shared/src/ledger/tx.rs @@ -57,7 +57,28 @@ pub enum Error { ExpectWrappedRun(Tx), /// Error during broadcasting a transaction #[error("Encountered error while broadcasting transaction: {0}")] - TxBroadcast(RpcError) + TxBroadcast(RpcError), + /// Invalid comission rate set + #[error("Invalid new commission rate, received {0}")] + InvalidCommisionRate(Decimal), + /// Invalid validator address + #[error("The address {0} doesn't belong to any known validator account.")] + InvalidValidatorAddress(Address), + /// Rate of epoch change too large for current epoch + #[error("New rate, {0}, is too large of a change with respect to \ + the predecessor epoch in which the rate will take \ + effect.")] + TooLargeOfChange(Decimal), + /// Error retrieving from storage + #[error("Error retrieving from storage")] + Retrival, + /// No unbonded bonds ready to withdraw in the current epoch + #[error("There are no unbonded bonds ready to withdraw in the \ + current epoch {0}.")] + NoUnbondReady(Epoch), + /// No unbonded bonds found + #[error("No unbonded bonds found")] + NoUnbondFound } /// Submit transaction and wait for result. Returns a list of addresses @@ -363,9 +384,8 @@ pub async fn submit_validator_commission_change, args: args::TxCommissionRateChange, -) { - let epoch = rpc::query_epoch(client) - .await; +) -> Result<(), Error> { + let epoch = rpc::query_epoch(client).await; let tx_code = args.tx_code_path; @@ -374,10 +394,13 @@ pub async fn submit_validator_commission_change Decimal::ONE { if args.tx.force { eprintln!("Invalid new commission rate, received {}", args.rate); + Ok(()) } else { - panic!("Invalid new commission rate, received {}", args.rate); + Err(Error::InvalidCommisionRate(args.rate)) } - } + } else { + Ok(()) + }?; let commission_rate_key = ledger::pos::validator_commission_rate_key(&validator); @@ -387,48 +410,51 @@ pub async fn submit_validator_commission_change( &client, &max_commission_rate_change_key, ) - .await; + .await; match (commission_rates, max_change) { (Some(rates), Some(max_change)) => { // Assuming that pipeline length = 2 let rate_next_epoch = rates.get(epoch.next()).unwrap(); - if (args.rate - rate_next_epoch).abs() > max_change { + let epoch_change = (args.rate - rate_next_epoch).abs(); + if epoch_change > max_change { if args.tx.force { eprintln!( - "New rate is too large of a change with respect to \ - the predecessor epoch in which the rate will take \ - effect." + "New rate, {epoch_change}, is too large of a change \ + with respect to the predecessor epoch in which the rate \ + will take effect." ); + Ok(()) } else { - panic!( - "New rate is too large of a change with respect to \ - the predecessor epoch in which the rate will take \ - effect." - ); + Err(Error::TooLargeOfChange(epoch_change)) } + } else { + Ok(()) } } _ => { if args.tx.force { eprintln!("Error retrieving from storage"); + Ok(()) } else { - panic!("Error retrieving from storage"); + Err(Error::Retrival) } } - } + }?; + Ok(()) } else { if args.tx.force { eprintln!("The given address {validator} is not a validator."); + Ok(()) } else { - panic!("The given address {validator} is not a validator."); + Err(Error::InvalidValidatorAddress(validator)) } - } + }?; let data = pos::CommissionChange { validator: args.validator.clone(), @@ -446,33 +472,18 @@ pub async fn submit_validator_commission_change( client: &C, wallet: &mut Wallet, args: args::Withdraw, -) { - let epoch = rpc::query_epoch(client) - .await; +) -> Result<(),Error> { + let epoch = rpc::query_epoch(client).await; - let validator = args.validator.clone(); - // Check that the validator address exists on chain - let is_validator = - rpc::is_validator(client, &validator).await; - if !is_validator { - if args.tx.force { - eprintln!( - "The address {} doesn't belong to any known validator account.", - validator - ); - } else { - panic!( - "The address {} doesn't belong to any known validator account.", - validator - ); - } - } + let validator = known_validator_or_err(args.validator.clone(), args.tx.force, client) + .await?; let source = args.source.clone(); let tx_code = args.tx_code_path; @@ -500,23 +511,24 @@ pub async fn submit_withdraw { if args.tx.force { eprintln!("No unbonded bonds found"); + Ok(()) } else { - panic!("No unbonded bonds found"); + Err(Error::NoUnbondFound) } } - } + }?; let data = pos::Withdraw { validator, source }; let data = data.try_to_vec().expect("Encoding tx data shouldn't fail"); @@ -530,32 +542,17 @@ pub async fn submit_withdraw( client: &C, wallet: &mut Wallet, args: args::Unbond, -) { - let validator = args.validator.clone(); - // Check that the validator address exists on chain - let is_validator = - rpc::is_validator(client, &validator).await; - if !is_validator { - if args.tx.force { - eprintln!( - "The address {} doesn't belong to any known validator account.", - validator - ); - } else { - panic!( - "The address {} doesn't belong to any known validator account.", - validator - ); - } - } - +) -> Result<(),Error> { + let validator = known_validator_or_err(args.validator.clone(), args.tx.force, client) + .await?; let source = args.source.clone(); let tx_code = args.tx_code_path; @@ -619,6 +616,7 @@ pub async fn submit_unbond( @@ -1183,3 +1181,29 @@ async fn expect_dry_broadcast(res: Result) -> Result { res.map_err(Error::TxBroadcast) } + +/// Returns Ok if the given address is a validator, otherwise returns +/// an error, force forces the address through even if it isn't a +/// validator +async fn known_validator_or_err( + validator: Address, + force: bool, + client: &C +) -> Result { + // Check that the validator address exists on chain + let is_validator = + rpc::is_validator(client, &validator).await; + if !is_validator { + if force { + eprintln!( + "The address {} doesn't belong to any known validator account.", + validator + ); + Ok(validator) + } else { + Err(Error::InvalidValidatorAddress(validator)) + } + } else { + Ok(validator) + } +} From dda4943d5f9d0137179033612296144603cffaad Mon Sep 17 00:00:00 2001 From: mariari Date: Mon, 19 Dec 2022 18:06:32 +0800 Subject: [PATCH 37/51] Abstract out rpc::known_adddress calls, converted more panics --- shared/src/ledger/tx.rs | 281 +++++++++++++++++++++------------------- 1 file changed, 151 insertions(+), 130 deletions(-) diff --git a/shared/src/ledger/tx.rs b/shared/src/ledger/tx.rs index 403925bb62..7fb18fb455 100644 --- a/shared/src/ledger/tx.rs +++ b/shared/src/ledger/tx.rs @@ -78,7 +78,43 @@ pub enum Error { NoUnbondReady(Epoch), /// No unbonded bonds found #[error("No unbonded bonds found")] - NoUnbondFound + NoUnbondFound, + /// No bonds found + #[error("No bonds found")] + NoBondFound, + /// Lower bond amount than the unbond + #[error("The total bonds of the source {0} is lower than the \ + amount to be unbonded. Amount to unbond is {1} and the \ + total bonds is {2}.")] + LowerBondThanUnbond(Address, token::Amount, token::Amount), + /// Balance is too low + #[error("The balance of the source {0} of token {1} is lower than \ + the amount to be transferred. Amount to transfer is {2} \ + and the balance is {3}.")] + BalanceTooLow(Address, Address, token::Amount, token::Amount), + /// Source address does not exist on chain + #[error("The source address {0} doesn't exist on chain.")] + SourceDoesNotExist(Address), + /// Token Address does not exist on chain + #[error("The token address {0} doesn't exist on chain.")] + TokenDoesNotExist(Address), + /// Source Address does not exist on chain + #[error("The source address {0} doesn't exist on chain.")] + SourceLocationDoesNotExist(Address), + /// Target Address does not exist on chain + #[error("The target address {0} doesn't exist on chain.")] + TargetLocationDoesNotExist(Address), + /// No Balance found for token + #[error("No balance found for the source {0} of token {1}")] + NoBalanceForToken(Address, Address), + /// Negative balance after transfer + #[error("The balance of the source {0} is lower than the \ + amount to be transferred and fees. Amount to \ + transfer is {1} {2} and fees are {3} {4}.")] + NegativeBalanceAfterTransfer(Address, token::Amount, Address, token::Amount, Address), + /// No Balance found for token + #[error("{0}")] + MaspError(builder::Error) } /// Submit transaction and wait for result. Returns a list of addresses @@ -580,24 +616,23 @@ pub async fn submit_unbond { if args.tx.force { eprintln!("No bonds found"); + Ok(()) } else { - panic!("No bonds found"); + Err(Error::NoBondFound) } } - } + }?; let data = pos::Unbond { validator, @@ -623,37 +658,18 @@ pub async fn submit_bond, args: args::Bond, -) { - let validator = args.validator.clone(); - // Check that the validator address exists on chain - let is_validator = - rpc::is_validator(client, &validator).await; - if !is_validator { - if args.tx.force { - eprintln!( - "The address {} doesn't belong to any known validator account.", - validator - ); - } else { - panic!( - "The address {} doesn't belong to any known validator account.", - validator - ); - } - } - let source = args.source.clone(); +) -> Result<(), Error> { + let validator = known_validator_or_err(args.validator.clone(), args.tx.force, client) + .await?; + // Check that the source address exists on chain - if let Some(source) = &source { - let source_exists = - rpc::known_address::(client, source).await; - if !source_exists { - if args.tx.force { - eprintln!("The source address {} doesn't exist on chain.", source); - } else { - panic!("The source address {} doesn't exist on chain.", source); - } - } - } + let source = args.source.clone(); + let source = match args.source.clone() { + Some(source) => source_exists_or_err(source, args.tx.force, client) + .await + .map(Some), + None => Ok(source) + }?; // Check bond's source (source for delegation or validator for self-bonds) // balance let bond_source = source.as_ref().unwrap_or(&validator); @@ -705,6 +721,7 @@ pub async fn submit_bond, args: args::TxIbcTransfer, -) { - let source = args.source.clone(); +) -> Result<(), Error> { // Check that the source address exists on chain - let source_exists = - rpc::known_address::(client, &source).await; - if !source_exists { - if args.tx.force { - eprintln!("The source address {} doesn't exist on chain.", source); - } else { - panic!("The source address {} doesn't exist on chain.", source); - } - } - + let source = source_exists_or_err(args.source.clone(), args.tx.force, client).await?; // We cannot check the receiver - let token = args.token; - // Check that the token address exists on chain - let token_exists = - rpc::known_address::(client, &token).await; - if !token_exists { - if args.tx.force { - eprintln!("The token address {} doesn't exist on chain.", token); - } else { - panic!("The token address {} doesn't exist on chain.", token); - } - } + + let token = token_exists_or_err(args.token, args.tx.force, client).await?; + // Check source balance let (sub_prefix, balance_key) = match args.sub_prefix { Some(sub_prefix) => { @@ -860,7 +859,8 @@ pub async fn submit_ibc_transfer(client, wallet, &args.tx, tx, TxSigningKey::WalletAddress(args.source)) - .await; + .await?; + Ok(()) } pub async fn submit_transfer>( @@ -868,49 +868,18 @@ pub async fn submit_transfer, shielded: &mut ShieldedContext, args: args::TxTransfer, -) { - let transfer_source = args.source; - let source = transfer_source.effective_address(); - let transfer_target = args.target.clone(); - let target = transfer_target.effective_address(); +) -> Result<(), Error> { // Check that the source address exists on chain - let source_exists = - rpc::known_address::(client, &source).await; - if !source_exists { - if args.tx.force { - eprintln!("The source address {} doesn't exist on chain.", source); - } else { - panic!("The source address {} doesn't exist on chain.", source); - } - } + let force = args.tx.force; + let transfer_source = args.source; + let source = source_exists_or_err(transfer_source.effective_address(), force, client).await?; // Check that the target address exists on chain - let target_exists = - rpc::known_address::(client, &target).await; - if !target_exists { - if args.tx.force { - eprintln!("The target address {} doesn't exist on chain.", target); - } else { - panic!("The target address {} doesn't exist on chain.", target); - } - } - let token = &args.token; + let transfer_target = args.target.clone(); + let target = target_exists_or_err(transfer_target.effective_address(), force, client).await?; + // Check that the token address exists on chain - let token_exists = - rpc::known_address::(client, &token) - .await; - if !token_exists { - if args.tx.force { - eprintln!( - "The token address {} doesn't exist on chain.", - token - ); - } else { - panic!( - "The token address {} doesn't exist on chain.", - token - ); - } - } + let token = &(token_exists_or_err(args.token.clone(), force, client).await?); + // Check source balance let (sub_prefix, balance_key) = match &args.sub_prefix { Some(sub_prefix) => { @@ -937,14 +906,12 @@ pub async fn submit_transfer { @@ -953,11 +920,9 @@ pub async fn submit_transfer stx.map(|x| x.0), + Ok(stx) => Ok(stx.map(|x| x.0)), Err(builder::Error::ChangeIsNegative(_)) => { - panic!( - "The balance of the source {} is lower than the \ - amount to be transferred and fees. Amount to \ - transfer is {} {} and fees are {} {}.", - source, - args.amount, - token, - args.tx.fee_amount, - &args.tx.fee_token, - ); + Err(Error::NegativeBalanceAfterTransfer(source.clone(), args.amount, token.clone(), args.tx.fee_amount, args.tx.fee_token.clone())) } - Err(err) => panic!("{}", err), - }; + Err(err) => Err(Error::MaspError(err)), + }?; let transfer = token::Transfer { source: source.clone(), @@ -1046,7 +1002,8 @@ pub async fn submit_transfer(client, wallet, &args.tx, tx, signing_address).await; + process_tx::(client, wallet, &args.tx, tx, signing_address).await?; + Ok(()) } pub async fn submit_init_account( @@ -1182,9 +1139,9 @@ fn lift_rpc_error(res: Result) -> Result { res.map_err(Error::TxBroadcast) } -/// Returns Ok if the given address is a validator, otherwise returns -/// an error, force forces the address through even if it isn't a -/// validator +/// Returns the given validator if the given address is a validator, +/// otherwise returns an error, force forces the address through even +/// if it isn't a validator async fn known_validator_or_err( validator: Address, force: bool, @@ -1207,3 +1164,67 @@ async fn known_validator_or_err( + addr: Address, + force: bool, + client: &C, + message: String, + err: F +) -> Result +where + C: Client + crate::ledger::queries::Client + Sync, + F: FnOnce(Address) -> Error +{ + let addr_exists = + rpc::known_address::(client, &addr).await; + if !addr_exists { + if force { + eprintln!("{}", message); + Ok(addr) + } else { + Err(err(addr)) + } + } else { + Ok(addr) + } +} + +/// Returns the given token if the given address exists on chain +/// otherwise returns an error, force forces the address through even +/// if it isn't on chain +async fn token_exists_or_err( + token: Address, + force: bool, + client: &C +) -> Result { + let message = format!("The token address {} doesn't exist on chain.", token); + address_exists_or_err(token, force, client, message, Error::TokenDoesNotExist).await +} + +/// Returns the given source address if the given address exists on chain +/// otherwise returns an error, force forces the address through even +/// if it isn't on chain +async fn source_exists_or_err( + token: Address, + force: bool, + client: &C +) -> Result { + let message = format!("The source address {} doesn't exist on chain.", token); + address_exists_or_err(token, force, client, message, Error::SourceDoesNotExist).await +} + +/// Returns the given target address if the given address exists on chain +/// otherwise returns an error, force forces the address through even +/// if it isn't on chain +async fn target_exists_or_err( + token: Address, + force: bool, + client: &C +) -> Result { + let message = format!("The target address {} doesn't exist on chain.", token); + address_exists_or_err(token, force, client, message, Error::TargetLocationDoesNotExist).await +} From 5f618b67a56d74125f0b8946a592e31e8fd0fbf7 Mon Sep 17 00:00:00 2001 From: mariari Date: Mon, 19 Dec 2022 18:08:28 +0800 Subject: [PATCH 38/51] Ran the formatter --- apps/src/bin/anoma-client/cli.rs | 221 ++++++-- apps/src/bin/anoma-wallet/cli.rs | 29 +- apps/src/lib/cli.rs | 213 ++++---- apps/src/lib/cli/context.rs | 12 +- apps/src/lib/client/rpc.rs | 427 +++++++++------ apps/src/lib/client/signing.rs | 29 +- apps/src/lib/client/tendermint_rpc_types.rs | 1 - apps/src/lib/client/tx.rs | 202 ++++--- apps/src/lib/client/utils.rs | 26 +- apps/src/lib/node/ledger/shell/mod.rs | 18 +- apps/src/lib/node/ledger/shims/abcipp_shim.rs | 6 +- apps/src/lib/wallet/alias.rs | 1 + apps/src/lib/wallet/defaults.rs | 5 +- apps/src/lib/wallet/keys.rs | 1 + apps/src/lib/wallet/mod.rs | 47 +- apps/src/lib/wallet/pre_genesis.rs | 32 +- apps/src/lib/wallet/store.rs | 25 +- shared/src/ledger/args.rs | 33 +- shared/src/ledger/masp.rs | 228 ++++---- shared/src/ledger/mod.rs | 8 +- shared/src/ledger/queries/shell.rs | 6 +- shared/src/ledger/rpc.rs | 184 ++++--- shared/src/ledger/signing.rs | 66 +-- shared/src/ledger/tx.rs | 509 +++++++++++------- shared/src/ledger/wallet/keys.rs | 1 + shared/src/ledger/wallet/mod.rs | 29 +- shared/src/ledger/wallet/pre_genesis.rs | 6 +- shared/src/ledger/wallet/store.rs | 30 +- 28 files changed, 1441 insertions(+), 954 deletions(-) diff --git a/apps/src/bin/anoma-client/cli.rs b/apps/src/bin/anoma-client/cli.rs index 98882418ed..cfa20d0539 100644 --- a/apps/src/bin/anoma-client/cli.rs +++ b/apps/src/bin/anoma-client/cli.rs @@ -1,13 +1,14 @@ //! Anoma client CLI. +use std::path::PathBuf; + use color_eyre::eyre::Result; use namada_apps::cli; +use namada_apps::cli::args::CliToSdk; use namada_apps::cli::cmds::*; use namada_apps::client::{rpc, tx, utils}; -use tendermint_rpc::{HttpClient, WebSocketClient, SubscriptionClient}; use namada_apps::wallet::CliWalletUtils; -use namada_apps::cli::args::CliToSdk; -use std::path::PathBuf; +use tendermint_rpc::{HttpClient, SubscriptionClient, WebSocketClient}; pub async fn main() -> Result<()> { match cli::anoma_client_cli()? { @@ -17,76 +18,161 @@ pub async fn main() -> Result<()> { match cmd { // Ledger cmds Sub::TxCustom(TxCustom(args)) => { - let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); + let client = + HttpClient::new(args.tx.ledger_address.clone()) + .unwrap(); let args = args.to_sdk(&mut ctx); let dry_run = args.tx.dry_run; - tx::submit_custom::(&client, &mut ctx.wallet, args).await; + tx::submit_custom::( + &client, + &mut ctx.wallet, + args, + ) + .await; if !dry_run { - namada_apps::wallet::save(&ctx.wallet).unwrap_or_else(|err| eprintln!("{}", err)); + namada_apps::wallet::save(&ctx.wallet) + .unwrap_or_else(|err| eprintln!("{}", err)); } else { - println!("Transaction dry run. No addresses have been saved.") + println!( + "Transaction dry run. No addresses have been \ + saved." + ) } } Sub::TxTransfer(TxTransfer(args)) => { - let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); + let client = + HttpClient::new(args.tx.ledger_address.clone()) + .unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_transfer::(&client, &mut ctx.wallet, &mut ctx.shielded, args).await; + tx::submit_transfer::( + &client, + &mut ctx.wallet, + &mut ctx.shielded, + args, + ) + .await; } Sub::TxIbcTransfer(TxIbcTransfer(args)) => { - let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); + let client = + HttpClient::new(args.tx.ledger_address.clone()) + .unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_ibc_transfer::(&client, &mut ctx.wallet, args).await; + tx::submit_ibc_transfer::( + &client, + &mut ctx.wallet, + args, + ) + .await; } Sub::TxUpdateVp(TxUpdateVp(args)) => { - let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); + let client = + HttpClient::new(args.tx.ledger_address.clone()) + .unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_update_vp::(&client, &mut ctx.wallet, args).await; + tx::submit_update_vp::( + &client, + &mut ctx.wallet, + args, + ) + .await; } Sub::TxInitAccount(TxInitAccount(args)) => { - let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); + let client = + HttpClient::new(args.tx.ledger_address.clone()) + .unwrap(); let args = args.to_sdk(&mut ctx); let dry_run = args.tx.dry_run; - tx::submit_init_account::(&client, &mut ctx.wallet, args).await; + tx::submit_init_account::( + &client, + &mut ctx.wallet, + args, + ) + .await; if !dry_run { - namada_apps::wallet::save(&ctx.wallet).unwrap_or_else(|err| eprintln!("{}", err)); + namada_apps::wallet::save(&ctx.wallet) + .unwrap_or_else(|err| eprintln!("{}", err)); } else { - println!("Transaction dry run. No addresses have been saved.") + println!( + "Transaction dry run. No addresses have been \ + saved." + ) } } Sub::TxInitValidator(TxInitValidator(args)) => { - let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); + let client = + HttpClient::new(args.tx.ledger_address.clone()) + .unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_init_validator::(&client, ctx, args).await; + tx::submit_init_validator::(&client, ctx, args) + .await; } Sub::TxInitProposal(TxInitProposal(args)) => { - let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); + let client = + HttpClient::new(args.tx.ledger_address.clone()) + .unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_init_proposal::(&client, ctx, args).await; + tx::submit_init_proposal::(&client, ctx, args) + .await; } Sub::TxVoteProposal(TxVoteProposal(args)) => { - let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); + let client = + HttpClient::new(args.tx.ledger_address.clone()) + .unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_vote_proposal::(&client, &mut ctx.wallet, args).await; + tx::submit_vote_proposal::( + &client, + &mut ctx.wallet, + args, + ) + .await; } Sub::TxRevealPk(TxRevealPk(args)) => { - let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); + let client = + HttpClient::new(args.tx.ledger_address.clone()) + .unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_reveal_pk::(&client, &mut ctx.wallet, args).await; + tx::submit_reveal_pk::( + &client, + &mut ctx.wallet, + args, + ) + .await; } Sub::Bond(Bond(args)) => { - let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); + let client = + HttpClient::new(args.tx.ledger_address.clone()) + .unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_bond::(&client, &mut ctx.wallet, args).await; + tx::submit_bond::( + &client, + &mut ctx.wallet, + args, + ) + .await; } Sub::Unbond(Unbond(args)) => { - let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); + let client = + HttpClient::new(args.tx.ledger_address.clone()) + .unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_unbond::(&client, &mut ctx.wallet, args).await; + tx::submit_unbond::( + &client, + &mut ctx.wallet, + args, + ) + .await; } Sub::Withdraw(Withdraw(args)) => { - let client = HttpClient::new(args.tx.ledger_address.clone()).unwrap(); + let client = + HttpClient::new(args.tx.ledger_address.clone()) + .unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_withdraw::(&client, &mut ctx.wallet, args).await; + tx::submit_withdraw::( + &client, + &mut ctx.wallet, + args, + ) + .await; } // Ledger queries Sub::QueryEpoch(QueryEpoch(args)) => { @@ -94,76 +180,115 @@ pub async fn main() -> Result<()> { rpc::query_epoch(&client).await; } Sub::QueryTransfers(QueryTransfers(args)) => { - let client = HttpClient::new(args.query.ledger_address.clone()).unwrap(); + let client = + HttpClient::new(args.query.ledger_address.clone()) + .unwrap(); let args = args.to_sdk(&mut ctx); - rpc::query_transfers(&client, &mut ctx.wallet, &mut ctx.shielded, args).await; + rpc::query_transfers( + &client, + &mut ctx.wallet, + &mut ctx.shielded, + args, + ) + .await; } Sub::QueryConversions(QueryConversions(args)) => { - let client = HttpClient::new(args.query.ledger_address.clone()).unwrap(); + let client = + HttpClient::new(args.query.ledger_address.clone()) + .unwrap(); let args = args.to_sdk(&mut ctx); rpc::query_conversions(&client, args).await; } Sub::QueryBlock(QueryBlock(args)) => { - let client = HttpClient::new(args.ledger_address.clone()).unwrap(); + let client = + HttpClient::new(args.ledger_address.clone()).unwrap(); rpc::query_block(&client).await; } Sub::QueryBalance(QueryBalance(args)) => { - let client = HttpClient::new(args.query.ledger_address.clone()).unwrap(); + let client = + HttpClient::new(args.query.ledger_address.clone()) + .unwrap(); let args = args.to_sdk(&mut ctx); - rpc::query_balance(&client, &mut ctx.wallet, &mut ctx.shielded, args).await; + rpc::query_balance( + &client, + &mut ctx.wallet, + &mut ctx.shielded, + args, + ) + .await; } Sub::QueryBonds(QueryBonds(args)) => { - let client = HttpClient::new(args.query.ledger_address.clone()).unwrap(); + let client = + HttpClient::new(args.query.ledger_address.clone()) + .unwrap(); let args = args.to_sdk(&mut ctx); rpc::query_bonds(&client, args).await; } Sub::QueryBondedStake(QueryBondedStake(args)) => { - let client = HttpClient::new(args.query.ledger_address.clone()).unwrap(); + let client = + HttpClient::new(args.query.ledger_address.clone()) + .unwrap(); let args = args.to_sdk(&mut ctx); rpc::query_bonded_stake(&client, args).await; } Sub::QueryCommissionRate(QueryCommissionRate(args)) => { - let client = HttpClient::new(args.query.ledger_address.clone()).unwrap(); + let client = + HttpClient::new(args.query.ledger_address.clone()) + .unwrap(); let args = args.to_sdk(&mut ctx); rpc::query_commission_rate(&client, args).await; } Sub::QuerySlashes(QuerySlashes(args)) => { - let client = HttpClient::new(args.query.ledger_address.clone()).unwrap(); + let client = + HttpClient::new(args.query.ledger_address.clone()) + .unwrap(); let args = args.to_sdk(&mut ctx); rpc::query_slashes(&client, args).await; } Sub::QueryResult(QueryResult(args)) => { // Connect to the Tendermint server holding the transactions - let (client, driver) = WebSocketClient::new(args.query.ledger_address.clone()).await?; - let driver_handle = tokio::spawn(async move { driver.run().await }); + let (client, driver) = + WebSocketClient::new(args.query.ledger_address.clone()) + .await?; + let driver_handle = + tokio::spawn(async move { driver.run().await }); let args = args.to_sdk(&mut ctx); rpc::query_result(&client, args).await; // Signal to the driver to terminate. client.close()?; - // Await the driver's termination to ensure proper connection closure. + // Await the driver's termination to ensure proper + // connection closure. let _ = driver_handle.await.unwrap_or_else(|x| { eprintln!("{}", x); cli::safe_exit(1) }); } Sub::QueryRawBytes(QueryRawBytes(args)) => { - let client = HttpClient::new(args.query.ledger_address.clone()).unwrap(); + let client = + HttpClient::new(args.query.ledger_address.clone()) + .unwrap(); let args = args.to_sdk(&mut ctx); rpc::query_raw_bytes(&client, args).await; } Sub::QueryProposal(QueryProposal(args)) => { - let client = HttpClient::new(args.query.ledger_address.clone()).unwrap(); + let client = + HttpClient::new(args.query.ledger_address.clone()) + .unwrap(); let args = args.to_sdk(&mut ctx); rpc::query_proposal(&client, args).await; } Sub::QueryProposalResult(QueryProposalResult(args)) => { - let client = HttpClient::new(args.query.ledger_address.clone()).unwrap(); + let client = + HttpClient::new(args.query.ledger_address.clone()) + .unwrap(); let args = args.to_sdk(&mut ctx); rpc::query_proposal_result(&client, args).await; } Sub::QueryProtocolParameters(QueryProtocolParameters(args)) => { - let client = HttpClient::new(args.query.ledger_address.clone()).unwrap(); + let client = + HttpClient::new(args.query.ledger_address.clone()) + .unwrap(); let args = args.to_sdk(&mut ctx); rpc::query_protocol_parameters(&client, args).await; } diff --git a/apps/src/bin/anoma-wallet/cli.rs b/apps/src/bin/anoma-wallet/cli.rs index 2a1af47483..f8e70fb1da 100644 --- a/apps/src/bin/anoma-wallet/cli.rs +++ b/apps/src/bin/anoma-wallet/cli.rs @@ -7,16 +7,15 @@ use borsh::BorshSerialize; use color_eyre::eyre::Result; use itertools::sorted; use masp_primitives::zip32::ExtendedFullViewingKey; +use namada::ledger::masp::find_valid_diversifier; +use namada::ledger::wallet::FindKeyError; use namada::types::key::*; use namada::types::masp::{MaspValue, PaymentAddress}; use namada_apps::cli; +use namada_apps::cli::args::CliToSdk; use namada_apps::cli::{args, cmds, Context}; -use namada::ledger::masp::find_valid_diversifier; -use namada_apps::wallet::DecryptionError; +use namada_apps::wallet::{CliWalletUtils, DecryptionError}; use rand_core::OsRng; -use namada_apps::wallet::CliWalletUtils; -use namada::ledger::wallet::FindKeyError; -use namada_apps::cli::args::CliToSdk; pub fn main() -> Result<()> { let (cmd, mut ctx) = cli::anoma_wallet_cli()?; @@ -204,7 +203,8 @@ fn spending_key_gen( let mut wallet = ctx.wallet; let alias = alias.to_lowercase(); let (alias, _key) = wallet.gen_spending_key(alias, unsafe_dont_encrypt); - namada_apps::wallet::save(&wallet).unwrap_or_else(|err| eprintln!("{}", err)); + namada_apps::wallet::save(&wallet) + .unwrap_or_else(|err| eprintln!("{}", err)); println!( "Successfully added a spending key with alias: \"{}\"", alias @@ -221,10 +221,7 @@ fn payment_address_gen( }: args::MaspPayAddrGen, ) { let alias = alias.to_lowercase(); - let viewing_key = - ExtendedFullViewingKey::from(viewing_key) - .fvk - .vk; + 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) @@ -239,7 +236,8 @@ fn payment_address_gen( eprintln!("Payment address not added"); cli::safe_exit(1); }); - namada_apps::wallet::save(&wallet).unwrap_or_else(|err| eprintln!("{}", err)); + namada_apps::wallet::save(&wallet) + .unwrap_or_else(|err| eprintln!("{}", err)); println!( "Successfully generated a payment address with the following alias: {}", alias, @@ -292,7 +290,8 @@ fn address_key_add( (alias, "payment address") } }; - namada_apps::wallet::save(&ctx.wallet).unwrap_or_else(|err| eprintln!("{}", err)); + namada_apps::wallet::save(&ctx.wallet) + .unwrap_or_else(|err| eprintln!("{}", err)); println!( "Successfully added a {} with the following alias to wallet: {}", typ, alias, @@ -311,7 +310,8 @@ fn key_and_address_gen( ) { let mut wallet = ctx.wallet; let (alias, _key) = wallet.gen_key(scheme, alias, unsafe_dont_encrypt); - namada_apps::wallet::save(&wallet).unwrap_or_else(|err| eprintln!("{}", err)); + namada_apps::wallet::save(&wallet) + .unwrap_or_else(|err| eprintln!("{}", err)); println!( "Successfully added a key and an address with alias: \"{}\"", alias @@ -492,7 +492,8 @@ fn address_add(ctx: Context, args: args::AddressAdd) { eprintln!("Address not added"); cli::safe_exit(1); } - namada_apps::wallet::save(&wallet).unwrap_or_else(|err| eprintln!("{}", err)); + namada_apps::wallet::save(&wallet) + .unwrap_or_else(|err| eprintln!("{}", err)); println!( "Successfully added a key and an address with alias: \"{}\"", args.alias.to_lowercase() diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index 30e6580517..2ce89ab0fa 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -628,7 +628,7 @@ pub mod cmds { .about( "Generates a payment address from the given spending key", ) - .add_args::>() + .add_args::>() } } @@ -853,7 +853,7 @@ pub mod cmds { } #[derive(Clone, Debug)] - pub struct QueryResult(pub args::QueryResult::); + pub struct QueryResult(pub args::QueryResult); impl SubCmd for QueryResult { const CMD: &'static str = "tx-result"; @@ -867,12 +867,12 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) .about("Query the result of a transaction.") - .add_args::>() + .add_args::>() } } #[derive(Clone, Debug)] - pub struct QueryProposal(pub args::QueryProposal::); + pub struct QueryProposal(pub args::QueryProposal); impl SubCmd for QueryProposal { const CMD: &'static str = "query-proposal"; @@ -889,12 +889,14 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) .about("Query proposals.") - .add_args::>() + .add_args::>() } } #[derive(Clone, Debug)] - pub struct QueryProposalResult(pub args::QueryProposalResult::); + pub struct QueryProposalResult( + pub args::QueryProposalResult, + ); impl SubCmd for QueryProposalResult { const CMD: &'static str = "query-proposal-result"; @@ -911,12 +913,14 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) .about("Query proposals result.") - .add_args::>() + .add_args::>() } } #[derive(Clone, Debug)] - pub struct QueryProtocolParameters(pub args::QueryProtocolParameters::); + pub struct QueryProtocolParameters( + pub args::QueryProtocolParameters, + ); impl SubCmd for QueryProtocolParameters { const CMD: &'static str = "query-protocol-parameters"; @@ -935,12 +939,12 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) .about("Query protocol parameters.") - .add_args::>() + .add_args::>() } } #[derive(Clone, Debug)] - pub struct TxCustom(pub args::TxCustom::); + pub struct TxCustom(pub args::TxCustom); impl SubCmd for TxCustom { const CMD: &'static str = "tx"; @@ -954,12 +958,12 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) .about("Send a transaction with custom WASM code.") - .add_args::>() + .add_args::>() } } #[derive(Clone, Debug)] - pub struct TxTransfer(pub args::TxTransfer::); + pub struct TxTransfer(pub args::TxTransfer); impl SubCmd for TxTransfer { const CMD: &'static str = "transfer"; @@ -978,7 +982,7 @@ pub mod cmds { } #[derive(Clone, Debug)] - pub struct TxIbcTransfer(pub args::TxIbcTransfer::); + pub struct TxIbcTransfer(pub args::TxIbcTransfer); impl SubCmd for TxIbcTransfer { const CMD: &'static str = "ibc-transfer"; @@ -992,12 +996,12 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) .about("Send a signed IBC transfer transaction.") - .add_args::>() + .add_args::>() } } #[derive(Clone, Debug)] - pub struct TxUpdateVp(pub args::TxUpdateVp::); + pub struct TxUpdateVp(pub args::TxUpdateVp); impl SubCmd for TxUpdateVp { const CMD: &'static str = "update"; @@ -1014,12 +1018,12 @@ pub mod cmds { "Send a signed transaction to update account's validity \ predicate.", ) - .add_args::>() + .add_args::>() } } #[derive(Clone, Debug)] - pub struct TxInitAccount(pub args::TxInitAccount::); + pub struct TxInitAccount(pub args::TxInitAccount); impl SubCmd for TxInitAccount { const CMD: &'static str = "init-account"; @@ -1036,12 +1040,12 @@ pub mod cmds { "Send a signed transaction to create a new established \ account.", ) - .add_args::>() + .add_args::>() } } #[derive(Clone, Debug)] - pub struct TxInitValidator(pub args::TxInitValidator::); + pub struct TxInitValidator(pub args::TxInitValidator); impl SubCmd for TxInitValidator { const CMD: &'static str = "init-validator"; @@ -1058,12 +1062,12 @@ pub mod cmds { "Send a signed transaction to create a new validator \ account.", ) - .add_args::>() + .add_args::>() } } #[derive(Clone, Debug)] - pub struct Bond(pub args::Bond::); + pub struct Bond(pub args::Bond); impl SubCmd for Bond { const CMD: &'static str = "bond"; @@ -1077,12 +1081,12 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) .about("Bond tokens in PoS system.") - .add_args::>() + .add_args::>() } } #[derive(Clone, Debug)] - pub struct Unbond(pub args::Unbond::); + pub struct Unbond(pub args::Unbond); impl SubCmd for Unbond { const CMD: &'static str = "unbond"; @@ -1096,12 +1100,12 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) .about("Unbond tokens from a PoS bond.") - .add_args::>() + .add_args::>() } } #[derive(Clone, Debug)] - pub struct Withdraw(pub args::Withdraw::); + pub struct Withdraw(pub args::Withdraw); impl SubCmd for Withdraw { const CMD: &'static str = "withdraw"; @@ -1115,12 +1119,12 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) .about("Withdraw tokens from previously unbonded PoS bond.") - .add_args::>() + .add_args::>() } } #[derive(Clone, Debug)] - pub struct QueryEpoch(pub args::Query::); + pub struct QueryEpoch(pub args::Query); impl SubCmd for QueryEpoch { const CMD: &'static str = "epoch"; @@ -1134,12 +1138,12 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) .about("Query the epoch of the last committed block.") - .add_args::>() + .add_args::>() } } #[derive(Clone, Debug)] - pub struct QueryConversions(pub args::QueryConversions::); + pub struct QueryConversions(pub args::QueryConversions); impl SubCmd for QueryConversions { const CMD: &'static str = "conversions"; @@ -1153,12 +1157,12 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) .about("Query currently applicable conversions.") - .add_args::>() + .add_args::>() } } #[derive(Clone, Debug)] - pub struct QueryBlock(pub args::Query::); + pub struct QueryBlock(pub args::Query); impl SubCmd for QueryBlock { const CMD: &'static str = "block"; @@ -1172,12 +1176,12 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) .about("Query the last committed block.") - .add_args::>() + .add_args::>() } } #[derive(Clone, Debug)] - pub struct QueryBalance(pub args::QueryBalance::); + pub struct QueryBalance(pub args::QueryBalance); impl SubCmd for QueryBalance { const CMD: &'static str = "balance"; @@ -1191,12 +1195,12 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) .about("Query balance(s) of tokens.") - .add_args::>() + .add_args::>() } } #[derive(Clone, Debug)] - pub struct QueryBonds(pub args::QueryBonds::); + pub struct QueryBonds(pub args::QueryBonds); impl SubCmd for QueryBonds { const CMD: &'static str = "bonds"; @@ -1210,12 +1214,12 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) .about("Query PoS bond(s).") - .add_args::>() + .add_args::>() } } #[derive(Clone, Debug)] - pub struct QueryBondedStake(pub args::QueryBondedStake::); + pub struct QueryBondedStake(pub args::QueryBondedStake); impl SubCmd for QueryBondedStake { const CMD: &'static str = "bonded-stake"; @@ -1229,12 +1233,12 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) .about("Query PoS bonded stake.") - .add_args::>() + .add_args::>() } } #[derive(Clone, Debug)] - pub struct QueryTransfers(pub args::QueryTransfers::); + pub struct QueryTransfers(pub args::QueryTransfers); impl SubCmd for QueryTransfers { const CMD: &'static str = "show-transfers"; @@ -1248,12 +1252,14 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) .about("Query the accepted transfers to date.") - .add_args::>() + .add_args::>() } } #[derive(Clone, Debug)] - pub struct QueryCommissionRate(pub args::QueryCommissionRate::); + pub struct QueryCommissionRate( + pub args::QueryCommissionRate, + ); impl SubCmd for QueryCommissionRate { const CMD: &'static str = "commission-rate"; @@ -1267,12 +1273,12 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) .about("Query commission rate.") - .add_args::>() + .add_args::>() } } #[derive(Clone, Debug)] - pub struct QuerySlashes(pub args::QuerySlashes::); + pub struct QuerySlashes(pub args::QuerySlashes); impl SubCmd for QuerySlashes { const CMD: &'static str = "slashes"; @@ -1289,12 +1295,12 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) .about("Query PoS applied slashes.") - .add_args::>() + .add_args::>() } } #[derive(Clone, Debug)] - pub struct QueryRawBytes(pub args::QueryRawBytes::); + pub struct QueryRawBytes(pub args::QueryRawBytes); impl SubCmd for QueryRawBytes { const CMD: &'static str = "query-bytes"; @@ -1308,12 +1314,12 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) .about("Query the raw bytes of a given storage key") - .add_args::>() + .add_args::>() } } #[derive(Clone, Debug)] - pub struct TxInitProposal(pub args::InitProposal::); + pub struct TxInitProposal(pub args::InitProposal); impl SubCmd for TxInitProposal { const CMD: &'static str = "init-proposal"; @@ -1330,12 +1336,12 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) .about("Create a new proposal.") - .add_args::>() + .add_args::>() } } #[derive(Clone, Debug)] - pub struct TxVoteProposal(pub args::VoteProposal::); + pub struct TxVoteProposal(pub args::VoteProposal); impl SubCmd for TxVoteProposal { const CMD: &'static str = "vote-proposal"; @@ -1352,12 +1358,12 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) .about("Vote a proposal.") - .add_args::>() + .add_args::>() } } #[derive(Clone, Debug)] - pub struct TxRevealPk(pub args::RevealPk::); + pub struct TxRevealPk(pub args::RevealPk); impl SubCmd for TxRevealPk { const CMD: &'static str = "reveal-pk"; @@ -1382,7 +1388,7 @@ pub mod cmds { signature verification on transactions authorized by \ this account.", ) - .add_args::>() + .add_args::>() } } @@ -1513,6 +1519,7 @@ pub mod args { use std::str::FromStr; use namada::ibc::core::ics24_host::identifier::{ChannelId, PortId}; + pub use namada::ledger::args::*; use namada::types::address::Address; use namada::types::chain::{ChainId, ChainIdPrefix}; use namada::types::governance::ProposalVote; @@ -1521,7 +1528,6 @@ pub mod args { use namada::types::storage::{self, Epoch}; use namada::types::token; use rust_decimal::Decimal; - pub use namada::ledger::args::*; use super::context::*; use super::utils::*; @@ -1543,7 +1549,8 @@ pub mod args { const TX_BOND_WASM: &str = "tx_bond.wasm"; const TX_UNBOND_WASM: &str = "tx_unbond.wasm"; const TX_WITHDRAW_WASM: &str = "tx_withdraw.wasm"; - const TX_CHANGE_COMMISSION_WASM: &str = "tx_change_validator_commission.wasm"; + const TX_CHANGE_COMMISSION_WASM: &str = + "tx_change_validator_commission.wasm"; const ADDRESS: Arg = arg("address"); const ALIAS_OPT: ArgOpt = ALIAS.opt(); @@ -1720,7 +1727,7 @@ pub mod args { } fn def(app: App) -> App { - app.add_args::>().arg( + app.add_args::>().arg( TX_HASH .def() .about("The hash of the transaction being looked up."), @@ -1734,7 +1741,8 @@ pub mod args { tx: self.tx.to_sdk(ctx), code_path: ctx.read_wasm(self.code_path), data_path: self.data_path.map(|data_path| { - std::fs::read(data_path).expect("Expected a file at given data path") + std::fs::read(data_path) + .expect("Expected a file at given data path") }), } } @@ -1753,7 +1761,7 @@ pub mod args { } fn def(app: App) -> App { - app.add_args::>() + app.add_args::>() .arg( CODE_PATH .def() @@ -1805,7 +1813,7 @@ pub mod args { } fn def(app: App) -> App { - app.add_args::>() + app.add_args::>() .arg(TRANSFER_SOURCE.def().about( "The source account address. The source's key may be used \ to produce the signature.", @@ -1867,7 +1875,7 @@ pub mod args { } fn def(app: App) -> App { - app.add_args::>() + app.add_args::>() .arg(SOURCE.def().about( "The source account address. The source's key is used to \ produce the signature.", @@ -1905,7 +1913,8 @@ pub mod args { fn parse(matches: &ArgMatches) -> Self { let tx = Tx::parse(matches); let source = SOURCE.parse(matches); - let vp_code_path = CODE_PATH_OPT.parse(matches) + let vp_code_path = CODE_PATH_OPT + .parse(matches) .unwrap_or(PathBuf::from(VP_USER_WASM)); let tx_code_path = PathBuf::from(TX_INIT_ACCOUNT_WASM); let public_key = PUBLIC_KEY.parse(matches); @@ -1919,7 +1928,7 @@ pub mod args { } fn def(app: App) -> App { - app.add_args::>() + app.add_args::>() .arg(SOURCE.def().about( "The source account's address that signs the transaction.", )) @@ -1946,7 +1955,8 @@ pub mod args { protocol_key: self.protocol_key.map(|x| ctx.get_cached(&x)), commission_rate: self.commission_rate, max_commission_rate_change: self.max_commission_rate_change, - validator_vp_code_path: ctx.read_wasm(self.validator_vp_code_path), + validator_vp_code_path: ctx + .read_wasm(self.validator_vp_code_path), unsafe_dont_encrypt: self.unsafe_dont_encrypt, tx_code_path: ctx.read_wasm(self.tx_code_path), } @@ -1964,7 +1974,8 @@ pub mod args { let commission_rate = COMMISSION_RATE.parse(matches); let max_commission_rate_change = MAX_COMMISSION_RATE_CHANGE.parse(matches); - let validator_vp_code_path = VALIDATOR_CODE_PATH.parse(matches) + let validator_vp_code_path = VALIDATOR_CODE_PATH + .parse(matches) .unwrap_or(PathBuf::from(VP_USER_WASM)); let unsafe_dont_encrypt = UNSAFE_DONT_ENCRYPT.parse(matches); let tx_code_path = PathBuf::from(TX_INIT_VALIDATOR_WASM); @@ -1984,7 +1995,7 @@ pub mod args { } fn def(app: App) -> App { - app.add_args::>() + app.add_args::>() .arg(SOURCE.def().about( "The source account's address that signs the transaction.", )) @@ -2053,7 +2064,7 @@ pub mod args { } fn def(app: App) -> App { - app.add_args::>() + app.add_args::>() .arg( CODE_PATH.def().about( "The path to the new validity predicate WASM code.", @@ -2098,7 +2109,7 @@ pub mod args { } fn def(app: App) -> App { - app.add_args::>() + app.add_args::>() .arg(VALIDATOR.def().about("Validator address.")) .arg(AMOUNT.def().about("Amount of tokens to stake in a bond.")) .arg(SOURCE_OPT.def().about( @@ -2137,7 +2148,7 @@ pub mod args { } fn def(app: App) -> App { - app.add_args::>() + app.add_args::>() .arg(VALIDATOR.def().about("Validator address.")) .arg( AMOUNT @@ -2196,7 +2207,7 @@ pub mod args { } fn def(app: App) -> App { - app.add_args::>() + app.add_args::>() .arg(DATA_PATH.def().about( "The data path file (json) that describes the proposal.", )) @@ -2257,7 +2268,7 @@ pub mod args { } fn def(app: App) -> App { - app.add_args::>() + app.add_args::>() .arg( PROPOSAL_ID_OPT .def() @@ -2308,7 +2319,7 @@ pub mod args { } fn def(app: App) -> App { - app.add_args::>() + app.add_args::>() .arg(PUBLIC_KEY.def().about("A public key to reveal.")) } } @@ -2331,7 +2342,7 @@ pub mod args { } fn def(app: App) -> App { - app.add_args::>() + app.add_args::>() .arg(PROPOSAL_ID_OPT.def().about("The proposal identifier.")) } } @@ -2375,7 +2386,7 @@ pub mod args { } fn def(app: App) -> App { - app.add_args::>() + app.add_args::>() .arg(PROPOSAL_ID_OPT.def().about("The proposal identifier.")) .arg( PROPOSAL_OFFLINE @@ -2398,8 +2409,13 @@ pub mod args { } } - impl CliToSdk> for QueryProtocolParameters { - fn to_sdk(self, ctx: &mut Context) -> QueryProtocolParameters { + impl CliToSdk> + for QueryProtocolParameters + { + fn to_sdk( + self, + ctx: &mut Context, + ) -> QueryProtocolParameters { QueryProtocolParameters:: { query: self.query.to_sdk(ctx), } @@ -2414,7 +2430,7 @@ pub mod args { } fn def(app: App) -> App { - app.add_args::>() + app.add_args::>() } } @@ -2444,7 +2460,7 @@ pub mod args { } fn def(app: App) -> App { - app.add_args::>() + app.add_args::>() .arg(VALIDATOR.def().about("Validator address.")) .arg(SOURCE_OPT.def().about( "Source address for withdrawing from delegations. For \ @@ -2477,7 +2493,7 @@ pub mod args { } fn def(app: App) -> App { - app.add_args::>() + app.add_args::>() .arg( EPOCH .def() @@ -2520,7 +2536,7 @@ pub mod args { } fn def(app: App) -> App { - app.add_args::>() + app.add_args::>() .arg( BALANCE_OWNER .def() @@ -2567,7 +2583,7 @@ pub mod args { } fn def(app: App) -> App { - app.add_args::>() + app.add_args::>() .arg(BALANCE_OWNER.def().about( "The account address that queried transfers must involve.", )) @@ -2600,7 +2616,7 @@ pub mod args { } fn def(app: App) -> App { - app.add_args::>() + app.add_args::>() .arg( OWNER.def().about( "The owner account address whose bonds to query.", @@ -2637,7 +2653,7 @@ pub mod args { } fn def(app: App) -> App { - app.add_args::>() + app.add_args::>() .arg(VALIDATOR_OPT.def().about( "The validator's address whose bonded stake to query.", )) @@ -2648,7 +2664,9 @@ pub mod args { } } - impl CliToSdk> for TxCommissionRateChange { + impl CliToSdk> + for TxCommissionRateChange + { fn to_sdk(self, ctx: &mut Context) -> TxCommissionRateChange { TxCommissionRateChange:: { tx: self.tx.to_sdk(ctx), @@ -2674,7 +2692,7 @@ pub mod args { } fn def(app: App) -> App { - app.add_args::>() + app.add_args::>() .arg(VALIDATOR.def().about( "The validator's address whose commission rate to change.", )) @@ -2709,7 +2727,7 @@ pub mod args { } fn def(app: App) -> App { - app.add_args::>() + app.add_args::>() .arg(VALIDATOR.def().about( "The validator's address whose commission rate to query.", )) @@ -2737,7 +2755,7 @@ pub mod args { } fn def(app: App) -> App { - app.add_args::>().arg( + app.add_args::>().arg( VALIDATOR_OPT .def() .about("The validator's address whose slashes to query."), @@ -2762,7 +2780,7 @@ pub mod args { } fn def(app: App) -> App { - app.add_args::>() + app.add_args::>() .arg(STORAGE_KEY.def().about("Storage key")) } } @@ -2773,24 +2791,15 @@ pub mod args { impl NamadaTypes for CliTypes { type Address = WalletAddress; - - type NativeAddress = (); - - type Keypair = WalletKeypair; - - type TendermintAddress = TendermintAddress; - - type ViewingKey = WalletViewingKey; - type BalanceOwner = WalletBalanceOwner; - + type Data = PathBuf; + type Keypair = WalletKeypair; + type NativeAddress = (); type PublicKey = WalletPublicKey; - + type TendermintAddress = TendermintAddress; type TransferSource = WalletTransferSource; - type TransferTarget = WalletTransferTarget; - - type Data = PathBuf; + type ViewingKey = WalletViewingKey; } impl CliToSdk> for Tx { @@ -2893,9 +2902,7 @@ pub mod args { impl CliToSdk> for Query { fn to_sdk(self, _ctx: &mut Context) -> Query { - Query:: { - ledger_address: (), - } + Query:: { ledger_address: () } } } diff --git a/apps/src/lib/cli/context.rs b/apps/src/lib/cli/context.rs index de27bf7cc8..9816f21d93 100644 --- a/apps/src/lib/cli/context.rs +++ b/apps/src/lib/cli/context.rs @@ -6,20 +6,20 @@ use std::path::{Path, PathBuf}; use std::str::FromStr; use color_eyre::eyre::Result; +use namada::ledger::masp::ShieldedContext; +use namada::ledger::wallet::Wallet; use namada::types::address::Address; use namada::types::chain::ChainId; use namada::types::key::*; use namada::types::masp::*; -use namada::ledger::masp::ShieldedContext; use super::args; use crate::client::tx::CLIShieldedUtils; use crate::config::genesis::genesis_config; use crate::config::global::GlobalConfig; use crate::config::{self, Config}; -use namada::ledger::wallet::Wallet; -use crate::wasm_loader; use crate::wallet::CliWalletUtils; +use crate::wasm_loader; /// Env. var to set chain ID const ENV_VAR_CHAIN_ID: &str = "ANOMA_CHAIN_ID"; @@ -100,8 +100,10 @@ impl Context { let native_token = genesis.native_token; let default_genesis = genesis_config::open_genesis_config(genesis_file_path)?; - let wallet = - crate::wallet::load_or_new_from_genesis(&chain_dir, default_genesis); + let wallet = crate::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() { diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index 6427ba9a35..ad9b3b175e 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -15,7 +15,7 @@ use async_std::prelude::*; use borsh::{BorshDeserialize, BorshSerialize}; use data_encoding::HEXLOWER; use eyre::{eyre, Context as EyreContext}; -use itertools::{Itertools, Either}; +use itertools::{Either, Itertools}; use masp_primitives::asset_type::AssetType; use masp_primitives::merkle_tree::MerklePath; use masp_primitives::primitives::ViewingKey; @@ -25,21 +25,24 @@ use masp_primitives::zip32::ExtendedFullViewingKey; use namada::ledger::events::Event; use namada::ledger::governance::parameters::GovParams; use namada::ledger::governance::storage as gov_storage; +use namada::ledger::masp::{ + Conversions, PinnedBalanceError, ShieldedContext, ShieldedUtils, +}; use namada::ledger::native_vp::governance::utils::Votes; use namada::ledger::parameters::{storage as param_storage, EpochDuration}; use namada::ledger::pos::types::{decimal_mult_u64, WeightedValidator}; use namada::ledger::pos::{ self, is_validator_slashes_key, BondId, Bonds, PosParams, Slash, Unbonds, }; -use namada::ledger::masp::ShieldedUtils; use namada::ledger::queries::{self, RPC}; +use namada::ledger::rpc::TxResponse; use namada::ledger::storage::ConversionState; +use namada::ledger::wallet::Wallet; use namada::types::address::{masp, tokens, Address}; use namada::types::governance::{ OfflineProposal, OfflineVote, ProposalResult, ProposalVote, TallyResult, VotePower, }; -use namada::ledger::masp::ShieldedContext; use namada::types::hash::Hash; use namada::types::key::*; use namada::types::masp::{BalanceOwner, ExtendedViewingKey, PaymentAddress}; @@ -51,10 +54,7 @@ use namada::types::{address, storage, token}; use rust_decimal::Decimal; use tokio::time::{Duration, Instant}; -use namada::ledger::wallet::Wallet; use crate::cli::{self, args}; -use namada::ledger::rpc::TxResponse; -use namada::ledger::masp::{Conversions, PinnedBalanceError}; use crate::facade::tendermint::merkle::proof::Proof; use crate::facade::tendermint_rpc::error::Error as TError; use crate::facade::tendermint_rpc::query::Query; @@ -67,7 +67,9 @@ use crate::wallet::CliWalletUtils; /// /// If a response is not delivered until `deadline`, we exit the cli with an /// error. -pub async fn query_tx_status( +pub async fn query_tx_status< + C: Client + namada::ledger::queries::Client + Sync, +>( client: &C, status: namada::ledger::rpc::TxEventQuery<'_>, deadline: Instant, @@ -76,7 +78,9 @@ pub async fn query_tx_status } /// Query the epoch of the last committed block -pub async fn query_epoch(client: &C) -> Epoch { +pub async fn query_epoch( + client: &C, +) -> Epoch { namada::ledger::rpc::query_epoch(client).await } @@ -88,7 +92,11 @@ pub async fn query_block( } /// Query the results of the last committed block -pub async fn query_results(client: &C) -> Vec { +pub async fn query_results< + C: Client + namada::ledger::queries::Client + Sync, +>( + client: &C, +) -> Vec { namada::ledger::rpc::query_results(client).await } @@ -97,23 +105,23 @@ pub async fn query_transfers>( client: &C, wallet: &mut Wallet, shielded: &mut ShieldedContext, - args: args::QueryTransfers + args: args::QueryTransfers, ) { let query_token = args.token; - let query_owner = args.owner - .map_or_else( - || Either::Right(wallet.get_addresses().into_values().collect()), - Either::Left, - ); + let query_owner = args.owner.map_or_else( + || Either::Right(wallet.get_addresses().into_values().collect()), + Either::Left, + ); let _ = shielded.load(); // Obtain the effects of all shielded and transparent transactions - let transfers = shielded.query_tx_deltas( - client, - &query_owner, - &query_token, - &wallet.get_viewing_keys(), - ) - .await; + let transfers = shielded + .query_tx_deltas( + client, + &query_owner, + &query_token, + &wallet.get_viewing_keys(), + ) + .await; // To facilitate lookups of human-readable token names let tokens = tokens(); let vks = wallet.get_viewing_keys(); @@ -148,8 +156,7 @@ pub async fn query_transfers>( ) .await .0; - let dec = - shielded.decode_amount(client, amt, epoch).await; + let dec = shielded.decode_amount(client, amt, epoch).await; shielded_accounts.insert(acc, dec); } // Check if this transfer pertains to the supplied token @@ -216,7 +223,12 @@ pub async fn query_transfers>( } /// Query the raw bytes of given storage key -pub async fn query_raw_bytes(client: &C, args: args::QueryRawBytes) { +pub async fn query_raw_bytes< + C: Client + namada::ledger::queries::Client + Sync, +>( + client: &C, + args: args::QueryRawBytes, +) { let response = unwrap_client_response::( RPC.shell() .storage_value(client, None, None, false, &args.storage_key) @@ -230,7 +242,10 @@ pub async fn query_raw_bytes } /// Query token balance(s) -pub async fn query_balance>( +pub async fn query_balance< + C: Client + namada::ledger::queries::Client + Sync, + U: ShieldedUtils, +>( client: &C, wallet: &mut Wallet, shielded: &mut ShieldedContext, @@ -252,7 +267,8 @@ pub async fn query_balance( +pub async fn query_transparent_balance< + C: Client + namada::ledger::queries::Client + Sync, +>( client: &C, wallet: &mut Wallet, args: args::QueryBalance, @@ -327,7 +345,8 @@ pub async fn query_transparent_balance(client, &key).await; + query_storage_prefix::(client, &key) + .await; if let Some(balances) = balances { print_balances(wallet, balances, &token, None); } @@ -345,9 +364,7 @@ pub async fn query_pinned_balance>( ) { // Map addresses to token names let tokens = address::tokens(); - let owners = if let Some(pa) = args - .owner - .and_then(|x| x.payment_address()) + let owners = if let Some(pa) = args.owner.and_then(|x| x.payment_address()) { vec![pa] } else { @@ -371,11 +388,7 @@ pub async fn query_pinned_balance>( // address for vk in &viewing_keys { balance = shielded - .compute_exchanged_pinned_balance( - client, - owner, - vk, - ) + .compute_exchanged_pinned_balance(client, owner, vk) .await; if balance != Err(PinnedBalanceError::InvalidViewingKey) { break; @@ -397,11 +410,7 @@ pub async fn query_pinned_balance>( let vk = ExtendedFullViewingKey::from(fvk).fvk.vk; // Use the given viewing key to decrypt pinned transaction data balance = shielded - .compute_exchanged_pinned_balance( - client, - owner, - &vk, - ) + .compute_exchanged_pinned_balance(client, owner, &vk) .await } // Now print out the received quantities according to CLI arguments @@ -439,9 +448,8 @@ pub async fn query_pinned_balance>( (Ok((balance, epoch)), None) => { let mut found_any = false; // Print balances by human-readable token names - let balance = shielded - .decode_amount(client, balance, epoch) - .await; + let balance = + shielded.decode_amount(client, balance, epoch).await; for (addr, value) in balance.components() { let asset_value = token::Amount::from(*value as u64); if !found_any { @@ -536,8 +544,15 @@ fn print_balances( } /// Query Proposals -pub async fn query_proposal(client: &C, args: args::QueryProposal) { - async fn print_proposal( +pub async fn query_proposal< + C: Client + namada::ledger::queries::Client + Sync, +>( + client: &C, + args: args::QueryProposal, +) { + async fn print_proposal< + C: Client + namada::ledger::queries::Client + Sync, + >( client: &C, id: u64, current_epoch: Epoch, @@ -563,7 +578,8 @@ pub async fn query_proposal( ) .await?; let grace_epoch = - query_storage_value::(client, &grace_epoch_key).await?; + query_storage_value::(client, &grace_epoch_key) + .await?; println!("Proposal: {}", id); println!("{:4}Author: {}", "", author); @@ -662,7 +678,10 @@ pub fn value_by_address( } /// Query token shielded balance(s) -pub async fn query_shielded_balance>( +pub async fn query_shielded_balance< + C: Client + namada::ledger::queries::Client + Sync, + U: ShieldedUtils, +>( client: &C, wallet: &mut Wallet, shielded: &mut ShieldedContext, @@ -670,9 +689,7 @@ pub async fn query_shielded_balance { // Only assets with the current timestamp count @@ -841,11 +849,7 @@ pub async fn query_shielded_balance( +pub async fn get_token_balance< + C: Client + namada::ledger::queries::Client + Sync, +>( client: &C, token: &Address, owner: &Address, @@ -945,7 +945,9 @@ pub async fn get_token_balance( +pub async fn query_proposal_result< + C: Client + namada::ledger::queries::Client + Sync, +>( client: &C, args: args::QueryProposalResult, ) { @@ -1037,12 +1039,10 @@ pub async fn query_proposal_result( +pub async fn query_protocol_parameters< + C: Client + namada::ledger::queries::Client + Sync, +>( client: &C, _args: args::QueryProtocolParameters, ) { @@ -1149,7 +1151,10 @@ pub async fn query_protocol_parameters(client: &C, args: args::QueryBonds) { +pub async fn query_bonds( + client: &C, + args: args::QueryBonds, +) { let epoch = query_epoch(client).await; match (args.owner, args.validator) { (Some(owner), Some(validator)) => { @@ -1164,7 +1169,8 @@ pub async fn query_bonds(cli // validator let unbond_key = pos::unbond_key(&bond_id); let unbonds = - query_storage_value::(client, &unbond_key).await; + query_storage_value::(client, &unbond_key) + .await; // Find validator's slashes, if any let slashes_key = pos::validator_slashes_key(&bond_id.validator); let slashes = @@ -1222,7 +1228,8 @@ pub async fn query_bonds(cli // Find validator's unbonded self-bonds let unbond_key = pos::unbond_key(&bond_id); let unbonds = - query_storage_value::(client, &unbond_key).await; + query_storage_value::(client, &unbond_key) + .await; // Find validator's slashes, if any let slashes_key = pos::validator_slashes_key(&bond_id.validator); let slashes = @@ -1265,9 +1272,11 @@ pub async fn query_bonds(cli .await; // Find owner's unbonds to any validator let unbonds_prefix = pos::unbonds_for_source_prefix(&owner); - let unbonds = - query_storage_prefix::(client, &unbonds_prefix) - .await; + let unbonds = query_storage_prefix::( + client, + &unbonds_prefix, + ) + .await; let mut total: token::Amount = 0.into(); let mut total_active: token::Amount = 0.into(); @@ -1279,12 +1288,13 @@ pub async fn query_bonds(cli // Find validator's slashes, if any let slashes_key = pos::validator_slashes_key(&validator); - let slashes = query_storage_value::( - client, - &slashes_key, - ) - .await - .unwrap_or_default(); + let slashes = + query_storage_value::( + client, + &slashes_key, + ) + .await + .unwrap_or_default(); let stdout = io::stdout(); let mut w = stdout.lock(); @@ -1330,12 +1340,13 @@ pub async fn query_bonds(cli // Find validator's slashes, if any let slashes_key = pos::validator_slashes_key(&validator); - let slashes = query_storage_value::( - client, - &slashes_key, - ) - .await - .unwrap_or_default(); + let slashes = + query_storage_value::( + client, + &slashes_key, + ) + .await + .unwrap_or_default(); let stdout = io::stdout(); let mut w = stdout.lock(); @@ -1381,9 +1392,11 @@ pub async fn query_bonds(cli .await; // Find all the unbonds let unbonds_prefix = pos::unbonds_prefix(); - let unbonds = - query_storage_prefix::(client, &unbonds_prefix) - .await; + let unbonds = query_storage_prefix::( + client, + &unbonds_prefix, + ) + .await; let mut total: token::Amount = 0.into(); let mut total_active: token::Amount = 0.into(); @@ -1394,12 +1407,13 @@ pub async fn query_bonds(cli // Find validator's slashes, if any let slashes_key = pos::validator_slashes_key(&validator); - let slashes = query_storage_value::( - client, - &slashes_key, - ) - .await - .unwrap_or_default(); + let slashes = + query_storage_value::( + client, + &slashes_key, + ) + .await + .unwrap_or_default(); let stdout = io::stdout(); let mut w = stdout.lock(); @@ -1445,12 +1459,13 @@ pub async fn query_bonds(cli // Find validator's slashes, if any let slashes_key = pos::validator_slashes_key(&validator); - let slashes = query_storage_value::( - client, - &slashes_key, - ) - .await - .unwrap_or_default(); + let slashes = + query_storage_value::( + client, + &slashes_key, + ) + .await + .unwrap_or_default(); let stdout = io::stdout(); let mut w = stdout.lock(); @@ -1495,7 +1510,12 @@ pub async fn query_bonds(cli } /// Query PoS bonded stake -pub async fn query_bonded_stake(client: &C, args: args::QueryBondedStake) { +pub async fn query_bonded_stake< + C: Client + namada::ledger::queries::Client + Sync, +>( + client: &C, + args: args::QueryBondedStake, +) { let epoch = match args.epoch { Some(epoch) => epoch, None => query_epoch(client).await, @@ -1503,10 +1523,12 @@ pub async fn query_bonded_stake(client, &validator_set_key) - .await - .expect("Validator set should always be set"); + let validator_sets = query_storage_value::( + client, + &validator_set_key, + ) + .await + .expect("Validator set should always be set"); let validator_set = validator_sets .get(epoch) .expect("Validator set should be always set in the current epoch"); @@ -1516,11 +1538,12 @@ pub async fn query_bonded_stake( - client, - &validator_deltas_key, - ) - .await; + let validator_deltas = + query_storage_value::( + client, + &validator_deltas_key, + ) + .await; match validator_deltas.and_then(|data| data.get(epoch)) { Some(val_stake) => { let bonded_stake: u64 = val_stake.try_into().expect( @@ -1594,7 +1617,9 @@ pub async fn query_bonded_stake( +pub async fn query_commission_rate< + C: Client + namada::ledger::queries::Client + Sync, +>( client: &C, args: args::QueryCommissionRate, ) { @@ -1603,8 +1628,7 @@ pub async fn query_commission_rate query_epoch(client).await, }; let validator = args.validator; - let is_validator = - is_validator(client, &validator).await; + let is_validator = is_validator(client, &validator).await; if is_validator { let validator_commission_key = @@ -1649,7 +1673,12 @@ pub async fn query_commission_rate(client: &C, args: args::QuerySlashes) { +pub async fn query_slashes< + C: Client + namada::ledger::queries::Client + Sync, +>( + client: &C, + args: args::QuerySlashes, +) { match args.validator { Some(validator) => { let validator = validator; @@ -1679,9 +1708,11 @@ pub async fn query_slashes(c None => { // Iterate slashes for all validators let slashes_prefix = pos::slashes_prefix(); - let slashes = - query_storage_prefix::(&client, &slashes_prefix) - .await; + let slashes = query_storage_prefix::( + &client, + &slashes_prefix, + ) + .await; match slashes { Some(slashes) => { @@ -1718,12 +1749,20 @@ pub async fn query_slashes(c } /// Dry run a transaction -pub async fn dry_run_tx(client: &C, tx_bytes: Vec) { - println!("Dry-run result: {}", namada::ledger::rpc::dry_run_tx(client, tx_bytes).await); +pub async fn dry_run_tx( + client: &C, + tx_bytes: Vec, +) { + println!( + "Dry-run result: {}", + namada::ledger::rpc::dry_run_tx(client, tx_bytes).await + ); } /// Get account's public key stored in its storage sub-space -pub async fn get_public_key( +pub async fn get_public_key< + C: Client + namada::ledger::queries::Client + Sync, +>( client: &C, address: &Address, ) -> Option { @@ -1731,7 +1770,9 @@ pub async fn get_public_key( } /// Check if the given address is a known validator. -pub async fn is_validator( +pub async fn is_validator< + C: Client + namada::ledger::queries::Client + Sync, +>( client: &C, address: &Address, ) -> bool { @@ -1739,14 +1780,18 @@ pub async fn is_validator( } /// Check if a given address is a known delegator -pub async fn is_delegator( +pub async fn is_delegator< + C: Client + namada::ledger::queries::Client + Sync, +>( client: &C, address: &Address, ) -> bool { namada::ledger::rpc::is_delegator(client, address).await } -pub async fn is_delegator_at( +pub async fn is_delegator_at< + C: Client + namada::ledger::queries::Client + Sync, +>( client: &C, address: &Address, epoch: Epoch, @@ -1757,7 +1802,9 @@ pub async fn is_delegator_at /// Check if the address exists on chain. Established address exists if it has a /// stored validity predicate. Implicit and internal addresses always return /// true. -pub async fn known_address( +pub async fn known_address< + C: Client + namada::ledger::queries::Client + Sync, +>( client: &C, address: &Address, ) -> bool { @@ -1899,7 +1946,12 @@ fn process_unbonds_query( } /// Query for all conversions. -pub async fn query_conversions(client: &C, args: args::QueryConversions) { +pub async fn query_conversions< + C: Client + namada::ledger::queries::Client + Sync, +>( + client: &C, + args: args::QueryConversions, +) { // The chosen token type of the conversions let target_token = args.token; // To facilitate human readable token addresses @@ -1961,7 +2013,9 @@ pub async fn query_conversions( +pub async fn query_conversion< + C: Client + namada::ledger::queries::Client + Sync, +>( client: &C, asset_type: AssetType, ) -> Option<( @@ -1974,7 +2028,10 @@ pub async fn query_conversion( +pub async fn query_storage_value< + C: Client + namada::ledger::queries::Client + Sync, + T, +>( client: &C, key: &storage::Key, ) -> Option @@ -1985,19 +2042,25 @@ where } /// Query a storage value and the proof without decoding. -pub async fn query_storage_value_bytes( +pub async fn query_storage_value_bytes< + C: Client + namada::ledger::queries::Client + Sync, +>( client: &C, key: &storage::Key, height: Option, prove: bool, ) -> (Option>, Option) { - namada::ledger::rpc::query_storage_value_bytes(client, key, height, prove).await + namada::ledger::rpc::query_storage_value_bytes(client, key, height, prove) + .await } /// Query a range of storage values with a matching prefix and decode them with /// [`BorshDeserialize`]. Returns an iterator of the storage keys paired with /// their associated values. -pub async fn query_storage_prefix( +pub async fn query_storage_prefix< + C: Client + namada::ledger::queries::Client + Sync, + T, +>( client: &C, key: &storage::Key, ) -> Option> @@ -2008,7 +2071,9 @@ where } /// Query to check if the given storage key exists. -pub async fn query_has_storage_key( +pub async fn query_has_storage_key< + C: Client + namada::ledger::queries::Client + Sync, +>( client: &C, key: &storage::Key, ) -> bool { @@ -2017,10 +2082,15 @@ pub async fn query_has_storage_key( +pub async fn query_tx_events< + C: Client + namada::ledger::queries::Client + Sync, +>( client: &C, tx_event_query: namada::ledger::rpc::TxEventQuery<'_>, -) -> std::result::Result, ::Error> { +) -> std::result::Result< + Option, + ::Error, +> { namada::ledger::rpc::query_tx_events(client, tx_event_query).await } @@ -2074,7 +2144,9 @@ pub async fn query_result( } } -pub async fn get_proposal_votes( +pub async fn get_proposal_votes< + C: Client + namada::ledger::queries::Client + Sync, +>( client: &C, epoch: Epoch, proposal_id: u64, @@ -2082,7 +2154,9 @@ pub async fn get_proposal_votes( +pub async fn get_proposal_offline_votes< + C: Client + namada::ledger::queries::Client + Sync, +>( client: &C, proposal: OfflineProposal, files: HashSet, @@ -2212,7 +2286,9 @@ pub async fn get_proposal_offline_votes( +pub async fn compute_tally< + C: Client + namada::ledger::queries::Client + Sync, +>( client: &C, epoch: Epoch, votes: Votes, @@ -2220,30 +2296,39 @@ pub async fn compute_tally( namada::ledger::rpc::compute_tally(client, epoch, votes).await } -pub async fn get_bond_amount_at( +pub async fn get_bond_amount_at< + C: Client + namada::ledger::queries::Client + Sync, +>( client: &C, delegator: &Address, validator: &Address, epoch: Epoch, ) -> Option { - namada::ledger::rpc::get_bond_amount_at(client, delegator, validator, epoch).await + namada::ledger::rpc::get_bond_amount_at(client, delegator, validator, epoch) + .await } -pub async fn get_all_validators( +pub async fn get_all_validators< + C: Client + namada::ledger::queries::Client + Sync, +>( client: &C, epoch: Epoch, ) -> HashSet
{ namada::ledger::rpc::get_all_validators(client, epoch).await } -pub async fn get_total_staked_tokens( +pub async fn get_total_staked_tokens< + C: Client + namada::ledger::queries::Client + Sync, +>( client: &C, epoch: Epoch, ) -> token::Amount { namada::ledger::rpc::get_total_staked_tokens(client, epoch).await } -async fn get_validator_stake( +async fn get_validator_stake< + C: Client + namada::ledger::queries::Client + Sync, +>( client: &C, epoch: Epoch, validator: &Address, @@ -2251,14 +2336,20 @@ async fn get_validator_stake namada::ledger::rpc::get_validator_stake(client, epoch, validator).await } -pub async fn get_delegators_delegation( +pub async fn get_delegators_delegation< + C: Client + namada::ledger::queries::Client + Sync, +>( client: &C, address: &Address, ) -> HashSet
{ namada::ledger::rpc::get_delegators_delegation(client, address).await } -pub async fn get_governance_parameters(client: &C) -> GovParams { +pub async fn get_governance_parameters< + C: Client + namada::ledger::queries::Client + Sync, +>( + client: &C, +) -> GovParams { namada::ledger::rpc::get_governance_parameters(client).await } @@ -2272,7 +2363,9 @@ fn lookup_alias(wallet: &Wallet, addr: &Address) -> String { } /// A helper to unwrap client's response. Will shut down process on error. -fn unwrap_client_response(response: Result) -> T { +fn unwrap_client_response( + response: Result, +) -> T { response.unwrap_or_else(|err| { eprintln!("Error in the query"); cli::safe_exit(1) diff --git a/apps/src/lib/client/signing.rs b/apps/src/lib/client/signing.rs index 0a1d712bfd..23cdda81d7 100644 --- a/apps/src/lib/client/signing.rs +++ b/apps/src/lib/client/signing.rs @@ -1,21 +1,23 @@ //! Helpers for making digital signatures using cryptographic keys from the //! wallet. +use namada::ledger::rpc::TxBroadcastData; +use namada::ledger::signing::TxSigningKey; +use namada::ledger::wallet::{Wallet, WalletUtils}; use namada::proto::Tx; use namada::types::address::Address; use namada::types::key::*; use namada::types::storage::Epoch; use crate::cli::args; -use namada::ledger::rpc::TxBroadcastData; -use namada::ledger::wallet::Wallet; -use namada::ledger::wallet::WalletUtils; use crate::facade::tendermint_rpc::Client; -use namada::ledger::signing::TxSigningKey; /// Find the public key for the given address and try to load the keypair /// for it from the wallet. Panics if the key cannot be found or loaded. -pub async fn find_keypair( +pub async fn find_keypair< + C: Client + namada::ledger::queries::Client + Sync, + U: WalletUtils, +>( client: &C, wallet: &mut Wallet, addr: &Address, @@ -27,13 +29,17 @@ pub async fn find_keypair( +pub async fn tx_signer< + C: Client + namada::ledger::queries::Client + Sync, + U: WalletUtils, +>( client: &C, wallet: &mut Wallet, args: &args::Tx, mut default: TxSigningKey, ) -> common::SecretKey { - namada::ledger::signing::tx_signer::(client, wallet, args, default).await + namada::ledger::signing::tx_signer::(client, wallet, args, default) + .await } /// Sign a transaction with a given signing key or public key of a given signer. @@ -44,14 +50,18 @@ pub async fn tx_signer( +pub async fn sign_tx< + C: Client + namada::ledger::queries::Client + Sync, + U: WalletUtils, +>( client: &C, wallet: &mut Wallet, tx: Tx, args: &args::Tx, default: TxSigningKey, ) -> TxBroadcastData { - namada::ledger::signing::sign_tx::(client, wallet, tx, args, default).await + namada::ledger::signing::sign_tx::(client, wallet, tx, args, default) + .await } /// Create a wrapper tx from a normal tx. Get the hash of the @@ -65,4 +75,3 @@ pub async fn sign_wrapper( ) -> TxBroadcastData { namada::ledger::signing::sign_wrapper(args, epoch, tx, keypair).await } - diff --git a/apps/src/lib/client/tendermint_rpc_types.rs b/apps/src/lib/client/tendermint_rpc_types.rs index db5dcf0c8e..e01efe5b35 100644 --- a/apps/src/lib/client/tendermint_rpc_types.rs +++ b/apps/src/lib/client/tendermint_rpc_types.rs @@ -6,4 +6,3 @@ use namada::types::address::Address; use serde::Serialize; use crate::cli::safe_exit; - diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index b8956d0cca..1a57e6f313 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -9,10 +9,12 @@ use async_std::io::prelude::WriteExt; use async_std::io::{self}; use borsh::{BorshDeserialize, BorshSerialize}; use masp_proofs::prover::LocalTxProver; - use namada::ledger::governance::storage as gov_storage; -use namada::ledger::masp; -use namada::ledger::masp::ShieldedContext; +use namada::ledger::masp::{ShieldedContext, ShieldedUtils}; +use namada::ledger::rpc::{TxBroadcastData, TxResponse}; +use namada::ledger::signing::TxSigningKey; +use namada::ledger::wallet::{Wallet, WalletUtils}; +use namada::ledger::{masp, tx}; use namada::proto::Tx; use namada::types::address::Address; use namada::types::governance::{ @@ -20,33 +22,28 @@ use namada::types::governance::{ }; use namada::types::key::*; use namada::types::storage::Epoch; -use crate::wallet::gen_validator_keys; +use namada::types::token; use namada::types::transaction::governance::{ InitProposalData, VoteProposalData, }; use namada::types::transaction::InitValidator; -use namada::types::token; use namada::vm; -use namada::ledger::masp::ShieldedUtils; -use namada::ledger::wallet::WalletUtils; use rust_decimal::Decimal; use super::rpc; use crate::cli::context::WalletAddress; use crate::cli::{args, safe_exit, Context}; use crate::client::signing::find_keypair; -use namada::ledger::signing::TxSigningKey; -use namada::ledger::rpc::{TxBroadcastData, TxResponse}; -use namada::ledger::tx; use crate::facade::tendermint_rpc::endpoint::broadcast::tx_sync::Response; use crate::facade::tendermint_rpc::error::Error as RpcError; use crate::facade::tendermint_rpc::{Client, HttpClient}; use crate::node::ledger::tendermint_node; -use namada::ledger::wallet::Wallet; -use crate::wallet::CliWalletUtils; - +use crate::wallet::{gen_validator_keys, CliWalletUtils}; -pub async fn submit_custom( +pub async fn submit_custom< + C: Client + namada::ledger::queries::Client + Sync, + U: WalletUtils, +>( client: &C, wallet: &mut Wallet, args: args::TxCustom, @@ -54,7 +51,10 @@ pub async fn submit_custom(client, wallet, args).await; } -pub async fn submit_update_vp( +pub async fn submit_update_vp< + C: Client + namada::ledger::queries::Client + Sync, + U: WalletUtils, +>( client: &C, wallet: &mut Wallet, args: args::TxUpdateVp, @@ -62,7 +62,10 @@ pub async fn submit_update_vp(client, wallet, args).await; } -pub async fn submit_init_account( +pub async fn submit_init_account< + C: Client + namada::ledger::queries::Client + Sync, + U: WalletUtils, +>( client: &C, wallet: &mut Wallet, args: args::TxInitAccount, @@ -70,7 +73,9 @@ pub async fn submit_init_account(client, wallet, args).await; } -pub async fn submit_init_validator( +pub async fn submit_init_validator< + C: Client + namada::ledger::queries::Client + Sync, +>( client: &C, mut ctx: Context, args::TxInitValidator { @@ -190,12 +195,18 @@ pub async fn submit_init_validator(client, &mut ctx.wallet, &tx_args, tx, TxSigningKey::WalletAddress(source)) - .await.unwrap_or_else(|err| { - eprintln!("Processing transaction failed with {}", err); - safe_exit(1) - }); + let initialized_accounts = process_tx::( + client, + &mut ctx.wallet, + &tx_args, + tx, + TxSigningKey::WalletAddress(source), + ) + .await + .unwrap_or_else(|err| { + eprintln!("Processing transaction failed with {}", err); + safe_exit(1) + }); if !tx_args.dry_run { let (validator_address_alias, validator_address) = match &initialized_accounts[..] { @@ -245,7 +256,8 @@ pub async fn submit_init_validator masp::ShieldedContext { + pub fn new(context_dir: PathBuf) -> masp::ShieldedContext { // Make sure that MASP parameters are downloaded to enable MASP // transaction building and verification later on let params_dir = masp::get_params_dir(); @@ -300,13 +310,18 @@ impl CLIShieldedUtils { } // Finally initialize a shielded context with the supplied directory let utils = Self { context_dir }; - masp::ShieldedContext { utils, ..Default::default() } + masp::ShieldedContext { + utils, + ..Default::default() + } } } impl Default for CLIShieldedUtils { fn default() -> Self { - Self { context_dir: PathBuf::from(FILE_NAME) } + Self { + context_dir: PathBuf::from(FILE_NAME), + } } } @@ -314,8 +329,7 @@ impl masp::ShieldedUtils for CLIShieldedUtils { type C = HttpClient; fn local_tx_prover(&self) -> LocalTxProver { - if let Ok(params_dir) = env::var(masp::ENV_VAR_MASP_PARAMS_DIR) - { + if let Ok(params_dir) = env::var(masp::ENV_VAR_MASP_PARAMS_DIR) { let params_dir = PathBuf::from(params_dir); let spend_path = params_dir.join(masp::SPEND_NAME); let convert_path = params_dir.join(masp::CONVERT_NAME); @@ -371,7 +385,11 @@ impl masp::ShieldedUtils for CLIShieldedUtils { } } -pub async fn submit_transfer>( +pub async fn submit_transfer< + C: Client + namada::ledger::queries::Client + Sync, + V: WalletUtils, + U: ShieldedUtils, +>( client: &C, wallet: &mut Wallet, shielded: &mut ShieldedContext, @@ -380,7 +398,10 @@ pub async fn submit_transfer(client, wallet, shielded, args).await; } -pub async fn submit_ibc_transfer( +pub async fn submit_ibc_transfer< + C: Client + namada::ledger::queries::Client + Sync, + U: WalletUtils, +>( client: &C, wallet: &mut Wallet, args: args::TxIbcTransfer, @@ -388,7 +409,9 @@ pub async fn submit_ibc_transfer(client, wallet, args).await; } -pub async fn submit_init_proposal( +pub async fn submit_init_proposal< + C: Client + namada::ledger::queries::Client + Sync, +>( client: &C, mut ctx: Context, args: args::InitProposal, @@ -399,8 +422,7 @@ pub async fn submit_init_proposal( - client, - &mut ctx.wallet, - &signer, - ) - .await; + let signing_key = + find_keypair::(client, &mut ctx.wallet, &signer) + .await; let offline_proposal = OfflineProposal::new(proposal, signer, &signing_key); let proposal_filename = args @@ -524,12 +543,21 @@ pub async fn submit_init_proposal(client, &mut ctx.wallet, &args.tx, tx, TxSigningKey::WalletAddress(signer)) - .await; + process_tx::( + client, + &mut ctx.wallet, + &args.tx, + tx, + TxSigningKey::WalletAddress(signer), + ) + .await; } } -pub async fn submit_vote_proposal( +pub async fn submit_vote_proposal< + C: Client + namada::ledger::queries::Client + Sync, + U: WalletUtils, +>( client: &C, wallet: &mut Wallet, args: args::VoteProposal, @@ -549,23 +577,15 @@ pub async fn submit_vote_proposal( - client, - wallet, - &signer, - ) - .await; + let signing_key = find_keypair::(client, wallet, &signer).await; let offline_vote = OfflineVote::new( &proposal, args.vote, @@ -591,8 +611,7 @@ pub async fn submit_vote_proposal( +pub async fn submit_reveal_pk< + C: Client + namada::ledger::queries::Client + Sync, + U: WalletUtils, +>( client: &C, wallet: &mut Wallet, args: args::RevealPk, @@ -687,7 +704,10 @@ pub async fn submit_reveal_pk(client, wallet, args).await; } -pub async fn reveal_pk_if_needed( +pub async fn reveal_pk_if_needed< + C: Client + namada::ledger::queries::Client + Sync, + U: WalletUtils, +>( client: &C, wallet: &mut Wallet, public_key: &common::PublicKey, @@ -696,14 +716,19 @@ pub async fn reveal_pk_if_needed(client, wallet, public_key, args).await } -pub async fn has_revealed_pk( +pub async fn has_revealed_pk< + C: Client + namada::ledger::queries::Client + Sync, +>( client: &C, addr: &Address, ) -> bool { tx::has_revealed_pk(client, addr).await } -pub async fn submit_reveal_pk_aux( +pub async fn submit_reveal_pk_aux< + C: Client + namada::ledger::queries::Client + Sync, + U: WalletUtils, +>( client: &C, wallet: &mut Wallet, public_key: &common::PublicKey, @@ -715,7 +740,9 @@ pub async fn submit_reveal_pk_aux( +async fn is_safe_voting_window< + C: Client + namada::ledger::queries::Client + Sync, +>( client: &C, proposal_id: u64, proposal_start_epoch: Epoch, @@ -725,7 +752,9 @@ async fn is_safe_voting_window( +async fn filter_delegations< + C: Client + namada::ledger::queries::Client + Sync, +>( client: &C, delegations: HashSet
, proposal_id: u64, @@ -745,8 +774,10 @@ async fn filter_delegations( ); if let Some(validator_vote) = - rpc::query_storage_value::(client, &vote_key) - .await + rpc::query_storage_value::( + client, &vote_key, + ) + .await { if &validator_vote == delegator_vote { return None; @@ -760,7 +791,10 @@ async fn filter_delegations( delegations.into_iter().flatten().collect() } -pub async fn submit_bond( +pub async fn submit_bond< + C: Client + namada::ledger::queries::Client + Sync, + U: WalletUtils, +>( client: &C, wallet: &mut Wallet, args: args::Bond, @@ -768,7 +802,10 @@ pub async fn submit_bond(client, wallet, args).await; } -pub async fn submit_unbond( +pub async fn submit_unbond< + C: Client + namada::ledger::queries::Client + Sync, + U: WalletUtils, +>( client: &C, wallet: &mut Wallet, args: args::Unbond, @@ -776,7 +813,10 @@ pub async fn submit_unbond(client, wallet, args).await; } -pub async fn submit_withdraw( +pub async fn submit_withdraw< + C: Client + namada::ledger::queries::Client + Sync, + U: WalletUtils, +>( client: &C, wallet: &mut Wallet, args: args::Withdraw, @@ -784,7 +824,10 @@ pub async fn submit_withdraw(client, wallet, args).await; } -pub async fn submit_validator_commission_change( +pub async fn submit_validator_commission_change< + C: Client + namada::ledger::queries::Client + Sync, + U: WalletUtils, +>( client: &C, wallet: &mut Wallet, args: args::TxCommissionRateChange, @@ -794,7 +837,10 @@ pub async fn submit_validator_commission_change( +async fn process_tx< + C: Client + namada::ledger::queries::Client + Sync, + U: WalletUtils, +>( client: &C, wallet: &mut Wallet, args: &args::Tx, diff --git a/apps/src/lib/client/utils.rs b/apps/src/lib/client/utils.rs index 59c30c53cf..0de3f0ea53 100644 --- a/apps/src/lib/client/utils.rs +++ b/apps/src/lib/client/utils.rs @@ -10,6 +10,7 @@ use borsh::BorshSerialize; use flate2::read::GzDecoder; use flate2::write::GzEncoder; use flate2::Compression; +use namada::ledger::wallet::Wallet; use namada::types::address; use namada::types::chain::ChainId; use namada::types::key::*; @@ -20,7 +21,6 @@ use rust_decimal::Decimal; use serde_json::json; use sha2::{Digest, Sha256}; -use crate::wallet::CliWalletUtils; use crate::cli::context::ENV_VAR_WASM_DIR; use crate::cli::{self, args}; use crate::config::genesis::genesis_config::{ @@ -31,8 +31,7 @@ use crate::config::{self, Config, TendermintMode}; use crate::facade::tendermint::node::Id as TendermintNodeId; use crate::facade::tendermint_config::net::Address as TendermintAddress; use crate::node::ledger::tendermint_node; -use crate::wallet::pre_genesis; -use namada::ledger::wallet::Wallet; +use crate::wallet::{pre_genesis, CliWalletUtils}; use crate::wasm_loader; pub const NET_ACCOUNTS_DIR: &str = "setup"; @@ -108,13 +107,12 @@ pub async fn join_network( validator_alias_and_dir.map(|(validator_alias, pre_genesis_dir)| { ( validator_alias, - pre_genesis::load(&pre_genesis_dir) - .unwrap_or_else(|err| { - eprintln!( - "Error loading validator pre-genesis wallet {err}", - ); - cli::safe_exit(1) - }), + pre_genesis::load(&pre_genesis_dir).unwrap_or_else(|err| { + eprintln!( + "Error loading validator pre-genesis wallet {err}", + ); + cli::safe_exit(1) + }), ) }); @@ -550,10 +548,10 @@ pub fn init_network( let validator_keys = crate::wallet::gen_validator_keys( &mut wallet, - Some(protocol_pk.clone()), - SchemeType::Ed25519, - ) - .expect("Generating new validator keys should not fail"); + Some(protocol_pk.clone()), + SchemeType::Ed25519, + ) + .expect("Generating new validator keys should not fail"); let pk = validator_keys.dkg_keypair.as_ref().unwrap().public(); wallet.add_validator_data(address.clone(), validator_keys); pk diff --git a/apps/src/lib/node/ledger/shell/mod.rs b/apps/src/lib/node/ledger/shell/mod.rs index 747ad6894c..eae5964605 100644 --- a/apps/src/lib/node/ledger/shell/mod.rs +++ b/apps/src/lib/node/ledger/shell/mod.rs @@ -49,8 +49,8 @@ use num_derive::{FromPrimitive, ToPrimitive}; use num_traits::{FromPrimitive, ToPrimitive}; use thiserror::Error; use tokio::sync::mpsc::UnboundedSender; -use crate::wallet::CliWalletUtils; +use crate::config; use crate::config::{genesis, TendermintMode}; #[cfg(feature = "abcipp")] use crate::facade::tendermint_proto::abci::response_verify_vote_extension::VerifyStatus; @@ -62,9 +62,9 @@ use crate::facade::tower_abci::{request, response}; use crate::node::ledger::shims::abcipp_shim_types::shim; use crate::node::ledger::shims::abcipp_shim_types::shim::response::TxResult; use crate::node::ledger::{storage, tendermint_node}; +use crate::wallet::CliWalletUtils; #[allow(unused_imports)] use crate::wallet::ValidatorData; -use crate::config; fn key_to_tendermint( pk: &common::PublicKey, @@ -285,11 +285,9 @@ where ); wallet .take_validator_data() - .map(|data| { - ShellMode::Validator { - data: data.clone(), - broadcast_sender, - } + .map(|data| ShellMode::Validator { + data: data.clone(), + broadcast_sender, }) .expect( "Validator data should have been stored in the \ @@ -298,10 +296,12 @@ where } #[cfg(feature = "dev")] { - let validator_keys = crate::wallet::defaults::validator_keys(); + let validator_keys = + crate::wallet::defaults::validator_keys(); ShellMode::Validator { data: crate::wallet::ValidatorData { - address: crate::wallet::defaults::validator_address(), + address: crate::wallet::defaults::validator_address( + ), keys: crate::wallet::ValidatorKeys { protocol_keypair: validator_keys.0, dkg_keypair: Some(validator_keys.1), diff --git a/apps/src/lib/node/ledger/shims/abcipp_shim.rs b/apps/src/lib/node/ledger/shims/abcipp_shim.rs index 0728e007a0..b4421a9b97 100644 --- a/apps/src/lib/node/ledger/shims/abcipp_shim.rs +++ b/apps/src/lib/node/ledger/shims/abcipp_shim.rs @@ -23,10 +23,12 @@ use super::abcipp_shim_types::shim::TxBytes; use super::abcipp_shim_types::shim::{Error, Request, Response}; use crate::config; #[cfg(not(feature = "abcipp"))] -use crate::facade::tendermint_proto::abci::{RequestBeginBlock, ResponseDeliverTx}; -use crate::facade::tower_abci::{BoxError, Request as Req, Response as Resp}; +use crate::facade::tendermint_proto::abci::{ + RequestBeginBlock, ResponseDeliverTx, +}; #[cfg(not(feature = "abcipp"))] use crate::facade::tower_abci::response::DeliverTx; +use crate::facade::tower_abci::{BoxError, Request as Req, Response as Resp}; /// The shim wraps the shell, which implements ABCI++. /// The shim makes a crude translation between the ABCI interface currently used diff --git a/apps/src/lib/wallet/alias.rs b/apps/src/lib/wallet/alias.rs index e69de29bb2..8b13789179 100644 --- a/apps/src/lib/wallet/alias.rs +++ b/apps/src/lib/wallet/alias.rs @@ -0,0 +1 @@ + diff --git a/apps/src/lib/wallet/defaults.rs b/apps/src/lib/wallet/defaults.rs index ed888beee0..643b0dd7cb 100644 --- a/apps/src/lib/wallet/defaults.rs +++ b/apps/src/lib/wallet/defaults.rs @@ -6,12 +6,12 @@ pub use dev::{ christel_address, christel_keypair, daewon_address, daewon_keypair, keys, validator_address, validator_keypair, validator_keys, }; +use namada::ledger::wallet::Alias; use namada::ledger::{eth_bridge, governance, pos}; use namada::types::address::Address; use namada::types::key::*; use crate::config::genesis::genesis_config::GenesisConfig; -use namada::ledger::wallet::Alias; /// The default addresses with their aliases. pub fn addresses_from_genesis(genesis: GenesisConfig) -> Vec<(Alias, Address)> { @@ -71,13 +71,12 @@ pub fn addresses_from_genesis(genesis: GenesisConfig) -> Vec<(Alias, Address)> { #[cfg(feature = "dev")] mod dev { use borsh::BorshDeserialize; + use namada::ledger::wallet::Alias; use namada::ledger::{governance, pos}; use namada::types::address::{self, Address}; use namada::types::key::dkg_session_keys::DkgKeypair; use namada::types::key::*; - use namada::ledger::wallet::Alias; - /// Generate a new protocol signing keypair and DKG session keypair pub fn validator_keys() -> (common::SecretKey, DkgKeypair) { let bytes: [u8; 33] = [ diff --git a/apps/src/lib/wallet/keys.rs b/apps/src/lib/wallet/keys.rs index e69de29bb2..8b13789179 100644 --- a/apps/src/lib/wallet/keys.rs +++ b/apps/src/lib/wallet/keys.rs @@ -0,0 +1 @@ + diff --git a/apps/src/lib/wallet/mod.rs b/apps/src/lib/wallet/mod.rs index 53360adc97..053c74a2ac 100644 --- a/apps/src/lib/wallet/mod.rs +++ b/apps/src/lib/wallet/mod.rs @@ -4,28 +4,28 @@ mod keys; pub mod pre_genesis; mod store; +use std::io::{self, Write}; use std::path::{Path, PathBuf}; use std::{env, fs}; +use namada::ledger::wallet::{ + Alias, ConfirmationResponse, FindKeyError, Wallet, WalletUtils, +}; +pub use namada::ledger::wallet::{ + DecryptionError, StoredKeypair, ValidatorData, ValidatorKeys, +}; use namada::types::key::*; pub use store::wallet_file; -use namada::ledger::wallet::ConfirmationResponse; -pub use namada::ledger::wallet::{DecryptionError, StoredKeypair}; -use namada::ledger::wallet::Wallet; -pub use namada::ledger::wallet::{ValidatorData, ValidatorKeys}; use crate::cli; use crate::config::genesis::genesis_config::GenesisConfig; -use namada::ledger::wallet::{WalletUtils, Alias}; -use std::io::{self, Write}; -use namada::ledger::wallet::FindKeyError; #[derive(Debug)] pub struct CliWalletUtils; impl WalletUtils for CliWalletUtils { type Storage = PathBuf; - + /// Prompt for pssword and confirm it if parameter is false fn new_password_prompt(unsafe_dont_encrypt: bool) -> Option { let password = if unsafe_dont_encrypt { @@ -50,7 +50,8 @@ impl WalletUtils for CliWalletUtils { password } - /// Read the password for encryption from the file/env/stdin with confirmation. + /// Read the password for encryption from the file/env/stdin with + /// confirmation. fn read_and_confirm_pwd(unsafe_dont_encrypt: bool) -> Option { let password = if unsafe_dont_encrypt { println!("Warning: The keypair will NOT be encrypted."); @@ -63,7 +64,8 @@ impl WalletUtils for CliWalletUtils { None } else { Some(Self::read_password( - "To confirm, please enter the same encryption password once more: ", + "To confirm, please enter the same encryption password once \ + more: ", )) }; if to_confirm != password { @@ -73,8 +75,8 @@ impl WalletUtils for CliWalletUtils { password } - /// Read the password for encryption/decryption from the file/env/stdin. Panics - /// if all options are empty/invalid. + /// Read the password for encryption/decryption from the file/env/stdin. + /// Panics if all options are empty/invalid. fn read_password(prompt_msg: &str) -> String { let pwd = match env::var("ANOMA_WALLET_PASSWORD_FILE") { Ok(path) => fs::read_to_string(path) @@ -109,8 +111,8 @@ impl WalletUtils for CliWalletUtils { alias_for: &str, ) -> ConfirmationResponse { print!( - "You're trying to create an alias \"{}\" that already exists for {} \ - in your store.\nWould you like to replace it? \ + "You're trying to create an alias \"{}\" that already exists for \ + {} in your store.\nWould you like to replace it? \ s(k)ip/re(p)lace/re(s)elect: ", alias, alias_for ); @@ -158,10 +160,12 @@ pub fn gen_validator_keys( scheme: SchemeType, ) -> Result { let protocol_keypair = protocol_pk.map(|pk| { - wallet.find_key_by_pkh(&PublicKeyHash::from(&pk)) + wallet + .find_key_by_pkh(&PublicKeyHash::from(&pk)) .ok() .or_else(|| { - wallet.store_mut() + wallet + .store_mut() .validator_data() .take() .map(|data| data.keys.protocol_keypair.clone()) @@ -178,7 +182,10 @@ pub fn gen_validator_keys( } /// Add addresses from a genesis configuration. -pub fn add_genesis_addresses(wallet: &mut Wallet, genesis: GenesisConfig) { +pub fn add_genesis_addresses( + wallet: &mut Wallet, + genesis: GenesisConfig, +) { for (alias, addr) in defaults::addresses_from_genesis(genesis) { wallet.add_address(alias.normalize(), addr); } @@ -195,7 +202,10 @@ pub fn load(store_dir: &Path) -> Option> { eprintln!("Unable to load the wallet: {}", err); cli::safe_exit(1) }); - Some(Wallet::::new(store_dir.to_path_buf(), store)) + Some(Wallet::::new( + store_dir.to_path_buf(), + store, + )) } /// Load a wallet from the store file or create a new wallet without any @@ -221,4 +231,3 @@ pub fn load_or_new_from_genesis( }); Wallet::::new(store_dir.to_path_buf(), store) } - diff --git a/apps/src/lib/wallet/pre_genesis.rs b/apps/src/lib/wallet/pre_genesis.rs index 9984dd182d..0db633ad41 100644 --- a/apps/src/lib/wallet/pre_genesis.rs +++ b/apps/src/lib/wallet/pre_genesis.rs @@ -3,12 +3,12 @@ use std::path::{Path, PathBuf}; use ark_serialize::{Read, Write}; use file_lock::{FileLock, FileOptions}; +use namada::ledger::wallet::pre_genesis::{ + ReadError, ValidatorStore, ValidatorWallet, +}; +use namada::ledger::wallet::{gen_key_to_store, WalletUtils}; use namada::types::key::SchemeType; -use namada::ledger::wallet::pre_genesis::ValidatorWallet; -use namada::ledger::wallet::pre_genesis::ReadError; -use namada::ledger::wallet::pre_genesis::ValidatorStore; -use namada::ledger::wallet::gen_key_to_store; -use namada::ledger::wallet::WalletUtils; + use crate::wallet::store::gen_validator_keys; use crate::wallet::CliWalletUtils; @@ -34,8 +34,7 @@ pub fn gen_and_store( let wallet_dir = wallet_path.parent().unwrap(); fs::create_dir_all(wallet_dir)?; // Write the file - let options = - FileOptions::new().create(true).write(true).truncate(true); + let options = FileOptions::new().create(true).write(true).truncate(true); let mut filelock = FileLock::lock(wallet_path.to_str().unwrap(), true, options)?; filelock.file.write_all(&data)?; @@ -66,17 +65,22 @@ pub fn load(store_dir: &Path) -> Result { || store.consensus_key.is_encrypted() || store.account_key.is_encrypted() { - Some(CliWalletUtils::read_password("Enter decryption password: ")) + Some(CliWalletUtils::read_password( + "Enter decryption password: ", + )) } else { None }; - let account_key = - store.account_key.get::(true, password.clone())?; - let consensus_key = - store.consensus_key.get::(true, password.clone())?; - let tendermint_node_key = - store.tendermint_node_key.get::(true, password)?; + let account_key = store + .account_key + .get::(true, password.clone())?; + let consensus_key = store + .consensus_key + .get::(true, password.clone())?; + let tendermint_node_key = store + .tendermint_node_key + .get::(true, password)?; Ok(ValidatorWallet { store, diff --git a/apps/src/lib/wallet/store.rs b/apps/src/lib/wallet/store.rs index 77436c62fa..80cb28b524 100644 --- a/apps/src/lib/wallet/store.rs +++ b/apps/src/lib/wallet/store.rs @@ -6,13 +6,12 @@ use std::path::{Path, PathBuf}; use ark_std::rand::prelude::*; use ark_std::rand::SeedableRng; use file_lock::{FileLock, FileOptions}; +use namada::ledger::wallet::{gen_sk, Store, StoredKeypair, ValidatorKeys}; use namada::types::address::Address; use namada::types::key::*; use namada::types::transaction::EllipticCurve; use thiserror::Error; -use namada::ledger::wallet::Store; -use namada::ledger::wallet::{StoredKeypair, ValidatorKeys, gen_sk}; use crate::config::genesis::genesis_config::GenesisConfig; use crate::wallet::CliWalletUtils; @@ -42,8 +41,7 @@ pub fn save(store: &Store, store_dir: &Path) -> std::io::Result<()> { let wallet_dir = wallet_path.parent().unwrap(); fs::create_dir_all(wallet_dir)?; // Write the file - let options = - FileOptions::new().create(true).write(true).truncate(true); + let options = FileOptions::new().create(true).write(true).truncate(true); let mut filelock = FileLock::lock(wallet_path.to_str().unwrap(), true, options)?; filelock.file.write_all(&data) @@ -53,9 +51,8 @@ pub fn save(store: &Store, store_dir: &Path) -> std::io::Result<()> { pub fn load_or_new(store_dir: &Path) -> Result { load(store_dir).or_else(|_| { let store = Store::default(); - save(&store, store_dir).map_err(|err| { - LoadStoreError::StoreNewWallet(err.to_string()) - })?; + save(&store, store_dir) + .map_err(|err| LoadStoreError::StoreNewWallet(err.to_string()))?; Ok(store) }) } @@ -75,9 +72,8 @@ pub fn load_or_new_from_genesis( let _ = genesis_cfg; new() }; - save(&store, store_dir).map_err(|err| { - LoadStoreError::StoreNewWallet(err.to_string()) - })?; + save(&store, store_dir) + .map_err(|err| LoadStoreError::StoreNewWallet(err.to_string()))?; Ok(store) }) } @@ -148,8 +144,7 @@ pub fn gen_validator_keys( protocol_keypair: Option, scheme: SchemeType, ) -> ValidatorKeys { - let protocol_keypair = - protocol_keypair.unwrap_or_else(|| gen_sk(scheme)); + let protocol_keypair = protocol_keypair.unwrap_or_else(|| gen_sk(scheme)); let dkg_keypair = ferveo_common::Keypair::::new( &mut StdRng::from_entropy(), ); @@ -166,8 +161,7 @@ mod test_wallet { #[test] fn test_toml_roundtrip_ed25519() { let mut store = new(); - let validator_keys = - gen_validator_keys(None, SchemeType::Ed25519); + let validator_keys = gen_validator_keys(None, SchemeType::Ed25519); store.add_validator_data( Address::decode("atest1v4ehgw36x3prswzxggunzv6pxqmnvdj9xvcyzvpsggeyvs3cg9qnywf589qnwvfsg5erg3fkl09rg5").unwrap(), validator_keys @@ -179,8 +173,7 @@ mod test_wallet { #[test] fn test_toml_roundtrip_secp256k1() { let mut store = new(); - let validator_keys = - gen_validator_keys(None, SchemeType::Secp256k1); + let validator_keys = gen_validator_keys(None, SchemeType::Secp256k1); store.add_validator_data( Address::decode("atest1v4ehgw36x3prswzxggunzv6pxqmnvdj9xvcyzvpsggeyvs3cg9qnywf589qnwvfsg5erg3fkl09rg5").unwrap(), validator_keys diff --git a/shared/src/ledger/args.rs b/shared/src/ledger/args.rs index 79476dc7eb..1111a8d7b9 100644 --- a/shared/src/ledger/args.rs +++ b/shared/src/ledger/args.rs @@ -1,14 +1,12 @@ +use rust_decimal::Decimal; + +use crate::ibc::core::ics24_host::identifier::{ChannelId, PortId}; use crate::types::address::Address; -use crate::types::transaction::GasLimit; +use crate::types::key::{common, SchemeType}; use crate::types::masp::MaspValue; use crate::types::storage::Epoch; -use crate::types::token; -use crate::types::storage; -use crate::types::key::common; -use rust_decimal::Decimal; -use crate::types::key::SchemeType; -use crate::ibc::core::ics24_host::identifier::ChannelId; -use crate::ibc::core::ics24_host::identifier::PortId; +use crate::types::transaction::GasLimit; +use crate::types::{storage, token}; /// Abstraction of types being used in Namada pub trait NamadaTypes: Clone + std::fmt::Debug { @@ -40,24 +38,15 @@ pub struct SdkTypes; impl NamadaTypes for SdkTypes { type Address = Address; - - type NativeAddress = Address; - - type Keypair = namada_core::types::key::common::SecretKey; - - type TendermintAddress = (); - - type ViewingKey = namada_core::types::masp::ExtendedViewingKey; - type BalanceOwner = namada_core::types::masp::BalanceOwner; - + type Data = Vec; + type Keypair = namada_core::types::key::common::SecretKey; + type NativeAddress = Address; type PublicKey = namada_core::types::key::common::PublicKey; - + type TendermintAddress = (); type TransferSource = namada_core::types::masp::TransferSource; - type TransferTarget = namada_core::types::masp::TransferTarget; - - type Data = Vec; + type ViewingKey = namada_core::types::masp::ExtendedViewingKey; } /// Common query arguments diff --git a/shared/src/ledger/masp.rs b/shared/src/ledger/masp.rs index a457eee009..88e5cc5422 100644 --- a/shared/src/ledger/masp.rs +++ b/shared/src/ledger/masp.rs @@ -1,32 +1,24 @@ //! MASP verification wrappers. +use std::collections::hash_map::Entry; +use std::collections::{BTreeMap, HashMap, HashSet}; use std::env; +use std::fmt::Debug; use std::fs::File; +#[cfg(feature = "masp-tx-gen")] +use std::io::Read; use std::ops::Deref; use std::path::PathBuf; +use async_trait::async_trait; use bellman::groth16::{prepare_verifying_key, PreparedVerifyingKey}; use bls12_381::Bls12; +// use async_std::io::prelude::WriteExt; +// use async_std::io::{self}; +use borsh::{BorshDeserialize, BorshSerialize}; +use itertools::Either; use masp_primitives::asset_type::AssetType; use masp_primitives::consensus::BranchId::Sapling; -use masp_primitives::redjubjub::PublicKey; -use masp_primitives::transaction::components::{ - ConvertDescription, OutputDescription, SpendDescription, -}; -use masp_primitives::transaction::{ - signature_hash_data, Transaction, SIGHASH_ALL, -}; -use masp_proofs::sapling::SaplingVerificationContext; - -use std::collections::hash_map::Entry; -use std::collections::{BTreeMap, HashMap, HashSet}; -use std::fmt::Debug; -#[cfg(feature = "masp-tx-gen")] -use std::io::Read; - -//use async_std::io::prelude::WriteExt; -//use async_std::io::{self}; -use borsh::{BorshDeserialize, BorshSerialize}; use masp_primitives::consensus::{BranchId, TestNetwork}; use masp_primitives::convert::AllowedConversion; use masp_primitives::ff::PrimeField; @@ -39,39 +31,46 @@ use masp_primitives::merkle_tree::{ }; use masp_primitives::note_encryption::*; use masp_primitives::primitives::{Diversifier, Note, ViewingKey}; +use masp_primitives::redjubjub::PublicKey; use masp_primitives::sapling::Node; #[cfg(feature = "masp-tx-gen")] use masp_primitives::transaction::builder::{self, secp256k1, *}; -use masp_primitives::transaction::components::Amount; +use masp_primitives::transaction::components::{ + Amount, ConvertDescription, OutputDescription, SpendDescription, +}; #[cfg(feature = "masp-tx-gen")] use masp_primitives::transaction::components::{OutPoint, TxOut}; +use masp_primitives::transaction::{ + signature_hash_data, Transaction, SIGHASH_ALL, +}; use masp_primitives::zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}; use masp_proofs::prover::LocalTxProver; +use masp_proofs::sapling::SaplingVerificationContext; +use namada_core::types::transaction::AffineCurve; +#[cfg(feature = "masp-tx-gen")] +use rand_core::{CryptoRng, OsRng, RngCore}; +#[cfg(feature = "masp-tx-gen")] +use sha2::Digest; +use tendermint_rpc::Client; + +use crate::ledger::rpc; +use crate::proto::{SignedTxData, Tx}; +use crate::tendermint_rpc::query::Query; +use crate::tendermint_rpc::Order; use crate::types::address::{masp, Address}; -use crate::types::masp::{PaymentAddress, BalanceOwner}; +use crate::types::masp::{BalanceOwner, ExtendedViewingKey, PaymentAddress}; #[cfg(feature = "masp-tx-gen")] use crate::types::masp::{TransferSource, TransferTarget}; use crate::types::storage::{ - BlockHeight, Epoch, Key, KeySeg, TxIndex, BlockResults, + BlockHeight, BlockResults, Epoch, Key, KeySeg, TxIndex, }; use crate::types::token::{ Transfer, HEAD_TX_KEY, PIN_KEY_PREFIX, TX_KEY_PREFIX, }; +use crate::types::transaction::{ + process_tx, DecryptedTx, EllipticCurve, PairingEngine, TxType, WrapperTx, +}; use crate::types::{storage, token}; -#[cfg(feature = "masp-tx-gen")] -use rand_core::{CryptoRng, OsRng, RngCore}; -#[cfg(feature = "masp-tx-gen")] -use sha2::Digest; -use async_trait::async_trait; -use crate::proto::{Tx, SignedTxData}; -use crate::types::transaction::{DecryptedTx, EllipticCurve, WrapperTx, TxType, PairingEngine, process_tx}; -use crate::tendermint_rpc::query::Query; -use crate::tendermint_rpc::Order; -use namada_core::types::transaction::AffineCurve; -use tendermint_rpc::Client; -use crate::types::masp::ExtendedViewingKey; -use itertools::Either; -use crate::ledger::rpc; /// Env var to point to a dir with MASP parameters. When not specified, /// the default OS specific path is used. @@ -259,9 +258,13 @@ pub fn get_params_dir() -> PathBuf { /// Abstracts platform specific details away from the logic of shielded pool /// operations. #[async_trait] -pub trait ShieldedUtils : Sized + BorshDeserialize + BorshSerialize + Default + Clone { +pub trait ShieldedUtils: + Sized + BorshDeserialize + BorshSerialize + Default + Clone +{ /// The type of the Tendermint client to make queries with - type C: crate::ledger::queries::Client + tendermint_rpc::Client + std::marker::Sync; + type C: crate::ledger::queries::Client + + tendermint_rpc::Client + + std::marker::Sync; /// Get a MASP transaction prover fn local_tx_prover(&self) -> LocalTxProver; @@ -470,7 +473,10 @@ impl ShieldedContext { txs = Self::fetch_shielded_transfers(client, 0).await; tx_iter = txs.iter(); // Do this by constructing a shielding context only for unknown keys - let mut tx_ctx = Self { utils: self.utils.clone(), ..Default::default() }; + let mut tx_ctx = Self { + utils: self.utils.clone(), + ..Default::default() + }; for vk in unknown_keys { tx_ctx.pos_map.entry(vk).or_insert_with(HashSet::new); } @@ -487,9 +493,7 @@ impl ShieldedContext { self.merge(tx_ctx); } else { // Load only transactions accepted from last_txid until this point - txs = - Self::fetch_shielded_transfers(client, self.last_txidx) - .await; + txs = Self::fetch_shielded_transfers(client, self.last_txidx).await; tx_iter = txs.iter(); } // Now that we possess the unspent notes corresponding to both old and @@ -515,9 +519,10 @@ impl ShieldedContext { .push(&HEAD_TX_KEY.to_owned()) .expect("Cannot obtain a storage key"); // Query for the index of the last accepted transaction - let head_txidx = rpc::query_storage_value::(client, &head_tx_key) - .await - .unwrap_or(0); + let head_txidx = + rpc::query_storage_value::(client, &head_tx_key) + .await + .unwrap_or(0); let mut shielded_txs = BTreeMap::new(); // Fetch all the transactions we do not have yet for i in last_txidx..head_txidx { @@ -527,10 +532,10 @@ impl ShieldedContext { .expect("Cannot obtain a storage key"); // Obtain the current transaction let (tx_epoch, tx_height, tx_index, current_tx) = - rpc::query_storage_value::( - client, - ¤t_tx_key, - ) + rpc::query_storage_value::< + U::C, + (Epoch, BlockHeight, TxIndex, Transfer), + >(client, ¤t_tx_key) .await .unwrap(); // Collect the current transaction @@ -977,13 +982,12 @@ impl ShieldedContext { .push(&(TX_KEY_PREFIX.to_owned() + &txidx.to_string())) .expect("Cannot obtain a storage key"); // Obtain the pointed to transaction - let (tx_epoch, _tx_height, _tx_index, tx) = - rpc::query_storage_value::( - client, - &tx_key, - ) - .await - .expect("Ill-formed epoch, transaction pair"); + let (tx_epoch, _tx_height, _tx_index, tx) = rpc::query_storage_value::< + U::C, + (Epoch, BlockHeight, TxIndex, Transfer), + >(client, &tx_key) + .await + .expect("Ill-formed epoch, transaction pair"); // Accumulate the combined output note value into this Amount let mut val_acc = Amount::zero(); let tx = tx @@ -1027,8 +1031,7 @@ impl ShieldedContext { ) -> Result<(Amount, Epoch), PinnedBalanceError> { // Obtain the balance that will be exchanged let (amt, ep) = - Self::compute_pinned_balance(client, owner, viewing_key) - .await?; + Self::compute_pinned_balance(client, owner, viewing_key).await?; // Finally, exchange the balance to the transaction's epoch Ok(( self.compute_exchanged_amount(client, amt, ep, HashMap::new()) @@ -1050,8 +1053,7 @@ impl ShieldedContext { let mut res = Amount::zero(); for (asset_type, val) in amt.components() { // Decode the asset type - let decoded = - self.decode_asset_type(client, *asset_type).await; + let decoded = self.decode_asset_type(client, *asset_type).await; // Only assets with the target timestamp count match decoded { Some((addr, epoch)) if epoch == target_epoch => { @@ -1073,8 +1075,7 @@ impl ShieldedContext { let mut res = Amount::zero(); for (asset_type, val) in amt.components() { // Decode the asset type - let decoded = - self.decode_asset_type(client, *asset_type).await; + let decoded = self.decode_asset_type(client, *asset_type).await; // Only assets with the target timestamp count if let Some((addr, epoch)) = decoded { res += &Amount::from_pair((addr, epoch), *val).unwrap() @@ -1083,13 +1084,13 @@ impl ShieldedContext { res } - /// Make shielded components to embed within a Transfer object. If no shielded - /// payment address nor spending key is specified, then no shielded components - /// are produced. Otherwise a transaction containing nullifiers and/or note - /// commitments are produced. Dummy transparent UTXOs are sometimes used to make - /// transactions balanced, but it is understood that transparent account changes - /// are effected only by the amounts and signatures specified by the containing - /// Transfer object. + /// Make shielded components to embed within a Transfer object. If no + /// shielded payment address nor spending key is specified, then no + /// shielded components are produced. Otherwise a transaction containing + /// nullifiers and/or note commitments are produced. Dummy transparent + /// UTXOs are sometimes used to make transactions balanced, but it is + /// understood that transparent account changes are effected only by the + /// amounts and signatures specified by the containing Transfer object. #[cfg(feature = "masp-tx-gen")] pub async fn gen_shielded_transfer( &mut self, @@ -1101,7 +1102,8 @@ impl ShieldedContext { fee_amount: token::Amount, fee_token: Address, shielded_gas: bool, - ) -> Result, builder::Error> { + ) -> Result, builder::Error> + { // No shielded components are needed when neither source nor destination // are shielded let spending_key = source.spending_key(); @@ -1114,13 +1116,13 @@ impl ShieldedContext { let spending_keys: Vec<_> = spending_key.into_iter().collect(); // Load the current shielded context given the spending key we possess let _ = self.load(); - self.fetch(client, &spending_keys, &[]) - .await; + self.fetch(client, &spending_keys, &[]).await; // Save the update state so that future fetches can be short-circuited let _ = self.save(); // Determine epoch in which to submit potential shielded transaction let epoch = rpc::query_epoch(client).await; - // Context required for storing which notes are in the source's possesion + // Context required for storing which notes are in the source's + // possesion let consensus_branch_id = BranchId::Sapling; let amt: u64 = args_amount.into(); let memo: Option = None; @@ -1137,11 +1139,10 @@ impl ShieldedContext { if let Some(sk) = spending_key { // Transaction fees need to match the amount in the wrapper Transfer // when MASP source is used - let (_, fee) = - convert_amount(epoch, &fee_token, fee_amount); + let (_, fee) = convert_amount(epoch, &fee_token, fee_amount); builder.set_fee(fee.clone())?; - // If the gas is coming from the shielded pool, then our shielded inputs - // must also cover the gas fee + // If the gas is coming from the shielded pool, then our shielded + // inputs must also cover the gas fee let required_amt = if shielded_gas { amount + fee } else { amount }; // Locate unspent notes that can help us meet the transaction amount let (_, unspent_notes, used_convs) = self @@ -1154,7 +1155,12 @@ impl ShieldedContext { .await; // Commit the notes found to our transaction for (diversifier, note, merkle_path) in unspent_notes { - builder.add_sapling_spend(sk, diversifier, note, merkle_path)?; + builder.add_sapling_spend( + sk, + diversifier, + note, + merkle_path, + )?; } // Commit the conversion notes used during summation for (conv, wit, value) in used_convs.values() { @@ -1170,14 +1176,16 @@ impl ShieldedContext { // No transfer fees come from the shielded transaction for non-MASP // sources builder.set_fee(Amount::zero())?; - // We add a dummy UTXO to our transaction, but only the source of the - // parent Transfer object is used to validate fund availability - let secp_sk = - secp256k1::SecretKey::from_slice(&[0xcd; 32]).expect("secret key"); - let secp_ctx = secp256k1::Secp256k1::::gen_new(); + // We add a dummy UTXO to our transaction, but only the source of + // the parent Transfer object is used to validate fund + // availability + let secp_sk = secp256k1::SecretKey::from_slice(&[0xcd; 32]) + .expect("secret key"); + let secp_ctx = + secp256k1::Secp256k1::::gen_new(); let secp_pk = secp256k1::PublicKey::from_secret_key(&secp_ctx, &secp_sk) - .serialize(); + .serialize(); let hash = ripemd160::Ripemd160::digest(&sha2::Sha256::digest(&secp_pk)); let script = TransparentAddress::PublicKey(hash.into()).script(); @@ -1205,8 +1213,8 @@ impl ShieldedContext { )?; } else { epoch_sensitive = false; - // Embed the transparent target address into the shielded transaction so - // that it can be signed + // Embed the transparent target address into the shielded + // transaction so that it can be signed let target_enc = target .address() .expect("target address should be transparent") @@ -1228,10 +1236,12 @@ impl ShieldedContext { if epoch_sensitive { let new_epoch = rpc::query_epoch(client).await; - // If epoch has changed, recalculate shielded outputs to match new epoch + // If epoch has changed, recalculate shielded outputs to match new + // epoch if new_epoch != epoch { // Hack: build new shielded transfer with updated outputs - let mut replay_builder = Builder::::new(0u32); + let mut replay_builder = + Builder::::new(0u32); replay_builder.set_fee(Amount::zero())?; let ovk_opt = spending_key.map(|x| x.expsk.ovk); let (new_asset_type, _) = @@ -1250,10 +1260,12 @@ impl ShieldedContext { secp256k1::Secp256k1::::gen_new(); let secp_pk = secp256k1::PublicKey::from_secret_key(&secp_ctx, &secp_sk) - .serialize(); - let hash = - ripemd160::Ripemd160::digest(&sha2::Sha256::digest(&secp_pk)); - let script = TransparentAddress::PublicKey(hash.into()).script(); + .serialize(); + let hash = ripemd160::Ripemd160::digest(&sha2::Sha256::digest( + &secp_pk, + )); + let script = + TransparentAddress::PublicKey(hash.into()).script(); replay_builder.add_transparent_input( secp_sk, OutPoint::new([0u8; 32], 0), @@ -1281,16 +1293,18 @@ impl ShieldedContext { /// Obtain the known effects of all accepted shielded and transparent /// transactions. If an owner is specified, then restrict the set to only - /// transactions crediting/debiting the given owner. If token is specified, then - /// restrict set to only transactions involving the given token. + /// 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( &mut self, client: &U::C, query_owner: &Either>, query_token: &Option
, viewing_keys: &HashMap, - ) -> BTreeMap<(BlockHeight, TxIndex), (Epoch, TransferDelta, TransactionDelta)> - { + ) -> BTreeMap< + (BlockHeight, TxIndex), + (Epoch, TransferDelta, TransactionDelta), + > { const TXS_PER_PAGE: u8 = 100; let _ = self.load(); let vks = viewing_keys; @@ -1311,7 +1325,8 @@ impl ShieldedContext { // MASP objects are dealt with outside of tx_search Either::Left(BalanceOwner::FullViewingKey(_viewing_key)) => vec![], Either::Left(BalanceOwner::PaymentAddress(_owner)) => vec![], - // Unspecified owner means all known addresses are considered relevant + // Unspecified owner means all known addresses are considered + // relevant Either::Right(addrs) => addrs.clone(), }; // Find all transactions to or from the relevant address set @@ -1321,7 +1336,8 @@ impl ShieldedContext { let mut tx_query = Query::eq(prop, addr.encode()); // Elaborate the query if requested by the user if let Some(token) = &query_token { - tx_query = tx_query.and_eq("transfer.token", token.encode()); + tx_query = + tx_query.and_eq("transfer.token", token.encode()); } for page in 1.. { let txs = &client @@ -1338,12 +1354,12 @@ impl ShieldedContext { for response_tx in txs { let height = BlockHeight(response_tx.height.value()); let idx = TxIndex(response_tx.index); - // Only process yet unprocessed transactions which have been - // accepted by node VPs + // Only process yet unprocessed transactions which have + // been accepted by node VPs let should_process = !transfers .contains_key(&(height, idx)) && block_results[u64::from(height) as usize] - .is_accepted(idx.0 as usize); + .is_accepted(idx.0 as usize); if !should_process { continue; } @@ -1353,10 +1369,11 @@ impl ShieldedContext { let mut transfer = None; extract_payload(tx, &mut wrapper, &mut transfer); // Epoch data is not needed for transparent transactions - let epoch = wrapper.map(|x| x.epoch).unwrap_or_default(); + let epoch = + wrapper.map(|x| x.epoch).unwrap_or_default(); if let Some(transfer) = transfer { - // Skip MASP addresses as they are already handled by - // ShieldedContext + // Skip MASP addresses as they are already handled + // by ShieldedContext if transfer.source == masp() || transfer.target == masp() { @@ -1369,13 +1386,14 @@ impl ShieldedContext { transfer.token.clone(), u64::from(transfer.amount), ) - .expect("invalid value for amount"); + .expect("invalid value for amount"); delta.insert( transfer.source, Amount::zero() - &tfer_delta, ); delta.insert(transfer.target, tfer_delta); - // No shielded accounts are affected by this Transfer + // No shielded accounts are affected by this + // Transfer transfers.insert( (height, idx), (epoch, delta, TransactionDelta::new()), diff --git a/shared/src/ledger/mod.rs b/shared/src/ledger/mod.rs index 36ad2dd022..e7ea6e403b 100644 --- a/shared/src/ledger/mod.rs +++ b/shared/src/ledger/mod.rs @@ -1,5 +1,6 @@ //! The ledger modules +pub mod args; pub mod eth_bridge; pub mod events; pub mod ibc; @@ -9,13 +10,12 @@ pub mod pos; #[cfg(all(feature = "wasm-runtime", feature = "ferveo-tpke"))] pub mod protocol; pub mod queries; +pub mod rpc; +pub mod signing; pub mod storage; +pub mod tx; pub mod vp_host_fns; pub mod wallet; -pub mod args; -pub mod rpc; -pub mod tx; -pub mod signing; pub use namada_core::ledger::{ gas, governance, parameters, storage_api, tx_env, vp_env, diff --git a/shared/src/ledger/queries/shell.rs b/shared/src/ledger/queries/shell.rs index a53107b9f1..beb10fcc24 100644 --- a/shared/src/ledger/queries/shell.rs +++ b/shared/src/ledger/queries/shell.rs @@ -111,11 +111,11 @@ where let mut results = vec![BlockResults::default(); ctx.storage.block.height.0 as usize + 1]; iter.for_each(|(key, value, _gas)| { - let key = u64::parse(key) - .expect("expected integer for block height"); + let key = u64::parse(key).expect("expected integer for block height"); let value = BlockResults::try_from_slice(&value) .expect("expected BlockResults bytes"); - let idx: usize = key.try_into() + let idx: usize = key + .try_into() .expect("expected block height to fit into usize"); results[idx] = value; }); diff --git a/shared/src/ledger/rpc.rs b/shared/src/ledger/rpc.rs index 44c8f7a607..0d4447510d 100644 --- a/shared/src/ledger/rpc.rs +++ b/shared/src/ledger/rpc.rs @@ -1,48 +1,42 @@ -use crate::tendermint_rpc::Client; -use crate::types::storage::Epoch; -use crate::ledger::queries::RPC; -use crate::types::storage::BlockResults; -use std::collections::HashMap; -use crate::types::token; -use namada_core::types::address::Address; +use std::collections::{HashMap, HashSet}; + use borsh::BorshDeserialize; +use itertools::Itertools; use masp_primitives::asset_type::AssetType; use masp_primitives::merkle_tree::MerklePath; -use crate::types::storage::{ - BlockHeight, PrefixValue, -}; -use crate::tendermint::merkle::proof::Proof; -use crate::ledger::pos::{ - self, BondId, Bonds, Slash, -}; -use crate::types::storage; use masp_primitives::sapling::Node; -use crate::types::token::balance_key; -use crate::types::key::*; -use crate::ledger::events::Event; -use crate::types::hash::Hash; -use crate::tendermint_rpc::query::Query; -use crate::proto::Tx; +use namada_core::types::address::Address; use serde::Serialize; -use crate::tendermint_rpc::error::Error as TError; -use crate::tendermint_rpc::Order; -use crate::types::governance::VotePower; -use crate::types::governance::ProposalVote; -use crate::ledger::native_vp::governance::utils::Votes; -use crate::ledger::governance::storage as gov_storage; -use std::collections::HashSet; +use tokio::time::{Duration, Instant}; + +use crate::ledger::events::Event; use crate::ledger::governance::parameters::GovParams; -use crate::types::governance::ProposalResult; -use crate::types::governance::TallyResult; -use itertools::Itertools; +use crate::ledger::governance::storage as gov_storage; +use crate::ledger::native_vp::governance::utils::Votes; use crate::ledger::pos::types::decimal_mult_u64; -use tokio::time::{Duration, Instant}; +use crate::ledger::pos::{self, BondId, Bonds, Slash}; +use crate::ledger::queries::RPC; +use crate::proto::Tx; +use crate::tendermint::merkle::proof::Proof; +use crate::tendermint_rpc::error::Error as TError; +use crate::tendermint_rpc::query::Query; +use crate::tendermint_rpc::{Client, Order}; +use crate::types::governance::{ + ProposalResult, ProposalVote, TallyResult, VotePower, +}; +use crate::types::hash::Hash; +use crate::types::key::*; +use crate::types::storage::{BlockHeight, BlockResults, Epoch, PrefixValue}; +use crate::types::token::balance_key; +use crate::types::{storage, token}; /// Query the status of a given transaction. /// /// If a response is not delivered until `deadline`, we exit the cli with an /// error. -pub async fn query_tx_status( +pub async fn query_tx_status< + C: Client + crate::ledger::queries::Client + Sync, +>( client: &C, status: TxEventQuery<'_>, deadline: Instant, @@ -69,7 +63,7 @@ pub async fn query_tx_status( let maybe_event = match query_tx_events(client, status).await { Ok(response) => response, Err(err) => { - //tracing::debug!(%err, "ABCI query failed"); + // tracing::debug!(%err, "ABCI query failed"); sleep_update(status, &mut backoff).await; continue; } @@ -89,7 +83,9 @@ pub async fn query_tx_status( } /// Query the epoch of the last committed block -pub async fn query_epoch(client: &C) -> Epoch { +pub async fn query_epoch( + client: &C, +) -> Epoch { let epoch = unwrap_client_response::(RPC.shell().epoch(client).await); println!("Last committed epoch: {}", epoch); epoch @@ -110,19 +106,27 @@ pub async fn query_block( } /// A helper to unwrap client's response. Will shut down process on error. -fn unwrap_client_response(response: Result) -> T { +fn unwrap_client_response( + response: Result, +) -> T { response.unwrap_or_else(|err| { panic!("Error in the query"); }) } /// Query the results of the last committed block -pub async fn query_results(client: &C) -> Vec { +pub async fn query_results< + C: Client + crate::ledger::queries::Client + Sync, +>( + client: &C, +) -> Vec { unwrap_client_response::(RPC.shell().read_results(client).await) } /// Query token amount of owner. -pub async fn get_token_balance( +pub async fn get_token_balance< + C: Client + crate::ledger::queries::Client + Sync, +>( client: &C, token: &Address, owner: &Address, @@ -132,7 +136,9 @@ pub async fn get_token_balance( +pub async fn get_public_key< + C: Client + crate::ledger::queries::Client + Sync, +>( client: &C, address: &Address, ) -> Option { @@ -145,7 +151,9 @@ pub async fn is_validator( client: &C, address: &Address, ) -> bool { - unwrap_client_response::(RPC.vp().pos().is_validator(client, address).await) + unwrap_client_response::( + RPC.vp().pos().is_validator(client, address).await, + ) } /// Check if a given address is a known delegator @@ -159,7 +167,9 @@ pub async fn is_delegator( bonds.is_some() && bonds.unwrap().count() > 0 } -pub async fn is_delegator_at( +pub async fn is_delegator_at< + C: Client + crate::ledger::queries::Client + Sync, +>( client: &C, address: &Address, epoch: Epoch, @@ -176,7 +186,9 @@ pub async fn is_delegator_at( /// Check if the address exists on chain. Established address exists if it has a /// stored validity predicate. Implicit and internal addresses always return /// true. -pub async fn known_address( +pub async fn known_address< + C: Client + crate::ledger::queries::Client + Sync, +>( client: &C, address: &Address, ) -> bool { @@ -191,7 +203,9 @@ pub async fn known_address( } /// Query a conversion. -pub async fn query_conversion( +pub async fn query_conversion< + C: Client + crate::ledger::queries::Client + Sync, +>( client: &C, asset_type: AssetType, ) -> Option<( @@ -206,7 +220,10 @@ pub async fn query_conversion } /// Query a storage value and decode it with [`BorshDeserialize`]. -pub async fn query_storage_value( +pub async fn query_storage_value< + C: Client + crate::ledger::queries::Client + Sync, + T, +>( client: &C, key: &storage::Key, ) -> Option @@ -243,7 +260,9 @@ where } /// Query a storage value and the proof without decoding. -pub async fn query_storage_value_bytes( +pub async fn query_storage_value_bytes< + C: Client + crate::ledger::queries::Client + Sync, +>( client: &C, key: &storage::Key, height: Option, @@ -265,7 +284,10 @@ pub async fn query_storage_value_bytes( +pub async fn query_storage_prefix< + C: Client + crate::ledger::queries::Client + Sync, + T, +>( client: &C, key: &storage::Key, ) -> Option> @@ -298,11 +320,15 @@ where } /// Query to check if the given storage key exists. -pub async fn query_has_storage_key( +pub async fn query_has_storage_key< + C: Client + crate::ledger::queries::Client + Sync, +>( client: &C, key: &storage::Key, ) -> bool { - unwrap_client_response::(RPC.shell().storage_has_key(client, key).await) + unwrap_client_response::( + RPC.shell().storage_has_key(client, key).await, + ) } /// Represents a query for an event pertaining to the specified transaction @@ -346,10 +372,15 @@ impl<'a> From> for Query { /// Call the corresponding `tx_event_query` RPC method, to fetch /// the current status of a transation. -pub async fn query_tx_events( +pub async fn query_tx_events< + C: Client + crate::ledger::queries::Client + Sync, +>( client: &C, tx_event_query: TxEventQuery<'_>, -) -> std::result::Result, ::Error> { +) -> std::result::Result< + Option, + ::Error, +> { let tx_hash: Hash = tx_event_query.tx_hash().try_into().unwrap(); match tx_event_query { TxEventQuery::Accepted(_) => RPC @@ -370,7 +401,10 @@ pub async fn query_tx_events( } /// Dry run a transaction -pub async fn dry_run_tx(client: &C, tx_bytes: Vec) -> namada_core::types::transaction::TxResult { +pub async fn dry_run_tx( + client: &C, + tx_bytes: Vec, +) -> namada_core::types::transaction::TxResult { let (data, height, prove) = (Some(tx_bytes), None, false); unwrap_client_response::( RPC.shell().dry_run_tx(client, data, height, prove).await, @@ -540,7 +574,9 @@ pub async fn query_tx_response( Ok(result) } -pub async fn get_proposal_votes( +pub async fn get_proposal_votes< + C: Client + crate::ledger::queries::Client + Sync, +>( client: &C, epoch: Epoch, proposal_id: u64, @@ -607,7 +643,9 @@ pub async fn get_proposal_votes( +pub async fn get_all_validators< + C: Client + crate::ledger::queries::Client + Sync, +>( client: &C, epoch: Epoch, ) -> HashSet
{ @@ -619,7 +657,9 @@ pub async fn get_all_validators( +pub async fn get_total_staked_tokens< + C: Client + crate::ledger::queries::Client + Sync, +>( client: &C, epoch: Epoch, ) -> token::Amount { @@ -628,7 +668,9 @@ pub async fn get_total_staked_tokens( +pub async fn get_validator_stake< + C: Client + crate::ledger::queries::Client + Sync, +>( client: &C, epoch: Epoch, validator: &Address, @@ -641,14 +683,22 @@ pub async fn get_validator_stake( +pub async fn get_delegators_delegation< + C: Client + crate::ledger::queries::Client + Sync, +>( client: &C, address: &Address, ) -> HashSet
{ - unwrap_client_response::(RPC.vp().pos().delegations(client, address).await) + unwrap_client_response::( + RPC.vp().pos().delegations(client, address).await, + ) } -pub async fn get_governance_parameters(client: &C) -> GovParams { +pub async fn get_governance_parameters< + C: Client + crate::ledger::queries::Client + Sync, +>( + client: &C, +) -> GovParams { use crate::types::token::Amount; let key = gov_storage::get_max_proposal_code_size_key(); let max_proposal_code_size = query_storage_value::(client, &key) @@ -691,7 +741,9 @@ pub async fn get_governance_parameters( +pub async fn compute_tally< + C: Client + crate::ledger::queries::Client + Sync, +>( client: &C, epoch: Epoch, votes: Votes, @@ -745,7 +797,9 @@ pub async fn compute_tally( } } -pub async fn get_bond_amount_at( +pub async fn get_bond_amount_at< + C: Client + crate::ledger::queries::Client + Sync, +>( client: &C, delegator: &Address, validator: &Address, @@ -759,7 +813,8 @@ pub async fn get_bond_amount_at(client, &bond_key).await; + let epoched_bonds = + query_storage_value::(client, &bond_key).await; match epoched_bonds { Some(epoched_bonds) => { let mut delegated_amount: token::Amount = 0.into(); @@ -779,12 +834,7 @@ pub async fn get_bond_amount_at= *epoch_start { delegated_amount += delta; } diff --git a/shared/src/ledger/signing.rs b/shared/src/ledger/signing.rs index 24d9feed86..d1ae55da65 100644 --- a/shared/src/ledger/signing.rs +++ b/shared/src/ledger/signing.rs @@ -1,20 +1,21 @@ -use crate::types::transaction::hash_tx; -use crate::types::transaction::{Fee, WrapperTx}; -use crate::tendermint_rpc::Client; +use borsh::BorshSerialize; +use namada_core::types::address::{Address, ImplicitAddress}; + +use crate::ledger::rpc::TxBroadcastData; use crate::ledger::wallet::{Wallet, WalletUtils}; +use crate::ledger::{args, rpc}; +use crate::proto::Tx; +use crate::tendermint_rpc::Client; use crate::types::key::*; -use namada_core::types::address::Address; -use crate::ledger::rpc; -use namada_core::types::address::ImplicitAddress; use crate::types::storage::Epoch; -use crate::ledger::rpc::TxBroadcastData; -use crate::proto::Tx; -use crate::ledger::args; -use borsh::BorshSerialize; +use crate::types::transaction::{hash_tx, Fee, WrapperTx}; /// Find the public key for the given address and try to load the keypair /// for it from the wallet. Panics if the key cannot be found or loaded. -pub async fn find_keypair( +pub async fn find_keypair< + C: Client + crate::ledger::queries::Client + Sync, + U: WalletUtils, +>( client: &C, wallet: &mut Wallet, addr: &Address, @@ -25,9 +26,8 @@ pub async fn find_keypair { - panic!( - "Internal address {} doesn't have any signing keys.", - addr - ); + panic!("Internal address {} doesn't have any signing keys.", addr); } } } @@ -78,7 +75,10 @@ pub enum TxSigningKey { /// signer. Return the given signing key or public key of the given signer if /// possible. If no explicit signer given, use the `default`. If no `default` /// is given, panics. -pub async fn tx_signer( +pub async fn tx_signer< + C: Client + crate::ledger::queries::Client + Sync, + U: WalletUtils, +>( client: &C, wallet: &mut Wallet, args: &args::Tx, @@ -92,29 +92,27 @@ pub async fn tx_signer { - signing_key - } + TxSigningKey::WalletKeypair(signing_key) => signing_key, TxSigningKey::WalletAddress(signer) => { let signer = signer; - let signing_key = find_keypair::( - client, - wallet, - &signer, - ) - .await; + let signing_key = + find_keypair::(client, wallet, &signer).await; // Check if the signer is implicit account that needs to reveal its // PK first if matches!(signer, Address::Implicit(_)) { let pk: common::PublicKey = signing_key.ref_to(); - super::tx::reveal_pk_if_needed::(client, wallet, &pk, args).await; + super::tx::reveal_pk_if_needed::( + client, wallet, &pk, args, + ) + .await; } signing_key } TxSigningKey::SecretKey(signing_key) => { // Check if the signing key needs to reveal its PK first let pk: common::PublicKey = signing_key.ref_to(); - super::tx::reveal_pk_if_needed::(client, wallet, &pk, args).await; + super::tx::reveal_pk_if_needed::(client, wallet, &pk, args) + .await; signing_key } TxSigningKey::None => { @@ -134,7 +132,10 @@ pub async fn tx_signer( +pub async fn sign_tx< + C: Client + crate::ledger::queries::Client + Sync, + U: WalletUtils, +>( client: &C, wallet: &mut Wallet, tx: Tx, @@ -144,8 +145,7 @@ pub async fn sign_tx(client, wallet, args, default).await; let tx = tx.sign(&keypair); - let epoch = rpc::query_epoch(client) - .await; + let epoch = rpc::query_epoch(client).await; let broadcast_data = if args.dry_run { TxBroadcastData::DryRun(tx) } else { diff --git a/shared/src/ledger/tx.rs b/shared/src/ledger/tx.rs index 7fb18fb455..affb209e54 100644 --- a/shared/src/ledger/tx.rs +++ b/shared/src/ledger/tx.rs @@ -1,56 +1,50 @@ -use itertools::Either::*; -use crate::ledger::args; -use crate::ledger::wallet::{Wallet, WalletUtils}; -use crate::tendermint_rpc::Client; -use crate::proto::Tx; -use namada_core::types::address::Address; -use crate::ledger::signing::sign_tx; -use crate::ledger::signing::TxSigningKey; -use crate::ledger::rpc::{self, TxBroadcastData}; -use crate::ledger::signing::find_keypair; -use crate::types::key::*; +use std::borrow::Cow; + use borsh::BorshSerialize; +use itertools::Either::*; +use masp_primitives::transaction::builder; +use namada_core::types::address::{masp, masp_tx_key, Address}; +use rust_decimal::Decimal; use thiserror::Error; -use crate::tendermint_rpc::error::Error as RpcError; -use crate::ledger::rpc::TxResponse; use tokio::time::{Duration, Instant}; -use crate::tendermint_rpc::endpoint::broadcast::tx_sync::Response; -use std::borrow::Cow; -use rust_decimal::Decimal; -use crate::ledger::pos::{BondId, Bonds, CommissionRates, Unbonds}; -use crate::ledger; -use crate::types::transaction::{pos, InitAccount, UpdateVp}; -use crate::types::{storage, token}; -use crate::types::storage::Epoch; -use crate::ledger::governance::storage as gov_storage; + +use crate::ibc::applications::ics20_fungible_token_transfer::msgs::transfer::MsgTransfer; use crate::ibc::signer::Signer; use crate::ibc::timestamp::Timestamp as IbcTimestamp; -use crate::ibc::applications::ics20_fungible_token_transfer::msgs::transfer::MsgTransfer; -use crate::types::time::DateTimeUtc; -use crate::ibc_proto::cosmos::base::v1beta1::Coin; -use crate::types::storage::RESERVED_ADDRESS_PREFIX; -use crate::ibc::Height as IbcHeight; use crate::ibc::tx_msg::Msg; -use crate::ledger::masp::ShieldedUtils; -use crate::ledger::masp::ShieldedContext; -use namada_core::types::address::masp; -use namada_core::types::address::masp_tx_key; -use crate::ledger::signing::tx_signer; -use masp_primitives::transaction::builder; +use crate::ibc::Height as IbcHeight; +use crate::ibc_proto::cosmos::base::v1beta1::Coin; +use crate::ledger::args; +use crate::ledger::governance::storage as gov_storage; +use crate::ledger::masp::{ShieldedContext, ShieldedUtils}; +use crate::ledger::pos::{BondId, Bonds, CommissionRates, Unbonds}; +use crate::ledger::rpc::{self, TxBroadcastData, TxResponse}; +use crate::ledger::signing::{find_keypair, sign_tx, tx_signer, TxSigningKey}; +use crate::ledger::wallet::{Wallet, WalletUtils}; +use crate::proto::Tx; +use crate::tendermint_rpc::endpoint::broadcast::tx_sync::Response; +use crate::tendermint_rpc::error::Error as RpcError; +use crate::tendermint_rpc::Client; +use crate::types::key::*; use crate::types::masp::TransferTarget; -use crate::vm; +use crate::types::storage::{Epoch, RESERVED_ADDRESS_PREFIX}; +use crate::types::time::DateTimeUtc; +use crate::types::transaction::{pos, InitAccount, UpdateVp}; +use crate::types::{storage, token}; +use crate::{ledger, vm}; /// Default timeout in seconds for requests to the `/accepted` /// and `/applied` ABCI query endpoints. const DEFAULT_NAMADA_EVENTS_MAX_WAIT_TIME_SECONDS: u64 = 60; - /// Errors to do with transaction events. #[derive(Error, Debug)] pub enum Error { /// Expect a dry running transaction - #[error("Expected a dry-run transaction, received a wrapper \ - transaction instead: {0:?}")] + #[error( + "Expected a dry-run transaction, received a wrapper transaction \ + instead: {0:?}" + )] ExpectDryRun(Tx), /// Expect a wrapped encrypted running transaction #[error("Cannot broadcast a dry-run transaction")] @@ -65,16 +59,19 @@ pub enum Error { #[error("The address {0} doesn't belong to any known validator account.")] InvalidValidatorAddress(Address), /// Rate of epoch change too large for current epoch - #[error("New rate, {0}, is too large of a change with respect to \ - the predecessor epoch in which the rate will take \ - effect.")] + #[error( + "New rate, {0}, is too large of a change with respect to the \ + predecessor epoch in which the rate will take effect." + )] TooLargeOfChange(Decimal), /// Error retrieving from storage #[error("Error retrieving from storage")] Retrival, /// No unbonded bonds ready to withdraw in the current epoch - #[error("There are no unbonded bonds ready to withdraw in the \ - current epoch {0}.")] + #[error( + "There are no unbonded bonds ready to withdraw in the current epoch \ + {0}." + )] NoUnbondReady(Epoch), /// No unbonded bonds found #[error("No unbonded bonds found")] @@ -83,14 +80,16 @@ pub enum Error { #[error("No bonds found")] NoBondFound, /// Lower bond amount than the unbond - #[error("The total bonds of the source {0} is lower than the \ - amount to be unbonded. Amount to unbond is {1} and the \ - total bonds is {2}.")] + #[error( + "The total bonds of the source {0} is lower than the amount to be \ + unbonded. Amount to unbond is {1} and the total bonds is {2}." + )] LowerBondThanUnbond(Address, token::Amount, token::Amount), /// Balance is too low - #[error("The balance of the source {0} of token {1} is lower than \ - the amount to be transferred. Amount to transfer is {2} \ - and the balance is {3}.")] + #[error( + "The balance of the source {0} of token {1} is lower than the amount \ + to be transferred. Amount to transfer is {2} and the balance is {3}." + )] BalanceTooLow(Address, Address, token::Amount, token::Amount), /// Source address does not exist on chain #[error("The source address {0} doesn't exist on chain.")] @@ -108,25 +107,37 @@ pub enum Error { #[error("No balance found for the source {0} of token {1}")] NoBalanceForToken(Address, Address), /// Negative balance after transfer - #[error("The balance of the source {0} is lower than the \ - amount to be transferred and fees. Amount to \ - transfer is {1} {2} and fees are {3} {4}.")] - NegativeBalanceAfterTransfer(Address, token::Amount, Address, token::Amount, Address), + #[error( + "The balance of the source {0} is lower than the amount to be \ + transferred and fees. Amount to transfer is {1} {2} and fees are {3} \ + {4}." + )] + NegativeBalanceAfterTransfer( + Address, + token::Amount, + Address, + token::Amount, + Address, + ), /// No Balance found for token #[error("{0}")] - MaspError(builder::Error) + MaspError(builder::Error), } /// Submit transaction and wait for result. Returns a list of addresses /// initialized in the transaction if any. In dry run, this is always empty. -pub async fn process_tx( +pub async fn process_tx< + C: Client + crate::ledger::queries::Client + Sync, + U: WalletUtils, +>( client: &C, wallet: &mut Wallet, args: &args::Tx, tx: Tx, default_signer: TxSigningKey, -) -> Result,Error> { - let to_broadcast = sign_tx::(client, wallet, tx, args, default_signer).await; +) -> Result, Error> { + let to_broadcast = + sign_tx::(client, wallet, tx, args, default_signer).await; // NOTE: use this to print the request JSON body: // let request = @@ -153,12 +164,15 @@ pub async fn process_tx Ok(result.initialized_accounts), Left(Ok(_)) => Ok(Vec::default()), Right(Err(err)) => Err(err), - Left(Err(err)) => Err(err) + Left(Err(err)) => Err(err), } } } -pub async fn submit_reveal_pk( +pub async fn submit_reveal_pk< + C: Client + crate::ledger::queries::Client + Sync, + U: WalletUtils, +>( client: &C, wallet: &mut Wallet, args: args::RevealPk, @@ -174,7 +188,10 @@ pub async fn submit_reveal_pk( +pub async fn reveal_pk_if_needed< + C: Client + crate::ledger::queries::Client + Sync, + U: WalletUtils, +>( client: &C, wallet: &mut Wallet, public_key: &common::PublicKey, @@ -182,8 +199,7 @@ pub async fn reveal_pk_if_needed bool { let addr: Address = public_key.into(); // Check if PK revealed - if args.force || !has_revealed_pk(client, &addr).await - { + if args.force || !has_revealed_pk(client, &addr).await { // If not, submit it submit_reveal_pk_aux::(client, wallet, public_key, args).await; true @@ -192,19 +208,24 @@ pub async fn reveal_pk_if_needed( +pub async fn has_revealed_pk< + C: Client + crate::ledger::queries::Client + Sync, +>( client: &C, addr: &Address, ) -> bool { rpc::get_public_key(client, addr).await.is_some() } -pub async fn submit_reveal_pk_aux( +pub async fn submit_reveal_pk_aux< + C: Client + crate::ledger::queries::Client + Sync, + U: WalletUtils, +>( client: &C, wallet: &mut Wallet, public_key: &common::PublicKey, args: &args::Tx, -) -> Result<(),Error> { +) -> Result<(), Error> { let addr: Address = public_key.into(); println!("Submitting a tx to reveal the public key for address {addr}..."); let tx_data = public_key @@ -218,13 +239,11 @@ pub async fn submit_reveal_pk_aux(client, wallet, &signer) - .await + find_keypair::(client, wallet, &signer).await } else { find_keypair::(client, wallet, &addr).await }; - let epoch = rpc::query_epoch(client) - .await; + let epoch = rpc::query_epoch(client).await; let to_broadcast = if args.dry_run { TxBroadcastData::DryRun(tx) } else { @@ -247,7 +266,7 @@ pub async fn submit_reveal_pk_aux Err(err), Left(Err(err)) => Err(err), - _ => Ok({}) + _ => Ok({}), } } } @@ -277,7 +296,8 @@ pub async fn broadcast_tx( // TODO: configure an explicit timeout value? we need to hack away at // `tendermint-rs` for this, which is currently using a hard-coded 30s // timeout. - let response = lift_rpc_error(rpc_cli.broadcast_tx_sync(tx.to_bytes().into()).await)?; + let response = + lift_rpc_error(rpc_cli.broadcast_tx_sync(tx.to_bytes().into()).await)?; if response.code == 0.into() { println!("Transaction added to mempool: {:?}", response); @@ -289,7 +309,9 @@ pub async fn broadcast_tx( } Ok(response) } else { - Err(Error::TxBroadcast(RpcError::server(serde_json::to_string(&response).unwrap()))) + Err(Error::TxBroadcast(RpcError::server( + serde_json::to_string(&response).unwrap(), + ))) } } @@ -317,9 +339,8 @@ pub async fn submit_tx( // Broadcast the supplied transaction broadcast_tx(client, &to_broadcast).await?; - let max_wait_time = Duration::from_secs( - DEFAULT_NAMADA_EVENTS_MAX_WAIT_TIME_SECONDS, - ); + let max_wait_time = + Duration::from_secs(DEFAULT_NAMADA_EVENTS_MAX_WAIT_TIME_SECONDS); let deadline = Instant::now() + max_wait_time; tracing::debug!( @@ -329,10 +350,9 @@ pub async fn submit_tx( ); let parsed = { - let wrapper_query = crate::ledger::rpc::TxEventQuery::Accepted(wrapper_hash.as_str()); - let event = - rpc::query_tx_status(client, wrapper_query, deadline) - .await; + let wrapper_query = + crate::ledger::rpc::TxEventQuery::Accepted(wrapper_hash.as_str()); + let event = rpc::query_tx_status(client, wrapper_query, deadline).await; let parsed = TxResponse::from_event(event); println!( @@ -397,9 +417,7 @@ pub async fn save_initialized_accounts( format!("{}{}", initialized_account_alias, ix).into() } } - None => { - U::read_alias(&encoded).into() - } + None => U::read_alias(&encoded).into(), }; let alias = alias.into_owned(); let added = wallet.add_address(alias.clone(), address.clone()); @@ -416,7 +434,10 @@ pub async fn save_initialized_accounts( } } -pub async fn submit_validator_commission_change( +pub async fn submit_validator_commission_change< + C: Client + crate::ledger::queries::Client + Sync, + U: WalletUtils, +>( client: &C, wallet: &mut Wallet, args: args::TxCommissionRateChange, @@ -429,7 +450,10 @@ pub async fn submit_validator_commission_change Decimal::ONE { if args.tx.force { - eprintln!("Invalid new commission rate, received {}", args.rate); + eprintln!( + "Invalid new commission rate, received {}", + args.rate + ); Ok(()) } else { Err(Error::InvalidCommisionRate(args.rate)) @@ -446,12 +470,12 @@ pub async fn submit_validator_commission_change( &client, &max_commission_rate_change_key, ) - .await; + .await; match (commission_rates, max_change) { (Some(rates), Some(max_change)) => { @@ -461,9 +485,9 @@ pub async fn submit_validator_commission_change max_change { if args.tx.force { eprintln!( - "New rate, {epoch_change}, is too large of a change \ - with respect to the predecessor epoch in which the rate \ - will take effect." + "New rate, {epoch_change}, is too large of a \ + change with respect to the predecessor epoch in \ + which the rate will take effect." ); Ok(()) } else { @@ -511,15 +535,19 @@ pub async fn submit_validator_commission_change( +pub async fn submit_withdraw< + C: Client + crate::ledger::queries::Client + Sync, + U: WalletUtils, +>( client: &C, wallet: &mut Wallet, args: args::Withdraw, -) -> Result<(),Error> { +) -> Result<(), Error> { let epoch = rpc::query_epoch(client).await; - let validator = known_validator_or_err(args.validator.clone(), args.tx.force, client) - .await?; + let validator = + known_validator_or_err(args.validator.clone(), args.tx.force, client) + .await?; let source = args.source.clone(); let tx_code = args.tx_code_path; @@ -531,7 +559,8 @@ pub async fn submit_withdraw(&client, &bond_key).await; + let unbonds = + rpc::query_storage_value::(&client, &bond_key).await; match unbonds { Some(unbonds) => { let mut unbonded_amount: token::Amount = 0.into(); @@ -551,8 +580,7 @@ pub async fn submit_withdraw( +pub async fn submit_unbond< + C: Client + crate::ledger::queries::Client + Sync, + U: WalletUtils, +>( client: &C, wallet: &mut Wallet, args: args::Unbond, -) -> Result<(),Error> { - let validator = known_validator_or_err(args.validator.clone(), args.tx.force, client) - .await?; +) -> Result<(), Error> { + let validator = + known_validator_or_err(args.validator.clone(), args.tx.force, client) + .await?; let source = args.source.clone(); let tx_code = args.tx_code_path; @@ -612,13 +644,17 @@ pub async fn submit_unbond( +pub async fn submit_bond< + C: Client + crate::ledger::queries::Client + Sync, + U: WalletUtils, +>( client: &C, wallet: &mut Wallet, args: args::Bond, ) -> Result<(), Error> { - let validator = known_validator_or_err(args.validator.clone(), args.tx.force, client) - .await?; + let validator = + known_validator_or_err(args.validator.clone(), args.tx.force, client) + .await?; // Check that the source address exists on chain let source = args.source.clone(); @@ -668,28 +708,29 @@ pub async fn submit_bond source_exists_or_err(source, args.tx.force, client) .await .map(Some), - None => Ok(source) + None => Ok(source), }?; // 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(&args.native_token, bond_source); - match rpc::query_storage_value::(&client, &balance_key).await + match rpc::query_storage_value::(&client, &balance_key) + .await { Some(balance) => { if balance < args.amount { if args.tx.force { eprintln!( - "The balance of the source {} is lower than the amount to \ - be transferred. Amount to transfer is {} and the balance \ - is {}.", + "The balance of the source {} is lower than the \ + amount to be transferred. Amount to transfer is {} \ + and the balance is {}.", bond_source, args.amount, balance ); } else { panic!( - "The balance of the source {} is lower than the amount to \ - be transferred. Amount to transfer is {} and the balance \ - is {}.", + "The balance of the source {} is lower than the \ + amount to be transferred. Amount to transfer is {} \ + and the balance is {}.", bond_source, args.amount, balance ); } @@ -727,7 +768,9 @@ pub async fn submit_bond( +pub async fn is_safe_voting_window< + C: Client + crate::ledger::queries::Client + Sync, +>( client: &C, proposal_id: u64, proposal_start_epoch: Epoch, @@ -754,16 +797,20 @@ pub async fn is_safe_voting_window( +pub async fn submit_ibc_transfer< + C: Client + crate::ledger::queries::Client + Sync, + U: WalletUtils, +>( client: &C, wallet: &mut Wallet, args: args::TxIbcTransfer, ) -> Result<(), Error> { // Check that the source address exists on chain - let source = source_exists_or_err(args.source.clone(), args.tx.force, client).await?; + let source = + source_exists_or_err(args.source.clone(), args.tx.force, client) + .await?; // We cannot check the receiver - let token = token_exists_or_err(args.token, args.tx.force, client).await?; // Check source balance @@ -778,22 +825,23 @@ pub async fn submit_ibc_transfer (None, token::balance_key(&token, &source)), }; - match rpc::query_storage_value::(&client, &balance_key).await + match rpc::query_storage_value::(&client, &balance_key) + .await { Some(balance) => { if balance < args.amount { if args.tx.force { eprintln!( - "The balance of the source {} of token {} is lower than \ - the amount to be transferred. Amount to transfer is {} \ - and the balance is {}.", + "The balance of the source {} of token {} is lower \ + than the amount to be transferred. Amount to \ + transfer is {} and the balance is {}.", source, token, args.amount, balance ); } else { panic!( - "The balance of the source {} of token {} is lower than \ - the amount to be transferred. Amount to transfer is {} \ - and the balance is {}.", + "The balance of the source {} of token {} is lower \ + than the amount to be transferred. Amount to \ + transfer is {} and the balance is {}.", source, token, args.amount, balance ); } @@ -858,12 +906,22 @@ pub async fn submit_ibc_transfer(client, wallet, &args.tx, tx, TxSigningKey::WalletAddress(args.source)) - .await?; + process_tx::( + client, + wallet, + &args.tx, + tx, + TxSigningKey::WalletAddress(args.source), + ) + .await?; Ok(()) } -pub async fn submit_transfer>( +pub async fn submit_transfer< + C: Client + crate::ledger::queries::Client + Sync, + V: WalletUtils, + U: ShieldedUtils, +>( client: &C, wallet: &mut Wallet, shielded: &mut ShieldedContext, @@ -872,22 +930,30 @@ pub async fn submit_transfer { let sub_prefix = storage::Key::parse(sub_prefix).unwrap(); - let prefix = token::multitoken_balance_prefix( - &token, - &sub_prefix, - ); + let prefix = token::multitoken_balance_prefix(&token, &sub_prefix); ( Some(sub_prefix), token::multitoken_balance_key(&prefix, &source), @@ -895,20 +961,26 @@ pub async fn submit_transfer (None, token::balance_key(&token, &source)), }; - match rpc::query_storage_value::(&client, &balance_key).await + match rpc::query_storage_value::(&client, &balance_key) + .await { Some(balance) => { if balance < args.amount { if args.tx.force { eprintln!( - "The balance of the source {} of token {} is lower than \ - the amount to be transferred. Amount to transfer is {} \ - and the balance is {}.", + "The balance of the source {} of token {} is lower \ + than the amount to be transferred. Amount to \ + transfer is {} and the balance is {}.", source, token, args.amount, balance ); Ok(()) } else { - Err(Error::BalanceTooLow(source.clone(), token.clone(), args.amount, balance)) + Err(Error::BalanceTooLow( + source.clone(), + token.clone(), + args.amount, + balance, + )) } } else { Ok(()) @@ -956,9 +1028,10 @@ pub async fn submit_transfer(client, wallet, &args.tx, default_signer.clone()) - .await - .ref_to(); + let chosen_signer = + tx_signer::(client, wallet, &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 &args.target { @@ -966,8 +1039,8 @@ pub async fn submit_transfer None, }; - let stx_result = - shielded.gen_shielded_transfer( + let stx_result = shielded + .gen_shielded_transfer( client, transfer_source, transfer_target, @@ -981,7 +1054,13 @@ pub async fn submit_transfer Ok(stx.map(|x| x.0)), Err(builder::Error::ChangeIsNegative(_)) => { - Err(Error::NegativeBalanceAfterTransfer(source.clone(), args.amount, token.clone(), args.tx.fee_amount, args.tx.fee_token.clone())) + Err(Error::NegativeBalanceAfterTransfer( + source.clone(), + args.amount, + token.clone(), + args.tx.fee_amount, + args.tx.fee_token.clone(), + )) } Err(err) => Err(Error::MaspError(err)), }?; @@ -1006,7 +1085,10 @@ pub async fn submit_transfer( +pub async fn submit_init_account< + C: Client + crate::ledger::queries::Client + Sync, + U: WalletUtils, +>( client: &C, wallet: &mut Wallet, args: args::TxInitAccount, @@ -1031,13 +1113,23 @@ pub async fn submit_init_account(client, wallet, &args.tx, tx, TxSigningKey::WalletAddress(args.source)) - .await.unwrap(); - save_initialized_accounts::(wallet, &args.tx, initialized_accounts).await; + let initialized_accounts = process_tx::( + client, + wallet, + &args.tx, + tx, + TxSigningKey::WalletAddress(args.source), + ) + .await + .unwrap(); + save_initialized_accounts::(wallet, &args.tx, initialized_accounts) + .await; } -pub async fn submit_update_vp( +pub async fn submit_update_vp< + C: Client + crate::ledger::queries::Client + Sync, + U: WalletUtils, +>( client: &C, wallet: &mut Wallet, args: args::TxUpdateVp, @@ -1047,8 +1139,7 @@ pub async fn submit_update_vp { - let exists = - rpc::known_address::(client, &addr).await; + let exists = rpc::known_address::(client, &addr).await; if !exists { if args.tx.force { eprintln!("The address {} doesn't exist on chain.", addr); @@ -1103,10 +1194,20 @@ pub async fn submit_update_vp(client, wallet, &args.tx, tx, TxSigningKey::WalletAddress(args.addr)).await; + process_tx::( + client, + wallet, + &args.tx, + tx, + TxSigningKey::WalletAddress(args.addr), + ) + .await; } -pub async fn submit_custom( +pub async fn submit_custom< + C: Client + crate::ledger::queries::Client + Sync, + U: WalletUtils, +>( client: &C, wallet: &mut Wallet, args: args::TxCustom, @@ -1115,41 +1216,50 @@ pub async fn submit_custom(client, wallet, &args.tx, tx, TxSigningKey::None).await?; - save_initialized_accounts::(wallet, &args.tx, initialized_accounts).await; + process_tx::(client, wallet, &args.tx, tx, TxSigningKey::None) + .await?; + save_initialized_accounts::(wallet, &args.tx, initialized_accounts) + .await; Ok(()) } -async fn expect_dry_broadcast( +async fn expect_dry_broadcast< + T, + C: Client + crate::ledger::queries::Client + Sync, +>( to_broadcast: TxBroadcastData, client: &C, - ret:T -) -> Result { + ret: T, +) -> Result { match to_broadcast { TxBroadcastData::DryRun(tx) => { rpc::dry_run_tx(client, tx.to_bytes()).await; Ok(ret) } - TxBroadcastData::Wrapper { tx, wrapper_hash: _, decrypted_hash: _ } => - Err(Error::ExpectDryRun(tx)) + TxBroadcastData::Wrapper { + tx, + wrapper_hash: _, + decrypted_hash: _, + } => Err(Error::ExpectDryRun(tx)), } } -fn lift_rpc_error(res: Result) -> Result { +fn lift_rpc_error(res: Result) -> Result { res.map_err(Error::TxBroadcast) } /// Returns the given validator if the given address is a validator, /// otherwise returns an error, force forces the address through even /// if it isn't a validator -async fn known_validator_or_err( +async fn known_validator_or_err< + C: Client + crate::ledger::queries::Client + Sync, +>( validator: Address, force: bool, - client: &C + client: &C, ) -> Result { // Check that the validator address exists on chain - let is_validator = - rpc::is_validator(client, &validator).await; + let is_validator = rpc::is_validator(client, &validator).await; if !is_validator { if force { eprintln!( @@ -1168,19 +1278,18 @@ async fn known_validator_or_err( +async fn address_exists_or_err( addr: Address, force: bool, client: &C, message: String, - err: F + err: F, ) -> Result where C: Client + crate::ledger::queries::Client + Sync, - F: FnOnce(Address) -> Error + F: FnOnce(Address) -> Error, { - let addr_exists = - rpc::known_address::(client, &addr).await; + let addr_exists = rpc::known_address::(client, &addr).await; if !addr_exists { if force { eprintln!("{}", message); @@ -1196,35 +1305,65 @@ where /// Returns the given token if the given address exists on chain /// otherwise returns an error, force forces the address through even /// if it isn't on chain -async fn token_exists_or_err( +async fn token_exists_or_err< + C: Client + crate::ledger::queries::Client + Sync, +>( token: Address, force: bool, - client: &C + client: &C, ) -> Result { - let message = format!("The token address {} doesn't exist on chain.", token); - address_exists_or_err(token, force, client, message, Error::TokenDoesNotExist).await + let message = + format!("The token address {} doesn't exist on chain.", token); + address_exists_or_err( + token, + force, + client, + message, + Error::TokenDoesNotExist, + ) + .await } /// Returns the given source address if the given address exists on chain /// otherwise returns an error, force forces the address through even /// if it isn't on chain -async fn source_exists_or_err( +async fn source_exists_or_err< + C: Client + crate::ledger::queries::Client + Sync, +>( token: Address, force: bool, - client: &C + client: &C, ) -> Result { - let message = format!("The source address {} doesn't exist on chain.", token); - address_exists_or_err(token, force, client, message, Error::SourceDoesNotExist).await + let message = + format!("The source address {} doesn't exist on chain.", token); + address_exists_or_err( + token, + force, + client, + message, + Error::SourceDoesNotExist, + ) + .await } /// Returns the given target address if the given address exists on chain /// otherwise returns an error, force forces the address through even /// if it isn't on chain -async fn target_exists_or_err( +async fn target_exists_or_err< + C: Client + crate::ledger::queries::Client + Sync, +>( token: Address, force: bool, - client: &C + client: &C, ) -> Result { - let message = format!("The target address {} doesn't exist on chain.", token); - address_exists_or_err(token, force, client, message, Error::TargetLocationDoesNotExist).await + let message = + format!("The target address {} doesn't exist on chain.", token); + address_exists_or_err( + token, + force, + client, + message, + Error::TargetLocationDoesNotExist, + ) + .await } diff --git a/shared/src/ledger/wallet/keys.rs b/shared/src/ledger/wallet/keys.rs index f2b7cfa619..ce8c1769dc 100644 --- a/shared/src/ledger/wallet/keys.rs +++ b/shared/src/ledger/wallet/keys.rs @@ -9,6 +9,7 @@ use data_encoding::HEXLOWER; use orion::{aead, kdf}; use serde::{Deserialize, Serialize}; use thiserror::Error; + use crate::ledger::wallet::WalletUtils; const ENCRYPTED_KEY_PREFIX: &str = "encrypted:"; diff --git a/shared/src/ledger/wallet/mod.rs b/shared/src/ledger/wallet/mod.rs index 375fa89e9d..7afc0ae95a 100644 --- a/shared/src/ledger/wallet/mod.rs +++ b/shared/src/ledger/wallet/mod.rs @@ -1,39 +1,38 @@ //! Provides functionality for managing keys and addresses for a user -mod store; mod alias; mod keys; pub mod pre_genesis; +mod store; use std::collections::HashMap; use std::fmt::Display; use std::str::FromStr; -pub use self::store::{ValidatorData, ValidatorKeys}; +pub use alias::Alias; use borsh::{BorshDeserialize, BorshSerialize}; use masp_primitives::zip32::ExtendedFullViewingKey; +pub use pre_genesis::gen_key_to_store; +pub use store::{gen_sk, Store}; +use thiserror::Error; + +pub use self::keys::{DecryptionError, StoredKeypair}; +pub use self::store::{ConfirmationResponse, ValidatorData, ValidatorKeys}; use crate::types::address::Address; use crate::types::key::*; use crate::types::masp::{ ExtendedSpendingKey, ExtendedViewingKey, PaymentAddress, }; -use thiserror::Error; - -pub use self::keys::{DecryptionError, StoredKeypair}; -pub use self::store::ConfirmationResponse; - -pub use store::{Store, gen_sk}; -pub use alias::Alias; -pub use pre_genesis::gen_key_to_store; /// Captures the interactive parts of the wallet's functioning pub trait WalletUtils { /// The location where the wallet is stored type Storage; - /// Read the password for encryption from the file/env/stdin with confirmation. + /// Read the password for encryption from the file/env/stdin with + /// confirmation. fn read_and_confirm_pwd(unsafe_dont_encrypt: bool) -> Option; - /// Read the password for encryption/decryption from the file/env/stdin. Panics - /// if all options are empty/invalid. + /// Read the password for encryption/decryption from the file/env/stdin. + /// Panics if all options are empty/invalid. fn read_password(prompt_msg: &str) -> String; /// Read an alias from the file/env/stdin. @@ -81,7 +80,7 @@ impl Wallet { decrypted_spendkey_cache: HashMap::default(), } } - + /// Generate a new keypair and derive an implicit address from its public /// and insert them into the store with the provided alias, converted to /// lower case. If none provided, the alias will be the public key hash (in @@ -268,7 +267,7 @@ impl Wallet { /// If a given storage key needs to be decrypted, prompt for password from /// stdin and if successfully decrypted, store it in a cache. fn decrypt_stored_key< - T: FromStr + Display + BorshSerialize + BorshDeserialize + Clone, + T: FromStr + Display + BorshSerialize + BorshDeserialize + Clone, >( decrypted_key_cache: &mut HashMap, stored_key: &StoredKeypair, diff --git a/shared/src/ledger/wallet/pre_genesis.rs b/shared/src/ledger/wallet/pre_genesis.rs index c01b14902b..333a9f21e7 100644 --- a/shared/src/ledger/wallet/pre_genesis.rs +++ b/shared/src/ledger/wallet/pre_genesis.rs @@ -1,9 +1,10 @@ //! Provides functionality for managing validator keys -use thiserror::Error; -use crate::types::key::{common, SchemeType}; use serde::{Deserialize, Serialize}; +use thiserror::Error; + use crate::ledger::wallet; use crate::ledger::wallet::{store, StoredKeypair}; +use crate::types::key::{common, SchemeType}; /// Ways in which wallet store operations can fail #[derive(Error, Debug)] @@ -77,4 +78,3 @@ impl From for ReadError { ReadError::Decryption(err) } } - diff --git a/shared/src/ledger/wallet/store.rs b/shared/src/ledger/wallet/store.rs index 4381390f3e..75e3d1e361 100644 --- a/shared/src/ledger/wallet/store.rs +++ b/shared/src/ledger/wallet/store.rs @@ -1,21 +1,21 @@ -use serde::{Deserialize, Serialize}; -use crate::types::key::*; -use crate::types::masp::{ - ExtendedSpendingKey, ExtendedViewingKey, PaymentAddress, -}; use std::collections::HashMap; -use super::alias::{self, Alias}; -use crate::types::address::{Address, ImplicitAddress}; +use std::str::FromStr; + use bimap::BiHashMap; -use crate::types::key::dkg_session_keys::DkgKeypair; use masp_primitives::zip32::ExtendedFullViewingKey; -use std::str::FromStr; -use crate::ledger::wallet::WalletUtils; #[cfg(feature = "masp-tx-gen")] use rand_core::RngCore; +use serde::{Deserialize, Serialize}; +use super::alias::{self, Alias}; use super::pre_genesis; -use crate::ledger::wallet::StoredKeypair; +use crate::ledger::wallet::{StoredKeypair, WalletUtils}; +use crate::types::address::{Address, ImplicitAddress}; +use crate::types::key::dkg_session_keys::DkgKeypair; +use crate::types::key::*; +use crate::types::masp::{ + ExtendedSpendingKey, ExtendedViewingKey, PaymentAddress, +}; /// Actions that can be taken when there is an alias conflict pub enum ConfirmationResponse { @@ -339,8 +339,9 @@ impl Store { match U::show_overwrite_confirmation(&alias, "a spending key") { ConfirmationResponse::Replace => {} ConfirmationResponse::Reselect(new_alias) => { - return self - .insert_spending_key::(new_alias, spendkey, viewkey); + return self.insert_spending_key::( + new_alias, spendkey, viewkey, + ); } ConfirmationResponse::Skip => return None, } @@ -409,7 +410,8 @@ impl Store { match U::show_overwrite_confirmation(&alias, "a payment address") { ConfirmationResponse::Replace => {} ConfirmationResponse::Reselect(new_alias) => { - return self.insert_payment_addr::(new_alias, payment_addr); + return self + .insert_payment_addr::(new_alias, payment_addr); } ConfirmationResponse::Skip => return None, } From 65c0b37dff0a7faed97d55ba347093f3e784ea4b Mon Sep 17 00:00:00 2001 From: mariari Date: Mon, 19 Dec 2022 21:18:25 +0800 Subject: [PATCH 39/51] Abstract out common query_storage_value pattern One small hickup that should be considered is that the native token has been unified with other token's erroring interfaces --- shared/src/ledger/tx.rs | 189 ++++++++++++++++++---------------------- 1 file changed, 86 insertions(+), 103 deletions(-) diff --git a/shared/src/ledger/tx.rs b/shared/src/ledger/tx.rs index affb209e54..31d95820b8 100644 --- a/shared/src/ledger/tx.rs +++ b/shared/src/ledger/tx.rs @@ -8,6 +8,7 @@ use rust_decimal::Decimal; use thiserror::Error; use tokio::time::{Duration, Instant}; +use super::args::KeyList; use crate::ibc::applications::ics20_fungible_token_transfer::msgs::transfer::MsgTransfer; use crate::ibc::signer::Signer; use crate::ibc::timestamp::Timestamp as IbcTimestamp; @@ -714,36 +715,18 @@ pub async fn submit_bond< // balance let bond_source = source.as_ref().unwrap_or(&validator); let balance_key = token::balance_key(&args.native_token, bond_source); - match rpc::query_storage_value::(&client, &balance_key) - .await - { - Some(balance) => { - if balance < args.amount { - if args.tx.force { - eprintln!( - "The balance of the source {} is lower than the \ - amount to be transferred. Amount to transfer is {} \ - and the balance is {}.", - bond_source, args.amount, balance - ); - } else { - panic!( - "The balance of the source {} is lower than the \ - amount to be transferred. Amount to transfer is {} \ - and the balance is {}.", - bond_source, args.amount, balance - ); - } - } - } - None => { - if args.tx.force { - eprintln!("No balance found for the source {}", bond_source); - } else { - panic!("No balance found for the source {}", bond_source); - } - } - } + + // TODO Should we state the same error message for the native token? + check_balance_too_low_err( + &args.native_token, + bond_source, + args.amount, + balance_key, + args.tx.force, + client, + ) + .await?; + let tx_code = args.tx_code_path; let bond = pos::Bond { validator, @@ -825,42 +808,17 @@ pub async fn submit_ibc_transfer< } None => (None, token::balance_key(&token, &source)), }; - match rpc::query_storage_value::(&client, &balance_key) - .await - { - Some(balance) => { - if balance < args.amount { - if args.tx.force { - eprintln!( - "The balance of the source {} of token {} is lower \ - than the amount to be transferred. Amount to \ - transfer is {} and the balance is {}.", - source, token, args.amount, balance - ); - } else { - panic!( - "The balance of the source {} of token {} is lower \ - than the amount to be transferred. Amount to \ - transfer is {} and the balance is {}.", - source, token, args.amount, balance - ); - } - } - } - None => { - if args.tx.force { - eprintln!( - "No balance found for the source {} of token {}", - source, token - ); - } else { - panic!( - "No balance found for the source {} of token {}", - source, token - ); - } - } - } + + check_balance_too_low_err( + &token, + &source, + args.amount, + balance_key, + args.tx.force, + client, + ) + .await?; + let tx_code = args.tx_code_path; let denom = match sub_prefix { @@ -961,43 +919,16 @@ pub async fn submit_transfer< } None => (None, token::balance_key(&token, &source)), }; - match rpc::query_storage_value::(&client, &balance_key) - .await - { - Some(balance) => { - if balance < args.amount { - if args.tx.force { - eprintln!( - "The balance of the source {} of token {} is lower \ - than the amount to be transferred. Amount to \ - transfer is {} and the balance is {}.", - source, token, args.amount, balance - ); - Ok(()) - } else { - Err(Error::BalanceTooLow( - source.clone(), - token.clone(), - args.amount, - balance, - )) - } - } else { - Ok(()) - } - } - None => { - if args.tx.force { - eprintln!( - "No balance found for the source {} of token {}", - source, token - ); - Ok(()) - } else { - Err(Error::NoBalanceForToken(source.clone(), token.clone())) - } - } - }; + + check_balance_too_low_err( + &token, + &source, + args.amount, + balance_key, + args.tx.force, + client, + ) + .await?; let tx_code = args.tx_code_path; let masp_addr = masp(); @@ -1367,3 +1298,55 @@ async fn target_exists_or_err< ) .await } + +/// checks the balance at the given address is enough to transfer the +/// given amount, along with the balance even existing. force +/// overrides this +async fn check_balance_too_low_err< + C: Client + crate::ledger::queries::Client + Sync, +>( + token: &Address, + source: &Address, + amount: token::Amount, + balance_key: storage::Key, + force: bool, + client: &C, +) -> Result<(), Error> { + match rpc::query_storage_value::(&client, &balance_key) + .await + { + Some(balance) => { + if balance < amount { + if force { + eprintln!( + "The balance of the source {} of token {} is lower \ + than the amount to be transferred. Amount to \ + transfer is {} and the balance is {}.", + source, token, amount, balance + ); + Ok(()) + } else { + Err(Error::BalanceTooLow( + source.clone(), + token.clone(), + amount, + balance, + )) + } + } else { + Ok(()) + } + } + None => { + if force { + eprintln!( + "No balance found for the source {} of token {}", + source, token + ); + Ok(()) + } else { + Err(Error::NoBalanceForToken(source.clone(), token.clone())) + } + } + } +} From 41dd2ca3947cda9c0d18f0d5fc38c7a7cfed7150 Mon Sep 17 00:00:00 2001 From: mariari Date: Tue, 20 Dec 2022 12:27:18 +0800 Subject: [PATCH 40/51] Turn `expect` into encoding failures Hopefully we can remove any function that calls panic, this is another step in that direction --- shared/src/ledger/tx.rs | 50 ++++++++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/shared/src/ledger/tx.rs b/shared/src/ledger/tx.rs index 31d95820b8..6505c41dc8 100644 --- a/shared/src/ledger/tx.rs +++ b/shared/src/ledger/tx.rs @@ -4,6 +4,7 @@ use borsh::BorshSerialize; use itertools::Either::*; use masp_primitives::transaction::builder; use namada_core::types::address::{masp, masp_tx_key, Address}; +use prost::EncodeError; use rust_decimal::Decimal; use thiserror::Error; use tokio::time::{Duration, Instant}; @@ -21,6 +22,7 @@ use crate::ledger::masp::{ShieldedContext, ShieldedUtils}; use crate::ledger::pos::{BondId, Bonds, CommissionRates, Unbonds}; use crate::ledger::rpc::{self, TxBroadcastData, TxResponse}; use crate::ledger::signing::{find_keypair, sign_tx, tx_signer, TxSigningKey}; +use crate::ledger::tx::Error::EncodeTxFailure; use crate::ledger::wallet::{Wallet, WalletUtils}; use crate::proto::Tx; use crate::tendermint_rpc::endpoint::broadcast::tx_sync::Response; @@ -32,6 +34,7 @@ use crate::types::storage::{Epoch, RESERVED_ADDRESS_PREFIX}; use crate::types::time::DateTimeUtc; use crate::types::transaction::{pos, InitAccount, UpdateVp}; use crate::types::{storage, token}; +use crate::vm::WasmValidationError; use crate::{ledger, vm}; /// Default timeout in seconds for requests to the `/accepted` @@ -123,6 +126,17 @@ pub enum Error { /// No Balance found for token #[error("{0}")] MaspError(builder::Error), + /// Wasm validation failed + #[error("Validity predicate code validation failed with {0}")] + WasmValidationFailure(WasmValidationError), + /// Encoding transaction failure + #[error("Encoding tx data, {0}, shouldn't fail")] + EncodeTxFailure(std::io::Error), + /// Encoding public key failure + #[error("Encoding a public key, {0}, shouldn't fail")] + EncodeKeyFailure(std::io::Error), + #[error("Encoding tx data, {0}, shouldn't fail")] + EncodeFailure(EncodeError), } /// Submit transaction and wait for result. Returns a list of addresses @@ -229,9 +243,7 @@ pub async fn submit_reveal_pk_aux< ) -> Result<(), Error> { let addr: Address = public_key.into(); println!("Submitting a tx to reveal the public key for address {addr}..."); - let tx_data = public_key - .try_to_vec() - .expect("Encoding a public key shouldn't fail"); + let tx_data = public_key.try_to_vec().map_err(Error::EncodeKeyFailure)?; let tx_code = args.tx_code_path.clone(); let tx = Tx::new(tx_code, Some(tx_data)); @@ -521,7 +533,7 @@ pub async fn submit_validator_commission_change< validator: args.validator.clone(), new_rate: args.rate, }; - let data = data.try_to_vec().expect("Encoding tx data shouldn't fail"); + let data = data.try_to_vec().map_err(Error::EncodeTxFailure)?; let tx = Tx::new(tx_code, Some(data)); let default_signer = args.validator.clone(); @@ -596,7 +608,7 @@ pub async fn submit_withdraw< }?; let data = pos::Withdraw { validator, source }; - let data = data.try_to_vec().expect("Encoding tx data shouldn't fail"); + let data = data.try_to_vec().map_err(Error::EncodeTxFailure)?; let tx = Tx::new(tx_code, Some(data)); let default_signer = args.source.unwrap_or(args.validator); @@ -676,7 +688,7 @@ pub async fn submit_unbond< amount: args.amount, source, }; - let data = data.try_to_vec().expect("Encoding tx data shouldn't fail"); + let data = data.try_to_vec().map_err(Error::EncodeTxFailure)?; let tx = Tx::new(tx_code, Some(data)); let default_signer = args.source.unwrap_or(args.validator); @@ -733,7 +745,7 @@ pub async fn submit_bond< amount: args.amount, source, }; - let data = bond.try_to_vec().expect("Encoding tx data shouldn't fail"); + let data = bond.try_to_vec().map_err(Error::EncodeTxFailure)?; let tx = Tx::new(tx_code, Some(data)); let default_signer = args.source.unwrap_or(args.validator); @@ -861,7 +873,7 @@ pub async fn submit_ibc_transfer< let any_msg = msg.to_any(); let mut data = vec![]; prost::Message::encode(&any_msg, &mut data) - .expect("Encoding tx data shouldn't fail"); + .map_err(Error::EncodeFailure)?; let tx = Tx::new(tx_code, Some(data)); process_tx::( @@ -1006,9 +1018,7 @@ pub async fn submit_transfer< shielded, }; tracing::debug!("Transfer data {:?}", transfer); - let data = transfer - .try_to_vec() - .expect("Encoding tx data shouldn't fail"); + let data = transfer.try_to_vec().map_err(Error::EncodeTxFailure)?; let tx = Tx::new(tx_code, Some(data)); let signing_address = TxSigningKey::WalletAddress(source); @@ -1023,25 +1033,27 @@ pub async fn submit_init_account< client: &C, wallet: &mut Wallet, args: args::TxInitAccount, -) { +) -> Result<(), Error> { let public_key = args.public_key; let vp_code = args.vp_code_path; // Validate the VP code if let Err(err) = vm::validate_untrusted_wasm(&vp_code) { if args.tx.force { eprintln!("Validity predicate code validation failed with {}", err); + Ok(()) } else { - panic!("Validity predicate code validation failed with {}", err); + Err(Error::WasmValidationFailure(err)) } - } + } else { + Ok(()) + }?; let tx_code = args.tx_code_path; let data = InitAccount { public_key, vp_code, }; - let data = data.try_to_vec().expect("Encoding tx data shouldn't fail"); - + let data = data.try_to_vec().map_err(Error::EncodeTxFailure)?; let tx = Tx::new(tx_code, Some(data)); // TODO Move unwrap to an either let initialized_accounts = process_tx::( @@ -1055,6 +1067,7 @@ pub async fn submit_init_account< .unwrap(); save_initialized_accounts::(wallet, &args.tx, initialized_accounts) .await; + Ok(()) } pub async fn submit_update_vp< @@ -1064,7 +1077,7 @@ pub async fn submit_update_vp< client: &C, wallet: &mut Wallet, args: args::TxUpdateVp, -) { +) -> Result<(), Error> { let addr = args.addr.clone(); // Check that the address is established and exists on chain @@ -1122,7 +1135,7 @@ pub async fn submit_update_vp< let tx_code = args.tx_code_path; let data = UpdateVp { addr, vp_code }; - let data = data.try_to_vec().expect("Encoding tx data shouldn't fail"); + let data = data.try_to_vec().map_err(Error::EncodeTxFailure)?; let tx = Tx::new(tx_code, Some(data)); process_tx::( @@ -1133,6 +1146,7 @@ pub async fn submit_update_vp< TxSigningKey::WalletAddress(args.addr), ) .await; + Ok(()) } pub async fn submit_custom< From ddc5b4c310fac4342e26355bf8fb5cb8a9878421 Mon Sep 17 00:00:00 2001 From: mariari Date: Tue, 20 Dec 2022 13:58:32 +0800 Subject: [PATCH 41/51] Converted all panics except for the boolean functions --- shared/src/ledger/tx.rs | 85 ++++++++++++++++++++++++++--------------- 1 file changed, 54 insertions(+), 31 deletions(-) diff --git a/shared/src/ledger/tx.rs b/shared/src/ledger/tx.rs index 6505c41dc8..19c27c3a9f 100644 --- a/shared/src/ledger/tx.rs +++ b/shared/src/ledger/tx.rs @@ -95,16 +95,16 @@ pub enum Error { to be transferred. Amount to transfer is {2} and the balance is {3}." )] BalanceTooLow(Address, Address, token::Amount, token::Amount), - /// Source address does not exist on chain - #[error("The source address {0} doesn't exist on chain.")] - SourceDoesNotExist(Address), /// Token Address does not exist on chain #[error("The token address {0} doesn't exist on chain.")] TokenDoesNotExist(Address), - /// Source Address does not exist on chain - #[error("The source address {0} doesn't exist on chain.")] - SourceLocationDoesNotExist(Address), + /// Source address does not exist on chain + #[error("The address {0} doesn't exist on chain.")] + LocationDoesNotExist(Address), /// Target Address does not exist on chain + #[error("The source address {0} doesn't exist on chain.")] + SourceDoesNotExist(Address), + /// Source Address does not exist on chain #[error("The target address {0} doesn't exist on chain.")] TargetLocationDoesNotExist(Address), /// No Balance found for token @@ -132,11 +132,26 @@ pub enum Error { /// Encoding transaction failure #[error("Encoding tx data, {0}, shouldn't fail")] EncodeTxFailure(std::io::Error), + /// Like EncodeTxFailure but for the encode error type + #[error("Encoding tx data, {0}, shouldn't fail")] + EncodeFailure(EncodeError), /// Encoding public key failure #[error("Encoding a public key, {0}, shouldn't fail")] EncodeKeyFailure(std::io::Error), - #[error("Encoding tx data, {0}, shouldn't fail")] - EncodeFailure(EncodeError), + /// Updating an VP of an implicit account + #[error( + "A validity predicate of an implicit address cannot be directly \ + updated. You can use an established address for this purpose." + )] + ImplicitUpdate, + // This should be removed? or rather refactored as it communicates + // the same information as the ImplicitUpdate + /// Updating a VP of an internal implicit address + #[error( + "A validity predicate of an internal address cannot be directly \ + updated." + )] + ImplicitInternalError, } /// Submit transaction and wait for result. Returns a list of addresses @@ -1037,16 +1052,7 @@ pub async fn submit_init_account< let public_key = args.public_key; let vp_code = args.vp_code_path; // Validate the VP code - if let Err(err) = vm::validate_untrusted_wasm(&vp_code) { - if args.tx.force { - eprintln!("Validity predicate code validation failed with {}", err); - Ok(()) - } else { - Err(Error::WasmValidationFailure(err)) - } - } else { - Ok(()) - }?; + validate_untrusted_code_err(&vp_code,args.tx.force)?; let tx_code = args.tx_code_path; let data = InitAccount { @@ -1087,9 +1093,12 @@ pub async fn submit_update_vp< if !exists { if args.tx.force { eprintln!("The address {} doesn't exist on chain.", addr); + Ok(()) } else { - panic!("The address {} doesn't exist on chain.", addr); + Err(Error::LocationDoesNotExist(addr.clone())) } + } else { + Ok(()) } } Address::Implicit(_) => { @@ -1099,12 +1108,9 @@ pub async fn submit_update_vp< directly updated. You can use an established address for \ this purpose." ); + Ok(()) } else { - panic!( - "A validity predicate of an implicit address cannot be \ - directly updated. You can use an established address for \ - this purpose." - ); + Err(Error::ImplicitUpdate) } } Address::Internal(_) => { @@ -1113,24 +1119,25 @@ pub async fn submit_update_vp< "A validity predicate of an internal address cannot be \ directly updated." ); + Ok(()) } else { - panic!( - "A validity predicate of an internal address cannot be \ - directly updated." - ); + Err(Error::ImplicitInternalError) } } - } + }?; let vp_code = args.vp_code_path; // Validate the VP code if let Err(err) = vm::validate_untrusted_wasm(&vp_code) { if args.tx.force { eprintln!("Validity predicate code validation failed with {}", err); + Ok(()) } else { - panic!("Validity predicate code validation failed with {}", err); + Err(Error::WasmValidationFailure(err)) } - } + } else { + Ok(()) + }?; let tx_code = args.tx_code_path; @@ -1364,3 +1371,19 @@ async fn check_balance_too_low_err< } } } + +fn validate_untrusted_code_err( + vp_code: &Vec, + force: bool, +) -> Result<(), Error> { + if let Err(err) = vm::validate_untrusted_wasm(&vp_code) { + if force { + eprintln!("Validity predicate code validation failed with {}", err); + Ok(()) + } else { + Err(Error::WasmValidationFailure(err)) + } + } else { + Ok(()) + } +} From da256ff1807e09d7706d0618e074a2937b405ff6 Mon Sep 17 00:00:00 2001 From: mariari Date: Tue, 20 Dec 2022 14:30:25 +0800 Subject: [PATCH 42/51] Convert over the boolean functions to being of Result --- apps/src/lib/client/tx.rs | 19 ++++++++++--------- shared/src/ledger/tx.rs | 31 ++++++++++++++++++++----------- 2 files changed, 30 insertions(+), 20 deletions(-) diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index 1a57e6f313..f511a52e20 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -561,7 +561,7 @@ pub async fn submit_vote_proposal< client: &C, wallet: &mut Wallet, args: args::VoteProposal, -) { +) -> Result<(), tx::Error> { let signer = if let Some(addr) = &args.tx.signer { addr } else { @@ -571,8 +571,9 @@ pub async fn submit_vote_proposal< if args.offline { let signer = signer; - let proposal_file_path = - args.proposal_data.expect("Proposal file should exist."); + let proposal_file_path = args + .proposal_data + .ok_or(tx::Error::Other(format!("Proposal file should exist.")))?; let file = File::open(&proposal_file_path).expect("File must exist."); let proposal: OfflineProposal = @@ -604,6 +605,7 @@ pub async fn submit_vote_proposal< "Proposal vote created: {}.", proposal_vote_filename.to_string_lossy() ); + Ok(()) } Err(e) => { eprintln!("Error while creating proposal vote file: {}.", e); @@ -647,7 +649,7 @@ pub async fn submit_vote_proposal< // validator changing his vote and, effectively, invalidating // the delgator's vote if !args.tx.force - && is_safe_voting_window(client, proposal_id, epoch).await + && is_safe_voting_window(client, proposal_id, epoch).await? { delegations = filter_delegations( client, @@ -679,15 +681,14 @@ pub async fn submit_vote_proposal< TxSigningKey::WalletAddress(signer.clone()), ) .await; + Ok(()) } None => { eprintln!( "Proposal start epoch for proposal id {} is not definied.", proposal_id ); - if !args.tx.force { - safe_exit(1) - } + if !args.tx.force { safe_exit(1) } else { Ok(()) } } } } @@ -712,7 +713,7 @@ pub async fn reveal_pk_if_needed< wallet: &mut Wallet, public_key: &common::PublicKey, args: &args::Tx, -) -> bool { +) -> Result { tx::reveal_pk_if_needed::(client, wallet, public_key, args).await } @@ -746,7 +747,7 @@ async fn is_safe_voting_window< client: &C, proposal_id: u64, proposal_start_epoch: Epoch, -) -> bool { +) -> Result { tx::is_safe_voting_window(client, proposal_id, proposal_start_epoch).await } diff --git a/shared/src/ledger/tx.rs b/shared/src/ledger/tx.rs index 19c27c3a9f..e126a2b429 100644 --- a/shared/src/ledger/tx.rs +++ b/shared/src/ledger/tx.rs @@ -152,6 +152,12 @@ pub enum Error { updated." )] ImplicitInternalError, + /// Epoch not in storage + #[error("Proposal end epoch is not in the storage.")] + EpochNotInStorage, + /// Other Errors that may show up when using the interface + #[error("{0}")] + Other(String), } /// Submit transaction and wait for result. Returns a list of addresses @@ -206,15 +212,18 @@ pub async fn submit_reveal_pk< client: &C, wallet: &mut Wallet, args: args::RevealPk, -) { +) -> Result<(), Error> { let args::RevealPk { tx: args, public_key, } = args; let public_key = public_key; - if !reveal_pk_if_needed::(client, wallet, &public_key, &args).await { + if !reveal_pk_if_needed::(client, wallet, &public_key, &args).await? { let addr: Address = (&public_key).into(); println!("PK for {addr} is already revealed, nothing to do."); + Ok(()) + } else { + Ok(()) } } @@ -226,15 +235,15 @@ pub async fn reveal_pk_if_needed< wallet: &mut Wallet, public_key: &common::PublicKey, args: &args::Tx, -) -> bool { +) -> Result { let addr: Address = public_key.into(); // Check if PK revealed if args.force || !has_revealed_pk(client, &addr).await { // If not, submit it - submit_reveal_pk_aux::(client, wallet, public_key, args).await; - true + submit_reveal_pk_aux::(client, wallet, public_key, args).await?; + Ok(true) } else { - false + Ok(false) } } @@ -784,7 +793,7 @@ pub async fn is_safe_voting_window< client: &C, proposal_id: u64, proposal_start_epoch: Epoch, -) -> bool { +) -> Result { let current_epoch = rpc::query_epoch(client).await; let proposal_end_epoch_key = @@ -795,14 +804,14 @@ pub async fn is_safe_voting_window< match proposal_end_epoch { Some(proposal_end_epoch) => { - !crate::ledger::native_vp::governance::utils::is_valid_validator_voting_period( + Ok(!crate::ledger::native_vp::governance::utils::is_valid_validator_voting_period( current_epoch, proposal_start_epoch, proposal_end_epoch, - ) + )) } None => { - panic!("Proposal end epoch is not in the storage."); + Err(Error::EpochNotInStorage) } } } @@ -1052,7 +1061,7 @@ pub async fn submit_init_account< let public_key = args.public_key; let vp_code = args.vp_code_path; // Validate the VP code - validate_untrusted_code_err(&vp_code,args.tx.force)?; + validate_untrusted_code_err(&vp_code, args.tx.force)?; let tx_code = args.tx_code_path; let data = InitAccount { From 02e8d3b371efa961e85d12592325661227ef621a Mon Sep 17 00:00:00 2001 From: mariari Date: Tue, 20 Dec 2022 14:53:32 +0800 Subject: [PATCH 43/51] Using the given result types in client, and progate usage in shared --- apps/src/lib/client/tx.rs | 53 ++++++++++++++++++++------------------- shared/src/ledger/tx.rs | 12 ++++----- 2 files changed, 32 insertions(+), 33 deletions(-) diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index f511a52e20..0899310817 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -35,7 +35,6 @@ use crate::cli::context::WalletAddress; use crate::cli::{args, safe_exit, Context}; use crate::client::signing::find_keypair; use crate::facade::tendermint_rpc::endpoint::broadcast::tx_sync::Response; -use crate::facade::tendermint_rpc::error::Error as RpcError; use crate::facade::tendermint_rpc::{Client, HttpClient}; use crate::node::ledger::tendermint_node; use crate::wallet::{gen_validator_keys, CliWalletUtils}; @@ -47,8 +46,8 @@ pub async fn submit_custom< client: &C, wallet: &mut Wallet, args: args::TxCustom, -) { - tx::submit_custom::(client, wallet, args).await; +) -> Result<(), tx::Error> { + tx::submit_custom::(client, wallet, args).await } pub async fn submit_update_vp< @@ -58,8 +57,8 @@ pub async fn submit_update_vp< client: &C, wallet: &mut Wallet, args: args::TxUpdateVp, -) { - tx::submit_update_vp::(client, wallet, args).await; +) -> Result<(), tx::Error> { + tx::submit_update_vp::(client, wallet, args).await } pub async fn submit_init_account< @@ -69,8 +68,8 @@ pub async fn submit_init_account< client: &C, wallet: &mut Wallet, args: args::TxInitAccount, -) { - tx::submit_init_account::(client, wallet, args).await; +) -> Result<(), tx::Error> { + tx::submit_init_account::(client, wallet, args).await } pub async fn submit_init_validator< @@ -394,8 +393,8 @@ pub async fn submit_transfer< wallet: &mut Wallet, shielded: &mut ShieldedContext, args: args::TxTransfer, -) { - tx::submit_transfer::(client, wallet, shielded, args).await; +) -> Result<(), tx::Error> { + tx::submit_transfer::(client, wallet, shielded, args).await } pub async fn submit_ibc_transfer< @@ -405,8 +404,8 @@ pub async fn submit_ibc_transfer< client: &C, wallet: &mut Wallet, args: args::TxIbcTransfer, -) { - tx::submit_ibc_transfer::(client, wallet, args).await; +) -> Result<(), tx::Error> { + tx::submit_ibc_transfer::(client, wallet, args).await } pub async fn submit_init_proposal< @@ -415,7 +414,7 @@ pub async fn submit_init_proposal< client: &C, mut ctx: Context, args: args::InitProposal, -) { +) -> Result<(), tx::Error> { 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"); @@ -503,6 +502,7 @@ pub async fn submit_init_proposal< safe_exit(1) } } + Ok(()) } else { let signer = ctx.get(&signer); let tx_data: Result = proposal.clone().try_into(); @@ -550,7 +550,8 @@ pub async fn submit_init_proposal< tx, TxSigningKey::WalletAddress(signer), ) - .await; + .await?; + Ok(()) } } @@ -680,7 +681,7 @@ pub async fn submit_vote_proposal< tx, TxSigningKey::WalletAddress(signer.clone()), ) - .await; + .await?; Ok(()) } None => { @@ -701,8 +702,8 @@ pub async fn submit_reveal_pk< client: &C, wallet: &mut Wallet, args: args::RevealPk, -) { - tx::submit_reveal_pk::(client, wallet, args).await; +) -> Result<(), tx::Error> { + tx::submit_reveal_pk::(client, wallet, args).await } pub async fn reveal_pk_if_needed< @@ -734,8 +735,8 @@ pub async fn submit_reveal_pk_aux< wallet: &mut Wallet, public_key: &common::PublicKey, args: &args::Tx, -) { - tx::submit_reveal_pk_aux::(client, wallet, public_key, args); +) -> Result<(), tx::Error> { + tx::submit_reveal_pk_aux::(client, wallet, public_key, args).await } /// Check if current epoch is in the last third of the voting period of the @@ -799,8 +800,8 @@ pub async fn submit_bond< client: &C, wallet: &mut Wallet, args: args::Bond, -) { - tx::submit_bond::(client, wallet, args).await; +) -> Result<(), tx::Error> { + tx::submit_bond::(client, wallet, args).await } pub async fn submit_unbond< @@ -810,8 +811,8 @@ pub async fn submit_unbond< client: &C, wallet: &mut Wallet, args: args::Unbond, -) { - tx::submit_unbond::(client, wallet, args).await; +) -> Result<(), tx::Error> { + tx::submit_unbond::(client, wallet, args).await } pub async fn submit_withdraw< @@ -821,8 +822,8 @@ pub async fn submit_withdraw< client: &C, wallet: &mut Wallet, args: args::Withdraw, -) { - tx::submit_withdraw::(client, wallet, args).await; +) -> Result<(), tx::Error> { + tx::submit_withdraw::(client, wallet, args).await } pub async fn submit_validator_commission_change< @@ -832,8 +833,8 @@ pub async fn submit_validator_commission_change< client: &C, wallet: &mut Wallet, args: args::TxCommissionRateChange, -) { - tx::submit_validator_commission_change::(client, wallet, args).await; +) -> Result<(), tx::Error> { + tx::submit_validator_commission_change::(client, wallet, args).await } /// Submit transaction and wait for result. Returns a list of addresses diff --git a/shared/src/ledger/tx.rs b/shared/src/ledger/tx.rs index e126a2b429..3677009419 100644 --- a/shared/src/ledger/tx.rs +++ b/shared/src/ledger/tx.rs @@ -9,7 +9,6 @@ use rust_decimal::Decimal; use thiserror::Error; use tokio::time::{Duration, Instant}; -use super::args::KeyList; use crate::ibc::applications::ics20_fungible_token_transfer::msgs::transfer::MsgTransfer; use crate::ibc::signer::Signer; use crate::ibc::timestamp::Timestamp as IbcTimestamp; @@ -22,7 +21,6 @@ use crate::ledger::masp::{ShieldedContext, ShieldedUtils}; use crate::ledger::pos::{BondId, Bonds, CommissionRates, Unbonds}; use crate::ledger::rpc::{self, TxBroadcastData, TxResponse}; use crate::ledger::signing::{find_keypair, sign_tx, tx_signer, TxSigningKey}; -use crate::ledger::tx::Error::EncodeTxFailure; use crate::ledger::wallet::{Wallet, WalletUtils}; use crate::proto::Tx; use crate::tendermint_rpc::endpoint::broadcast::tx_sync::Response; @@ -568,7 +566,7 @@ pub async fn submit_validator_commission_change< tx, TxSigningKey::WalletAddress(default_signer), ) - .await; + .await?; Ok(()) } @@ -643,7 +641,7 @@ pub async fn submit_withdraw< tx, TxSigningKey::WalletAddress(default_signer), ) - .await; + .await?; Ok(()) } @@ -723,7 +721,7 @@ pub async fn submit_unbond< tx, TxSigningKey::WalletAddress(default_signer), ) - .await; + .await?; Ok(()) } @@ -780,7 +778,7 @@ pub async fn submit_bond< tx, TxSigningKey::WalletAddress(default_signer), ) - .await; + .await?; Ok(()) } @@ -1161,7 +1159,7 @@ pub async fn submit_update_vp< tx, TxSigningKey::WalletAddress(args.addr), ) - .await; + .await?; Ok(()) } From b30e11ed82dbb883bcc55f212063ca19c5d1f436 Mon Sep 17 00:00:00 2001 From: mariari Date: Tue, 20 Dec 2022 15:34:03 +0800 Subject: [PATCH 44/51] Removing panics in singing and promoting them to the Result type Also remove mutable logic in tx_signer --- apps/src/lib/client/signing.rs | 9 ++-- apps/src/lib/client/tx.rs | 4 +- shared/src/ledger/signing.rs | 85 ++++++++++++++++++---------------- shared/src/ledger/tx.rs | 8 ++-- 4 files changed, 55 insertions(+), 51 deletions(-) diff --git a/apps/src/lib/client/signing.rs b/apps/src/lib/client/signing.rs index 23cdda81d7..c1c2db45a4 100644 --- a/apps/src/lib/client/signing.rs +++ b/apps/src/lib/client/signing.rs @@ -8,6 +8,7 @@ use namada::proto::Tx; use namada::types::address::Address; use namada::types::key::*; use namada::types::storage::Epoch; +use namada::ledger::tx; use crate::cli::args; use crate::facade::tendermint_rpc::Client; @@ -21,7 +22,7 @@ pub async fn find_keypair< client: &C, wallet: &mut Wallet, addr: &Address, -) -> common::SecretKey { +) -> Result { namada::ledger::signing::find_keypair::(client, wallet, addr).await } @@ -36,8 +37,8 @@ pub async fn tx_signer< client: &C, wallet: &mut Wallet, args: &args::Tx, - mut default: TxSigningKey, -) -> common::SecretKey { + default: TxSigningKey, +) -> Result { namada::ledger::signing::tx_signer::(client, wallet, args, default) .await } @@ -59,7 +60,7 @@ pub async fn sign_tx< tx: Tx, args: &args::Tx, default: TxSigningKey, -) -> TxBroadcastData { +) -> Result { namada::ledger::signing::sign_tx::(client, wallet, tx, args, default) .await } diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index 0899310817..8affaaeb65 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -481,7 +481,7 @@ pub async fn submit_init_proposal< let signer = ctx.get(&signer); let signing_key = find_keypair::(client, &mut ctx.wallet, &signer) - .await; + .await?; let offline_proposal = OfflineProposal::new(proposal, signer, &signing_key); let proposal_filename = args @@ -587,7 +587,7 @@ pub async fn submit_vote_proposal< safe_exit(1) } - let signing_key = find_keypair::(client, wallet, &signer).await; + let signing_key = find_keypair::(client, wallet, &signer).await?; let offline_vote = OfflineVote::new( &proposal, args.vote, diff --git a/shared/src/ledger/signing.rs b/shared/src/ledger/signing.rs index d1ae55da65..aeb4d3801f 100644 --- a/shared/src/ledger/signing.rs +++ b/shared/src/ledger/signing.rs @@ -2,6 +2,7 @@ use borsh::BorshSerialize; use namada_core::types::address::{Address, ImplicitAddress}; use crate::ledger::rpc::TxBroadcastData; +use crate::ledger::tx::Error; use crate::ledger::wallet::{Wallet, WalletUtils}; use crate::ledger::{args, rpc}; use crate::proto::Tx; @@ -11,7 +12,7 @@ use crate::types::storage::Epoch; use crate::types::transaction::{hash_tx, Fee, WrapperTx}; /// Find the public key for the given address and try to load the keypair -/// for it from the wallet. Panics if the key cannot be found or loaded. +/// for it from the wallet. Errors if the key cannot be found or loaded. pub async fn find_keypair< C: Client + crate::ledger::queries::Client + Sync, U: WalletUtils, @@ -19,41 +20,41 @@ pub async fn find_keypair< client: &C, wallet: &mut Wallet, addr: &Address, -) -> common::SecretKey { +) -> Result { match addr { Address::Established(_) => { println!( "Looking-up public key of {} from the ledger...", addr.encode() ); - let public_key = - rpc::get_public_key(client, addr).await.unwrap_or_else(|| { - panic!( - "No public key found for the address {}", - addr.encode() - ); - }); - wallet.find_key_by_pk(&public_key).unwrap_or_else(|err| { - panic!( + let public_key = rpc::get_public_key(client, addr).await.ok_or( + Error::Other(format!( + "No public key found for the address {}", + addr.encode() + )), + )?; + wallet.find_key_by_pk(&public_key).map_err(|err| { + Error::Other(format!( "Unable to load the keypair from the wallet for public \ key {}. Failed with: {}", public_key, err - ); + )) }) } Address::Implicit(ImplicitAddress(pkh)) => { - wallet.find_key_by_pkh(pkh).unwrap_or_else(|err| { - panic!( + wallet.find_key_by_pkh(pkh).map_err(|err| { + Error::Other(format!( "Unable to load the keypair from the wallet for the \ implicit address {}. Failed with: {}", addr.encode(), err - ); + )) }) } - Address::Internal(_) => { - panic!("Internal address {} doesn't have any signing keys.", addr); - } + Address::Internal(_) => other_err(format!( + "Internal address {} doesn't have any signing keys.", + addr + )), } } @@ -74,7 +75,7 @@ pub enum TxSigningKey { /// Given CLI arguments and some defaults, determine the rightful transaction /// signer. Return the given signing key or public key of the given signer if /// possible. If no explicit signer given, use the `default`. If no `default` -/// is given, panics. +/// is given, an `Error` is returned. pub async fn tx_signer< C: Client + crate::ledger::queries::Client + Sync, U: WalletUtils, @@ -82,21 +83,23 @@ pub async fn tx_signer< client: &C, wallet: &mut Wallet, args: &args::Tx, - mut default: TxSigningKey, -) -> common::SecretKey { + default: TxSigningKey, +) -> Result { // Override the default signing key source if possible - if let Some(signing_key) = &args.signing_key { - default = TxSigningKey::WalletKeypair(signing_key.clone()); + let default = if let Some(signing_key) = &args.signing_key { + TxSigningKey::WalletKeypair(signing_key.clone()) } else if let Some(signer) = &args.signer { - default = TxSigningKey::WalletAddress(signer.clone()); - } + TxSigningKey::WalletAddress(signer.clone()) + } else { + default + }; // Now actually fetch the signing key and apply it match default { - TxSigningKey::WalletKeypair(signing_key) => signing_key, + TxSigningKey::WalletKeypair(signing_key) => Ok(signing_key), TxSigningKey::WalletAddress(signer) => { let signer = signer; let signing_key = - find_keypair::(client, wallet, &signer).await; + find_keypair::(client, wallet, &signer).await?; // Check if the signer is implicit account that needs to reveal its // PK first if matches!(signer, Address::Implicit(_)) { @@ -106,27 +109,24 @@ pub async fn tx_signer< ) .await; } - signing_key + Ok(signing_key) } TxSigningKey::SecretKey(signing_key) => { // Check if the signing key needs to reveal its PK first let pk: common::PublicKey = signing_key.ref_to(); super::tx::reveal_pk_if_needed::(client, wallet, &pk, args) .await; - signing_key - } - TxSigningKey::None => { - panic!( - "All transactions must be signed; please either specify the \ - key or the address from which to look up the signing key." - ); + Ok(signing_key) } + TxSigningKey::None => + other_err("All transactions must be signed; please either specify the \ + key or the address from which to look up the signing key.".to_string()) } } /// Sign a transaction with a given signing key or public key of a given signer. /// If no explicit signer given, use the `default`. If no `default` is given, -/// panics. +/// Error. /// /// If this is not a dry run, the tx is put in a wrapper and returned along with /// hashes needed for monitoring the tx on chain. @@ -141,17 +141,16 @@ pub async fn sign_tx< tx: Tx, args: &args::Tx, default: TxSigningKey, -) -> TxBroadcastData { - let keypair = tx_signer::(client, wallet, args, default).await; +) -> Result { + let keypair = tx_signer::(client, wallet, args, default).await?; let tx = tx.sign(&keypair); let epoch = rpc::query_epoch(client).await; - let broadcast_data = if args.dry_run { + Ok(if args.dry_run { TxBroadcastData::DryRun(tx) } else { sign_wrapper(args, epoch, tx, &keypair).await - }; - broadcast_data + }) } /// Create a wrapper tx from a normal tx. Get the hash of the @@ -191,3 +190,7 @@ pub async fn sign_wrapper( decrypted_hash, } } + +fn other_err(string: String) -> Result { + Err(Error::Other(string)) +} diff --git a/shared/src/ledger/tx.rs b/shared/src/ledger/tx.rs index 3677009419..76d88221e7 100644 --- a/shared/src/ledger/tx.rs +++ b/shared/src/ledger/tx.rs @@ -171,7 +171,7 @@ pub async fn process_tx< default_signer: TxSigningKey, ) -> Result, Error> { let to_broadcast = - sign_tx::(client, wallet, tx, args, default_signer).await; + sign_tx::(client, wallet, tx, args, default_signer).await?; // NOTE: use this to print the request JSON body: // let request = @@ -271,13 +271,13 @@ pub async fn submit_reveal_pk_aux< // submit_tx without signing the inner tx let keypair = if let Some(signing_key) = &args.signing_key { - signing_key.clone() + Ok(signing_key.clone()) } else if let Some(signer) = args.signer.as_ref() { let signer = signer; find_keypair::(client, wallet, &signer).await } else { find_keypair::(client, wallet, &addr).await - }; + }?; let epoch = rpc::query_epoch(client).await; let to_broadcast = if args.dry_run { TxBroadcastData::DryRun(tx) @@ -995,7 +995,7 @@ pub async fn submit_transfer< // will need to cover the gas fees. let chosen_signer = tx_signer::(client, wallet, &args.tx, default_signer.clone()) - .await + .await? .ref_to(); let shielded_gas = masp_tx_key().ref_to() == chosen_signer; // Determine whether to pin this transaction to a storage key From 0118b13fca21e78f56cb9caa04b7796c4efb565d Mon Sep 17 00:00:00 2001 From: mariari Date: Tue, 20 Dec 2022 17:53:53 +0800 Subject: [PATCH 45/51] Remove all warnings not associated with a lack of a doc string --- apps/src/bin/anoma-client/cli.rs | 24 ++++++++++----------- apps/src/lib/client/mod.rs | 1 - apps/src/lib/client/rpc.rs | 21 +++++++----------- apps/src/lib/client/tendermint_rpc_types.rs | 8 ------- apps/src/lib/node/ledger/shell/mod.rs | 1 - apps/src/lib/wallet/store.rs | 3 +-- shared/src/ledger/masp.rs | 5 ++--- shared/src/ledger/rpc.rs | 4 ++-- shared/src/ledger/signing.rs | 4 ++-- shared/src/ledger/wallet/mod.rs | 2 +- 10 files changed, 27 insertions(+), 46 deletions(-) delete mode 100644 apps/src/lib/client/tendermint_rpc_types.rs diff --git a/apps/src/bin/anoma-client/cli.rs b/apps/src/bin/anoma-client/cli.rs index cfa20d0539..f8a605455b 100644 --- a/apps/src/bin/anoma-client/cli.rs +++ b/apps/src/bin/anoma-client/cli.rs @@ -1,7 +1,5 @@ //! Anoma client CLI. -use std::path::PathBuf; - use color_eyre::eyre::Result; use namada_apps::cli; use namada_apps::cli::args::CliToSdk; @@ -28,7 +26,7 @@ pub async fn main() -> Result<()> { &mut ctx.wallet, args, ) - .await; + .await?; if !dry_run { namada_apps::wallet::save(&ctx.wallet) .unwrap_or_else(|err| eprintln!("{}", err)); @@ -50,7 +48,7 @@ pub async fn main() -> Result<()> { &mut ctx.shielded, args, ) - .await; + .await?; } Sub::TxIbcTransfer(TxIbcTransfer(args)) => { let client = @@ -62,7 +60,7 @@ pub async fn main() -> Result<()> { &mut ctx.wallet, args, ) - .await; + .await?; } Sub::TxUpdateVp(TxUpdateVp(args)) => { let client = @@ -74,7 +72,7 @@ pub async fn main() -> Result<()> { &mut ctx.wallet, args, ) - .await; + .await?; } Sub::TxInitAccount(TxInitAccount(args)) => { let client = @@ -87,7 +85,7 @@ pub async fn main() -> Result<()> { &mut ctx.wallet, args, ) - .await; + .await?; if !dry_run { namada_apps::wallet::save(&ctx.wallet) .unwrap_or_else(|err| eprintln!("{}", err)); @@ -112,7 +110,7 @@ pub async fn main() -> Result<()> { .unwrap(); let args = args.to_sdk(&mut ctx); tx::submit_init_proposal::(&client, ctx, args) - .await; + .await?; } Sub::TxVoteProposal(TxVoteProposal(args)) => { let client = @@ -124,7 +122,7 @@ pub async fn main() -> Result<()> { &mut ctx.wallet, args, ) - .await; + .await?; } Sub::TxRevealPk(TxRevealPk(args)) => { let client = @@ -136,7 +134,7 @@ pub async fn main() -> Result<()> { &mut ctx.wallet, args, ) - .await; + .await?; } Sub::Bond(Bond(args)) => { let client = @@ -148,7 +146,7 @@ pub async fn main() -> Result<()> { &mut ctx.wallet, args, ) - .await; + .await?; } Sub::Unbond(Unbond(args)) => { let client = @@ -160,7 +158,7 @@ pub async fn main() -> Result<()> { &mut ctx.wallet, args, ) - .await; + .await?; } Sub::Withdraw(Withdraw(args)) => { let client = @@ -172,7 +170,7 @@ pub async fn main() -> Result<()> { &mut ctx.wallet, args, ) - .await; + .await?; } // Ledger queries Sub::QueryEpoch(QueryEpoch(args)) => { diff --git a/apps/src/lib/client/mod.rs b/apps/src/lib/client/mod.rs index 9807ca6a30..57f3c5a043 100644 --- a/apps/src/lib/client/mod.rs +++ b/apps/src/lib/client/mod.rs @@ -1,5 +1,4 @@ pub mod rpc; pub mod signing; -pub mod tendermint_rpc_types; pub mod tx; pub mod utils; diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index ad9b3b175e..71b70c47fe 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -14,7 +14,6 @@ use async_std::path::PathBuf; use async_std::prelude::*; use borsh::{BorshDeserialize, BorshSerialize}; use data_encoding::HEXLOWER; -use eyre::{eyre, Context as EyreContext}; use itertools::{Either, Itertools}; use masp_primitives::asset_type::AssetType; use masp_primitives::merkle_tree::MerklePath; @@ -32,35 +31,31 @@ use namada::ledger::native_vp::governance::utils::Votes; use namada::ledger::parameters::{storage as param_storage, EpochDuration}; use namada::ledger::pos::types::{decimal_mult_u64, WeightedValidator}; use namada::ledger::pos::{ - self, is_validator_slashes_key, BondId, Bonds, PosParams, Slash, Unbonds, + self, is_validator_slashes_key, Bonds, PosParams, Slash, Unbonds, }; -use namada::ledger::queries::{self, RPC}; +use namada::ledger::queries::RPC; use namada::ledger::rpc::TxResponse; use namada::ledger::storage::ConversionState; use namada::ledger::wallet::Wallet; use namada::types::address::{masp, tokens, Address}; use namada::types::governance::{ - OfflineProposal, OfflineVote, ProposalResult, ProposalVote, TallyResult, + OfflineProposal, OfflineVote, ProposalResult, VotePower, }; -use namada::types::hash::Hash; + use namada::types::key::*; use namada::types::masp::{BalanceOwner, ExtendedViewingKey, PaymentAddress}; use namada::types::storage::{ - BlockHeight, BlockResults, Epoch, Key, KeySeg, PrefixValue, + BlockHeight, BlockResults, Epoch, Key, KeySeg, }; -use namada::types::token::balance_key; use namada::types::{address, storage, token}; use rust_decimal::Decimal; -use tokio::time::{Duration, Instant}; +use tokio::time::Instant; use crate::cli::{self, args}; use crate::facade::tendermint::merkle::proof::Proof; use crate::facade::tendermint_rpc::error::Error as TError; -use crate::facade::tendermint_rpc::query::Query; -use crate::facade::tendermint_rpc::{ - Client, HttpClient, Order, WebSocketClient, -}; +use crate::facade::tendermint_rpc::Client; use crate::wallet::CliWalletUtils; /// Query the status of a given transaction. @@ -2366,7 +2361,7 @@ fn lookup_alias(wallet: &Wallet, addr: &Address) -> String { fn unwrap_client_response( response: Result, ) -> T { - response.unwrap_or_else(|err| { + response.unwrap_or_else(|_err| { eprintln!("Error in the query"); cli::safe_exit(1) }) diff --git a/apps/src/lib/client/tendermint_rpc_types.rs b/apps/src/lib/client/tendermint_rpc_types.rs deleted file mode 100644 index e01efe5b35..0000000000 --- a/apps/src/lib/client/tendermint_rpc_types.rs +++ /dev/null @@ -1,8 +0,0 @@ -use std::convert::TryFrom; - -use namada::ledger::events::Event; -use namada::proto::Tx; -use namada::types::address::Address; -use serde::Serialize; - -use crate::cli::safe_exit; diff --git a/apps/src/lib/node/ledger/shell/mod.rs b/apps/src/lib/node/ledger/shell/mod.rs index eae5964605..3b86c114f6 100644 --- a/apps/src/lib/node/ledger/shell/mod.rs +++ b/apps/src/lib/node/ledger/shell/mod.rs @@ -62,7 +62,6 @@ use crate::facade::tower_abci::{request, response}; use crate::node::ledger::shims::abcipp_shim_types::shim; use crate::node::ledger::shims::abcipp_shim_types::shim::response::TxResult; use crate::node::ledger::{storage, tendermint_node}; -use crate::wallet::CliWalletUtils; #[allow(unused_imports)] use crate::wallet::ValidatorData; diff --git a/apps/src/lib/wallet/store.rs b/apps/src/lib/wallet/store.rs index 80cb28b524..cd0bf55fee 100644 --- a/apps/src/lib/wallet/store.rs +++ b/apps/src/lib/wallet/store.rs @@ -6,8 +6,7 @@ use std::path::{Path, PathBuf}; use ark_std::rand::prelude::*; use ark_std::rand::SeedableRng; use file_lock::{FileLock, FileOptions}; -use namada::ledger::wallet::{gen_sk, Store, StoredKeypair, ValidatorKeys}; -use namada::types::address::Address; +use namada::ledger::wallet::{gen_sk, Store, ValidatorKeys}; use namada::types::key::*; use namada::types::transaction::EllipticCurve; use thiserror::Error; diff --git a/shared/src/ledger/masp.rs b/shared/src/ledger/masp.rs index 88e5cc5422..1c1df46379 100644 --- a/shared/src/ledger/masp.rs +++ b/shared/src/ledger/masp.rs @@ -6,7 +6,6 @@ use std::env; use std::fmt::Debug; use std::fs::File; #[cfg(feature = "masp-tx-gen")] -use std::io::Read; use std::ops::Deref; use std::path::PathBuf; @@ -62,7 +61,7 @@ use crate::types::masp::{BalanceOwner, ExtendedViewingKey, PaymentAddress}; #[cfg(feature = "masp-tx-gen")] use crate::types::masp::{TransferSource, TransferTarget}; use crate::types::storage::{ - BlockHeight, BlockResults, Epoch, Key, KeySeg, TxIndex, + BlockHeight, Epoch, Key, KeySeg, TxIndex, }; use crate::types::token::{ Transfer, HEAD_TX_KEY, PIN_KEY_PREFIX, TX_KEY_PREFIX, @@ -70,7 +69,7 @@ use crate::types::token::{ use crate::types::transaction::{ process_tx, DecryptedTx, EllipticCurve, PairingEngine, TxType, WrapperTx, }; -use crate::types::{storage, token}; +use crate::types::token; /// Env var to point to a dir with MASP parameters. When not specified, /// the default OS specific path is used. diff --git a/shared/src/ledger/rpc.rs b/shared/src/ledger/rpc.rs index 0d4447510d..045e7c698c 100644 --- a/shared/src/ledger/rpc.rs +++ b/shared/src/ledger/rpc.rs @@ -62,7 +62,7 @@ pub async fn query_tx_status< tracing::debug!(query = ?status, "Querying tx status"); let maybe_event = match query_tx_events(client, status).await { Ok(response) => response, - Err(err) => { + Err(_err) => { // tracing::debug!(%err, "ABCI query failed"); sleep_update(status, &mut backoff).await; continue; @@ -109,7 +109,7 @@ pub async fn query_block( fn unwrap_client_response( response: Result, ) -> T { - response.unwrap_or_else(|err| { + response.unwrap_or_else(|_err| { panic!("Error in the query"); }) } diff --git a/shared/src/ledger/signing.rs b/shared/src/ledger/signing.rs index aeb4d3801f..ca215382f2 100644 --- a/shared/src/ledger/signing.rs +++ b/shared/src/ledger/signing.rs @@ -107,7 +107,7 @@ pub async fn tx_signer< super::tx::reveal_pk_if_needed::( client, wallet, &pk, args, ) - .await; + .await?; } Ok(signing_key) } @@ -115,7 +115,7 @@ pub async fn tx_signer< // Check if the signing key needs to reveal its PK first let pk: common::PublicKey = signing_key.ref_to(); super::tx::reveal_pk_if_needed::(client, wallet, &pk, args) - .await; + .await?; Ok(signing_key) } TxSigningKey::None => diff --git a/shared/src/ledger/wallet/mod.rs b/shared/src/ledger/wallet/mod.rs index 7afc0ae95a..bccd5d6857 100644 --- a/shared/src/ledger/wallet/mod.rs +++ b/shared/src/ledger/wallet/mod.rs @@ -129,7 +129,7 @@ impl Wallet { } /// Returns the validator data, if it exists. - /// [`Wallet::save`] cannot be called after using this + /// [`save`] cannot be called after using this /// method as it involves a partial move pub fn take_validator_data(&mut self) -> Option<&mut ValidatorData> { self.store.validator_data() From 37803201efa7ca12e37e4af0d2cd08dd890c6875 Mon Sep 17 00:00:00 2001 From: mariari Date: Tue, 20 Dec 2022 17:59:39 +0800 Subject: [PATCH 46/51] Remove an explicit reference to `save` as it is in apps not shared --- apps/src/lib/client/rpc.rs | 8 ++------ apps/src/lib/client/signing.rs | 6 +++--- shared/src/ledger/masp.rs | 6 ++---- shared/src/ledger/signing.rs | 8 +++++--- shared/src/ledger/wallet/mod.rs | 6 +++--- 5 files changed, 15 insertions(+), 19 deletions(-) diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index 71b70c47fe..5c32140ddc 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -39,15 +39,11 @@ use namada::ledger::storage::ConversionState; use namada::ledger::wallet::Wallet; use namada::types::address::{masp, tokens, Address}; use namada::types::governance::{ - OfflineProposal, OfflineVote, ProposalResult, - VotePower, + OfflineProposal, OfflineVote, ProposalResult, VotePower, }; - use namada::types::key::*; use namada::types::masp::{BalanceOwner, ExtendedViewingKey, PaymentAddress}; -use namada::types::storage::{ - BlockHeight, BlockResults, Epoch, Key, KeySeg, -}; +use namada::types::storage::{BlockHeight, BlockResults, Epoch, Key, KeySeg}; use namada::types::{address, storage, token}; use rust_decimal::Decimal; use tokio::time::Instant; diff --git a/apps/src/lib/client/signing.rs b/apps/src/lib/client/signing.rs index c1c2db45a4..b5a446a074 100644 --- a/apps/src/lib/client/signing.rs +++ b/apps/src/lib/client/signing.rs @@ -3,12 +3,12 @@ use namada::ledger::rpc::TxBroadcastData; use namada::ledger::signing::TxSigningKey; +use namada::ledger::tx; use namada::ledger::wallet::{Wallet, WalletUtils}; use namada::proto::Tx; use namada::types::address::Address; use namada::types::key::*; use namada::types::storage::Epoch; -use namada::ledger::tx; use crate::cli::args; use crate::facade::tendermint_rpc::Client; @@ -38,7 +38,7 @@ pub async fn tx_signer< wallet: &mut Wallet, args: &args::Tx, default: TxSigningKey, -) -> Result { +) -> Result { namada::ledger::signing::tx_signer::(client, wallet, args, default) .await } @@ -60,7 +60,7 @@ pub async fn sign_tx< tx: Tx, args: &args::Tx, default: TxSigningKey, -) -> Result { +) -> Result { namada::ledger::signing::sign_tx::(client, wallet, tx, args, default) .await } diff --git a/shared/src/ledger/masp.rs b/shared/src/ledger/masp.rs index 1c1df46379..04dafb5e9f 100644 --- a/shared/src/ledger/masp.rs +++ b/shared/src/ledger/masp.rs @@ -60,16 +60,14 @@ use crate::types::address::{masp, Address}; use crate::types::masp::{BalanceOwner, ExtendedViewingKey, PaymentAddress}; #[cfg(feature = "masp-tx-gen")] use crate::types::masp::{TransferSource, TransferTarget}; -use crate::types::storage::{ - BlockHeight, Epoch, Key, KeySeg, TxIndex, -}; +use crate::types::storage::{BlockHeight, Epoch, Key, KeySeg, TxIndex}; +use crate::types::token; use crate::types::token::{ Transfer, HEAD_TX_KEY, PIN_KEY_PREFIX, TX_KEY_PREFIX, }; use crate::types::transaction::{ process_tx, DecryptedTx, EllipticCurve, PairingEngine, TxType, WrapperTx, }; -use crate::types::token; /// Env var to point to a dir with MASP parameters. When not specified, /// the default OS specific path is used. diff --git a/shared/src/ledger/signing.rs b/shared/src/ledger/signing.rs index ca215382f2..e478670bc0 100644 --- a/shared/src/ledger/signing.rs +++ b/shared/src/ledger/signing.rs @@ -118,9 +118,11 @@ pub async fn tx_signer< .await?; Ok(signing_key) } - TxSigningKey::None => - other_err("All transactions must be signed; please either specify the \ - key or the address from which to look up the signing key.".to_string()) + TxSigningKey::None => other_err( + "All transactions must be signed; please either specify the key \ + or the address from which to look up the signing key." + .to_string(), + ), } } diff --git a/shared/src/ledger/wallet/mod.rs b/shared/src/ledger/wallet/mod.rs index bccd5d6857..274a257071 100644 --- a/shared/src/ledger/wallet/mod.rs +++ b/shared/src/ledger/wallet/mod.rs @@ -128,9 +128,9 @@ impl Wallet { self.store.get_validator_data() } - /// Returns the validator data, if it exists. - /// [`save`] cannot be called after using this - /// method as it involves a partial move + /// Returns the validator data, if it exists. the save function + /// cannot be called after using this method as it involves a + /// partial move pub fn take_validator_data(&mut self) -> Option<&mut ValidatorData> { self.store.validator_data() } From 853ab459ebe3c2e33dbc41b517f27ba7480d7d00 Mon Sep 17 00:00:00 2001 From: mariari Date: Wed, 21 Dec 2022 16:00:50 +0800 Subject: [PATCH 47/51] Turn off the default features of tokio --- shared/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/Cargo.toml b/shared/Cargo.toml index 73e6eddd1c..82c5122754 100644 --- a/shared/Cargo.toml +++ b/shared/Cargo.toml @@ -136,7 +136,7 @@ zeroize = "1.5.5" toml = "0.5.8" bimap = {version = "0.6.2", features = ["serde"]} orion = "0.16.0" -tokio = {version = "1.8.2"} +tokio = {version = "1.8.2", default-features = false} [dev-dependencies] assert_matches = "1.5.0" From 6a217af4feccee51f9659d517b72074728191461 Mon Sep 17 00:00:00 2001 From: mariari Date: Thu, 22 Dec 2022 11:59:34 +0800 Subject: [PATCH 48/51] UMove the useage of tendermint_rpc to the crate level --- shared/src/ledger/masp.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shared/src/ledger/masp.rs b/shared/src/ledger/masp.rs index 04dafb5e9f..7a620cdc27 100644 --- a/shared/src/ledger/masp.rs +++ b/shared/src/ledger/masp.rs @@ -50,7 +50,7 @@ use namada_core::types::transaction::AffineCurve; use rand_core::{CryptoRng, OsRng, RngCore}; #[cfg(feature = "masp-tx-gen")] use sha2::Digest; -use tendermint_rpc::Client; +use crate::tendermint_rpc::Client; use crate::ledger::rpc; use crate::proto::{SignedTxData, Tx}; @@ -260,7 +260,7 @@ pub trait ShieldedUtils: { /// The type of the Tendermint client to make queries with type C: crate::ledger::queries::Client - + tendermint_rpc::Client + + crate::tendermint_rpc::Client + std::marker::Sync; /// Get a MASP transaction prover From 04fa51bbab2bc97b37a7ef8025a6e0f6150d52e5 Mon Sep 17 00:00:00 2001 From: mariari Date: Thu, 22 Dec 2022 12:54:39 +0800 Subject: [PATCH 49/51] Add a namada-sdk feature --- shared/Cargo.toml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/shared/Cargo.toml b/shared/Cargo.toml index 82c5122754..23348f8563 100644 --- a/shared/Cargo.toml +++ b/shared/Cargo.toml @@ -9,7 +9,7 @@ version = "0.10.1" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] -default = ["abciplus"] +default = ["abciplus", "namada-sdk"] # NOTE "dev" features that shouldn't be used in live networks are enabled by default for now dev = [] ferveo-tpke = [ @@ -80,6 +80,13 @@ testing = [ "tempfile", ] +namada-sdk = [ + "tendermint-rpc", + "masp-tx-gen", + "ferveo-tpke", + "masp_primitives/transparent-inputs", +] + [dependencies] namada_core = {path = "../core", features = ["secp256k1-sign-verify"]} namada_proof_of_stake = {path = "../proof_of_stake"} From 9e0c9b26f3414abbb7be6ba8fa3bf188180c2481 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Thu, 22 Dec 2022 11:54:36 +0100 Subject: [PATCH 50/51] deps: update to masp crate with wasm-compatible libsecp256k1 --- Cargo.lock | 103 ++++++--- apps/Cargo.toml | 4 +- core/Cargo.toml | 2 +- shared/Cargo.toml | 4 +- shared/src/ledger/masp.rs | 18 +- tx_prelude/Cargo.toml | 2 +- vm_env/Cargo.toml | 4 +- wasm/Cargo.lock | 305 ++++++++++++++++++++++++-- wasm/wasm_source/Cargo.toml | 4 +- wasm_for_tests/wasm_source/Cargo.lock | 305 ++++++++++++++++++++++++-- 10 files changed, 663 insertions(+), 88 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8d751eef10..a09dda12c4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -613,7 +613,7 @@ checksum = "42b2a9a8e3c7544f5ce2b475f2f56580a3102b37e0ee001558ad4faedcf56cf4" dependencies = [ "bech32", "bitcoin_hashes", - "secp256k1 0.22.1", + "secp256k1", "serde 1.0.147", ] @@ -1885,7 +1885,7 @@ dependencies = [ [[package]] name = "equihash" version = "0.1.0" -source = "git+https://github.com/zcash/librustzcash/?rev=2425a08#2425a0869098e3b0588ccd73c42716bcf418612c" +source = "git+https://github.com/zcash/librustzcash?rev=2425a08#2425a0869098e3b0588ccd73c42716bcf418612c" dependencies = [ "blake2b_simd 1.0.0", "byteorder", @@ -3177,15 +3177,45 @@ dependencies = [ "base64 0.13.1", "digest 0.9.0", "hmac-drbg", - "libsecp256k1-core", - "libsecp256k1-gen-ecmult", - "libsecp256k1-gen-genmult", + "libsecp256k1-core 0.3.0 (git+https://github.com/heliaxdev/libsecp256k1?rev=bbb3bd44a49db361f21d9db80f9a087c194c0ae9)", + "libsecp256k1-gen-ecmult 0.3.0 (git+https://github.com/heliaxdev/libsecp256k1?rev=bbb3bd44a49db361f21d9db80f9a087c194c0ae9)", + "libsecp256k1-gen-genmult 0.3.0 (git+https://github.com/heliaxdev/libsecp256k1?rev=bbb3bd44a49db361f21d9db80f9a087c194c0ae9)", + "rand 0.8.5", + "serde 1.0.147", + "sha2 0.9.9", + "typenum", +] + +[[package]] +name = "libsecp256k1" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95b09eff1b35ed3b33b877ced3a691fc7a481919c7e29c53c906226fcf55e2a1" +dependencies = [ + "arrayref", + "base64 0.13.1", + "digest 0.9.0", + "hmac-drbg", + "libsecp256k1-core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libsecp256k1-gen-ecmult 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libsecp256k1-gen-genmult 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.8.5", "serde 1.0.147", "sha2 0.9.9", "typenum", ] +[[package]] +name = "libsecp256k1-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451" +dependencies = [ + "crunchy 0.2.2", + "digest 0.9.0", + "subtle", +] + [[package]] name = "libsecp256k1-core" version = "0.3.0" @@ -3196,12 +3226,30 @@ dependencies = [ "subtle", ] +[[package]] +name = "libsecp256k1-gen-ecmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3038c808c55c87e8a172643a7d87187fc6c4174468159cb3090659d55bcb4809" +dependencies = [ + "libsecp256k1-core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "libsecp256k1-gen-ecmult" version = "0.3.0" source = "git+https://github.com/heliaxdev/libsecp256k1?rev=bbb3bd44a49db361f21d9db80f9a087c194c0ae9#bbb3bd44a49db361f21d9db80f9a087c194c0ae9" dependencies = [ - "libsecp256k1-core", + "libsecp256k1-core 0.3.0 (git+https://github.com/heliaxdev/libsecp256k1?rev=bbb3bd44a49db361f21d9db80f9a087c194c0ae9)", +] + +[[package]] +name = "libsecp256k1-gen-genmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db8d6ba2cec9eacc40e6e8ccc98931840301f1006e95647ceb2dd5c3aa06f7c" +dependencies = [ + "libsecp256k1-core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -3209,7 +3257,7 @@ name = "libsecp256k1-gen-genmult" version = "0.3.0" source = "git+https://github.com/heliaxdev/libsecp256k1?rev=bbb3bd44a49db361f21d9db80f9a087c194c0ae9#bbb3bd44a49db361f21d9db80f9a087c194c0ae9" dependencies = [ - "libsecp256k1-core", + "libsecp256k1-core 0.3.0 (git+https://github.com/heliaxdev/libsecp256k1?rev=bbb3bd44a49db361f21d9db80f9a087c194c0ae9)", ] [[package]] @@ -3341,7 +3389,7 @@ dependencies = [ [[package]] name = "masp_primitives" version = "0.5.0" -source = "git+https://github.com/anoma/masp?rev=bee40fc465f6afbd10558d12fe96eb1742eee45c#bee40fc465f6afbd10558d12fe96eb1742eee45c" +source = "git+https://github.com/anoma/masp?rev=117bff3b0d3e15aff993c25ff6222d411136559d#117bff3b0d3e15aff993c25ff6222d411136559d" dependencies = [ "aes", "bip0039", @@ -3360,10 +3408,10 @@ dependencies = [ "incrementalmerkletree", "jubjub", "lazy_static", + "libsecp256k1 0.7.1", "rand 0.8.5", "rand_core 0.6.4", "ripemd160", - "secp256k1 0.20.3", "serde 1.0.147", "sha2 0.9.9", "subtle", @@ -3374,7 +3422,7 @@ dependencies = [ [[package]] name = "masp_proofs" version = "0.5.0" -source = "git+https://github.com/anoma/masp?rev=bee40fc465f6afbd10558d12fe96eb1742eee45c#bee40fc465f6afbd10558d12fe96eb1742eee45c" +source = "git+https://github.com/anoma/masp?rev=117bff3b0d3e15aff993c25ff6222d411136559d#117bff3b0d3e15aff993c25ff6222d411136559d" dependencies = [ "bellman", "blake2b_simd 1.0.0", @@ -3639,12 +3687,13 @@ dependencies = [ "clru", "data-encoding", "derivative", + "getrandom 0.2.8", "ibc 0.14.0 (git+https://github.com/heliaxdev/ibc-rs?rev=9fcc1c8c19db6af50806ffe5b2f6c214adcbfd5d)", "ibc 0.14.0 (git+https://github.com/heliaxdev/ibc-rs.git?rev=f4703dfe2c1f25cc431279ab74f10f3e0f6827e2)", "ibc-proto 0.17.1 (git+https://github.com/heliaxdev/ibc-rs?rev=9fcc1c8c19db6af50806ffe5b2f6c214adcbfd5d)", "ibc-proto 0.17.1 (git+https://github.com/heliaxdev/ibc-rs.git?rev=f4703dfe2c1f25cc431279ab74f10f3e0f6827e2)", "itertools", - "libsecp256k1", + "libsecp256k1 0.7.0", "loupe", "masp_primitives", "masp_proofs", @@ -3798,7 +3847,7 @@ dependencies = [ "ibc-proto 0.17.1 (git+https://github.com/heliaxdev/ibc-rs.git?rev=f4703dfe2c1f25cc431279ab74f10f3e0f6827e2)", "ics23", "itertools", - "libsecp256k1", + "libsecp256k1 0.7.0", "masp_primitives", "pretty_assertions", "proptest", @@ -5556,34 +5605,16 @@ dependencies = [ "zeroize", ] -[[package]] -name = "secp256k1" -version = "0.20.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97d03ceae636d0fed5bae6a7f4f664354c5f4fcedf6eef053fef17e49f837d0a" -dependencies = [ - "secp256k1-sys 0.4.2", -] - [[package]] name = "secp256k1" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26947345339603ae8395f68e2f3d85a6b0a8ddfe6315818e80b8504415099db0" dependencies = [ - "secp256k1-sys 0.5.2", + "secp256k1-sys", "serde 1.0.147", ] -[[package]] -name = "secp256k1-sys" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957da2573cde917463ece3570eab4a0b3f19de6f1646cde62e6fd3868f566036" -dependencies = [ - "cc", -] - [[package]] name = "secp256k1-sys" version = "0.5.2" @@ -8016,7 +8047,7 @@ dependencies = [ [[package]] name = "zcash_encoding" version = "0.0.0" -source = "git+https://github.com/zcash/librustzcash/?rev=2425a08#2425a0869098e3b0588ccd73c42716bcf418612c" +source = "git+https://github.com/zcash/librustzcash?rev=2425a08#2425a0869098e3b0588ccd73c42716bcf418612c" dependencies = [ "byteorder", "nonempty", @@ -8037,7 +8068,7 @@ dependencies = [ [[package]] name = "zcash_note_encryption" version = "0.1.0" -source = "git+https://github.com/zcash/librustzcash/?rev=2425a08#2425a0869098e3b0588ccd73c42716bcf418612c" +source = "git+https://github.com/zcash/librustzcash?rev=2425a08#2425a0869098e3b0588ccd73c42716bcf418612c" dependencies = [ "chacha20", "chacha20poly1305", @@ -8048,7 +8079,7 @@ dependencies = [ [[package]] name = "zcash_primitives" version = "0.5.0" -source = "git+https://github.com/zcash/librustzcash/?rev=2425a08#2425a0869098e3b0588ccd73c42716bcf418612c" +source = "git+https://github.com/zcash/librustzcash?rev=2425a08#2425a0869098e3b0588ccd73c42716bcf418612c" dependencies = [ "aes", "bip0039", @@ -8074,13 +8105,13 @@ dependencies = [ "sha2 0.9.9", "subtle", "zcash_encoding", - "zcash_note_encryption 0.1.0 (git+https://github.com/zcash/librustzcash/?rev=2425a08)", + "zcash_note_encryption 0.1.0 (git+https://github.com/zcash/librustzcash?rev=2425a08)", ] [[package]] name = "zcash_proofs" version = "0.5.0" -source = "git+https://github.com/zcash/librustzcash/?rev=2425a08#2425a0869098e3b0588ccd73c42716bcf418612c" +source = "git+https://github.com/zcash/librustzcash?rev=2425a08#2425a0869098e3b0588ccd73c42716bcf418612c" dependencies = [ "bellman", "blake2b_simd 1.0.0", diff --git a/apps/Cargo.toml b/apps/Cargo.toml index edd67efb1d..e456f5f2cd 100644 --- a/apps/Cargo.toml +++ b/apps/Cargo.toml @@ -143,8 +143,8 @@ tracing-subscriber = {version = "0.3.7", features = ["env-filter"]} websocket = "0.26.2" winapi = "0.3.9" #libmasp = { git = "https://github.com/anoma/masp", branch = "murisi/masp-incentive" } -masp_primitives = { git = "https://github.com/anoma/masp", rev = "bee40fc465f6afbd10558d12fe96eb1742eee45c", features = ["transparent-inputs"] } -masp_proofs = { git = "https://github.com/anoma/masp", rev = "bee40fc465f6afbd10558d12fe96eb1742eee45c", features = ["bundled-prover", "download-params"] } +masp_primitives = { git = "https://github.com/anoma/masp", rev = "117bff3b0d3e15aff993c25ff6222d411136559d", features = ["transparent-inputs"] } +masp_proofs = { git = "https://github.com/anoma/masp", rev = "117bff3b0d3e15aff993c25ff6222d411136559d", features = ["bundled-prover", "download-params"] } bimap = {version = "0.6.2", features = ["serde"]} rust_decimal = "1.26.1" rust_decimal_macros = "1.26.1" diff --git a/core/Cargo.toml b/core/Cargo.toml index 50015c4f50..bbe20059f8 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -77,7 +77,7 @@ ibc-proto-abcipp = {package = "ibc-proto", git = "https://github.com/heliaxdev/i ics23 = "0.7.0" itertools = "0.10.0" libsecp256k1 = {git = "https://github.com/heliaxdev/libsecp256k1", rev = "bbb3bd44a49db361f21d9db80f9a087c194c0ae9", default-features = false, features = ["std", "static-context"]} -masp_primitives = { git = "https://github.com/anoma/masp", rev = "bee40fc465f6afbd10558d12fe96eb1742eee45c" } +masp_primitives = { git = "https://github.com/anoma/masp", rev = "117bff3b0d3e15aff993c25ff6222d411136559d" } proptest = {git = "https://github.com/heliaxdev/proptest", branch = "tomas/sm", optional = true} prost = "0.9.0" prost-types = "0.9.0" diff --git a/shared/Cargo.toml b/shared/Cargo.toml index 23348f8563..c6f5b951db 100644 --- a/shared/Cargo.toml +++ b/shared/Cargo.toml @@ -135,8 +135,8 @@ wasmer-engine-universal = {version = "=2.2.0", optional = true} wasmer-vm = {version = "2.2.0", optional = true} wasmparser = "0.83.0" #libmasp = { git = "https://github.com/anoma/masp", branch = "murisi/masp-incentive" } -masp_primitives = { git = "https://github.com/anoma/masp", rev = "bee40fc465f6afbd10558d12fe96eb1742eee45c" } -masp_proofs = { git = "https://github.com/anoma/masp", rev = "bee40fc465f6afbd10558d12fe96eb1742eee45c" } +masp_primitives = { git = "https://github.com/anoma/masp", rev = "117bff3b0d3e15aff993c25ff6222d411136559d" } +masp_proofs = { git = "https://github.com/anoma/masp", rev = "117bff3b0d3e15aff993c25ff6222d411136559d" } rand = {version = "0.8", default-features = false, optional = true} rand_core = {version = "0.6", default-features = false, optional = true} zeroize = "1.5.5" diff --git a/shared/src/ledger/masp.rs b/shared/src/ledger/masp.rs index 7a620cdc27..9555012ba1 100644 --- a/shared/src/ledger/masp.rs +++ b/shared/src/ledger/masp.rs @@ -33,7 +33,7 @@ use masp_primitives::primitives::{Diversifier, Note, ViewingKey}; use masp_primitives::redjubjub::PublicKey; use masp_primitives::sapling::Node; #[cfg(feature = "masp-tx-gen")] -use masp_primitives::transaction::builder::{self, secp256k1, *}; +use masp_primitives::transaction::builder::{self, libsecp256k1, *}; use masp_primitives::transaction::components::{ Amount, ConvertDescription, OutputDescription, SpendDescription, }; @@ -50,12 +50,11 @@ use namada_core::types::transaction::AffineCurve; use rand_core::{CryptoRng, OsRng, RngCore}; #[cfg(feature = "masp-tx-gen")] use sha2::Digest; -use crate::tendermint_rpc::Client; use crate::ledger::rpc; use crate::proto::{SignedTxData, Tx}; use crate::tendermint_rpc::query::Query; -use crate::tendermint_rpc::Order; +use crate::tendermint_rpc::{Client, Order}; use crate::types::address::{masp, Address}; use crate::types::masp::{BalanceOwner, ExtendedViewingKey, PaymentAddress}; #[cfg(feature = "masp-tx-gen")] @@ -1176,13 +1175,10 @@ impl ShieldedContext { // We add a dummy UTXO to our transaction, but only the source of // the parent Transfer object is used to validate fund // availability - let secp_sk = secp256k1::SecretKey::from_slice(&[0xcd; 32]) + let secp_sk = libsecp256k1::SecretKey::parse_slice(&[0xcd; 32]) .expect("secret key"); - let secp_ctx = - secp256k1::Secp256k1::::gen_new(); let secp_pk = - secp256k1::PublicKey::from_secret_key(&secp_ctx, &secp_sk) - .serialize(); + libsecp256k1::PublicKey::from_secret_key(&secp_sk).serialize(); let hash = ripemd160::Ripemd160::digest(&sha2::Sha256::digest(&secp_pk)); let script = TransparentAddress::PublicKey(hash.into()).script(); @@ -1251,12 +1247,10 @@ impl ShieldedContext { memo, )?; - let secp_sk = secp256k1::SecretKey::from_slice(&[0xcd; 32]) + let secp_sk = libsecp256k1::SecretKey::parse_slice(&[0xcd; 32]) .expect("secret key"); - let secp_ctx = - secp256k1::Secp256k1::::gen_new(); let secp_pk = - secp256k1::PublicKey::from_secret_key(&secp_ctx, &secp_sk) + libsecp256k1::PublicKey::from_secret_key(&secp_sk) .serialize(); let hash = ripemd160::Ripemd160::digest(&sha2::Sha256::digest( &secp_pk, diff --git a/tx_prelude/Cargo.toml b/tx_prelude/Cargo.toml index c15293016a..76b859f480 100644 --- a/tx_prelude/Cargo.toml +++ b/tx_prelude/Cargo.toml @@ -10,7 +10,7 @@ version = "0.10.1" default = [] [dependencies] -masp_primitives = { git = "https://github.com/anoma/masp", rev = "bee40fc465f6afbd10558d12fe96eb1742eee45c" } +masp_primitives = { git = "https://github.com/anoma/masp", rev = "117bff3b0d3e15aff993c25ff6222d411136559d" } namada_core = {path = "../core", features = ["abciplus"]} namada_macros = {path = "../macros"} namada_proof_of_stake = {path = "../proof_of_stake"} diff --git a/vm_env/Cargo.toml b/vm_env/Cargo.toml index 161f6079de..b7388c4dbe 100644 --- a/vm_env/Cargo.toml +++ b/vm_env/Cargo.toml @@ -13,6 +13,6 @@ default = [] namada_core = {path = "../core"} borsh = "0.9.0" #libmasp = { git = "https://github.com/anoma/masp", branch = "murisi/masp-incentive" } -masp_primitives = { git = "https://github.com/anoma/masp", rev = "bee40fc465f6afbd10558d12fe96eb1742eee45c" } -masp_proofs = { git = "https://github.com/anoma/masp", rev = "bee40fc465f6afbd10558d12fe96eb1742eee45c" } +masp_primitives = { git = "https://github.com/anoma/masp", rev = "117bff3b0d3e15aff993c25ff6222d411136559d" } +masp_proofs = { git = "https://github.com/anoma/masp", rev = "117bff3b0d3e15aff993c25ff6222d411136559d" } hex = "0.4.3" diff --git a/wasm/Cargo.lock b/wasm/Cargo.lock index be208e97bd..a3dc617ade 100644 --- a/wasm/Cargo.lock +++ b/wasm/Cargo.lock @@ -98,6 +98,18 @@ dependencies = [ "zeroize", ] +[[package]] +name = "ark-ed-on-bls12-381" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43b7ada17db3854f5994e74e60b18e10e818594935ee7e1d329800c117b32970" +dependencies = [ + "ark-bls12-381", + "ark-ec", + "ark-ff", + "ark-std", +] + [[package]] name = "ark-ff" version = "0.3.0" @@ -138,6 +150,19 @@ dependencies = [ "syn", ] +[[package]] +name = "ark-poly" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b0f78f47537c2f15706db7e98fe64cc1711dbf9def81218194e17239e53e5aa" +dependencies = [ + "ark-ff", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.11.2", +] + [[package]] name = "ark-serialize" version = "0.3.0" @@ -312,6 +337,24 @@ dependencies = [ "crunchy 0.1.6", ] +[[package]] +name = "bimap" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc0455254eb5c6964c4545d8bac815e1a1be4f3afe0ae695ea539c12d728d44b" +dependencies = [ + "serde", +] + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "bip0039" version = "0.9.0" @@ -380,6 +423,15 @@ dependencies = [ "wyz", ] +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest 0.10.5", +] + [[package]] name = "blake2b_simd" version = "0.5.11" @@ -966,6 +1018,12 @@ dependencies = [ "crypto_api", ] +[[package]] +name = "ct-codecs" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3b7eb4404b8195a9abb6356f4ac07d8ba267045c8d6d220ac4dc992e6cc75df" + [[package]] name = "ct-logs" version = "0.8.0" @@ -1221,6 +1279,7 @@ version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e9c280362032ea4203659fc489832d0204ef09f247a0506f170dafcac08c369" dependencies = [ + "serde", "signature", ] @@ -1247,6 +1306,10 @@ checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" dependencies = [ "curve25519-dalek", "ed25519", + "merlin", + "rand 0.7.3", + "serde", + "serde_bytes", "sha2 0.9.9", "zeroize", ] @@ -1359,6 +1422,43 @@ dependencies = [ "instant", ] +[[package]] +name = "ferveo" +version = "0.1.1" +source = "git+https://github.com/anoma/ferveo#1022ab2c7ccc689abcc05e5a08df6fb0c2a3fc65" +dependencies = [ + "anyhow", + "ark-bls12-381", + "ark-ec", + "ark-ed-on-bls12-381", + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "bincode", + "blake2", + "blake2b_simd 1.0.0", + "borsh", + "digest 0.10.5", + "ed25519-dalek", + "either", + "ferveo-common", + "group-threshold-cryptography", + "hex", + "itertools", + "measure_time", + "miracl_core", + "num", + "rand 0.7.3", + "rand 0.8.5", + "serde", + "serde_bytes", + "serde_json", + "subproductdomain", + "subtle", + "zeroize", +] + [[package]] name = "ferveo-common" version = "0.1.0" @@ -1592,6 +1692,30 @@ dependencies = [ "subtle", ] +[[package]] +name = "group-threshold-cryptography" +version = "0.1.0" +source = "git+https://github.com/anoma/ferveo#1022ab2c7ccc689abcc05e5a08df6fb0c2a3fc65" +dependencies = [ + "anyhow", + "ark-bls12-381", + "ark-ec", + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "blake2b_simd 1.0.0", + "chacha20", + "hex", + "itertools", + "miracl_core", + "rand 0.8.5", + "rand_core 0.6.4", + "rayon", + "subproductdomain", + "thiserror", +] + [[package]] name = "gumdrop" version = "0.8.1" @@ -2074,6 +2198,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ "cfg-if 1.0.0", + "js-sys", + "wasm-bindgen", + "web-sys", ] [[package]] @@ -2176,15 +2303,45 @@ dependencies = [ "base64", "digest 0.9.0", "hmac-drbg", - "libsecp256k1-core", - "libsecp256k1-gen-ecmult", - "libsecp256k1-gen-genmult", + "libsecp256k1-core 0.3.0 (git+https://github.com/heliaxdev/libsecp256k1?rev=bbb3bd44a49db361f21d9db80f9a087c194c0ae9)", + "libsecp256k1-gen-ecmult 0.3.0 (git+https://github.com/heliaxdev/libsecp256k1?rev=bbb3bd44a49db361f21d9db80f9a087c194c0ae9)", + "libsecp256k1-gen-genmult 0.3.0 (git+https://github.com/heliaxdev/libsecp256k1?rev=bbb3bd44a49db361f21d9db80f9a087c194c0ae9)", + "rand 0.8.5", + "serde", + "sha2 0.9.9", + "typenum", +] + +[[package]] +name = "libsecp256k1" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95b09eff1b35ed3b33b877ced3a691fc7a481919c7e29c53c906226fcf55e2a1" +dependencies = [ + "arrayref", + "base64", + "digest 0.9.0", + "hmac-drbg", + "libsecp256k1-core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libsecp256k1-gen-ecmult 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libsecp256k1-gen-genmult 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.8.5", "serde", "sha2 0.9.9", "typenum", ] +[[package]] +name = "libsecp256k1-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451" +dependencies = [ + "crunchy 0.2.2", + "digest 0.9.0", + "subtle", +] + [[package]] name = "libsecp256k1-core" version = "0.3.0" @@ -2195,12 +2352,30 @@ dependencies = [ "subtle", ] +[[package]] +name = "libsecp256k1-gen-ecmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3038c808c55c87e8a172643a7d87187fc6c4174468159cb3090659d55bcb4809" +dependencies = [ + "libsecp256k1-core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "libsecp256k1-gen-ecmult" version = "0.3.0" source = "git+https://github.com/heliaxdev/libsecp256k1?rev=bbb3bd44a49db361f21d9db80f9a087c194c0ae9#bbb3bd44a49db361f21d9db80f9a087c194c0ae9" dependencies = [ - "libsecp256k1-core", + "libsecp256k1-core 0.3.0 (git+https://github.com/heliaxdev/libsecp256k1?rev=bbb3bd44a49db361f21d9db80f9a087c194c0ae9)", +] + +[[package]] +name = "libsecp256k1-gen-genmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db8d6ba2cec9eacc40e6e8ccc98931840301f1006e95647ceb2dd5c3aa06f7c" +dependencies = [ + "libsecp256k1-core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2208,7 +2383,7 @@ name = "libsecp256k1-gen-genmult" version = "0.3.0" source = "git+https://github.com/heliaxdev/libsecp256k1?rev=bbb3bd44a49db361f21d9db80f9a087c194c0ae9#bbb3bd44a49db361f21d9db80f9a087c194c0ae9" dependencies = [ - "libsecp256k1-core", + "libsecp256k1-core 0.3.0 (git+https://github.com/heliaxdev/libsecp256k1?rev=bbb3bd44a49db361f21d9db80f9a087c194c0ae9)", ] [[package]] @@ -2272,7 +2447,7 @@ dependencies = [ [[package]] name = "masp_primitives" version = "0.5.0" -source = "git+https://github.com/anoma/masp?rev=bee40fc465f6afbd10558d12fe96eb1742eee45c#bee40fc465f6afbd10558d12fe96eb1742eee45c" +source = "git+https://github.com/anoma/masp?rev=117bff3b0d3e15aff993c25ff6222d411136559d#117bff3b0d3e15aff993c25ff6222d411136559d" dependencies = [ "aes", "bip0039", @@ -2291,8 +2466,10 @@ dependencies = [ "incrementalmerkletree", "jubjub", "lazy_static", + "libsecp256k1 0.7.1", "rand 0.8.5", "rand_core 0.6.4", + "ripemd160", "serde", "sha2 0.9.9", "subtle", @@ -2303,7 +2480,7 @@ dependencies = [ [[package]] name = "masp_proofs" version = "0.5.0" -source = "git+https://github.com/anoma/masp?rev=bee40fc465f6afbd10558d12fe96eb1742eee45c#bee40fc465f6afbd10558d12fe96eb1742eee45c" +source = "git+https://github.com/anoma/masp?rev=117bff3b0d3e15aff993c25ff6222d411136559d#117bff3b0d3e15aff993c25ff6222d411136559d" dependencies = [ "bellman", "blake2b_simd 1.0.0", @@ -2336,6 +2513,16 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" +[[package]] +name = "measure_time" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56220900f1a0923789ecd6bf25fbae8af3b2f1ff3e9e297fc9b6b8674dd4d852" +dependencies = [ + "instant", + "log", +] + [[package]] name = "memchr" version = "2.5.0" @@ -2384,6 +2571,18 @@ dependencies = [ "nonempty", ] +[[package]] +name = "merlin" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e261cf0f8b3c42ded9f7d2bb59dea03aa52bc8a1cbc7482f9fc3fd1229d3b42" +dependencies = [ + "byteorder", + "keccak", + "rand_core 0.5.1", + "zeroize", +] + [[package]] name = "mime" version = "0.3.16" @@ -2411,6 +2610,12 @@ dependencies = [ "windows-sys 0.42.0", ] +[[package]] +name = "miracl_core" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94c7128ba23c81f6471141b90f17654f89ef44a56e14b8a4dd0fddfccd655277" + [[package]] name = "moka" version = "0.8.6" @@ -2451,12 +2656,14 @@ version = "0.10.1" dependencies = [ "async-trait", "bellman", + "bimap", "bls12_381", "borsh", "circular-queue", "clru", "data-encoding", "derivative", + "getrandom 0.2.8", "ibc", "ibc-proto", "itertools", @@ -2465,19 +2672,26 @@ dependencies = [ "masp_proofs", "namada_core", "namada_proof_of_stake", + "orion", "parity-wasm", "paste", "proptest", "prost", "pwasm-utils", + "rand 0.8.5", + "rand_core 0.6.4", "rayon", "rust_decimal", + "serde", "serde_json", "sha2 0.9.9", "tempfile", "tendermint", "tendermint-proto", + "tendermint-rpc", "thiserror", + "tokio", + "toml", "tracing", "wasmer", "wasmer-cache", @@ -2494,6 +2708,7 @@ name = "namada_core" version = "0.9.0" dependencies = [ "ark-bls12-381", + "ark-ec", "ark-serialize", "bech32", "bellman", @@ -2503,12 +2718,14 @@ dependencies = [ "data-encoding", "derivative", "ed25519-consensus", + "ferveo", "ferveo-common", + "group-threshold-cryptography", "ibc", "ibc-proto", "ics23", "itertools", - "libsecp256k1", + "libsecp256k1 0.7.0", "masp_primitives", "proptest", "prost", @@ -2655,6 +2872,20 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e9e591e719385e6ebaeb5ce5d3887f7d5676fceca6411d1925ccc95745f3d6f7" +[[package]] +name = "num" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43db66d1170d347f9a065114077f7dccb00c1b9478c89384490a3425279a4606" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + [[package]] name = "num-bigint" version = "0.4.3" @@ -2667,6 +2898,15 @@ dependencies = [ "serde", ] +[[package]] +name = "num-complex" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ae39348c8bc5fbd7f40c727a9925f03517afd2ab27d46702108b6a7e5414c19" +dependencies = [ + "num-traits", +] + [[package]] name = "num-derive" version = "0.3.3" @@ -2688,6 +2928,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-iter" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-rational" version = "0.4.1" @@ -2786,6 +3037,18 @@ dependencies = [ "zcash_note_encryption 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "orion" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6624905ddd92e460ff0685567539ed1ac985b2dee4c92c7edcd64fce905b00c" +dependencies = [ + "ct-codecs", + "getrandom 0.2.8", + "subtle", + "zeroize", +] + [[package]] name = "pairing" version = "0.21.0" @@ -2910,11 +3173,10 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "pest" -version = "2.4.1" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a528564cc62c19a7acac4d81e01f39e53e25e17b934878f4c6d25cc2836e62f8" +checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" dependencies = [ - "thiserror", "ucd-trie", ] @@ -3990,6 +4252,19 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "subproductdomain" +version = "0.1.0" +source = "git+https://github.com/anoma/ferveo#1022ab2c7ccc689abcc05e5a08df6fb0c2a3fc65" +dependencies = [ + "anyhow", + "ark-ec", + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", +] + [[package]] name = "subtle" version = "2.4.1" @@ -4229,18 +4504,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.37" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" +checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.37" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" +checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" dependencies = [ "proc-macro2", "quote", diff --git a/wasm/wasm_source/Cargo.toml b/wasm/wasm_source/Cargo.toml index fc11660728..6532d0b1ff 100644 --- a/wasm/wasm_source/Cargo.toml +++ b/wasm/wasm_source/Cargo.toml @@ -40,8 +40,8 @@ once_cell = {version = "1.8.0", optional = true} rust_decimal = {version = "1.26.1", optional = true} wee_alloc = "0.4.5" getrandom = { version = "0.2", features = ["custom"] } -masp_proofs = { git = "https://github.com/anoma/masp", rev = "bee40fc465f6afbd10558d12fe96eb1742eee45c", optional = true } -masp_primitives = { git = "https://github.com/anoma/masp", rev = "bee40fc465f6afbd10558d12fe96eb1742eee45c", optional = true } +masp_proofs = { git = "https://github.com/anoma/masp", rev = "117bff3b0d3e15aff993c25ff6222d411136559d", optional = true } +masp_primitives = { git = "https://github.com/anoma/masp", rev = "117bff3b0d3e15aff993c25ff6222d411136559d", optional = true } [dev-dependencies] namada = {path = "../../shared"} diff --git a/wasm_for_tests/wasm_source/Cargo.lock b/wasm_for_tests/wasm_source/Cargo.lock index 21c207b531..3ba8ddc24b 100644 --- a/wasm_for_tests/wasm_source/Cargo.lock +++ b/wasm_for_tests/wasm_source/Cargo.lock @@ -98,6 +98,18 @@ dependencies = [ "zeroize", ] +[[package]] +name = "ark-ed-on-bls12-381" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43b7ada17db3854f5994e74e60b18e10e818594935ee7e1d329800c117b32970" +dependencies = [ + "ark-bls12-381", + "ark-ec", + "ark-ff", + "ark-std", +] + [[package]] name = "ark-ff" version = "0.3.0" @@ -138,6 +150,19 @@ dependencies = [ "syn", ] +[[package]] +name = "ark-poly" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b0f78f47537c2f15706db7e98fe64cc1711dbf9def81218194e17239e53e5aa" +dependencies = [ + "ark-ff", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.11.2", +] + [[package]] name = "ark-serialize" version = "0.3.0" @@ -312,6 +337,24 @@ dependencies = [ "crunchy 0.1.6", ] +[[package]] +name = "bimap" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc0455254eb5c6964c4545d8bac815e1a1be4f3afe0ae695ea539c12d728d44b" +dependencies = [ + "serde", +] + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "bip0039" version = "0.9.0" @@ -380,6 +423,15 @@ dependencies = [ "wyz", ] +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest 0.10.5", +] + [[package]] name = "blake2b_simd" version = "0.5.11" @@ -966,6 +1018,12 @@ dependencies = [ "crypto_api", ] +[[package]] +name = "ct-codecs" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3b7eb4404b8195a9abb6356f4ac07d8ba267045c8d6d220ac4dc992e6cc75df" + [[package]] name = "ct-logs" version = "0.8.0" @@ -1221,6 +1279,7 @@ version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e9c280362032ea4203659fc489832d0204ef09f247a0506f170dafcac08c369" dependencies = [ + "serde", "signature", ] @@ -1247,6 +1306,10 @@ checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" dependencies = [ "curve25519-dalek", "ed25519", + "merlin", + "rand 0.7.3", + "serde", + "serde_bytes", "sha2 0.9.9", "zeroize", ] @@ -1359,6 +1422,43 @@ dependencies = [ "instant", ] +[[package]] +name = "ferveo" +version = "0.1.1" +source = "git+https://github.com/anoma/ferveo#1022ab2c7ccc689abcc05e5a08df6fb0c2a3fc65" +dependencies = [ + "anyhow", + "ark-bls12-381", + "ark-ec", + "ark-ed-on-bls12-381", + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "bincode", + "blake2", + "blake2b_simd 1.0.0", + "borsh", + "digest 0.10.5", + "ed25519-dalek", + "either", + "ferveo-common", + "group-threshold-cryptography", + "hex", + "itertools", + "measure_time", + "miracl_core", + "num", + "rand 0.7.3", + "rand 0.8.5", + "serde", + "serde_bytes", + "serde_json", + "subproductdomain", + "subtle", + "zeroize", +] + [[package]] name = "ferveo-common" version = "0.1.0" @@ -1592,6 +1692,30 @@ dependencies = [ "subtle", ] +[[package]] +name = "group-threshold-cryptography" +version = "0.1.0" +source = "git+https://github.com/anoma/ferveo#1022ab2c7ccc689abcc05e5a08df6fb0c2a3fc65" +dependencies = [ + "anyhow", + "ark-bls12-381", + "ark-ec", + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "blake2b_simd 1.0.0", + "chacha20", + "hex", + "itertools", + "miracl_core", + "rand 0.8.5", + "rand_core 0.6.4", + "rayon", + "subproductdomain", + "thiserror", +] + [[package]] name = "gumdrop" version = "0.8.1" @@ -2074,6 +2198,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ "cfg-if 1.0.0", + "js-sys", + "wasm-bindgen", + "web-sys", ] [[package]] @@ -2176,15 +2303,45 @@ dependencies = [ "base64", "digest 0.9.0", "hmac-drbg", - "libsecp256k1-core", - "libsecp256k1-gen-ecmult", - "libsecp256k1-gen-genmult", + "libsecp256k1-core 0.3.0 (git+https://github.com/heliaxdev/libsecp256k1?rev=bbb3bd44a49db361f21d9db80f9a087c194c0ae9)", + "libsecp256k1-gen-ecmult 0.3.0 (git+https://github.com/heliaxdev/libsecp256k1?rev=bbb3bd44a49db361f21d9db80f9a087c194c0ae9)", + "libsecp256k1-gen-genmult 0.3.0 (git+https://github.com/heliaxdev/libsecp256k1?rev=bbb3bd44a49db361f21d9db80f9a087c194c0ae9)", + "rand 0.8.5", + "serde", + "sha2 0.9.9", + "typenum", +] + +[[package]] +name = "libsecp256k1" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95b09eff1b35ed3b33b877ced3a691fc7a481919c7e29c53c906226fcf55e2a1" +dependencies = [ + "arrayref", + "base64", + "digest 0.9.0", + "hmac-drbg", + "libsecp256k1-core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libsecp256k1-gen-ecmult 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libsecp256k1-gen-genmult 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.8.5", "serde", "sha2 0.9.9", "typenum", ] +[[package]] +name = "libsecp256k1-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451" +dependencies = [ + "crunchy 0.2.2", + "digest 0.9.0", + "subtle", +] + [[package]] name = "libsecp256k1-core" version = "0.3.0" @@ -2195,12 +2352,30 @@ dependencies = [ "subtle", ] +[[package]] +name = "libsecp256k1-gen-ecmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3038c808c55c87e8a172643a7d87187fc6c4174468159cb3090659d55bcb4809" +dependencies = [ + "libsecp256k1-core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "libsecp256k1-gen-ecmult" version = "0.3.0" source = "git+https://github.com/heliaxdev/libsecp256k1?rev=bbb3bd44a49db361f21d9db80f9a087c194c0ae9#bbb3bd44a49db361f21d9db80f9a087c194c0ae9" dependencies = [ - "libsecp256k1-core", + "libsecp256k1-core 0.3.0 (git+https://github.com/heliaxdev/libsecp256k1?rev=bbb3bd44a49db361f21d9db80f9a087c194c0ae9)", +] + +[[package]] +name = "libsecp256k1-gen-genmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db8d6ba2cec9eacc40e6e8ccc98931840301f1006e95647ceb2dd5c3aa06f7c" +dependencies = [ + "libsecp256k1-core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2208,7 +2383,7 @@ name = "libsecp256k1-gen-genmult" version = "0.3.0" source = "git+https://github.com/heliaxdev/libsecp256k1?rev=bbb3bd44a49db361f21d9db80f9a087c194c0ae9#bbb3bd44a49db361f21d9db80f9a087c194c0ae9" dependencies = [ - "libsecp256k1-core", + "libsecp256k1-core 0.3.0 (git+https://github.com/heliaxdev/libsecp256k1?rev=bbb3bd44a49db361f21d9db80f9a087c194c0ae9)", ] [[package]] @@ -2272,7 +2447,7 @@ dependencies = [ [[package]] name = "masp_primitives" version = "0.5.0" -source = "git+https://github.com/anoma/masp?rev=bee40fc465f6afbd10558d12fe96eb1742eee45c#bee40fc465f6afbd10558d12fe96eb1742eee45c" +source = "git+https://github.com/anoma/masp?rev=117bff3b0d3e15aff993c25ff6222d411136559d#117bff3b0d3e15aff993c25ff6222d411136559d" dependencies = [ "aes", "bip0039", @@ -2291,8 +2466,10 @@ dependencies = [ "incrementalmerkletree", "jubjub", "lazy_static", + "libsecp256k1 0.7.1", "rand 0.8.5", "rand_core 0.6.4", + "ripemd160", "serde", "sha2 0.9.9", "subtle", @@ -2303,7 +2480,7 @@ dependencies = [ [[package]] name = "masp_proofs" version = "0.5.0" -source = "git+https://github.com/anoma/masp?rev=bee40fc465f6afbd10558d12fe96eb1742eee45c#bee40fc465f6afbd10558d12fe96eb1742eee45c" +source = "git+https://github.com/anoma/masp?rev=117bff3b0d3e15aff993c25ff6222d411136559d#117bff3b0d3e15aff993c25ff6222d411136559d" dependencies = [ "bellman", "blake2b_simd 1.0.0", @@ -2336,6 +2513,16 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" +[[package]] +name = "measure_time" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56220900f1a0923789ecd6bf25fbae8af3b2f1ff3e9e297fc9b6b8674dd4d852" +dependencies = [ + "instant", + "log", +] + [[package]] name = "memchr" version = "2.5.0" @@ -2384,6 +2571,18 @@ dependencies = [ "nonempty", ] +[[package]] +name = "merlin" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e261cf0f8b3c42ded9f7d2bb59dea03aa52bc8a1cbc7482f9fc3fd1229d3b42" +dependencies = [ + "byteorder", + "keccak", + "rand_core 0.5.1", + "zeroize", +] + [[package]] name = "mime" version = "0.3.16" @@ -2411,6 +2610,12 @@ dependencies = [ "windows-sys 0.42.0", ] +[[package]] +name = "miracl_core" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94c7128ba23c81f6471141b90f17654f89ef44a56e14b8a4dd0fddfccd655277" + [[package]] name = "moka" version = "0.8.6" @@ -2451,12 +2656,14 @@ version = "0.10.1" dependencies = [ "async-trait", "bellman", + "bimap", "bls12_381", "borsh", "circular-queue", "clru", "data-encoding", "derivative", + "getrandom 0.2.8", "ibc", "ibc-proto", "itertools", @@ -2465,19 +2672,26 @@ dependencies = [ "masp_proofs", "namada_core", "namada_proof_of_stake", + "orion", "parity-wasm", "paste", "proptest", "prost", "pwasm-utils", + "rand 0.8.5", + "rand_core 0.6.4", "rayon", "rust_decimal", + "serde", "serde_json", "sha2 0.9.9", "tempfile", "tendermint", "tendermint-proto", + "tendermint-rpc", "thiserror", + "tokio", + "toml", "tracing", "wasmer", "wasmer-cache", @@ -2494,6 +2708,7 @@ name = "namada_core" version = "0.9.0" dependencies = [ "ark-bls12-381", + "ark-ec", "ark-serialize", "bech32", "bellman", @@ -2503,12 +2718,14 @@ dependencies = [ "data-encoding", "derivative", "ed25519-consensus", + "ferveo", "ferveo-common", + "group-threshold-cryptography", "ibc", "ibc-proto", "ics23", "itertools", - "libsecp256k1", + "libsecp256k1 0.7.0", "masp_primitives", "proptest", "prost", @@ -2647,6 +2864,20 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e9e591e719385e6ebaeb5ce5d3887f7d5676fceca6411d1925ccc95745f3d6f7" +[[package]] +name = "num" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43db66d1170d347f9a065114077f7dccb00c1b9478c89384490a3425279a4606" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + [[package]] name = "num-bigint" version = "0.4.3" @@ -2659,6 +2890,15 @@ dependencies = [ "serde", ] +[[package]] +name = "num-complex" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ae39348c8bc5fbd7f40c727a9925f03517afd2ab27d46702108b6a7e5414c19" +dependencies = [ + "num-traits", +] + [[package]] name = "num-derive" version = "0.3.3" @@ -2680,6 +2920,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-iter" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-rational" version = "0.4.1" @@ -2778,6 +3029,18 @@ dependencies = [ "zcash_note_encryption 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "orion" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6624905ddd92e460ff0685567539ed1ac985b2dee4c92c7edcd64fce905b00c" +dependencies = [ + "ct-codecs", + "getrandom 0.2.8", + "subtle", + "zeroize", +] + [[package]] name = "pairing" version = "0.21.0" @@ -2902,11 +3165,10 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "pest" -version = "2.4.1" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a528564cc62c19a7acac4d81e01f39e53e25e17b934878f4c6d25cc2836e62f8" +checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" dependencies = [ - "thiserror", "ucd-trie", ] @@ -3982,6 +4244,19 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "subproductdomain" +version = "0.1.0" +source = "git+https://github.com/anoma/ferveo#1022ab2c7ccc689abcc05e5a08df6fb0c2a3fc65" +dependencies = [ + "anyhow", + "ark-ec", + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", +] + [[package]] name = "subtle" version = "2.4.1" @@ -4221,18 +4496,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.37" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" +checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.37" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" +checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" dependencies = [ "proc-macro2", "quote", From 09a1430fda8ff7512be7ee95c2bf4b8e424cc40f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Thu, 22 Dec 2022 11:55:37 +0100 Subject: [PATCH 51/51] shared: feature-guard "http-client" for wasm-compat --- apps/Cargo.toml | 2 ++ shared/Cargo.toml | 9 ++++++++- shared/src/ledger/queries/mod.rs | 1 + 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/apps/Cargo.toml b/apps/Cargo.toml index e456f5f2cd..a6bef81d6b 100644 --- a/apps/Cargo.toml +++ b/apps/Cargo.toml @@ -49,6 +49,7 @@ testing = ["dev"] abcipp = [ "namada/abcipp", "namada/tendermint-rpc-abcipp", + "namada/http-client-abcipp", "tendermint-abcipp", "tendermint-config-abcipp", "tendermint-proto-abcipp", @@ -59,6 +60,7 @@ abcipp = [ abciplus = [ "namada/abciplus", "namada/tendermint-rpc", + "namada/http-client", "tendermint", "tendermint-config", "tendermint-rpc", diff --git a/shared/Cargo.toml b/shared/Cargo.toml index c6f5b951db..d94d2ce3a8 100644 --- a/shared/Cargo.toml +++ b/shared/Cargo.toml @@ -41,6 +41,12 @@ tendermint-rpc-abcipp = [ "async-client", "dep:tendermint-rpc-abcipp", ] +http-client = [ + "tendermint-rpc/http-client", +] +http-client-abcipp = [ + "tendermint-rpc-abcipp/http-client", +] abcipp = [ "namada_core/abcipp", @@ -99,6 +105,7 @@ circular-queue = "0.2.6" clru = {git = "https://github.com/marmeladema/clru-rs.git", rev = "71ca566"} data-encoding = "2.3.2" derivative = "2.2.0" +getrandom = { version = "0.2", features = ["custom"] } # TODO using the same version of tendermint-rs as we do here. ibc-abcipp = {package = "ibc", git = "https://github.com/heliaxdev/ibc-rs", rev = "9fcc1c8c19db6af50806ffe5b2f6c214adcbfd5d", default-features = false, optional = true} ibc-proto-abcipp = {package = "ibc-proto", git = "https://github.com/heliaxdev/ibc-rs", rev = "9fcc1c8c19db6af50806ffe5b2f6c214adcbfd5d", default-features = false, optional = true} @@ -123,7 +130,7 @@ tendermint-abcipp = {package = "tendermint", git = "https://github.com/heliaxdev tendermint-rpc-abcipp = {package = "tendermint-rpc", git = "https://github.com/heliaxdev/tendermint-rs", rev = "95c52476bc37927218374f94ac8e2a19bd35bec9", features = ["http-client"], optional = true} tendermint-proto-abcipp = {package = "tendermint-proto", git = "https://github.com/heliaxdev/tendermint-rs", rev = "95c52476bc37927218374f94ac8e2a19bd35bec9", optional = true} tendermint = {version = "0.23.6", optional = true} -tendermint-rpc = {version = "0.23.6", features = ["http-client"], optional = true} +tendermint-rpc = {version = "0.23.6", optional = true} tendermint-proto = {version = "0.23.6", optional = true} thiserror = "1.0.30" tracing = "0.1.30" diff --git a/shared/src/ledger/queries/mod.rs b/shared/src/ledger/queries/mod.rs index 4644909b1a..3ee2d95655 100644 --- a/shared/src/ledger/queries/mod.rs +++ b/shared/src/ledger/queries/mod.rs @@ -108,6 +108,7 @@ pub mod tm { InvalidHeight(BlockHeight), } + #[cfg(any(feature = "http-client", feature = "http-client-abcipp"))] #[async_trait::async_trait] impl Client for crate::tendermint_rpc::HttpClient { type Error = Error;