Skip to content

Commit

Permalink
client/tx and signing: add wrapper signer balance check and add PoW
Browse files Browse the repository at this point in the history
  • Loading branch information
tzemanovic committed Dec 30, 2022
1 parent 4d2d670 commit b3d5d2e
Show file tree
Hide file tree
Showing 2 changed files with 164 additions and 21 deletions.
66 changes: 63 additions & 3 deletions apps/src/lib/client/signing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ use namada::proto::Tx;
use namada::types::address::{Address, ImplicitAddress};
use namada::types::key::*;
use namada::types::storage::Epoch;
use namada::types::token;
use namada::types::token::Amount;
use namada::types::transaction::{hash_tx, Fee, WrapperTx};
use tendermint_rpc::HttpClient;

use super::rpc;
use crate::cli::context::{WalletAddress, WalletKeypair};
Expand Down Expand Up @@ -146,6 +148,7 @@ pub async fn sign_tx(
tx: Tx,
args: &args::Tx,
default: TxSigningKey,
#[cfg(not(feature = "mainnet"))] requires_pow: bool,
) -> (Context, TxBroadcastData) {
let keypair = tx_signer(&mut ctx, args, default).await;
let tx = tx.sign(&keypair);
Expand All @@ -157,7 +160,16 @@ 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(
&ctx,
args,
epoch,
tx,
&keypair,
#[cfg(not(feature = "mainnet"))]
requires_pow,
)
.await
};
(ctx, broadcast_data)
}
Expand All @@ -171,19 +183,67 @@ pub async fn sign_wrapper(
epoch: Epoch,
tx: Tx,
keypair: &common::SecretKey,
#[cfg(not(feature = "mainnet"))] requires_pow: bool,
) -> TxBroadcastData {
let fee_amount = Amount::from(100);
let fee_token = ctx.get(&args.fee_token);
let source = Address::from(&keypair.ref_to());
let balance_key = token::balance_key(&fee_token, &source);
let client = HttpClient::new(args.ledger_address.clone()).unwrap();
let balance =
rpc::query_storage_value::<token::Amount>(&client, &balance_key)
.await
.unwrap_or_default();
if balance < fee_amount {
eprintln!(
"The source doesn't have enough balance to pay fee {fee_amount}, \
got {balance}.{}",
if cfg!(not(feature = "mainnet")) {
" Will attempt to solve a PoW challenge instead."
} else {
""
}
);
if !args.force && cfg!(feature = "mainnet") {
cli::safe_exit(1);
}
}

#[cfg(not(feature = "mainnet"))]
// A PoW solution can be used to allow zero-fee testnet transactions
let pow_solution: Option<namada::core::ledger::testnet_pow::Solution> = {
// If the address derived from the keypair doesn't have enough balance
// to pay for the fee, allow to find a PoW solution instead.
if requires_pow || balance < fee_amount {
// Obtain a PoW challenge for faucet withdrawal
let challenge = rpc::get_testnet_pow_challenge(
source,
args.ledger_address.clone(),
)
.await;

// Solve the solution, this blocks until a solution is found
let solution = challenge.solve();
Some(solution)
} else {
None
}
};

let tx = {
WrapperTx::new(
Fee {
amount: Amount::from(100),
token: ctx.get(&args.fee_token),
amount: fee_amount,
token: fee_token,
},
keypair,
epoch,
args.gas_limit.clone(),
tx,
// TODO: Actually use the fetched encryption key
Default::default(),
#[cfg(not(feature = "mainnet"))]
pow_solution,
)
};

Expand Down
119 changes: 101 additions & 18 deletions apps/src/lib/client/tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,15 @@ pub async fn submit_custom(ctx: Context, args: args::TxCustom) {
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;
let (ctx, initialized_accounts) = process_tx(
ctx,
&args.tx,
tx,
TxSigningKey::None,
#[cfg(not(feature = "mainnet"))]
false,
)
.await;
save_initialized_accounts(ctx, &args.tx, initialized_accounts).await;
}

Expand Down Expand Up @@ -163,7 +170,15 @@ 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(
ctx,
&args.tx,
tx,
TxSigningKey::WalletAddress(args.addr),
#[cfg(not(feature = "mainnet"))]
false,
)
.await;
}

pub async fn submit_init_account(mut ctx: Context, args: args::TxInitAccount) {
Expand All @@ -188,9 +203,15 @@ pub async fn submit_init_account(mut ctx: Context, args: args::TxInitAccount) {
let data = data.try_to_vec().expect("Encoding tx data shouldn't fail");

let tx = Tx::new(tx_code, Some(data));
let (ctx, initialized_accounts) =
process_tx(ctx, &args.tx, tx, TxSigningKey::WalletAddress(args.source))
.await;
let (ctx, initialized_accounts) = process_tx(
ctx,
&args.tx,
tx,
TxSigningKey::WalletAddress(args.source),
#[cfg(not(feature = "mainnet"))]
false,
)
.await;
save_initialized_accounts(ctx, &args.tx, initialized_accounts).await;
}

Expand Down Expand Up @@ -315,9 +336,15 @@ 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))
.await;
let (mut ctx, initialized_accounts) = process_tx(
ctx,
&tx_args,
tx,
TxSigningKey::WalletAddress(source),
#[cfg(not(feature = "mainnet"))]
false,
)
.await;
if !tx_args.dry_run {
let (validator_address_alias, validator_address) =
match &initialized_accounts[..] {
Expand Down Expand Up @@ -1552,7 +1579,6 @@ pub async fn submit_transfer(mut ctx: Context, args: args::TxTransfer) {
}
};

let tx_code = ctx.read_wasm(TX_TRANSFER_WASM);
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
Expand Down Expand Up @@ -1591,6 +1617,10 @@ pub async fn submit_transfer(mut ctx: Context, args: args::TxTransfer) {
_ => None,
};

#[cfg(not(feature = "mainnet"))]
let is_source_faucet =
rpc::is_faucet_account(&source, args.tx.ledger_address.clone()).await;

let transfer = token::Transfer {
source,
target,
Expand Down Expand Up @@ -1646,10 +1676,21 @@ pub async fn submit_transfer(mut ctx: Context, args: args::TxTransfer) {
let data = transfer
.try_to_vec()
.expect("Encoding tx data shouldn't fail");

let tx_code = ctx.read_wasm(TX_TRANSFER_WASM);
let tx = Tx::new(tx_code, Some(data));
let signing_address = TxSigningKey::WalletAddress(args.source.to_address());
process_tx(ctx, &args.tx, tx, signing_address).await;

#[cfg(not(feature = "mainnet"))]
dbg!(is_source_faucet);
process_tx(
ctx,
&args.tx,
tx,
signing_address,
#[cfg(not(feature = "mainnet"))]
is_source_faucet,
)
.await;
}

pub async fn submit_ibc_transfer(ctx: Context, args: args::TxIbcTransfer) {
Expand Down Expand Up @@ -1759,8 +1800,15 @@ 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))
.await;
process_tx(
ctx,
&args.tx,
tx,
TxSigningKey::WalletAddress(args.source),
#[cfg(not(feature = "mainnet"))]
false,
)
.await;
}

pub async fn submit_init_proposal(mut ctx: Context, args: args::InitProposal) {
Expand Down Expand Up @@ -1898,8 +1946,15 @@ 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))
.await;
process_tx(
ctx,
&args.tx,
tx,
TxSigningKey::WalletAddress(signer),
#[cfg(not(feature = "mainnet"))]
false,
)
.await;
}
}

Expand Down Expand Up @@ -2036,6 +2091,8 @@ pub async fn submit_vote_proposal(mut ctx: Context, args: args::VoteProposal) {
&args.tx,
tx,
TxSigningKey::WalletAddress(signer.clone()),
#[cfg(not(feature = "mainnet"))]
false,
)
.await;
}
Expand Down Expand Up @@ -2118,7 +2175,16 @@ 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(
ctx,
args,
epoch,
tx,
&keypair,
#[cfg(not(feature = "mainnet"))]
false,
)
.await
};

if args.dry_run {
Expand Down Expand Up @@ -2297,6 +2363,8 @@ pub async fn submit_bond(ctx: Context, args: args::Bond) {
&args.tx,
tx,
TxSigningKey::WalletAddress(default_signer),
#[cfg(not(feature = "mainnet"))]
false,
)
.await;
}
Expand Down Expand Up @@ -2370,6 +2438,8 @@ pub async fn submit_unbond(ctx: Context, args: args::Unbond) {
&args.tx,
tx,
TxSigningKey::WalletAddress(default_signer),
#[cfg(not(feature = "mainnet"))]
false,
)
.await;
}
Expand Down Expand Up @@ -2443,6 +2513,8 @@ pub async fn submit_withdraw(ctx: Context, args: args::Withdraw) {
&args.tx,
tx,
TxSigningKey::WalletAddress(default_signer),
#[cfg(not(feature = "mainnet"))]
false,
)
.await;
}
Expand Down Expand Up @@ -2525,6 +2597,8 @@ pub async fn submit_validator_commission_change(
&args.tx,
tx,
TxSigningKey::WalletAddress(default_signer),
#[cfg(not(feature = "mainnet"))]
false,
)
.await;
}
Expand All @@ -2536,8 +2610,17 @@ async fn process_tx(
args: &args::Tx,
tx: Tx,
default_signer: TxSigningKey,
#[cfg(not(feature = "mainnet"))] requires_pow: bool,
) -> (Context, Vec<Address>) {
let (ctx, to_broadcast) = sign_tx(ctx, tx, args, default_signer).await;
let (ctx, to_broadcast) = sign_tx(
ctx,
tx,
args,
default_signer,
#[cfg(not(feature = "mainnet"))]
requires_pow,
)
.await;
// NOTE: use this to print the request JSON body:

// let request =
Expand Down

0 comments on commit b3d5d2e

Please sign in to comment.