diff --git a/Cargo.lock b/Cargo.lock index 2f538bf5432..9ee0fec6eeb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1080,26 +1080,26 @@ checksum = "cca491388666e04d7248af3f60f0c40cfb0991c72205595d7c396e3510207d1a" [[package]] name = "ciborium" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c137568cc60b904a7724001b35ce2630fd00d5d84805fbb608ab89509d788f" +checksum = "effd91f6c78e5a4ace8a5d3c0b6bfaec9e2baaef55f3efc00e45fb2e477ee926" dependencies = [ "ciborium-io", "ciborium-ll", - "serde 1.0.145", + "serde 1.0.163", ] [[package]] name = "ciborium-io" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "346de753af073cc87b52b2083a506b38ac176a44cfb05497b622e27be899b369" +checksum = "cdf919175532b369853f5d5e20b26b43112613fd6fe7aee757e35f7a44642656" [[package]] name = "ciborium-ll" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213030a2b5a4e0c0892b6652260cf6ccac84827b83a85a534e178e3906c4cf1b" +checksum = "defaa24ecc093c77630e6c15e17c51f5e187bf35ee514f4e2d67baaa96dae22b" dependencies = [ "ciborium-io", "half", @@ -1164,9 +1164,9 @@ dependencies = [ [[package]] name = "clap" -version = "3.2.23" +version = "3.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5" +checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" dependencies = [ "bitflags", "clap_lex", @@ -1180,16 +1180,7 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" dependencies = [ - "os_str_bytes 6.5.0", -] - -[[package]] -name = "cloudabi" -version = "0.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" -dependencies = [ - "bitflags", + "os_str_bytes 6.5.1", ] [[package]] @@ -1395,7 +1386,7 @@ dependencies = [ "atty", "cast", "ciborium", - "clap 3.2.23", + "clap 3.2.25", "criterion-plot", "itertools", "lazy_static", @@ -1404,7 +1395,7 @@ dependencies = [ "plotters", "rayon", "regex", - "serde 1.0.145", + "serde 1.0.163", "serde_derive", "serde_json", "tinytemplate", @@ -3883,16 +3874,14 @@ dependencies = [ [[package]] name = "namada_benchmarks" -version = "0.15.0" +version = "0.17.5" dependencies = [ "async-trait", - "borsh 0.9.4", + "borsh", "criterion", "ferveo-common", - "ibc-proto 0.26.0 (git+https://github.com/heliaxdev/ibc-proto-rs.git?rev=acc378e5e1865fbf559fa4e36e3c2b0dc1da51bb)", - "ibc-relayer", - "ibc-relayer-types", "masp_primitives", + "masp_proofs", "namada", "namada_apps", "namada_test_utils", @@ -3900,6 +3889,7 @@ dependencies = [ "rand 0.8.5", "rand_core 0.6.4", "rust_decimal", + "sha2 0.9.9", "tempfile", "tokio", ] @@ -4378,9 +4368,9 @@ checksum = "afb2e1c3ee07430c2cf76151675e583e0f19985fa6efae47d6848a3e2c824f85" [[package]] name = "os_str_bytes" -version = "6.5.0" +version = "6.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" +checksum = "4d5d9eb14b174ee9aa2ef96dc2b94637a2d4b6e7cb873c7e171f0c20c6cf3eac" [[package]] name = "output_vt100" @@ -4617,9 +4607,9 @@ checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "plotters" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2538b639e642295546c50fcd545198c9d64ee2a38620a628724a3b266d5fbf97" +checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45" dependencies = [ "num-traits 0.2.15", "plotters-backend", @@ -4630,15 +4620,15 @@ dependencies = [ [[package]] name = "plotters-backend" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "193228616381fecdc1224c62e96946dfbc73ff4384fba576e052ff8c1bea8142" +checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609" [[package]] name = "plotters-svg" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a81d2759aae1dae668f783c308bc5c8ebd191ff4184aaa1b37f65a6ae5a56f" +checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab" dependencies = [ "plotters-backend", ] @@ -6469,7 +6459,7 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" dependencies = [ - "serde 1.0.145", + "serde 1.0.163", "serde_json", ] diff --git a/apps/src/bin/namada-client/cli.rs b/apps/src/bin/namada-client/cli.rs index bf9921a5278..e43ba748c18 100644 --- a/apps/src/bin/namada-client/cli.rs +++ b/apps/src/bin/namada-client/cli.rs @@ -26,8 +26,7 @@ pub async fn main() -> Result<()> { .unwrap(); let args = args.to_sdk(&mut ctx); let dry_run = args.tx.dry_run; - tx::submit_custom::(&client, &mut ctx, args) - .await?; + tx::submit_custom(&client, &mut ctx, args).await?; if !dry_run { namada_apps::wallet::save(&ctx.wallet) .unwrap_or_else(|err| eprintln!("{}", err)); @@ -52,8 +51,7 @@ pub async fn main() -> Result<()> { 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, ctx, args).await?; } Sub::TxUpdateVp(TxUpdateVp(args)) => { wait_until_node_is_synched(&args.tx.ledger_address).await; @@ -61,8 +59,7 @@ pub async fn main() -> Result<()> { HttpClient::new(args.tx.ledger_address.clone()) .unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_update_vp::(&client, &mut ctx, args) - .await?; + tx::submit_update_vp(&client, &mut ctx, args).await?; } Sub::TxInitAccount(TxInitAccount(args)) => { wait_until_node_is_synched(&args.tx.ledger_address).await; @@ -71,10 +68,7 @@ pub async fn main() -> Result<()> { .unwrap(); let args = args.to_sdk(&mut ctx); let dry_run = args.tx.dry_run; - tx::submit_init_account::( - &client, &mut ctx, args, - ) - .await?; + tx::submit_init_account(&client, &mut ctx, args).await?; if !dry_run { namada_apps::wallet::save(&ctx.wallet) .unwrap_or_else(|err| eprintln!("{}", err)); @@ -91,8 +85,7 @@ pub async fn main() -> Result<()> { 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)) => { wait_until_node_is_synched(&args.tx.ledger_address).await; @@ -100,8 +93,7 @@ pub async fn main() -> Result<()> { 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)) => { wait_until_node_is_synched(&args.tx.ledger_address).await; @@ -109,8 +101,7 @@ pub async fn main() -> Result<()> { 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, ctx, args).await?; } Sub::TxRevealPk(TxRevealPk(args)) => { wait_until_node_is_synched(&args.tx.ledger_address).await; @@ -118,8 +109,7 @@ pub async fn main() -> Result<()> { HttpClient::new(args.tx.ledger_address.clone()) .unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_reveal_pk::(&client, &mut ctx, args) - .await?; + tx::submit_reveal_pk(&client, &mut ctx, args).await?; } Sub::Bond(Bond(args)) => { wait_until_node_is_synched(&args.tx.ledger_address).await; @@ -127,8 +117,7 @@ pub async fn main() -> Result<()> { HttpClient::new(args.tx.ledger_address.clone()) .unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_bond::(&client, &mut ctx, args) - .await?; + tx::submit_bond(&client, &mut ctx, args).await?; } Sub::Unbond(Unbond(args)) => { wait_until_node_is_synched(&args.tx.ledger_address).await; @@ -136,8 +125,7 @@ pub async fn main() -> Result<()> { HttpClient::new(args.tx.ledger_address.clone()) .unwrap(); let args = args.to_sdk(&mut ctx); - tx::submit_unbond::(&client, &mut ctx, args) - .await?; + tx::submit_unbond(&client, &mut ctx, args).await?; } Sub::Withdraw(Withdraw(args)) => { wait_until_node_is_synched(&args.tx.ledger_address).await; @@ -145,8 +133,7 @@ pub async fn main() -> Result<()> { 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, ctx, args).await?; } // Ledger queries Sub::QueryEpoch(QueryEpoch(args)) => { diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index 92e30c8483f..2e6409953e8 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -1771,6 +1771,7 @@ pub mod args { use namada::types::storage::{self, BlockHeight, Epoch}; use namada::types::time::DateTimeUtc; use namada::types::token; + use namada::types::transaction::GasLimit; use rust_decimal::Decimal; use super::context::*; @@ -1835,13 +1836,13 @@ pub mod args { pub const DUMP_TX: ArgFlag = flag("dump-tx"); pub const EPOCH: ArgOpt = arg_opt("epoch"); pub const EXPIRATION_OPT: ArgOpt = arg_opt("expiration"); - pub const FEE_UNSHIELD_SPENDING_KEY: ArgOpt = + pub const FEE_UNSHIELD_SPENDING_KEY: ArgOpt = arg_opt("fee-spending-key"); pub const FORCE: ArgFlag = flag("force"); pub const DONT_PREFETCH_WASM: ArgFlag = flag("dont-prefetch-wasm"); pub const FEE_AMOUNT: ArgOpt = arg_opt("fee-amount"); - pub const GAS_LIMIT: ArgDefault = - arg_default("gas-limit", DefaultFn(|| token::Amount::from(0))); + pub const GAS_LIMIT: ArgDefault = + arg_default("gas-limit", DefaultFn(|| GasLimit::from(1_000_000))); pub const FEE_TOKEN: ArgDefaultFromCtx = arg_default_from_ctx("fee-token", DefaultFn(|| "NAM".into())); pub const GENESIS_PATH: Arg = arg("genesis-path"); @@ -3299,7 +3300,9 @@ pub mod args { wallet_alias_force: self.wallet_alias_force, fee_amount: self.fee_amount, fee_token: ctx.get(&self.fee_token), - fee_unshield, self.fee_unshield, + fee_unshield: self + .fee_unshield + .map(|ref fee_unshield| ctx.get_cached(fee_unshield)), gas_limit: self.gas_limit, signing_key: self.signing_key.map(|x| ctx.get_cached(&x)), signer: self.signer.map(|x| ctx.get(&x)), @@ -3353,10 +3356,10 @@ pub mod args { .arg(GAS_LIMIT.def().about( "The multiplier of the gas limit resolution defining the \ maximum amount of gas needed to run transaction.", + )) .arg(WALLET_ALIAS_FORCE.def().about( "Override the alias without confirmation if it already exists.", )) - )) .arg(EXPIRATION_OPT.def().about( "The expiration datetime of the transaction, after which the \ tx won't be accepted anymore. All of these examples are \ diff --git a/apps/src/lib/cli/context.rs b/apps/src/lib/cli/context.rs index 0e112a6539b..ea12ce9817d 100644 --- a/apps/src/lib/cli/context.rs +++ b/apps/src/lib/cli/context.rs @@ -17,7 +17,7 @@ use namada::types::masp::*; use super::args; use crate::client::tx::CLIShieldedUtils; -use crate::config::genesis::genesis_config; +use crate::config::genesis; use crate::config::global::GlobalConfig; use crate::config::{self, Config}; use crate::wallet::CliWalletUtils; @@ -96,6 +96,7 @@ impl Context { .base_dir .join(global_config.default_chain_id.as_str()); + //FIXME: need this block? #[cfg(not(feature = "dev"))] let genesis = genesis::genesis( &global_args.base_dir, @@ -111,8 +112,9 @@ impl Context { "{}.toml", global_config.default_chain_id.as_str() )); - let default_genesis = - genesis_config::open_genesis_config(genesis_file_path)?; + let default_genesis = genesis::genesis_config::open_genesis_config( + genesis_file_path, + )?; crate::wallet::load_or_new_from_genesis(&chain_dir, default_genesis) }; #[cfg(feature = "dev")] diff --git a/apps/src/lib/client/signing.rs b/apps/src/lib/client/signing.rs index b82acda8989..6555e29573d 100644 --- a/apps/src/lib/client/signing.rs +++ b/apps/src/lib/client/signing.rs @@ -1,6 +1,8 @@ //! Helpers for making digital signatures using cryptographic keys from the //! wallet. +use namada::core::types::token::Amount; +use namada::ledger::masp::{ShieldedContext, ShieldedUtils}; use namada::ledger::rpc::TxBroadcastData; use namada::ledger::signing::TxSigningKey; use namada::ledger::tx; @@ -35,14 +37,18 @@ pub async fn find_keypair< pub async fn tx_signer< C: namada::ledger::queries::Client + Sync, U: WalletUtils, + V: ShieldedUtils, >( client: &C, wallet: &mut Wallet, + shielded: &mut ShieldedContext, args: &args::Tx, default: TxSigningKey, ) -> Result { - namada::ledger::signing::tx_signer::(client, wallet, args, default) - .await + namada::ledger::signing::tx_signer::( + client, wallet, shielded, args, default, + ) + .await } /// Sign a transaction with a given signing key or public key of a given signer. @@ -58,21 +64,25 @@ pub async fn tx_signer< pub async fn sign_tx< C: namada::ledger::queries::Client + Sync, U: WalletUtils, + V: ShieldedUtils, >( client: &C, wallet: &mut Wallet, + shielded: &mut ShieldedContext, tx: Tx, args: &args::Tx, default: TxSigningKey, mut updated_balance: Option, #[cfg(not(feature = "mainnet"))] requires_pow: bool, ) -> Result<(TxBroadcastData, Option), tx::Error> { - namada::ledger::signing::sign_tx::( + namada::ledger::signing::sign_tx::( client, wallet, + shielded, tx, args, default, + updated_balance, #[cfg(not(feature = "mainnet"))] requires_pow, ) @@ -86,9 +96,11 @@ pub async fn sign_wrapper< 'key, C: namada::ledger::queries::Client + Sync, U: WalletUtils, + V: ShieldedUtils, >( client: &C, wallet: &mut Wallet, + shielded: &mut ShieldedContext, args: &args::Tx, epoch: Epoch, tx: Tx, @@ -99,6 +111,7 @@ pub async fn sign_wrapper< namada::ledger::signing::sign_wrapper( client, wallet, + shielded, args, epoch, tx, diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index 940937087db..e6fb5630948 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -12,6 +12,7 @@ use borsh::{BorshDeserialize, BorshSerialize}; use data_encoding::HEXLOWER_PERMISSIVE; use masp_proofs::prover::LocalTxProver; use namada::ledger::governance::storage as gov_storage; +use namada::ledger::masp::{ShieldedContext, ShieldedUtils}; use namada::ledger::rpc::{TxBroadcastData, TxResponse}; use namada::ledger::signing::TxSigningKey; use namada::ledger::wallet::{Wallet, WalletUtils}; @@ -47,8 +48,8 @@ use crate::wallet::{ gen_validator_keys, read_and_confirm_encryption_password, CliWalletUtils, }; -pub async fn submit_custom( - client: &C, +pub async fn submit_custom( + client: &HttpClient, ctx: &mut Context, mut args: args::TxCustom, ) -> Result<(), tx::Error> { @@ -56,11 +57,11 @@ pub async fn submit_custom( .tx .chain_id .or_else(|| Some(ctx.config.ledger.chain_id.clone())); - tx::submit_custom::(client, &mut ctx.wallet, args).await + tx::submit_custom(client, &mut ctx.wallet, &mut ctx.shielded, args).await } -pub async fn submit_update_vp( - client: &C, +pub async fn submit_update_vp( + client: &HttpClient, ctx: &mut Context, mut args: args::TxUpdateVp, ) -> Result<(), tx::Error> { @@ -68,11 +69,11 @@ pub async fn submit_update_vp( .tx .chain_id .or_else(|| Some(ctx.config.ledger.chain_id.clone())); - tx::submit_update_vp::(client, &mut ctx.wallet, args).await + tx::submit_update_vp(client, &mut ctx.wallet, &mut ctx.shielded, args).await } -pub async fn submit_init_account( - client: &C, +pub async fn submit_init_account( + client: &HttpClient, ctx: &mut Context, mut args: args::TxInitAccount, ) -> Result<(), tx::Error> { @@ -80,13 +81,12 @@ pub async fn submit_init_account( .tx .chain_id .or_else(|| Some(ctx.config.ledger.chain_id.clone())); - tx::submit_init_account::(client, &mut ctx.wallet, args).await + tx::submit_init_account(client, &mut ctx.wallet, &mut ctx.shielded, args) + .await } -pub async fn submit_init_validator< - C: namada::ledger::queries::Client + Sync, ->( - client: &C, +pub async fn submit_init_validator( + client: &HttpClient, mut ctx: Context, args::TxInitValidator { tx: tx_args, @@ -176,12 +176,10 @@ pub async fn submit_init_validator< .expect("DKG sessions keys should have been created") .public(); - let validator_vp_code_hash = query_wasm_code_hash::( - client, - validator_vp_code_path.to_str().unwrap(), - ) - .await - .unwrap(); + let validator_vp_code_hash = + query_wasm_code_hash(client, validator_vp_code_path.to_str().unwrap()) + .await + .unwrap(); // Validate the commission rate data if commission_rate > Decimal::ONE || commission_rate < Decimal::ZERO { @@ -309,7 +307,7 @@ pub async fn submit_init_validator< .unwrap(); let key = pos::params_key(); - let pos_params = rpc::query_storage_value::(client, &key) + let pos_params = rpc::query_storage_value::<_, PosParams>(client, &key) .await .expect("Pos parameter should be defined."); @@ -456,8 +454,8 @@ pub async fn submit_transfer( tx::submit_transfer(client, &mut ctx.wallet, &mut ctx.shielded, args).await } -pub async fn submit_ibc_transfer( - client: &C, +pub async fn submit_ibc_transfer( + client: &HttpClient, mut ctx: Context, mut args: args::TxIbcTransfer, ) -> Result<(), tx::Error> { @@ -465,11 +463,12 @@ pub async fn submit_ibc_transfer( .tx .chain_id .or_else(|| Some(ctx.config.ledger.chain_id.clone())); - tx::submit_ibc_transfer::(client, &mut ctx.wallet, args).await + tx::submit_ibc_transfer(client, &mut ctx.wallet, &mut ctx.shielded, args) + .await } -pub async fn submit_init_proposal( - client: &C, +pub async fn submit_init_proposal( + client: &HttpClient, mut ctx: Context, mut args: args::InitProposal, ) -> Result<(), tx::Error> { @@ -542,7 +541,7 @@ pub async fn submit_init_proposal( if args.offline { let signer = ctx.get(&signer); let signing_key = - find_keypair::(client, &mut ctx.wallet, &signer) + find_keypair::<_, CliWalletUtils>(client, &mut ctx.wallet, &signer) .await?; let offline_proposal = OfflineProposal::new(proposal, signer, &signing_key); @@ -611,7 +610,7 @@ pub async fn submit_init_proposal( tx.set_data(Data::new(data)); tx.set_code(Code::from_hash(tx_code_hash)); - process_tx::( + process_tx( client, ctx, &args.tx, @@ -626,8 +625,8 @@ pub async fn submit_init_proposal( } } -pub async fn submit_vote_proposal( - client: &C, +pub async fn submit_vote_proposal( + client: &HttpClient, mut ctx: Context, mut args: args::VoteProposal, ) -> Result<(), tx::Error> { @@ -718,7 +717,7 @@ pub async fn submit_vote_proposal( } let signing_key = - find_keypair::(client, &mut ctx.wallet, signer) + find_keypair::<_, CliWalletUtils>(client, &mut ctx.wallet, signer) .await?; let offline_vote = OfflineVote::new( &proposal, @@ -752,7 +751,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::<_, Epoch>( client, &proposal_start_epoch_key, ) @@ -761,7 +760,7 @@ pub async fn submit_vote_proposal( // Check vote type and memo let proposal_type_key = gov_storage::get_proposal_type_key(proposal_id); let proposal_type: ProposalType = rpc::query_storage_value::< - C, + _, ProposalType, >(client, &proposal_type_key) .await @@ -783,7 +782,7 @@ pub async fn submit_vote_proposal( match address { Address::Established(_) => { let vp_key = Key::validity_predicate(address); - if !rpc::query_has_storage_key::(client, &vp_key) + if !rpc::query_has_storage_key(client, &vp_key) .await { eprintln!( @@ -867,7 +866,7 @@ pub async fn submit_vote_proposal( tx.set_data(Data::new(data)); tx.set_code(Code::from_hash(tx_code_hash)); - process_tx::( + process_tx( client, ctx, &args.tx, @@ -895,8 +894,8 @@ pub async fn submit_vote_proposal( } } -pub async fn submit_reveal_pk( - client: &C, +pub async fn submit_reveal_pk( + client: &HttpClient, ctx: &mut Context, mut args: args::RevealPk, ) -> Result<(), tx::Error> { @@ -904,11 +903,11 @@ pub async fn submit_reveal_pk( .tx .chain_id .or_else(|| Some(ctx.config.ledger.chain_id.clone())); - tx::submit_reveal_pk::(client, &mut ctx.wallet, args).await + tx::submit_reveal_pk(client, &mut ctx.wallet, &mut ctx.shielded, args).await } -pub async fn reveal_pk_if_needed( - client: &C, +pub async fn reveal_pk_if_needed( + client: &HttpClient, ctx: &mut Context, public_key: &common::PublicKey, args: &args::Tx, @@ -920,19 +919,22 @@ pub async fn reveal_pk_if_needed( .or_else(|| Some(ctx.config.ledger.chain_id.clone())), ..args.clone() }; - tx::reveal_pk_if_needed::(client, &mut ctx.wallet, public_key, &args) - .await + tx::reveal_pk_if_needed( + client, + &mut ctx.wallet, + &mut ctx.shielded, + public_key, + &args, + ) + .await } -pub async fn has_revealed_pk( - client: &C, - addr: &Address, -) -> bool { +pub async fn has_revealed_pk(client: &HttpClient, addr: &Address) -> bool { tx::has_revealed_pk(client, addr).await } -pub async fn submit_reveal_pk_aux( - client: &C, +pub async fn submit_reveal_pk_aux( + client: &HttpClient, ctx: &mut Context, public_key: &common::PublicKey, args: &args::Tx, @@ -944,15 +946,21 @@ pub async fn submit_reveal_pk_aux( .or_else(|| Some(ctx.config.ledger.chain_id.clone())), ..args.clone() }; - tx::submit_reveal_pk_aux::(client, &mut ctx.wallet, public_key, &args) - .await + tx::submit_reveal_pk_aux( + client, + &mut ctx.wallet, + &mut ctx.shielded, + public_key, + &args, + ) + .await } /// 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: &C, +async fn is_safe_voting_window( + client: &HttpClient, proposal_id: u64, proposal_start_epoch: Epoch, ) -> Result { @@ -961,8 +969,8 @@ async fn is_safe_voting_window( /// Removes validators whose vote corresponds to that of the delegator (needless /// vote) -async fn filter_delegations( - client: &C, +async fn filter_delegations( + client: &HttpClient, delegations: HashSet
, proposal_id: u64, delegator_vote: &ProposalVote, @@ -981,7 +989,7 @@ async fn filter_delegations( ); if let Some(validator_vote) = - rpc::query_storage_value::( + rpc::query_storage_value::<_, ProposalVote>( client, &vote_key, ) .await @@ -998,8 +1006,8 @@ async fn filter_delegations( delegations.into_iter().flatten().collect() } -pub async fn submit_bond( - client: &C, +pub async fn submit_bond( + client: &HttpClient, ctx: &mut Context, mut args: args::Bond, ) -> Result<(), tx::Error> { @@ -1007,11 +1015,11 @@ pub async fn submit_bond( .tx .chain_id .or_else(|| Some(ctx.config.ledger.chain_id.clone())); - tx::submit_bond::(client, &mut ctx.wallet, args).await + tx::submit_bond(client, &mut ctx.wallet, &mut ctx.shielded, args).await } -pub async fn submit_unbond( - client: &C, +pub async fn submit_unbond( + client: &HttpClient, ctx: &mut Context, mut args: args::Unbond, ) -> Result<(), tx::Error> { @@ -1019,11 +1027,11 @@ pub async fn submit_unbond( .tx .chain_id .or_else(|| Some(ctx.config.ledger.chain_id.clone())); - tx::submit_unbond::(client, &mut ctx.wallet, args).await + tx::submit_unbond(client, &mut ctx.wallet, &mut ctx.shielded, args).await } -pub async fn submit_withdraw( - client: &C, +pub async fn submit_withdraw( + client: &HttpClient, mut ctx: Context, mut args: args::Withdraw, ) -> Result<(), tx::Error> { @@ -1031,13 +1039,11 @@ pub async fn submit_withdraw( .tx .chain_id .or_else(|| Some(ctx.config.ledger.chain_id.clone())); - tx::submit_withdraw::(client, &mut ctx.wallet, args).await + tx::submit_withdraw(client, &mut ctx.wallet, &mut ctx.shielded, args).await } -pub async fn submit_validator_commission_change< - C: namada::ledger::queries::Client + Sync, ->( - client: &C, +pub async fn submit_validator_commission_change( + client: &HttpClient, mut ctx: Context, mut args: args::TxCommissionRateChange, ) -> Result<(), tx::Error> { @@ -1045,18 +1051,17 @@ pub async fn submit_validator_commission_change< .tx .chain_id .or_else(|| Some(ctx.config.ledger.chain_id.clone())); - tx::submit_validator_commission_change::( + tx::submit_validator_commission_change( client, &mut ctx.wallet, + &mut ctx.shielded, args, ) .await } -pub async fn submit_unjail_validator< - C: namada::ledger::queries::Client + Sync, ->( - client: &C, +pub async fn submit_unjail_validator( + client: &HttpClient, mut ctx: Context, mut args: args::TxUnjailValidator, ) -> Result<(), tx::Error> { @@ -1064,13 +1069,19 @@ pub async fn submit_unjail_validator< .tx .chain_id .or_else(|| Some(ctx.config.ledger.chain_id.clone())); - tx::submit_unjail_validator::(client, &mut ctx.wallet, args).await + tx::submit_unjail_validator( + client, + &mut ctx.wallet, + &mut ctx.shielded, + 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( - client: &C, +async fn process_tx( + client: &HttpClient, mut ctx: Context, args: &args::Tx, tx: Tx, @@ -1085,12 +1096,14 @@ async fn process_tx( .or_else(|| Some(tx.header.chain_id.clone())), ..args.clone() }; - let res: Vec
= tx::process_tx::( + let res: Vec
= tx::process_tx( client, &mut ctx.wallet, + &mut ctx.shielded, &args, tx, default_signer, + updated_balance, #[cfg(not(feature = "mainnet"))] requires_pow, ) @@ -1112,8 +1125,8 @@ pub 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: &C, +pub async fn broadcast_tx( + rpc_cli: &HttpClient, to_broadcast: &TxBroadcastData, ) -> Result { tx::broadcast_tx(rpc_cli, to_broadcast).await @@ -1127,8 +1140,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: &C, +pub async fn submit_tx( + client: &HttpClient, to_broadcast: TxBroadcastData, ) -> Result { tx::submit_tx(client, to_broadcast).await diff --git a/apps/src/lib/node/ledger/shell/finalize_block.rs b/apps/src/lib/node/ledger/shell/finalize_block.rs index c38ed4cdbfc..8d8b00532ee 100644 --- a/apps/src/lib/node/ledger/shell/finalize_block.rs +++ b/apps/src/lib/node/ledger/shell/finalize_block.rs @@ -175,7 +175,7 @@ where ); continue; }; - let tx_type = tx.header(); + let tx_header = tx.header(); // If [`process_proposal`] rejected a Tx, emit an event here and // move on to next tx if ErrorCodes::from_u32(processed_tx.result.code).unwrap() @@ -190,7 +190,7 @@ where // if the rejected tx was decrypted, remove it // from the queue of txs to be processed and remove the hash // from storage - if let TxType::Decrypted(_) = &tx_type.tx_type { + if let TxType::Decrypted(_) = &tx_header.tx_type { let tx_hash = self .wl_storage .storage @@ -210,7 +210,7 @@ where } #[cfg(not(any(feature = "abciplus", feature = "abcipp")))] - if let TxType::Wrapper(wrapper) = &tx_type { + if let TxType::Wrapper(wrapper) = &tx_header { // Charge fee if wrapper transaction went out of gas or failed because of fees let error_code = ErrorCodes::from_u32(processed_tx.result.code).unwrap(); @@ -254,7 +254,7 @@ where mut tx_gas_meter, has_valid_pow, wrapper, - ) = match &tx_type.tx_type { + ) = match &tx_header.tx_type { TxType::Wrapper(wrapper) => { stats.increment_wrapper_txs(); let mut tx_event = Event::new_tx_event(&tx, height.0); @@ -271,21 +271,17 @@ where gas_meter, #[cfg(not(feature = "mainnet"))] has_valid_pow, - Some(wrapper.to_owned()), + Some(tx.clone()), //FIXME: can avoid this clone? ) } TxType::Decrypted(inner) => { // We remove the corresponding wrapper tx from the queue - let wrapper = self + let mut tx_in_queue = self .wl_storage .storage .tx_queue .pop() - .expect("Missing wrapper tx in queue") - .tx - .clone() - .update_header(TxType::Raw) - .header_hash(); + .expect("Missing wrapper tx in queue"); let mut event = Event::new_tx_event(&tx, height.0); match inner { @@ -308,8 +304,13 @@ where ( event, - Some(wrapper.tx.tx_hash), - TxGasMeter::new(wrapper.gas), + Some( + tx_in_queue + .tx + .update_header(TxType::Raw) + .header_hash(), + ), + TxGasMeter::new(tx_in_queue.gas), #[cfg(not(feature = "mainnet"))] false, None, @@ -332,7 +333,7 @@ where }; match protocol::apply_tx( - tx_type, + tx, processed_tx.tx.as_ref(), TxIndex( tx_index @@ -969,7 +970,7 @@ mod test_finalize_block { let mut wrapper = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( Fee { - amount: 1.into(), + amount_per_gas_unit: 1.into(), token: shell.wl_storage.storage.native_token.clone(), }, &keypair, @@ -977,6 +978,7 @@ mod test_finalize_block { GAS_LIMIT_MULTIPLIER.into(), #[cfg(not(feature = "mainnet"))] None, + None, )))); wrapper.header.chain_id = shell.chain_id.clone(); wrapper.set_data(Data::new("wasm_code".as_bytes().to_owned())); @@ -998,9 +1000,16 @@ mod test_finalize_block { }, }); } else { + let wrapper_info = + if let TxType::Wrapper(w) = wrapper.header().tx_type { + w + } else { + panic!("Unexpected tx type"); + }; shell.enqueue_tx( wrapper.clone(), - u64::from(&wrapper.gas_limit) - tx.to_bytes().len() as u64, + u64::from(&wrapper_info.gas_limit) + - wrapper.to_bytes().len() as u64, ); } @@ -1045,7 +1054,7 @@ mod test_finalize_block { fn test_process_proposal_rejected_decrypted_tx() { let (mut shell, _) = setup(1); let keypair = gen_keypair(); - let mut outer_tx = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( + let mut wrapper = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( Fee { amount_per_gas_unit: 0.into(), token: shell.wl_storage.storage.native_token.clone(), @@ -1055,28 +1064,30 @@ mod test_finalize_block { GAS_LIMIT_MULTIPLIER.into(), #[cfg(not(feature = "mainnet"))] None, + None, )))); - outer_tx.header.chain_id = shell.chain_id.clone(); - outer_tx.set_code(Code::new("wasm_code".as_bytes().to_owned())); - outer_tx.set_data(Data::new( + wrapper.header.chain_id = shell.chain_id.clone(); + wrapper.set_code(Code::new("wasm_code".as_bytes().to_owned())); + wrapper.set_data(Data::new( String::from("transaction data").as_bytes().to_owned(), )); - outer_tx.encrypt(&Default::default()); - shell.enqueue_tx(outer_tx.clone()); + wrapper.encrypt(&Default::default()); + let gas_limit = + u64::from(&wrapper.header().wrapper().unwrap().gas_limit) + - wrapper.to_bytes().len() as u64; + shell.enqueue_tx(wrapper.clone(), gas_limit); - outer_tx.update_header(TxType::Decrypted(DecryptedTx::Decrypted { + wrapper.update_header(TxType::Decrypted(DecryptedTx::Decrypted { #[cfg(not(feature = "mainnet"))] has_valid_pow: false, })); let processed_tx = ProcessedTx { - tx: outer_tx.to_bytes(), + tx: wrapper.to_bytes(), result: TxResult { code: ErrorCodes::InvalidTx.into(), info: "".into(), }, }; - let gas_limit = - u64::from(&wrapper.gas_limit) - signed_wrapper.len() as u64; // check that the decrypted tx was not applied for event in shell @@ -1102,22 +1113,18 @@ mod test_finalize_block { let keypair = crate::wallet::defaults::daewon_keypair(); // not valid tx bytes - let wrapper = WrapperTx { - fee: Fee { + let wrapper = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( + Fee { amount_per_gas_unit: 0.into(), token: shell.wl_storage.storage.native_token.clone(), }, - pk: keypair.ref_to(), - epoch: Epoch(0), - gas_limit: GAS_LIMIT_MULTIPLIER.into(), + &keypair, + Epoch(0), + GAS_LIMIT_MULTIPLIER.into(), #[cfg(not(feature = "mainnet"))] - pow_solution: None, - unshield: None, - }; - let signed_wrapper = wrapper - .sign(&keypair, shell.chain_id.clone(), None) - .unwrap() - .to_bytes(); + None, + None, + )))); let processed_tx = ProcessedTx { tx: Tx::new(TxType::Decrypted(DecryptedTx::Undecryptable)) .to_bytes(), @@ -1128,9 +1135,9 @@ mod test_finalize_block { }; let gas_limit = - u64::from(&wrapper.gas_limit) - signed_wrapper.len() as u64; - let tx = Tx::new(TxType::Wrapper(Box::new(wrapper))); - shell.enqueue_tx(tx, gas_limit); + u64::from(&wrapper.header().wrapper().unwrap().gas_limit) + - wrapper.to_bytes().len() as u64; + shell.enqueue_tx(wrapper, gas_limit); // check that correct error message is returned for event in shell @@ -1174,10 +1181,10 @@ mod test_finalize_block { // create two decrypted txs let tx_code = TestWasms::TxNoOp.read_bytes(); for i in 0..2 { - let mut outer_tx = + let mut wrapper = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( Fee { - amount: 1.into(), + amount_per_gas_unit: 1.into(), token: shell.wl_storage.storage.native_token.clone(), }, &keypair, @@ -1185,24 +1192,27 @@ mod test_finalize_block { GAS_LIMIT_MULTIPLIER.into(), #[cfg(not(feature = "mainnet"))] None, + None, )))); - outer_tx.header.chain_id = shell.chain_id.clone(); - outer_tx.set_code(Code::new(tx_code.clone())); - outer_tx.set_data(Data::new( + wrapper.header.chain_id = shell.chain_id.clone(); + wrapper.set_code(Code::new(tx_code.clone())); + wrapper.set_data(Data::new( format!("Decrypted transaction data: {}", i) .as_bytes() .to_owned(), )); - outer_tx.encrypt(&Default::default()); - shell.enqueue_tx(outer_tx.clone()); - outer_tx.update_header(TxType::Decrypted(DecryptedTx::Decrypted { + wrapper.encrypt(&Default::default()); + let gas = u64::from(wrapper.header().wrapper().unwrap().gas_limit) + - wrapper.to_bytes().len() as u64; + shell.enqueue_tx(wrapper.clone(), gas); + wrapper.update_header(TxType::Decrypted(DecryptedTx::Decrypted { #[cfg(not(feature = "mainnet"))] has_valid_pow: false, })); - outer_tx.decrypt(::G2Affine::prime_subgroup_generator()) + wrapper.decrypt(::G2Affine::prime_subgroup_generator()) .expect("Test failed"); processed_txs.push(ProcessedTx { - tx: outer_tx.to_bytes(), + tx: wrapper.to_bytes(), result: TxResult { code: ErrorCodes::Ok.into(), info: "".into(), @@ -1214,7 +1224,7 @@ mod test_finalize_block { let mut wrapper_tx = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( Fee { - amount: 1.into(), + amount_per_gas_unit: 1.into(), token: shell.wl_storage.storage.native_token.clone(), }, &keypair, @@ -1222,6 +1232,7 @@ mod test_finalize_block { GAS_LIMIT_MULTIPLIER.into(), #[cfg(not(feature = "mainnet"))] None, + None, )))); wrapper_tx.header.chain_id = shell.chain_id.clone(); wrapper_tx.set_code(Code::new("wasm_code".as_bytes().to_owned())); @@ -1733,25 +1744,25 @@ mod test_finalize_block { wasm_path.push("wasm_for_tests/tx_no_op.wasm"); let tx_code = std::fs::read(wasm_path) .expect("Expected a file at given code path"); - let mut wrapper_tx = - Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( - Fee { - amount: 0.into(), - token: shell.wl_storage.storage.native_token.clone(), - }, - &keypair, - Epoch(0), - 0.into(), - #[cfg(not(feature = "mainnet"))] - None, - )))); - wrapper_tx.header.chain_id = shell.chain_id.clone(); - wrapper_tx.set_code(Code::new(tx_code)); - wrapper_tx.set_data(Data::new( + let mut wrapper = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( + Fee { + amount_per_gas_unit: 0.into(), + token: shell.wl_storage.storage.native_token.clone(), + }, + &keypair, + Epoch(0), + 0.into(), + #[cfg(not(feature = "mainnet"))] + None, + None, + )))); + wrapper.header.chain_id = shell.chain_id.clone(); + wrapper.set_code(Code::new(tx_code)); + wrapper.set_data(Data::new( "Encrypted transaction data".as_bytes().to_owned(), )); - let mut decrypted_tx = wrapper_tx.clone(); - wrapper_tx.encrypt(&Default::default()); + let mut decrypted_tx = wrapper.clone(); + wrapper.encrypt(&Default::default()); decrypted_tx.update_header(TxType::Decrypted(DecryptedTx::Decrypted { #[cfg(not(feature = "mainnet"))] @@ -1760,7 +1771,7 @@ mod test_finalize_block { // Write inner hash in storage let inner_hash_key = replay_protection::get_tx_hash_key( - &wrapper_tx.clone().update_header(TxType::Raw).header_hash(), + &wrapper.clone().update_header(TxType::Raw).header_hash(), ); shell .wl_storage @@ -1775,8 +1786,10 @@ mod test_finalize_block { info: "".into(), }, }; - let gas_limit = u64::from(&wrapper_tx.gas_limit); - shell.enqueue_tx(wrapper_tx, gas_limit); + let gas_limit = + u64::from(&wrapper.header().wrapper().unwrap().gas_limit) + - wrapper.to_bytes().len() as u64; + shell.enqueue_tx(wrapper, gas_limit); let event = &shell .finalize_block(FinalizeBlock { @@ -1824,13 +1837,7 @@ mod test_finalize_block { wasm_path.push("wasm_for_tests/tx_no_op.wasm"); let tx_code = std::fs::read(wasm_path) .expect("Expected a file at given code path"); - let raw_tx = Tx::new( - tx_code, - Some("Encrypted transaction data".as_bytes().to_owned()), - shell.chain_id.clone(), - None, - ); - let wrapper_tx = WrapperTx::new( + let mut wrapper = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( Fee { amount_per_gas_unit: 1.into(), token: shell.wl_storage.storage.native_token.clone(), @@ -1838,25 +1845,26 @@ mod test_finalize_block { &crate::wallet::defaults::albert_keypair(), Epoch(0), GAS_LIMIT_MULTIPLIER.into(), - raw_tx.clone(), - Default::default(), #[cfg(not(feature = "mainnet"))] None, None, - ); - let fee_amount = wrapper_tx.get_tx_fee().unwrap(); + )))); + wrapper.header.chain_id = shell.chain_id.clone(); + wrapper.set_code(Code::new(tx_code)); + wrapper.set_data(Data::new( + "Enxrypted transaction data".as_bytes().to_owned(), + )); + wrapper.add_section(Section::Signature(Signature::new( + &wrapper.header_hash(), + &crate::wallet::defaults::albert_keypair(), + ))); + let fee_amount = + wrapper.header().wrapper().unwrap().get_tx_fee().unwrap(); - let wrapper = wrapper_tx - .sign( - &crate::wallet::defaults::albert_keypair(), - shell.chain_id.clone(), - None, - ) - .expect("Test failed"); let signer_balance = storage_api::token::read_balance( &shell.wl_storage, &shell.wl_storage.storage.native_token, - &wrapper_tx.fee_payer(), + &wrapper.header().wrapper().unwrap().fee_payer(), ) .unwrap(); @@ -1895,7 +1903,7 @@ mod test_finalize_block { let new_signer_balance = storage_api::token::read_balance( &shell.wl_storage, &shell.wl_storage.storage.native_token, - &wrapper_tx.fee_payer(), + &wrapper.header().wrapper().unwrap().fee_payer(), ) .unwrap(); assert_eq!( diff --git a/apps/src/lib/node/ledger/shell/mod.rs b/apps/src/lib/node/ledger/shell/mod.rs index 3a231cec191..7f62edaac38 100644 --- a/apps/src/lib/node/ledger/shell/mod.rs +++ b/apps/src/lib/node/ledger/shell/mod.rs @@ -23,13 +23,14 @@ use std::path::{Path, PathBuf}; use std::rc::Rc; use borsh::{BorshDeserialize, BorshSerialize}; +use masp_primitives::transaction::Transaction; use namada::ledger::events::log::EventLog; use namada::ledger::events::Event; use namada::ledger::gas::TxGasMeter; use namada::ledger::pos::namada_proof_of_stake::types::{ ConsensusValidator, ValidatorSetUpdate, }; -use namada::ledger::protocol::{apply_tx, load_transfer_code_from_storage}; +use namada::ledger::protocol::{apply_tx, get_transfer_hash_from_storage}; use namada::ledger::storage::write_log::WriteLog; use namada::ledger::storage::{ DBIter, Sha256Hasher, Storage, StorageHasher, TempWlStorage, WlStorage, DB, @@ -46,7 +47,7 @@ use namada::types::storage::{BlockHeight, Key, TxIndex}; use namada::types::time::{DateTimeUtc, TimeZone, Utc}; use namada::types::transaction::{ hash_tx, verify_decrypted_correctly, AffineCurve, DecryptedTx, - EllipticCurve, PairingEngine, TxType, + EllipticCurve, PairingEngine, TxType, WrapperTx, }; use namada::types::{address, hash, token}; use namada::vm::wasm::{TxCache, VpCache}; @@ -677,6 +678,7 @@ where tx_bytes: &[u8], temp_wl_storage: &mut TempWlStorage, ) -> Result<()> { + //FIXME: better to write hash of the wrapper first? Maybe we can avoid to deserialize once more? let inner_tx_hash = wrapper.clone().update_header(TxType::Raw).header_hash(); let inner_hash_key = replay_protection::get_tx_hash_key(&inner_tx_hash); @@ -849,9 +851,22 @@ where return response; } + let fee_unshield = wrapper + .unshield_hash + .map(|ref hash| tx.get_section(hash)) + .flatten() + .map(|section| { + if let Section::MaspTx(transaction) = section { + Some(transaction.to_owned()) + } else { + None + } + }) + .flatten(); // Validate wrapper fees if let Err(e) = self.wrapper_fee_check( &wrapper, + fee_unshield, &mut TempWlStorage::new(&self.wl_storage.storage), None, &mut self.vp_wasm_cache.clone(), @@ -886,7 +901,7 @@ where let mut cumulated_gas = 0; let mut vp_wasm_cache = self.vp_wasm_cache.read_only(); let mut tx_wasm_cache = self.tx_wasm_cache.read_only(); - let raw_tx = match Tx::try_from(tx_bytes) { + let mut tx = match Tx::try_from(tx_bytes) { Ok(tx) => tx, Err(err) => { response.code = 1; @@ -894,17 +909,14 @@ where return response; } }; - let mut tx = match process_tx(raw_tx.clone()) { - Ok(tx_type) => tx_type, - Err(err) => { - response.code = 1; - response.log = err.to_string(); - return response; - } + if let Err(e) = tx.validate_header() { + response.code = 1; + response.log = e.to_string(); + return response; }; // Wrapper dry run to allow estimating the gas cost of a transaction - let mut tx_gas_meter = match tx { + let mut tx_gas_meter = match tx.header().tx_type { TxType::Wrapper(ref wrapper) => { let mut tx_gas_meter = TxGasMeter::new(wrapper.gas_limit.to_owned().into()); @@ -933,15 +945,13 @@ where // NOTE: the encryption key for a dry-run should always be an hardcoded, dummy one let privkey = ::G2Affine::prime_subgroup_generator(); - tx = TxType::Decrypted(DecryptedTx::Decrypted { - tx: wrapper - .decrypt(privkey) - .expect("Could not decrypt the inner tx"), - #[cfg(not(feature = "mainnet"))] - // To be able to dry-run testnet faucet withdrawal, pretend + + tx.update_header(TxType::Decrypted(DecryptedTx::Decrypted { + // To be able to dry-run testnet faucet withdrawal, pretend // that we got a valid PoW + #[cfg(not(feature = "mainnet"))] has_valid_pow: true, - }); + })); TxGasMeter::new( tx_gas_meter .tx_gas_limit @@ -958,14 +968,14 @@ where .expect("Missing parameter in storage"), ) } - TxType::Raw(raw) => { + TxType::Raw => { // Cast tx to a decrypted for execution - tx = TxType::Decrypted(DecryptedTx::Decrypted { - tx: raw, - + tx.update_header(TxType::Decrypted(DecryptedTx::Decrypted { + // To be able to dry-run testnet faucet withdrawal, pretend + // that we got a valid PoW #[cfg(not(feature = "mainnet"))] has_valid_pow: true, - }); + })); // If dry run only the inner tx, use the max block gas as the gas limit TxGasMeter::new( @@ -1094,6 +1104,7 @@ where pub fn wrapper_fee_check( &self, wrapper: &WrapperTx, + masp_transaction: Option, temp_wl_storage: &mut TempWlStorage, gas_table: Option>>, vp_wasm_cache: &mut VpCache, @@ -1126,28 +1137,31 @@ where ) .expect("Token balance read in the protocol must not fail"); - if wrapper.unshield.is_some() { + if wrapper.unshield_hash.is_some() { + //FIXME: isn't enough to pass the Transaction? If we already heck the validity of the Hash then I can only check if the argument is Some or None. Same in finalize block + let transaction = + masp_transaction + .ok_or(Error::TxApply(protocol::Error::FeeUnshieldingError( + namada::types::transaction::WrapperTxErr::InvalidUnshield( + "Missing expected fee unshielding tx".to_string(), + ), + )))?; + // Validate data and generate unshielding tx - let transfer_code = - load_transfer_code_from_storage(temp_wl_storage); + let transfer_code_hash = + get_transfer_hash_from_storage(temp_wl_storage); let descriptions_limit = self.wl_storage.read(¶meters::storage::get_fee_unshielding_descriptions_limit_key()).expect("Error reading the storage").expect("Missing fee unshielding descriptions limit param in storage"); let unshield = wrapper .check_and_generate_fee_unshielding( balance, - transfer_code, + transfer_code_hash, descriptions_limit, + transaction, ) .map_err(|e| { - Error::TxApply(protocol::Error::FeeUnshieldingError( - e, - )) - })? - .ok_or_else(|| { - Error::TxApply(protocol::Error::FeeUnshieldingError(namada::types::transaction::WrapperTxErr::InvalidUnshield( - "Missing expected fee unshielding tx".to_string(), - ))) + Error::TxApply(protocol::Error::FeeUnshieldingError(e)) })?; let gas_table = gas_table.unwrap_or_else(|| { @@ -1164,11 +1178,7 @@ where // Runtime check match apply_tx( - TxType::Decrypted(DecryptedTx::Decrypted { - tx: unshield, - #[cfg(not(feature = "mainnet"))] - has_valid_pow: false, - }), + unshield, &vec![], TxIndex::default(), &mut TxGasMeter::new(fee_unshielding_gas_limit), @@ -1225,10 +1235,10 @@ where /// for the shell #[cfg(test)] mod test_utils { + use crate::facade::tendermint_proto::abci::RequestPrepareProposal; use data_encoding::HEXUPPER; use std::ops::{Deref, DerefMut}; use std::path::PathBuf; - use tendermint_proto::abci::RequestPrepareProposal; use namada::ledger::storage::mockdb::MockDB; use namada::ledger::storage::{update_allowed_conversions, Sha256Hasher}; @@ -1416,6 +1426,7 @@ mod test_utils { /// wrapper as parameter. #[cfg(test)] pub fn enqueue_tx(&mut self, tx: Tx, inner_tx_gas: u64) { + //FIXME: should this take the gas meter of the wrapper instead of the reamining gas limit? self.shell.wl_storage.storage.tx_queue.push(TxInQueue { tx, gas: inner_tx_gas, @@ -1514,13 +1525,15 @@ mod test_utils { 300_000.into(), #[cfg(not(feature = "mainnet"))] None, + None, )))); wrapper.header.chain_id = shell.chain_id.clone(); wrapper.set_code(Code::new("wasm_code".as_bytes().to_owned())); wrapper.set_data(Data::new("transaction data".as_bytes().to_owned())); wrapper.encrypt(&Default::default()); let gas_limit = - u64::from(&wrapper.gas_limit) - signed_wrapper.len() as u64; + u64::from(&wrapper.header().wrapper().unwrap().gas_limit) + - wrapper.to_bytes().len() as u64; shell.wl_storage.storage.tx_queue.push(TxInQueue { tx: wrapper, @@ -1587,6 +1600,7 @@ mod test_mempool_validate { 0.into(), #[cfg(not(feature = "mainnet"))] None, + None, )))); unsigned_wrapper.header.chain_id = shell.chain_id.clone(); unsigned_wrapper.set_code(Code::new("wasm_code".as_bytes().to_owned())); @@ -1624,6 +1638,7 @@ mod test_mempool_validate { 0.into(), #[cfg(not(feature = "mainnet"))] None, + None, )))); invalid_wrapper.header.chain_id = shell.chain_id.clone(); invalid_wrapper.set_code(Code::new("wasm_code".as_bytes().to_owned())); @@ -1638,7 +1653,7 @@ mod test_mempool_validate { // we mount a malleability attack to try and remove the fee let mut new_wrapper = invalid_wrapper.header().wrapper().expect("Test failed"); - new_wrapper.fee.amount = 0.into(); + new_wrapper.fee.amount_per_gas_unit = 0.into(); invalid_wrapper.update_header(TxType::Wrapper(Box::new(new_wrapper))); let mut result = shell.mempool_validate( @@ -1688,6 +1703,7 @@ mod test_mempool_validate { GAS_LIMIT_MULTIPLIER.into(), #[cfg(not(feature = "mainnet"))] None, + None, )))); wrapper.header.chain_id = shell.chain_id.clone(); wrapper.set_code(Code::new("wasm_code".as_bytes().to_owned())); @@ -1842,14 +1858,7 @@ mod test_mempool_validate { .expect("Missing max_block_gas parameter in storage"); let keypair = super::test_utils::gen_keypair(); - let tx = Tx::new( - "wasm_code".as_bytes().to_owned(), - Some("transaction data".as_bytes().to_owned()), - shell.chain_id.clone(), - None, - ); - - let wrapper = WrapperTx::new( + let mut wrapper = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( Fee { amount_per_gas_unit: 100.into(), token: shell.wl_storage.storage.native_token.clone(), @@ -1857,14 +1866,17 @@ mod test_mempool_validate { &keypair, Epoch(0), (block_gas_limit + 1).into(), - tx, - Default::default(), #[cfg(not(feature = "mainnet"))] None, None, - ) - .sign(&keypair, shell.chain_id.clone(), None) - .expect("Wrapper signing failed"); + )))); + wrapper.header.chain_id = shell.chain_id.clone(); + wrapper.set_code(Code::new("wasm_code".as_bytes().to_owned())); + wrapper.set_data(Data::new("transaction data".as_bytes().to_owned())); + wrapper.add_section(Section::Signature(Signature::new( + &wrapper.header_hash(), + &keypair, + ))); let result = shell.mempool_validate( wrapper.to_bytes().as_ref(), @@ -1877,17 +1889,9 @@ mod test_mempool_validate { #[test] fn test_exceeding_gas_limit_tx() { let (shell, _) = test_utils::setup(1); - let keypair = super::test_utils::gen_keypair(); - let tx = Tx::new( - "wasm_code".as_bytes().to_owned(), - Some("transaction data".as_bytes().to_owned()), - shell.chain_id.clone(), - None, - ); - - let wrapper = WrapperTx::new( + let mut wrapper = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( Fee { amount_per_gas_unit: 100.into(), token: shell.wl_storage.storage.native_token.clone(), @@ -1895,14 +1899,17 @@ mod test_mempool_validate { &keypair, Epoch(0), 0.into(), - tx, - Default::default(), #[cfg(not(feature = "mainnet"))] None, None, - ) - .sign(&keypair, shell.chain_id.clone(), None) - .expect("Wrapper signing failed"); + )))); + wrapper.header.chain_id = shell.chain_id.clone(); + wrapper.set_code(Code::new("wasm_code".as_bytes().to_owned())); + wrapper.set_data(Data::new("transaction data".as_bytes().to_owned())); + wrapper.add_section(Section::Signature(Signature::new( + &wrapper.header_hash(), + &keypair, + ))); let result = shell.mempool_validate( wrapper.to_bytes().as_ref(), @@ -1916,14 +1923,7 @@ mod test_mempool_validate { fn test_fee_non_whitelisted_token() { let (shell, _) = test_utils::setup(1); - let tx = Tx::new( - "wasm_code".as_bytes().to_owned(), - Some("transaction data".as_bytes().to_owned()), - shell.chain_id.clone(), - None, - ); - - let wrapper = WrapperTx::new( + let mut wrapper = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( Fee { amount_per_gas_unit: 100.into(), token: address::btc(), @@ -1931,18 +1931,17 @@ mod test_mempool_validate { &crate::wallet::defaults::albert_keypair(), Epoch(0), GAS_LIMIT_MULTIPLIER.into(), - tx, - Default::default(), #[cfg(not(feature = "mainnet"))] None, None, - ) - .sign( + )))); + wrapper.header.chain_id = shell.chain_id.clone(); + wrapper.set_code(Code::new("wasm_code".as_bytes().to_owned())); + wrapper.set_data(Data::new("transaction data".as_bytes().to_owned())); + wrapper.add_section(Section::Signature(Signature::new( + &wrapper.header_hash(), &crate::wallet::defaults::albert_keypair(), - shell.chain_id.clone(), - None, - ) - .expect("Wrapper signing failed"); + ))); let result = shell.mempool_validate( wrapper.to_bytes().as_ref(), @@ -1956,14 +1955,7 @@ mod test_mempool_validate { fn test_fee_wrong_minimum_amount() { let (shell, _) = test_utils::setup(1); - let tx = Tx::new( - "wasm_code".as_bytes().to_owned(), - Some("transaction data".as_bytes().to_owned()), - shell.chain_id.clone(), - None, - ); - - let wrapper = WrapperTx::new( + let mut wrapper = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( Fee { amount_per_gas_unit: 0.into(), token: shell.wl_storage.storage.native_token.clone(), @@ -1971,18 +1963,17 @@ mod test_mempool_validate { &crate::wallet::defaults::albert_keypair(), Epoch(0), GAS_LIMIT_MULTIPLIER.into(), - tx, - Default::default(), #[cfg(not(feature = "mainnet"))] None, None, - ) - .sign( + )))); + wrapper.header.chain_id = shell.chain_id.clone(); + wrapper.set_code(Code::new("wasm_code".as_bytes().to_owned())); + wrapper.set_data(Data::new("transaction data".as_bytes().to_owned())); + wrapper.add_section(Section::Signature(Signature::new( + &wrapper.header_hash(), &crate::wallet::defaults::albert_keypair(), - shell.chain_id.clone(), - None, - ) - .expect("Wrapper signing failed"); + ))); let result = shell.mempool_validate( wrapper.to_bytes().as_ref(), @@ -1996,14 +1987,7 @@ mod test_mempool_validate { fn test_insufficient_balance_for_fee() { let (shell, _) = test_utils::setup(1); - let tx = Tx::new( - "wasm_code".as_bytes().to_owned(), - Some("transaction data".as_bytes().to_owned()), - shell.chain_id.clone(), - None, - ); - - let wrapper = WrapperTx::new( + let mut wrapper = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( Fee { amount_per_gas_unit: 1_000_000.into(), token: shell.wl_storage.storage.native_token.clone(), @@ -2011,18 +1995,17 @@ mod test_mempool_validate { &crate::wallet::defaults::albert_keypair(), Epoch(0), GAS_LIMIT_MULTIPLIER.into(), - tx, - Default::default(), #[cfg(not(feature = "mainnet"))] None, None, - ) - .sign( + )))); + wrapper.header.chain_id = shell.chain_id.clone(); + wrapper.set_code(Code::new("wasm_code".as_bytes().to_owned())); + wrapper.set_data(Data::new("transaction data".as_bytes().to_owned())); + wrapper.add_section(Section::Signature(Signature::new( + &wrapper.header_hash(), &crate::wallet::defaults::albert_keypair(), - shell.chain_id.clone(), - None, - ) - .expect("Wrapper signing failed"); + ))); let result = shell.mempool_validate( wrapper.to_bytes().as_ref(), @@ -2036,14 +2019,7 @@ mod test_mempool_validate { fn test_wrapper_fee_overflow() { let (shell, _) = test_utils::setup(1); - let tx = Tx::new( - "wasm_code".as_bytes().to_owned(), - Some("transaction data".as_bytes().to_owned()), - shell.chain_id.clone(), - None, - ); - - let wrapper = WrapperTx::new( + let mut wrapper = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( Fee { amount_per_gas_unit: token::Amount::max(), token: shell.wl_storage.storage.native_token.clone(), @@ -2051,18 +2027,17 @@ mod test_mempool_validate { &crate::wallet::defaults::albert_keypair(), Epoch(0), GAS_LIMIT_MULTIPLIER.into(), - tx, - Default::default(), #[cfg(not(feature = "mainnet"))] None, None, - ) - .sign( + )))); + wrapper.header.chain_id = shell.chain_id.clone(); + wrapper.set_code(Code::new("wasm_code".as_bytes().to_owned())); + wrapper.set_data(Data::new("transaction data".as_bytes().to_owned())); + wrapper.add_section(Section::Signature(Signature::new( + &wrapper.header_hash(), &crate::wallet::defaults::albert_keypair(), - shell.chain_id.clone(), - None, - ) - .expect("Wrapper signing failed"); + ))); let result = shell.mempool_validate( wrapper.to_bytes().as_ref(), diff --git a/apps/src/lib/node/ledger/shell/prepare_proposal.rs b/apps/src/lib/node/ledger/shell/prepare_proposal.rs index 48f23aa23af..dea7aad7ee0 100644 --- a/apps/src/lib/node/ledger/shell/prepare_proposal.rs +++ b/apps/src/lib/node/ledger/shell/prepare_proposal.rs @@ -10,7 +10,7 @@ use namada::ledger::storage::{DBIter, StorageHasher, TempWlStorage, DB}; use namada::ledger::storage_api::StorageRead; use namada::proof_of_stake::find_validator_by_raw_hash; use namada::proof_of_stake::pos_queries::PosQueries; -use namada::proto::Tx; +use namada::proto::{Tx, Section}; use namada::types::address::Address; use namada::types::key::tm_raw_hash_to_string; use namada::types::internal::TxInQueue; @@ -132,7 +132,6 @@ where fn build_encrypted_txs( &self, mut alloc: EncryptedTxBatchAllocator, - mut temp_wl_storage: TempWlStorage, txs: &[TxBytes], block_time: &Option, block_proposer: &Address, @@ -224,22 +223,25 @@ where // retrieved from block default to last block datetime which has // already been checked by mempool_validate, so it's valid if let (Some(block_time), Some(exp)) = - (block_time.as_ref(), &tx.expiration) + (block_time.as_ref(), &tx.header().expiration) { if block_time > exp { return Err(()); } } - if let TxType::Wrapper(wrapper) = process_tx(tx).map_err(|_| ())? { + tx.validate_header().map_err(|_| ())?; + if let TxType::Wrapper(wrapper) = tx.header().tx_type { // Check tx gas limit for tx size let mut tx_gas_meter = TxGasMeter::new(wrapper.gas_limit.clone().into()); tx_gas_meter.add_tx_size_gas(tx_bytes).map_err(|_| ())?; // Check fees + let fee_unshield = wrapper.unshield_hash.map(|ref hash| tx.get_section(hash).map(|section| if let Section::MaspTx(transaction) = section {Some(transaction.to_owned())} else {None} ).flatten()).flatten(); match self.wrapper_fee_check( &wrapper, + fee_unshield, temp_wl_storage, Some(Cow::Borrowed(gas_table)), vp_wasm_cache, @@ -408,6 +410,7 @@ use data_encoding::HEXUPPER; 0.into(), #[cfg(not(feature = "mainnet"))] None, + None )))); wrapper.header.chain_id = shell.chain_id.clone(); wrapper.set_code(Code::new("wasm_code".as_bytes().to_owned())); @@ -460,6 +463,7 @@ use data_encoding::HEXUPPER; GAS_LIMIT_MULTIPLIER.into(), #[cfg(not(feature = "mainnet"))] None, + None )))); tx.header.chain_id = shell.chain_id.clone(); tx.set_code(Code::new("wasm_code".as_bytes().to_owned())); @@ -472,7 +476,8 @@ use data_encoding::HEXUPPER; ))); tx.encrypt(&Default::default()); - shell.enqueue_tx(tx.clone()); + let gas = u64::from(tx.header().wrapper().expect("Wrong tx type").gas_limit) - tx.to_bytes().len() as u64; + shell.enqueue_tx(tx.clone(), gas); expected_wrapper.push(tx.clone()); req.txs.push(tx.to_bytes()); tx.update_header(TxType::Decrypted(DecryptedTx::Decrypted { @@ -521,7 +526,7 @@ use data_encoding::HEXUPPER; let keypair = crate::wallet::defaults::daewon_keypair(); let mut wrapper = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( Fee { - amount: 0.into(), + amount_per_gas_unit: 0.into(), token: shell.wl_storage.storage.native_token.clone(), }, &keypair, @@ -529,6 +534,7 @@ use data_encoding::HEXUPPER; 0.into(), #[cfg(not(feature = "mainnet"))] None, + None )))); wrapper.header.chain_id = shell.chain_id.clone(); wrapper.set_code(Code::new("wasm_code".as_bytes().to_owned())); @@ -573,7 +579,7 @@ use data_encoding::HEXUPPER; let keypair = crate::wallet::defaults::daewon_keypair(); let mut wrapper = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( Fee { - amount: 0.into(), + amount_per_gas_unit: 0.into(), token: shell.wl_storage.storage.native_token.clone(), }, &keypair, @@ -581,6 +587,7 @@ use data_encoding::HEXUPPER; 0.into(), #[cfg(not(feature = "mainnet"))] None, + None )))); wrapper.header.chain_id = shell.chain_id.clone(); wrapper.set_code(Code::new("wasm_code".as_bytes().to_owned())); @@ -614,7 +621,7 @@ use data_encoding::HEXUPPER; let keypair = crate::wallet::defaults::daewon_keypair(); let mut wrapper = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( Fee { - amount: 0.into(), + amount_per_gas_unit: 0.into(), token: shell.wl_storage.storage.native_token.clone(), }, &keypair, @@ -622,6 +629,7 @@ use data_encoding::HEXUPPER; 0.into(), #[cfg(not(feature = "mainnet"))] None, + None )))); wrapper.header.chain_id = shell.chain_id.clone(); wrapper.set_code(Code::new("wasm_code".as_bytes().to_owned())); @@ -667,7 +675,7 @@ use data_encoding::HEXUPPER; let keypair_2 = crate::wallet::defaults::daewon_keypair(); let mut wrapper = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( Fee { - amount: 0.into(), + amount_per_gas_unit: 0.into(), token: shell.wl_storage.storage.native_token.clone(), }, &keypair, @@ -675,6 +683,7 @@ use data_encoding::HEXUPPER; 0.into(), #[cfg(not(feature = "mainnet"))] None, + None )))); wrapper.header.chain_id = shell.chain_id.clone(); let tx_code = Code::new("wasm_code".as_bytes().to_owned()); @@ -690,7 +699,7 @@ use data_encoding::HEXUPPER; let mut new_wrapper = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( Fee { - amount: 0.into(), + amount_per_gas_unit: 0.into(), token: shell.wl_storage.storage.native_token.clone(), }, &keypair_2, @@ -698,6 +707,7 @@ use data_encoding::HEXUPPER; 0.into(), #[cfg(not(feature = "mainnet"))] None, + None )))); new_wrapper.header.chain_id = shell.chain_id.clone(); new_wrapper.header.timestamp = wrapper.header.timestamp; @@ -740,6 +750,7 @@ use data_encoding::HEXUPPER; 0.into(), #[cfg(not(feature = "mainnet"))] None, + None )))); wrapper_tx.header.chain_id = shell.chain_id.clone(); wrapper_tx.header.expiration = Some(tx_time); @@ -782,13 +793,6 @@ use data_encoding::HEXUPPER; .expect("Missing max_block_gas parameter in storage"); let keypair = gen_keypair(); - let tx = Tx::new( - "wasm_code".as_bytes().to_owned(), - Some("transaction data".as_bytes().to_owned()), - shell.chain_id.clone(), - None, - ); - let wrapper = WrapperTx::new( Fee { amount_per_gas_unit: 100.into(), @@ -797,17 +801,18 @@ use data_encoding::HEXUPPER; &keypair, Epoch(0), (block_gas_limit + 1).into(), - tx, - Default::default(), #[cfg(not(feature = "mainnet"))] None, None, - ) - .sign(&keypair, shell.chain_id.clone(), None) - .expect("Wrapper signing failed"); + ); + let mut wrapper_tx = Tx::new(TxType::Wrapper(Box::new(wrapper))); + wrapper_tx.header.chain_id = shell.chain_id.clone(); + wrapper_tx.set_code(Code::new("wasm_code".as_bytes().to_owned())); + wrapper_tx.set_data(Data::new("transaction data".as_bytes().to_owned())); + wrapper_tx.add_section(Section::Signature(Signature::new(&wrapper_tx.header_hash(), &keypair))); let req = RequestPrepareProposal { - txs: vec![wrapper.to_bytes()], + txs: vec![wrapper_tx.to_bytes()], max_tx_bytes: 0, time: None, ..Default::default() @@ -824,13 +829,6 @@ use data_encoding::HEXUPPER; let (shell, _) = test_utils::setup(1); let keypair = gen_keypair(); - let tx = Tx::new( - "wasm_code".as_bytes().to_owned(), - Some("transaction data".as_bytes().to_owned()), - shell.chain_id.clone(), - None, - ); - let wrapper = WrapperTx::new( Fee { amount_per_gas_unit: 100.into(), @@ -839,17 +837,19 @@ use data_encoding::HEXUPPER; &keypair, Epoch(0), 0.into(), - tx, - Default::default(), #[cfg(not(feature = "mainnet"))] None, None, - ) - .sign(&keypair, shell.chain_id.clone(), None) - .expect("Wrapper signing failed"); + ); + + let mut wrapper_tx = Tx::new(TxType::Wrapper(Box::new(wrapper))); + wrapper_tx.header.chain_id = shell.chain_id.clone(); + wrapper_tx.set_code(Code::new("wasm_code".as_bytes().to_owned())); + wrapper_tx.set_data(Data::new("transaction data".as_bytes().to_owned())); + wrapper_tx.add_section(Section::Signature(Signature::new(&wrapper_tx.header_hash(), &keypair))); let req = RequestPrepareProposal { - txs: vec![wrapper.to_bytes()], + txs: vec![wrapper_tx.to_bytes()], max_tx_bytes: 0, time: None, ..Default::default() @@ -864,13 +864,6 @@ use data_encoding::HEXUPPER; fn test_fee_non_whitelisted_token() { let (shell, _) = test_utils::setup(1); - let tx = Tx::new( - "wasm_code".as_bytes().to_owned(), - Some("transaction data".as_bytes().to_owned()), - shell.chain_id.clone(), - None, - ); - let wrapper = WrapperTx::new( Fee { amount_per_gas_unit: 100.into(), @@ -879,21 +872,19 @@ use data_encoding::HEXUPPER; &crate::wallet::defaults::albert_keypair(), Epoch(0), GAS_LIMIT_MULTIPLIER.into(), - tx, - Default::default(), #[cfg(not(feature = "mainnet"))] None, None, - ) - .sign( - &crate::wallet::defaults::albert_keypair(), - shell.chain_id.clone(), - None, - ) - .expect("Wrapper signing failed"); + ); + + let mut wrapper_tx = Tx::new(TxType::Wrapper(Box::new(wrapper))); + wrapper_tx.header.chain_id = shell.chain_id.clone(); + wrapper_tx.set_code(Code::new("wasm_code".as_bytes().to_owned())); + wrapper_tx.set_data(Data::new("transaction data".as_bytes().to_owned())); + wrapper_tx.add_section(Section::Signature(Signature::new(&wrapper_tx.header_hash(), &crate::wallet::defaults::albert_keypair()))); let req = RequestPrepareProposal { - txs: vec![wrapper.to_bytes()], + txs: vec![wrapper_tx.to_bytes()], max_tx_bytes: 0, time: None, ..Default::default() @@ -908,13 +899,6 @@ use data_encoding::HEXUPPER; fn test_fee_wrong_minimum_amount() { let (shell, _) = test_utils::setup(1); - let tx = Tx::new( - "wasm_code".as_bytes().to_owned(), - Some("transaction data".as_bytes().to_owned()), - shell.chain_id.clone(), - None, - ); - let wrapper = WrapperTx::new( Fee { amount_per_gas_unit: 0.into(), @@ -923,21 +907,18 @@ use data_encoding::HEXUPPER; &crate::wallet::defaults::albert_keypair(), Epoch(0), GAS_LIMIT_MULTIPLIER.into(), - tx, - Default::default(), #[cfg(not(feature = "mainnet"))] None, None, - ) - .sign( - &crate::wallet::defaults::albert_keypair(), - shell.chain_id.clone(), - None, - ) - .expect("Wrapper signing failed"); + ); + let mut wrapper_tx = Tx::new(TxType::Wrapper(Box::new(wrapper))); + wrapper_tx.header.chain_id = shell.chain_id.clone(); + wrapper_tx.set_code(Code::new("wasm_code".as_bytes().to_owned())); + wrapper_tx.set_data(Data::new("transaction data".as_bytes().to_owned())); + wrapper_tx.add_section(Section::Signature(Signature::new(&wrapper_tx.header_hash(), &crate::wallet::defaults::albert_keypair()))); let req = RequestPrepareProposal { - txs: vec![wrapper.to_bytes()], + txs: vec![wrapper_tx.to_bytes()], max_tx_bytes: 0, time: None, ..Default::default() @@ -952,13 +933,6 @@ use data_encoding::HEXUPPER; fn test_insufficient_balance_for_fee() { let (shell, _) = test_utils::setup(1); - let tx = Tx::new( - "wasm_code".as_bytes().to_owned(), - Some("transaction data".as_bytes().to_owned()), - shell.chain_id.clone(), - None, - ); - let wrapper = WrapperTx::new( Fee { amount_per_gas_unit: 1_000_000.into(), @@ -967,21 +941,18 @@ use data_encoding::HEXUPPER; &crate::wallet::defaults::albert_keypair(), Epoch(0), GAS_LIMIT_MULTIPLIER.into(), - tx, - Default::default(), #[cfg(not(feature = "mainnet"))] None, None, - ) - .sign( - &crate::wallet::defaults::albert_keypair(), - shell.chain_id.clone(), - None, - ) - .expect("Wrapper signing failed"); + ); + let mut wrapper_tx = Tx::new(TxType::Wrapper(Box::new(wrapper))); + wrapper_tx.header.chain_id = shell.chain_id.clone(); + wrapper_tx.set_code(Code::new("wasm_code".as_bytes().to_owned())); + wrapper_tx.set_data(Data::new("transaction data".as_bytes().to_owned())); + wrapper_tx.add_section(Section::Signature(Signature::new(&wrapper_tx.header_hash(), &crate::wallet::defaults::albert_keypair()))); let req = RequestPrepareProposal { - txs: vec![wrapper.to_bytes()], + txs: vec![wrapper_tx.to_bytes()], max_tx_bytes: 0, time: None, ..Default::default() @@ -996,13 +967,6 @@ use data_encoding::HEXUPPER; fn test_wrapper_fee_overflow() { let (shell, _) = test_utils::setup(1); - let tx = Tx::new( - "wasm_code".as_bytes().to_owned(), - Some("transaction data".as_bytes().to_owned()), - shell.chain_id.clone(), - None, - ); - let wrapper = WrapperTx::new( Fee { amount_per_gas_unit: token::Amount::max(), @@ -1011,21 +975,18 @@ use data_encoding::HEXUPPER; &crate::wallet::defaults::albert_keypair(), Epoch(0), GAS_LIMIT_MULTIPLIER.into(), - tx, - Default::default(), #[cfg(not(feature = "mainnet"))] None, None, - ) - .sign( - &crate::wallet::defaults::albert_keypair(), - shell.chain_id.clone(), - None, - ) - .expect("Wrapper signing failed"); + ); + let mut wrapper_tx = Tx::new(TxType::Wrapper(Box::new(wrapper))); + wrapper_tx.header.chain_id = shell.chain_id.clone(); + wrapper_tx.set_code(Code::new("wasm_code".as_bytes().to_owned())); + wrapper_tx.set_data(Data::new("transaction data".as_bytes().to_owned())); + wrapper_tx.add_section(Section::Signature(Signature::new(&wrapper_tx.header_hash(), &crate::wallet::defaults::albert_keypair()))); let req = RequestPrepareProposal { - txs: vec![wrapper.to_bytes()], + txs: vec![wrapper_tx.to_bytes()], max_tx_bytes: 0, time: None, ..Default::default() diff --git a/apps/src/lib/node/ledger/shell/process_proposal.rs b/apps/src/lib/node/ledger/shell/process_proposal.rs index 0bb3dee2cc9..fed05ca86e4 100644 --- a/apps/src/lib/node/ledger/shell/process_proposal.rs +++ b/apps/src/lib/node/ledger/shell/process_proposal.rs @@ -10,6 +10,7 @@ use namada::ledger::gas::GasMetering; use namada::ledger::storage::TempWlStorage; use namada::proof_of_stake::find_validator_by_raw_hash; use namada::proof_of_stake::pos_queries::PosQueries; +use namada::proto::Commitment; use namada::types::hash::HASH_LENGTH; use namada::types::internal::TxInQueue; @@ -351,7 +352,7 @@ where metadata.has_decrypted_txs = true; match tx_queue_iter.next() { Some(wrapper) => { - let mut inner_tx = tx; + let mut inner_tx = tx.clone(); inner_tx.update_header(TxType::Raw); if wrapper .tx @@ -402,23 +403,26 @@ where // Tx gas (partial check) let tx_hash = - if tx.code_or_hash.len() == HASH_LENGTH { - match Hash::try_from( - tx.code_or_hash.as_slice(), - ) { - Ok(hash) => hash, - Err(_) => return TxResult { + match tx + .get_section(tx.code_sechash()) + .and_then(Section::code_sec) + { + Some(code) => match code.code { + Commitment::Hash(hash) => hash, + Commitment::Id(code) => { + hash::Hash::sha256(&code) + } + }, + None => return TxResult { code: ErrorCodes::DecryptedTxGasLimit .into(), - info: "Failed conversion of \ - transaction's hash" + info: "Missing transaction code" .to_string(), }, - } - } else { - Hash(tx.code_hash()) }; + + //FIXME: are we writing the gas cost of txs together with their hash and code or in a different place? let tx_gas = match gas_table.get( &tx_hash.to_string().to_ascii_lowercase(), ) { @@ -566,39 +570,6 @@ where }; } - // Write inner hash to WAL - temp_wl_storage - .write_log - .write(&inner_hash_key, vec![]) - .expect( - "Couldn't write inner transaction hash to write \ - log", - ); - - let tx = Tx::try_from(tx_bytes) - .expect("Deserialization shouldn't fail"); - let wrapper_hash = Hash(tx.unsigned_hash()); - let wrapper_hash_key = - replay_protection::get_tx_hash_key(&wrapper_hash); - if temp_wl_storage.has_key(&wrapper_hash_key).expect( - "Error while checking wrapper tx hash key in storage", - ) { - return TxResult { - code: ErrorCodes::ReplayTx.into(), - info: format!( - "Wrapper transaction hash {} already in \ - storage, replay attempt", - wrapper_hash - ), - }; - } - - // Write wrapper hash to WAL - temp_wl_storage - .write_log - .write(&wrapper_hash_key, vec![]) - .expect("Couldn't write wrapper tx hash to write log"); - // Check that the fee payer has sufficient balance. // The temporary write log is populated by now. We need a // new, empty one, to simulate the unshielding tx (to @@ -609,8 +580,26 @@ where // the previous one in case of success let mut clone_wl_storage = TempWlStorage::new(&self.wl_storage.storage); + let fee_unshield = wrapper + .unshield_hash + .map(|ref hash| { + tx.get_section(hash) + .map(|section| { + if let Section::MaspTx(transaction) = + section + { + Some(transaction.to_owned()) + } else { + None + } + }) + .flatten() + }) + .flatten(); + match self.wrapper_fee_check( &wrapper, + fee_unshield, &mut clone_wl_storage, Some(Cow::Borrowed(gas_table)), vp_wasm_cache, @@ -661,13 +650,11 @@ where mod test_process_proposal { use borsh::BorshDeserialize; use namada::ledger::storage_api::StorageWrite; - use namada::proto::SignedTxData; use namada::proto::{Code, Data, Section, Signature}; use namada::types::hash::Hash; use namada::types::key::*; use namada::types::storage::Epoch; use namada::types::token::Amount; - use namada::types::transaction::encrypted::EncryptedTx; use namada::types::transaction::protocol::{ProtocolTx, ProtocolTxType}; use namada::types::transaction::{EncryptionKey, Fee, WrapperTx}; @@ -694,6 +681,7 @@ mod test_process_proposal { GAS_LIMIT_MULTIPLIER.into(), #[cfg(not(feature = "mainnet"))] None, + None, )))); outer_tx.header.chain_id = shell.chain_id.clone(); outer_tx.set_code(Code::new("wasm_code".as_bytes().to_owned())); @@ -739,6 +727,7 @@ mod test_process_proposal { GAS_LIMIT_MULTIPLIER.into(), #[cfg(not(feature = "mainnet"))] None, + None, )))); outer_tx.header.chain_id = shell.chain_id.clone(); outer_tx.set_code(Code::new("wasm_code".as_bytes().to_owned())); @@ -751,7 +740,7 @@ mod test_process_proposal { let mut new_tx = outer_tx.clone(); if let TxType::Wrapper(wrapper) = &mut new_tx.header.tx_type { // we mount a malleability attack to try and remove the fee - wrapper.fee.amount = 0.into(); + wrapper.fee.amount_per_gas_unit = 0.into(); } else { panic!("Test failed") }; @@ -805,6 +794,7 @@ mod test_process_proposal { GAS_LIMIT_MULTIPLIER.into(), #[cfg(not(feature = "mainnet"))] None, + None, )))); outer_tx.header.chain_id = shell.chain_id.clone(); outer_tx.set_code(Code::new("wasm_code".as_bytes().to_owned())); @@ -864,6 +854,7 @@ mod test_process_proposal { GAS_LIMIT_MULTIPLIER.into(), #[cfg(not(feature = "mainnet"))] None, + None, )))); outer_tx.header.chain_id = shell.chain_id.clone(); outer_tx.set_code(Code::new("wasm_code".as_bytes().to_owned())); @@ -914,6 +905,7 @@ mod test_process_proposal { GAS_LIMIT_MULTIPLIER.into(), #[cfg(not(feature = "mainnet"))] None, + None, )))); outer_tx.header.chain_id = shell.chain_id.clone(); outer_tx.set_code(Code::new("wasm_code".as_bytes().to_owned())); @@ -921,7 +913,10 @@ mod test_process_proposal { format!("transaction data: {}", i).as_bytes().to_owned(), )); outer_tx.encrypt(&Default::default()); - shell.enqueue_tx(outer_tx.clone()); + let gas_limit = + u64::from(&outer_tx.header().wrapper().unwrap().gas_limit) + - outer_tx.to_bytes().len() as u64; + shell.enqueue_tx(outer_tx.clone(), gas_limit); outer_tx.update_header(TxType::Decrypted(DecryptedTx::Decrypted { #[cfg(not(feature = "mainnet"))] @@ -973,12 +968,15 @@ mod test_process_proposal { GAS_LIMIT_MULTIPLIER.into(), #[cfg(not(feature = "mainnet"))] None, + None, )))); tx.header.chain_id = shell.chain_id.clone(); tx.set_code(Code::new("wasm_code".as_bytes().to_owned())); tx.set_data(Data::new("transaction data".as_bytes().to_owned())); tx.encrypt(&Default::default()); - shell.enqueue_tx(tx.clone()); + let gas_limit = u64::from(&tx.header().wrapper().unwrap().gas_limit) + - tx.to_bytes().len() as u64; + shell.enqueue_tx(tx.clone(), gas_limit); tx.header.tx_type = TxType::Decrypted(DecryptedTx::Undecryptable); let request = ProcessProposal { @@ -1021,6 +1019,7 @@ mod test_process_proposal { GAS_LIMIT_MULTIPLIER.into(), #[cfg(not(feature = "mainnet"))] None, + None, )))); tx.header.chain_id = shell.chain_id.clone(); tx.set_code(Code::new("wasm_code".as_bytes().to_owned())); @@ -1029,7 +1028,9 @@ mod test_process_proposal { tx.set_data_sechash(Hash([0u8; 32])); tx.encrypt(&Default::default()); - shell.enqueue_tx(tx.clone()); + let gas_limit = u64::from(&tx.header().wrapper().unwrap().gas_limit) + - tx.to_bytes().len() as u64; + shell.enqueue_tx(tx.clone(), gas_limit); tx.header.tx_type = TxType::Decrypted(DecryptedTx::Undecryptable); let request = ProcessProposal { @@ -1065,14 +1066,16 @@ mod test_process_proposal { gas_limit: GAS_LIMIT_MULTIPLIER.into(), #[cfg(not(feature = "mainnet"))] pow_solution: None, - unshield: None, + unshield_hash: None, }; let tx = Tx::new(TxType::Wrapper(Box::new(wrapper))); let mut decrypted = tx.clone(); decrypted.update_header(TxType::Decrypted(DecryptedTx::Undecryptable)); - shell.enqueue_tx(tx); + let gas_limit = u64::from(&tx.header().wrapper().unwrap().gas_limit) + - tx.to_bytes().len() as u64; + shell.enqueue_tx(tx, gas_limit); let request = ProcessProposal { txs: vec![decrypted.to_bytes()], @@ -1173,6 +1176,7 @@ mod test_process_proposal { GAS_LIMIT_MULTIPLIER.into(), #[cfg(not(feature = "mainnet"))] None, + None, )))); wrapper.header.chain_id = shell.chain_id.clone(); wrapper.set_data(Data::new("transaction data".as_bytes().to_owned())); @@ -1245,6 +1249,7 @@ mod test_process_proposal { GAS_LIMIT_MULTIPLIER.into(), #[cfg(not(feature = "mainnet"))] None, + None, )))); wrapper.header.chain_id = shell.chain_id.clone(); wrapper.set_code(Code::new("wasm_code".as_bytes().to_owned())); @@ -1303,6 +1308,7 @@ mod test_process_proposal { GAS_LIMIT_MULTIPLIER.into(), #[cfg(not(feature = "mainnet"))] None, + None, )))); wrapper.header.chain_id = shell.chain_id.clone(); wrapper.set_code(Code::new("wasm_code".as_bytes().to_owned())); @@ -1387,6 +1393,7 @@ mod test_process_proposal { GAS_LIMIT_MULTIPLIER.into(), #[cfg(not(feature = "mainnet"))] None, + None, )))); wrapper.header.chain_id = shell.chain_id.clone(); wrapper.set_code(Code::new("wasm_code".as_bytes().to_owned())); @@ -1410,6 +1417,7 @@ mod test_process_proposal { GAS_LIMIT_MULTIPLIER.into(), #[cfg(not(feature = "mainnet"))] None, + None, )))); new_wrapper.add_section(Section::Signature(Signature::new( &new_wrapper.header_hash(), @@ -1458,6 +1466,7 @@ mod test_process_proposal { GAS_LIMIT_MULTIPLIER.into(), #[cfg(not(feature = "mainnet"))] None, + None, )))); let wrong_chain_id = ChainId("Wrong chain id".to_string()); wrapper.header.chain_id = wrong_chain_id.clone(); @@ -1522,6 +1531,7 @@ mod test_process_proposal { GAS_LIMIT_MULTIPLIER.into(), #[cfg(not(feature = "mainnet"))] None, + None, )))); wrapper.header.chain_id = wrong_chain_id.clone(); wrapper.set_code(Code::new("wasm_code".as_bytes().to_owned())); @@ -1537,6 +1547,8 @@ mod test_process_proposal { &decrypted.header_hash(), &keypair, ))); + let gas_limit = u64::from(wrapper.header.wrapper().unwrap().gas_limit) + - wrapper.to_bytes().len() as u64; let wrapper_in_queue = TxInQueue { tx: wrapper, gas: gas_limit, @@ -1584,6 +1596,7 @@ mod test_process_proposal { GAS_LIMIT_MULTIPLIER.into(), #[cfg(not(feature = "mainnet"))] None, + None, )))); wrapper.header.chain_id = shell.chain_id.clone(); wrapper.header.expiration = Some(DateTimeUtc::now()); @@ -1627,6 +1640,7 @@ mod test_process_proposal { GAS_LIMIT_MULTIPLIER.into(), #[cfg(not(feature = "mainnet"))] None, + None, )))); wrapper.header.chain_id = shell.chain_id.clone(); wrapper.header.expiration = Some(DateTimeUtc::now()); @@ -1643,6 +1657,8 @@ mod test_process_proposal { &decrypted.header_hash(), &keypair, ))); + let gas_limit = u64::from(wrapper.header.wrapper().unwrap().gas_limit) + - wrapper.to_bytes().len() as u64; let wrapper_in_queue = TxInQueue { tx: wrapper, gas: gas_limit, @@ -1672,19 +1688,7 @@ mod test_process_proposal { let (mut shell, _) = test_utils::setup(1); let keypair = crate::wallet::defaults::daewon_keypair(); - let tx = Tx::new( - "wasm_code".as_bytes().to_owned(), - Some("new transaction data".as_bytes().to_owned()), - shell.chain_id.clone(), - None, - ); - let decrypted: Tx = DecryptedTx::Decrypted { - tx: tx.clone(), - has_valid_pow: false, - } - .into(); - let signed_decrypted = decrypted.sign(&keypair); - let wrapper = WrapperTx::new( + let mut wrapper = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( Fee { amount_per_gas_unit: 0.into(), token: shell.wl_storage.storage.native_token.clone(), @@ -1692,14 +1696,26 @@ mod test_process_proposal { &keypair, Epoch(0), 0.into(), - tx, - Default::default(), #[cfg(not(feature = "mainnet"))] None, None, - ); - let gas = u64::from(&wrapper.gas_limit); - let wrapper_in_queue = WrapperTxInQueue { + )))); + wrapper.header.chain_id = shell.chain_id.clone(); + wrapper.set_code(Code::new("wasm_code".as_bytes().to_owned())); + wrapper.set_data(Data::new("transaction data".as_bytes().to_owned())); + + let mut decrypted_tx = wrapper.clone(); + decrypted_tx.update_header(TxType::Decrypted(DecryptedTx::Decrypted { + #[cfg(not(feature = "mainnet"))] + has_valid_pow: false, + })); + decrypted_tx.add_section(Section::Signature(Signature::new( + &decrypted_tx.header_hash(), + &keypair, + ))); + + let gas = u64::from(&wrapper.header.wrapper().unwrap().gas_limit); + let wrapper_in_queue = TxInQueue { tx: wrapper, gas, has_valid_pow: false, @@ -1708,7 +1724,7 @@ mod test_process_proposal { // Run validation let request = ProcessProposal { - txs: vec![signed_decrypted.to_bytes()], + txs: vec![decrypted_tx.to_bytes()], }; match shell.process_proposal(request) { Ok(response) => { @@ -1734,14 +1750,7 @@ mod test_process_proposal { .expect("Missing max_block_gas parameter in storage"); let keypair = super::test_utils::gen_keypair(); - let tx = Tx::new( - "wasm_code".as_bytes().to_owned(), - Some("transaction data".as_bytes().to_owned()), - shell.chain_id.clone(), - None, - ); - - let wrapper = WrapperTx::new( + let mut wrapper = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( Fee { amount_per_gas_unit: 100.into(), token: shell.wl_storage.storage.native_token.clone(), @@ -1749,14 +1758,18 @@ mod test_process_proposal { &keypair, Epoch(0), (block_gas_limit + 1).into(), - tx, - Default::default(), #[cfg(not(feature = "mainnet"))] None, None, - ) - .sign(&keypair, shell.chain_id.clone(), None) - .expect("Wrapper signing failed"); + )))); + wrapper.header.chain_id = shell.chain_id.clone(); + wrapper.set_code(Code::new("wasm_code".as_bytes().to_owned())); + wrapper.set_data(Data::new("transaction data".as_bytes().to_owned())); + wrapper.add_section(Section::Signature(Signature::new( + &wrapper.header_hash(), + &keypair, + ))); + wrapper.encrypt(&Default::default()); // Run validation let request = ProcessProposal { @@ -1780,14 +1793,7 @@ mod test_process_proposal { let (shell, _) = test_utils::setup(1); let keypair = super::test_utils::gen_keypair(); - let tx = Tx::new( - "wasm_code".as_bytes().to_owned(), - Some("transaction data".as_bytes().to_owned()), - shell.chain_id.clone(), - None, - ); - - let wrapper = WrapperTx::new( + let mut wrapper = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( Fee { amount_per_gas_unit: 100.into(), token: shell.wl_storage.storage.native_token.clone(), @@ -1795,14 +1801,18 @@ mod test_process_proposal { &keypair, Epoch(0), 0.into(), - tx, - Default::default(), #[cfg(not(feature = "mainnet"))] None, None, - ) - .sign(&keypair, shell.chain_id.clone(), None) - .expect("Wrapper signing failed"); + )))); + wrapper.header.chain_id = shell.chain_id.clone(); + wrapper.set_code(Code::new("wasm_code".as_bytes().to_owned())); + wrapper.set_data(Data::new("transaction data".as_bytes().to_owned())); + wrapper.add_section(Section::Signature(Signature::new( + &wrapper.header_hash(), + &keypair, + ))); + wrapper.encrypt(&Default::default()); // Run validation let request = ProcessProposal { @@ -1824,14 +1834,7 @@ mod test_process_proposal { fn test_fee_non_whitelisted_token() { let (shell, _) = test_utils::setup(1); - let tx = Tx::new( - "wasm_code".as_bytes().to_owned(), - Some("transaction data".as_bytes().to_owned()), - shell.chain_id.clone(), - None, - ); - - let wrapper = WrapperTx::new( + let mut wrapper = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( Fee { amount_per_gas_unit: 100.into(), token: address::btc(), @@ -1839,18 +1842,18 @@ mod test_process_proposal { &crate::wallet::defaults::albert_keypair(), Epoch(0), GAS_LIMIT_MULTIPLIER.into(), - tx, - Default::default(), #[cfg(not(feature = "mainnet"))] None, None, - ) - .sign( + )))); + wrapper.header.chain_id = shell.chain_id.clone(); + wrapper.set_code(Code::new("wasm_code".as_bytes().to_owned())); + wrapper.set_data(Data::new("transaction data".as_bytes().to_owned())); + wrapper.add_section(Section::Signature(Signature::new( + &wrapper.header_hash(), &crate::wallet::defaults::albert_keypair(), - shell.chain_id.clone(), - None, - ) - .expect("Wrapper signing failed"); + ))); + wrapper.encrypt(&Default::default()); // Run validation let request = ProcessProposal { @@ -1872,14 +1875,7 @@ mod test_process_proposal { fn test_fee_wrong_minimum_amount() { let (shell, _) = test_utils::setup(1); - let tx = Tx::new( - "wasm_code".as_bytes().to_owned(), - Some("transaction data".as_bytes().to_owned()), - shell.chain_id.clone(), - None, - ); - - let wrapper = WrapperTx::new( + let mut wrapper = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( Fee { amount_per_gas_unit: 0.into(), token: shell.wl_storage.storage.native_token.clone(), @@ -1887,18 +1883,18 @@ mod test_process_proposal { &crate::wallet::defaults::albert_keypair(), Epoch(0), GAS_LIMIT_MULTIPLIER.into(), - tx, - Default::default(), #[cfg(not(feature = "mainnet"))] None, None, - ) - .sign( + )))); + wrapper.header.chain_id = shell.chain_id.clone(); + wrapper.set_code(Code::new("wasm code".as_bytes().to_owned())); + wrapper.set_data(Data::new("transaction data".as_bytes().to_owned())); + wrapper.add_section(Section::Signature(Signature::new( + &wrapper.header_hash(), &crate::wallet::defaults::albert_keypair(), - shell.chain_id.clone(), - None, - ) - .expect("Wrapper signing failed"); + ))); + wrapper.encrypt(&Default::default()); // Run validation let request = ProcessProposal { @@ -1920,14 +1916,7 @@ mod test_process_proposal { fn test_insufficient_balance_for_fee() { let (shell, _) = test_utils::setup(1); - let tx = Tx::new( - "wasm_code".as_bytes().to_owned(), - Some("transaction data".as_bytes().to_owned()), - shell.chain_id.clone(), - None, - ); - - let wrapper = WrapperTx::new( + let mut wrapper = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( Fee { amount_per_gas_unit: 1_000_000.into(), token: shell.wl_storage.storage.native_token.clone(), @@ -1935,18 +1924,18 @@ mod test_process_proposal { &crate::wallet::defaults::albert_keypair(), Epoch(0), GAS_LIMIT_MULTIPLIER.into(), - tx, - Default::default(), #[cfg(not(feature = "mainnet"))] None, None, - ) - .sign( + )))); + wrapper.header.chain_id = shell.chain_id.clone(); + wrapper.set_code(Code::new("wasm code".as_bytes().to_owned())); + wrapper.set_data(Data::new("transaction data".as_bytes().to_owned())); + wrapper.add_section(Section::Signature(Signature::new( + &wrapper.header_hash(), &crate::wallet::defaults::albert_keypair(), - shell.chain_id.clone(), - None, - ) - .expect("Wrapper signing failed"); + ))); + wrapper.encrypt(&Default::default()); // Run validation let request = ProcessProposal { @@ -1968,14 +1957,7 @@ mod test_process_proposal { fn test_wrapper_fee_overflow() { let (shell, _) = test_utils::setup(1); - let tx = Tx::new( - "wasm_code".as_bytes().to_owned(), - Some("transaction data".as_bytes().to_owned()), - shell.chain_id.clone(), - None, - ); - - let wrapper = WrapperTx::new( + let mut wrapper = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( Fee { amount_per_gas_unit: token::Amount::max(), token: shell.wl_storage.storage.native_token.clone(), @@ -1983,18 +1965,18 @@ mod test_process_proposal { &crate::wallet::defaults::albert_keypair(), Epoch(0), GAS_LIMIT_MULTIPLIER.into(), - tx, - Default::default(), #[cfg(not(feature = "mainnet"))] None, None, - ) - .sign( + )))); + wrapper.header.chain_id = shell.chain_id.clone(); + wrapper.set_code(Code::new("wasm code".as_bytes().to_owned())); + wrapper.set_data(Data::new("transaction data".as_bytes().to_owned())); + wrapper.add_section(Section::Signature(Signature::new( + &wrapper.header_hash(), &crate::wallet::defaults::albert_keypair(), - shell.chain_id.clone(), - None, - ) - .expect("Wrapper signing failed"); + ))); + wrapper.encrypt(&Default::default()); // Run validation let request = ProcessProposal { diff --git a/benches/Cargo.toml b/benches/Cargo.toml index 5acf2042ad4..3be01d41bb3 100644 --- a/benches/Cargo.toml +++ b/benches/Cargo.toml @@ -1,11 +1,16 @@ [package] -authors = ["Heliax AG "] -description = "Namada benchmarks" -edition = "2021" -license = "GPL-3.0" name = "namada_benchmarks" +description = "Namada benchmarks" resolver = "2" -version = "0.15.0" +authors.workspace = true +edition.workspace = true +documentation.workspace = true +homepage.workspace = true +keywords.workspace = true +license.workspace = true +readme.workspace = true +repository.workspace = true +version.workspace = true [lib] name = "namada_benches" @@ -37,22 +42,21 @@ harness = false path = "host_env.rs" [dependencies] -async-trait = "0.1.51" -borsh = "0.9.0" -ferveo-common = {git = "https://github.com/anoma/ferveo", rev = "e5abd0acc938da90140351a65a26472eb495ce4d"} -ibc-relayer = { version = "0.22.0", default-features = false } -ibc-relayer-types = { version = "0.22.0", default-features = false } -ibc-proto = { version = "0.26.0", default-features = false } -masp_primitives = { git = "https://github.com/anoma/masp", rev = "bee40fc465f6afbd10558d12fe96eb1742eee45c" } +async-trait.workspace = true +borsh.workspace = true +ferveo-common.workspace = true +masp_primitives.workspace = true +masp_proofs.workspace = true namada = { path = "../shared" } namada_apps = { path = "../apps" } namada_test_utils = { path = "../test_utils" } -prost = "0.11.6" -rand = "0.8" -rand_core = "0.6" -rust_decimal = "1.26.1" -tokio = "1.8.2" -tempfile = "3.2.0" +prost.workspace = true +rand.workspace = true +rand_core.workspace = true +rust_decimal.workspace = true +sha2.workspace = true +tokio.workspace = true +tempfile.workspace = true [dev-dependencies] criterion = { version = "0.4", features = ["html_reports"] } diff --git a/benches/host_env.rs b/benches/host_env.rs index f936dd81454..ae9fa80db19 100644 --- a/benches/host_env.rs +++ b/benches/host_env.rs @@ -1,9 +1,9 @@ use borsh::BorshDeserialize; use criterion::{criterion_group, criterion_main, Criterion}; -use namada::core::proto::SignedTxData; use namada::core::types::address; use namada::core::types::key::RefTo; use namada::core::types::token::{Amount, Transfer}; +use namada::proto::Section; use namada_apps::wallet::defaults; use namada_benches::{generate_tx, TX_TRANSFER_WASM}; @@ -19,16 +19,19 @@ fn tx_signature_validation(c: &mut Criterion) { key: None, shielded: None, }, + None, &defaults::albert_keypair(), ); - let SignedTxData { data: _, ref sig } = - SignedTxData::try_from_slice(tx.data.as_ref().unwrap()).unwrap(); + let header_hash = tx.header_hash(); c.bench_function("tx_signature_validation", |b| { b.iter(|| { - tx.verify_sig(&defaults::albert_keypair().ref_to(), sig) - .unwrap() + tx.verify_signature( + &defaults::albert_keypair().ref_to(), + &header_hash, + ) + .unwrap() }) }); } diff --git a/benches/mod.rs b/benches/mod.rs index 4bbadf96e0b..2baf93a3fed 100644 --- a/benches/mod.rs +++ b/benches/mod.rs @@ -14,12 +14,22 @@ //! For more realistic results these benchmarks should be run on all the //! combination of supported OS/architecture. +use masp_primitives::transaction::Transaction; +use masp_proofs::prover::LocalTxProver; +use namada::ledger::masp; +use namada::ledger::masp::{ShieldedContext, ShieldedUtils}; +use namada::ledger::wallet::{Wallet, WalletUtils}; +use namada::tendermint_rpc::{self, HttpClient}; +use sha2::{Digest, Sha256}; use std::collections::BTreeMap; +use std::fs::File; +use std::fs::OpenOptions; +use std::io::{Read, Write}; use std::ops::{Deref, DerefMut}; +use std::path::PathBuf; use std::str::FromStr; -use borsh::BorshSerialize; -use ibc_proto::google::protobuf::Any; +use borsh::{BorshDeserialize, BorshSerialize}; use masp_primitives::zip32::ExtendedFullViewingKey; use namada::core::ledger::ibc::storage::port_key; use namada::core::types::address::{self, Address}; @@ -32,9 +42,12 @@ use namada::ibc::clients::ics07_tendermint::client_state::{ }; use namada::ibc::clients::ics07_tendermint::consensus_state::ConsensusState; use namada::ibc::core::ics02_client::client_type::ClientType; +use namada::ibc::core::ics02_client::client_type::ClientType as NamadaClientType; +use namada::ibc::core::ics02_client::consensus_state::ConsensusState as TraitConsensusState; use namada::ibc::core::ics02_client::trust_threshold::TrustThreshold; +use namada::ibc::core::ics03_connection::connection::ConnectionEnd; use namada::ibc::core::ics03_connection::connection::{ - ConnectionEnd, Counterparty, State as ConnectionState, + Counterparty, State as ConnectionState, }; use namada::ibc::core::ics03_connection::version::Version; use namada::ibc::core::ics04_channel::channel::{ @@ -42,13 +55,18 @@ use namada::ibc::core::ics04_channel::channel::{ }; use namada::ibc::core::ics04_channel::timeout::TimeoutHeight; use namada::ibc::core::ics04_channel::Version as ChannelVersion; -use namada::ibc::core::ics23_commitment::commitment::{ - CommitmentPrefix, CommitmentRoot, -}; +use namada::ibc::core::ics23_commitment::commitment::CommitmentPrefix; +use namada::ibc::core::ics23_commitment::commitment::CommitmentRoot; use namada::ibc::core::ics23_commitment::specs::ProofSpecs; +use namada::ibc::core::ics24_host::identifier::ChannelId as NamadaChannelId; +use namada::ibc::core::ics24_host::identifier::ClientId; +use namada::ibc::core::ics24_host::identifier::ClientId as NamadaClientId; +use namada::ibc::core::ics24_host::identifier::ConnectionId; +use namada::ibc::core::ics24_host::identifier::ConnectionId as NamadaConnectionId; +use namada::ibc::core::ics24_host::identifier::PortChannelId as NamadaPortChannelId; +use namada::ibc::core::ics24_host::identifier::PortId as NamadaPortId; use namada::ibc::core::ics24_host::identifier::{ - ChainId as IbcChainId, ChannelId, ClientId, ConnectionId, PortChannelId, - PortId, + ChainId as IbcChainId, ChannelId, PortChannelId, PortId, }; use namada::ibc::core::ics24_host::Path as IbcPath; use namada::ibc::signer::Signer; @@ -56,6 +74,7 @@ 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::ibc_proto::google::protobuf::Any; use namada::ibc_proto::protobuf::Protobuf; use namada::ledger::gas::TxGasMeter; use namada::ledger::ibc::storage::{channel_key, connection_key}; @@ -64,6 +83,7 @@ use namada::ledger::queries::{ }; use namada::proof_of_stake; use namada::proto::Tx; +use namada::proto::{Code, Data, Section, Signature}; use namada::tendermint::Hash; use namada::types::address::InternalAddress; use namada::types::chain::ChainId; @@ -85,7 +105,7 @@ use namada_apps::facade::tendermint_config::net::Address as TendermintAddress; use namada_apps::facade::tendermint_proto::abci::RequestInitChain; use namada_apps::facade::tendermint_proto::google::protobuf::Timestamp; use namada_apps::node::ledger::shell::Shell; -use namada_apps::wallet::defaults; +use namada_apps::wallet::{defaults, CliWalletUtils}; use namada_apps::{config, wasm_loader}; use namada_test_utils::tx_data::TxWriteData; use rand_core::OsRng; @@ -109,6 +129,9 @@ pub const ALBERT_SPENDING_KEY: &str = "albert_spending"; pub const BERTHA_PAYMENT_ADDRESS: &str = "bertha_payment"; const BERTHA_SPENDING_KEY: &str = "bertha_spending"; +const FILE_NAME: &str = "shielded.dat"; +const TMP_FILE_NAME: &str = "shielded.tmp"; + pub struct BenchShell { pub inner: Shell, /// NOTE: Temporary directory should be dropped last since Shell need to @@ -171,7 +194,7 @@ impl Default for BenchShell { source: Some(defaults::albert_address()), }; let signed_tx = - generate_tx(TX_BOND_WASM, bond, &defaults::albert_keypair()); + generate_tx(TX_BOND_WASM, bond, None, &defaults::albert_keypair()); let mut bench_shell = BenchShell { inner: shell, @@ -193,12 +216,13 @@ impl Default for BenchShell { voting_end_epoch: 15.into(), grace_epoch: 18.into(), }, + None, &defaults::albert_keypair(), ); bench_shell.execute_tx(&signed_tx); bench_shell.wl_storage.commit_tx(); - bench_shell.commit(); + bench_shell.inner.commit(); // Advance epoch for pos benches for _ in 0..=12 { @@ -217,8 +241,7 @@ impl BenchShell { &mut TxGasMeter::new(u64::MAX), &BTreeMap::default(), &TxIndex(0), - &tx.code_or_hash, - tx.data.as_ref().unwrap(), + tx, &mut self.inner.vp_wasm_cache, &mut self.inner.tx_wasm_cache, ) @@ -248,7 +271,7 @@ impl BenchShell { pub fn init_ibc_channel(&mut self) { // Set connection open let client_id = - ClientId::new(ClientType::new("01-tendermint".to_string()), 1) + ClientId::new(ClientType::new("tendermint-1".to_string()), 1) .unwrap(); let connection = ConnectionEnd::new( ConnectionState::Open, @@ -265,14 +288,14 @@ impl BenchShell { let addr_key = Key::from(Address::Internal(InternalAddress::Ibc).to_db_key()); - let connection_key = connection_key(&ConnectionId::new(1)); + let connection_key = connection_key(&NamadaConnectionId::new(1)); self.wl_storage .storage .write(&connection_key, connection.encode_vec().unwrap()) .unwrap(); // Set port - let port_key = port_key(&PortId::transfer()); + let port_key = port_key(&NamadaPortId::transfer()); let index_key = addr_key .join(&Key::from("capabilities/index".to_string().to_db_key())); @@ -303,9 +326,9 @@ impl BenchShell { vec![ConnectionId::new(1)], ChannelVersion::new("ics20-1".to_string()), ); - let channel_key = channel_key(&PortChannelId::new( - ChannelId::new(5), - PortId::transfer(), + let channel_key = channel_key(&NamadaPortChannelId::new( + NamadaChannelId::new(5), + NamadaPortId::transfer(), )); self.wl_storage .storage @@ -314,7 +337,7 @@ impl BenchShell { // Set client state let client_id = - ClientId::new(ClientType::new("01-tendermint".to_string()), 1) + ClientId::new(ClientType::new("tendermint-1".to_string()), 1) .unwrap(); let client_state_key = addr_key.join(&Key::from( IbcPath::ClientState( @@ -341,13 +364,11 @@ impl BenchShell { None, ) .unwrap(); + let bytes = + >::encode_vec(&client_state).unwrap(); self.wl_storage .storage - .write( - &client_state_key, - >::encode_vec(&client_state) - .unwrap(), - ) + .write(&client_state_key, bytes) .expect("write failed"); // Set consensus state @@ -371,50 +392,72 @@ impl BenchShell { next_validators_hash: Hash::Sha256([0u8; 32]), }; + let bytes = + >::encode_vec(&consensus_state) + .unwrap(); self.wl_storage .storage - .write( - &consensus_key, - >::encode_vec(&consensus_state) - .unwrap(), - ) + .write(&consensus_key, bytes) .unwrap(); } } pub fn generate_tx( + //FIXME: rename to generate_signed_tx wasm_code_path: &str, - data: impl BorshSerialize, + data: impl BorshSerialize, //FIXME: shouldn't this be ProtoBuf? + shielded: Option, signer: &SecretKey, ) -> Tx { - let tx = Tx::new( - wasm_loader::read_wasm_or_exit(WASM_DIR, wasm_code_path), - Some(data.try_to_vec().unwrap()), - ChainId::default(), - None, - ); - - tx.sign(signer) + let mut tx = Tx::new(namada::types::transaction::TxType::Decrypted( + namada::types::transaction::DecryptedTx::Decrypted { + #[cfg(not(feature = "mainnet"))] + has_valid_pow: true, + }, + )); + //FIXME: need to chance the chain_id to bench? + //FIXME: do I ever need to attach the fee unshielding transaction here? + tx.set_code(Code::new(wasm_loader::read_wasm_or_exit( + WASM_DIR, + wasm_code_path, + ))); + tx.set_data(Data::new(data.try_to_vec().unwrap())); + if let Some(transaction) = shielded { + tx.add_section(Section::MaspTx(transaction)); //FIXME: need to sign this section? + } + tx.add_section(Section::Signature(Signature::new( + //FIXME: need to sign the header for the decrypted? Of not I can use this function also for reveal_pk + &tx.header_hash(), + signer, + ))); + + tx } pub fn generate_foreign_key_tx(signer: &SecretKey) -> Tx { let wasm_code = std::fs::read("../wasm_for_tests/tx_write.wasm").unwrap(); - let tx = Tx::new( - wasm_code, - Some( - TxWriteData { - key: Key::from("bench_foreing_key".to_string().to_db_key()), - value: vec![0; 64], - } - .try_to_vec() - .unwrap(), - ), - ChainId::default(), - None, - ); - - tx.sign(signer) + let mut tx = Tx::new(namada::types::transaction::TxType::Decrypted( + namada::types::transaction::DecryptedTx::Decrypted { + #[cfg(not(feature = "mainnet"))] + has_valid_pow: true, + }, + )); + tx.set_code(Code::new(wasm_code)); + tx.set_data(Data::new( + TxWriteData { + key: Key::from("bench_foreign_key".to_string().to_db_key()), + value: vec![0; 64], + } + .try_to_vec() + .unwrap(), + )); //FIXME: protobuf? + tx.add_section(Section::Signature(Signature::new( + &tx.header_hash(), + signer, + ))); + + tx } pub fn generate_ibc_transfer_tx() -> Tx { @@ -445,18 +488,98 @@ pub fn generate_ibc_transfer_tx() -> Tx { prost::Message::encode(&any_msg, &mut data).unwrap(); // Don't use execute_tx to avoid serializing the data again with borsh - Tx::new( - wasm_loader::read_wasm_or_exit(WASM_DIR, TX_IBC_WASM), - Some(data), - ChainId::default(), - None, - ) - .sign(&defaults::albert_keypair()) + let mut tx = Tx::new(namada::types::transaction::TxType::Decrypted( + namada::types::transaction::DecryptedTx::Decrypted { + #[cfg(not(feature = "mainnet"))] + has_valid_pow: true, + }, + )); + tx.set_code(Code::new(wasm_loader::read_wasm_or_exit( + WASM_DIR, + TX_IBC_WASM, + ))); + tx.set_data(Data::new(data)); + tx.add_section(Section::Signature(Signature::new( + &tx.header_hash(), + &defaults::albert_keypair(), + ))); + + tx } pub struct BenchShieldedCtx { - pub ctx: Context, + pub shielded: ShieldedContext, pub shell: BenchShell, + pub wallet: Wallet, +} + +#[derive(BorshSerialize, BorshDeserialize, Debug, Clone, Default)] +pub struct BenchShieldedUtils { + #[borsh_skip] + context_dir: PathBuf, +} + +#[async_trait::async_trait(?Send)] +impl ShieldedUtils for BenchShieldedUtils { + type C = BenchShell; + + //FIXME: make everything work on tempdir? + //FIXME: impl this trait on BenchShell? + fn local_tx_prover(&self) -> LocalTxProver { + if let Ok(params_dir) = std::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. + async 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 + async 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(()) + } } #[async_trait::async_trait(?Send)] @@ -491,6 +614,16 @@ impl Client for BenchShell { RPC.handle(ctx, &request) .map_err(|_| std::io::Error::from(std::io::ErrorKind::NotFound)) } + + async fn perform( + &self, + request: R, + ) -> Result + where + R: tendermint_rpc::SimpleRequest, + { + tendermint_rpc::Response::from_string("MOCK RESPONSE") + } } impl Default for BenchShieldedCtx { @@ -501,16 +634,21 @@ impl Default for BenchShieldedCtx { chain_id: None, base_dir: shell.tempdir.as_ref().canonicalize().unwrap(), wasm_dir: None, - mode: None, }) - .unwrap(); + .unwrap(); //FIXME: remove? // Generate spending key for Albert and Bertha - ctx.wallet - .gen_spending_key(ALBERT_SPENDING_KEY.to_string(), true); - ctx.wallet - .gen_spending_key(BERTHA_SPENDING_KEY.to_string(), true); - ctx.wallet.save().unwrap(); + ctx.wallet.gen_spending_key( + ALBERT_SPENDING_KEY.to_string(), + None, + true, + ); + ctx.wallet.gen_spending_key( + BERTHA_SPENDING_KEY.to_string(), + None, + true, + ); + namada_apps::wallet::save(&ctx.wallet).unwrap(); //FIXME: need this? // Generate payment addresses for both Albert and Bertha for (alias, viewing_alias) in [ @@ -529,24 +667,30 @@ impl Default for BenchShieldedCtx { ExtendedFullViewingKey::from(ctx.get_cached(&viewing_key)) .fvk .vk; - let (div, _g_d) = tx::find_valid_diversifier(&mut OsRng); + let (div, _g_d) = + namada::ledger::masp::find_valid_diversifier(&mut OsRng); let payment_addr = viewing_key.to_payment_address(div).unwrap(); let _ = ctx .wallet .insert_payment_addr( alias, PaymentAddress::from(payment_addr).pinned(false), + true, ) .unwrap(); } - ctx.wallet.save().unwrap(); + namada_apps::wallet::save(&ctx.wallet).unwrap(); namada::ledger::storage::update_allowed_conversions( &mut shell.wl_storage, ) .unwrap(); - Self { ctx, shell } + Self { + shielded: ShieldedContext::default(), + shell, + wallet: ctx.wallet, + } } } @@ -563,52 +707,64 @@ impl BenchShieldedCtx { dump_tx: false, force: false, broadcast_only: false, - ledger_address: TendermintAddress::Tcp { - peer_id: None, - host: "bench-host".to_string(), - port: 1, - }, + ledger_address: (), + // ledger_address: TendermintAddress::Tcp { //FIXME: remove? + // peer_id: None, + // host: "bench-host".to_string(), + // port: 1, + // }, initialized_account_alias: None, fee_amount: None, - fee_token: FromContext::new(address::nam().to_string()), + fee_token: address::nam(), fee_unshield: None, gas_limit: GasLimit::from(u64::MAX), expiration: None, disposable_signing_key: false, - signing_key: Some(FromContext::new( - defaults::albert_keypair().to_string(), - )), + signing_key: Some(defaults::albert_keypair()), signer: None, + wallet_alias_force: true, + chain_id: None, + tx_reveal_code_path: TX_REVEAL_PK_WASM.into(), + password: None, }; let args = TxTransfer { tx: mock_args, - source: FromContext::new(source.to_string()), - target: FromContext::new(target.to_string()), - token: FromContext::new(address::nam().to_string()), + source: source.clone(), + target: target.clone(), + token: address::nam(), sub_prefix: None, amount, + native_token: self.shell.wl_storage.storage.native_token.clone(), + tx_code_path: TX_TRANSFER_WASM.into(), }; let async_runtime = tokio::runtime::Runtime::new().unwrap(); let spending_key = self - .ctx .wallet - .find_spending_key(ALBERT_SPENDING_KEY) + .find_spending_key(ALBERT_SPENDING_KEY, None) .unwrap(); - async_runtime.block_on(self.ctx.shielded.fetch( + async_runtime.block_on(self.shielded.fetch( &self.shell, &[spending_key.into()], &[], )); let shielded = async_runtime - .block_on(tx::gen_shielded_transfer( - &mut self.ctx, - &self.shell, - &args, - )) + .block_on(self.shielded.gen_shielded_transfer(&self.shell, args)) .unwrap() - .map(|x| x.0); + .map(|(_, tx, _, _)| tx); + + let mut hasher = Sha256::new(); + let shielded_section_hash = shielded.clone().map(|transaction| { + namada::core::types::hash::Hash( + Section::MaspTx(transaction) + .hash(&mut hasher) + .finalize_reset() + .into(), + ) + }); + + // FIXME: the tx section for fee unshieldign must not be encrypted! generate_tx( TX_TRANSFER_WASM, @@ -619,8 +775,9 @@ impl BenchShieldedCtx { sub_prefix: None, amount, key: None, - shielded, + shielded: shielded_section_hash, }, + shielded, &defaults::albert_keypair(), ) } diff --git a/benches/native_vps.rs b/benches/native_vps.rs index dea8585d13d..46faf0a31b5 100644 --- a/benches/native_vps.rs +++ b/benches/native_vps.rs @@ -69,7 +69,7 @@ fn replay_protection(c: &mut Criterion) { // here replay_protection .validate_tx( - tx.data.as_ref().unwrap(), + &tx, replay_protection.ctx.keys_changed, replay_protection.ctx.verifiers, ) @@ -102,6 +102,7 @@ fn governance(c: &mut Criterion) { voter: defaults::albert_address(), delegations: vec![defaults::validator_address()], }, + None, &defaults::albert_keypair(), ), "validator_vote" => generate_tx( @@ -112,6 +113,7 @@ fn governance(c: &mut Criterion) { voter: defaults::validator_address(), delegations: vec![], }, + None, &defaults::validator_keypair(), ), "minimal_proposal" => generate_tx( @@ -125,6 +127,7 @@ fn governance(c: &mut Criterion) { voting_end_epoch: 15.into(), grace_epoch: 18.into(), }, + None, &defaults::albert_keypair(), ), "complete_proposal" => { @@ -159,6 +162,7 @@ fn governance(c: &mut Criterion) { voting_end_epoch: 15.into(), grace_epoch: 18.into(), }, + None, &defaults::albert_keypair(), ) } @@ -189,15 +193,13 @@ fn governance(c: &mut Criterion) { group.bench_function(bench_name, |b| { b.iter(|| { - assert!( - governance - .validate_tx( - signed_tx.data.as_ref().unwrap(), - governance.ctx.keys_changed, - governance.ctx.verifiers, - ) - .unwrap() - ) + assert!(governance + .validate_tx( + &signed_tx, + governance.ctx.keys_changed, + governance.ctx.verifiers, + ) + .unwrap()) }) }); } @@ -223,6 +225,7 @@ fn slash_fund(c: &mut Criterion) { voting_end_epoch: 15.into(), grace_epoch: 18.into(), }, + None, &defaults::albert_keypair(), ); @@ -256,15 +259,13 @@ fn slash_fund(c: &mut Criterion) { group.bench_function(bench_name, |b| { b.iter(|| { - assert!( - slash_fund - .validate_tx( - tx.data.as_ref().unwrap(), - slash_fund.ctx.keys_changed, - slash_fund.ctx.verifiers, - ) - .unwrap() - ) + assert!(slash_fund + .validate_tx( + &tx, + slash_fund.ctx.keys_changed, + slash_fund.ctx.verifiers, + ) + .unwrap()) }) }); } @@ -298,13 +299,8 @@ fn ibc(c: &mut Criterion) { .expect("Encoding tx data shouldn't fail"); // Avoid serializing the data again with borsh - let open_connection = Tx::new( - wasm_loader::read_wasm_or_exit(WASM_DIR, TX_IBC_WASM), - Some(data), - ChainId::default(), - None, - ) - .sign(&defaults::albert_keypair()); + let open_connection = + generate_tx(TX_IBC_WASM, data, None, &defaults::albert_keypair()); // Channel handshake let msg = MsgChannelOpenInit { @@ -323,13 +319,8 @@ fn ibc(c: &mut Criterion) { .expect("Encoding tx data shouldn't fail"); // Avoid serializing the data again with borsh - let open_channel = Tx::new( - wasm_loader::read_wasm_or_exit(WASM_DIR, TX_IBC_WASM), - Some(data), - ChainId::default(), - None, - ) - .sign(&defaults::albert_keypair()); + let open_channel = + generate_tx(TX_IBC_WASM, data, None, &defaults::albert_keypair()); // Ibc transfer let outgoing_transfer = generate_ibc_transfer_tx(); @@ -364,14 +355,13 @@ fn ibc(c: &mut Criterion) { group.bench_function(bench_name, |b| { b.iter(|| { - assert!( - ibc.validate_tx( - signed_tx.data.as_ref().unwrap(), + assert!(ibc + .validate_tx( + &signed_tx, ibc.ctx.keys_changed, ibc.ctx.verifiers, ) - .unwrap() - ) + .unwrap()) }) }); } @@ -424,14 +414,13 @@ fn ibc_token(c: &mut Criterion) { group.bench_function(bench_name, |b| { b.iter(|| { - assert!( - ibc.validate_tx( - signed_tx.data.as_ref().unwrap(), + assert!(ibc + .validate_tx( + &signed_tx, ibc.ctx.keys_changed, ibc.ctx.verifiers, ) - .unwrap() - ) + .unwrap()) }) }); } diff --git a/benches/process_wrapper.rs b/benches/process_wrapper.rs index 304e25beeed..b866e091cde 100644 --- a/benches/process_wrapper.rs +++ b/benches/process_wrapper.rs @@ -4,6 +4,7 @@ use criterion::{criterion_group, criterion_main, Criterion}; use namada::core::types::address; use namada::core::types::token::{Amount, Transfer}; use namada::ledger::storage::TempWlStorage; +use namada::proto::Signature; use namada::types::chain::ChainId; use namada::types::storage::BlockHeight; use namada::types::time::DateTimeUtc; @@ -16,8 +17,10 @@ fn process_tx(c: &mut Criterion) { let mut shell = BenchShell::default(); // Advance chain height to allow the inclusion of wrapper txs by the block // space allocator - shell.wl_storage.storage.last_height = BlockHeight(2); - let tx = generate_tx( + shell.wl_storage.storage.last_block.as_mut().unwrap().height = + BlockHeight(2); + + let mut tx = generate_tx( TX_TRANSFER_WASM, Transfer { source: defaults::albert_address(), @@ -28,25 +31,29 @@ fn process_tx(c: &mut Criterion) { key: None, shielded: None, }, + None, &defaults::albert_keypair(), ); - let wrapper = WrapperTx::new( - Fee { - token: address::nam(), - amount_per_gas_unit: Amount::whole(200), - }, + tx.update_header(namada::types::transaction::TxType::Wrapper(Box::new( + WrapperTx::new( + Fee { + token: address::nam(), + amount_per_gas_unit: Amount::whole(200), + }, + &defaults::albert_keypair(), + 0.into(), + 1000.into(), + #[cfg(not(feature = "mainnet"))] + None, + None, + ), + ))); + tx.add_section(namada::proto::Section::Signature(Signature::new( + &tx.header_hash(), &defaults::albert_keypair(), - 0.into(), - 1000.into(), - tx, - Default::default(), - None, - None, - ) - .sign(&defaults::albert_keypair(), ChainId::default(), None) - .unwrap() - .to_bytes(); + ))); + let wrapper = tx.to_bytes(); let datetime = DateTimeUtc::now(); let gas_table = BTreeMap::default(); diff --git a/benches/txs.rs b/benches/txs.rs index ae054658356..8f33262fd3d 100644 --- a/benches/txs.rs +++ b/benches/txs.rs @@ -49,19 +49,16 @@ fn transfer(c: &mut Criterion) { let mut shielded_ctx = BenchShieldedCtx::default(); let albert_spending_key = shielded_ctx - .ctx .wallet - .find_spending_key(ALBERT_SPENDING_KEY) + .find_spending_key(ALBERT_SPENDING_KEY, None) .unwrap() .to_owned(); let albert_payment_addr = shielded_ctx - .ctx .wallet .find_payment_addr(ALBERT_PAYMENT_ADDRESS) .unwrap() .to_owned(); let bertha_payment_addr = shielded_ctx - .ctx .wallet .find_payment_addr(BERTHA_PAYMENT_ADDRESS) .unwrap() @@ -128,6 +125,7 @@ fn bond(c: &mut Criterion) { amount: Amount::whole(1000), source: Some(defaults::albert_address()), }, + None, &defaults::albert_keypair(), ); @@ -138,6 +136,7 @@ fn bond(c: &mut Criterion) { amount: Amount::whole(1000), source: None, }, + None, &defaults::validator_keypair(), ); @@ -166,6 +165,7 @@ fn unbond(c: &mut Criterion) { amount: Amount::whole(1000), source: Some(defaults::albert_address()), }, + None, &defaults::albert_keypair(), ); @@ -176,6 +176,7 @@ fn unbond(c: &mut Criterion) { amount: Amount::whole(1000), source: None, }, + None, &defaults::validator_keypair(), ); @@ -203,6 +204,7 @@ fn withdraw(c: &mut Criterion) { validator: defaults::validator_address(), source: Some(defaults::albert_address()), }, + None, &defaults::albert_keypair(), ); @@ -212,6 +214,7 @@ fn withdraw(c: &mut Criterion) { validator: defaults::validator_address(), source: None, }, + None, &defaults::validator_keypair(), ); @@ -233,6 +236,7 @@ fn withdraw(c: &mut Criterion) { amount: Amount::whole(1000), source: Some(defaults::albert_address()), }, + None, &defaults::albert_keypair(), ), "self_withdraw" => generate_tx( @@ -242,6 +246,7 @@ fn withdraw(c: &mut Criterion) { amount: Amount::whole(1000), source: None, }, + None, &defaults::validator_keypair(), ), _ => panic!("Unexpected bench test"), @@ -279,12 +284,20 @@ fn reveal_pk(c: &mut Criterion) { .try_to_sk() .unwrap(); - let tx = Tx::new( - wasm_loader::read_wasm_or_exit(WASM_DIR, TX_REVEAL_PK_WASM), - Some(new_implicit_account.to_public().try_to_vec().unwrap()), - ChainId("bench".to_string()), - None, - ); + let mut tx = Tx::new(namada::types::transaction::TxType::Decrypted( + namada::types::transaction::DecryptedTx::Decrypted { + #[cfg(not(feature = "mainnet"))] + has_valid_pow: true, + }, + )); + //FIXME: need to chance the chain_id to bench? + tx.set_code(namada::proto::Code::new(wasm_loader::read_wasm_or_exit( + WASM_DIR, + TX_REVEAL_PK_WASM, + ))); + tx.set_data(namada::proto::Data::new( + Some(new_implicit_account.to_public()).try_to_vec().unwrap(), + )); c.bench_function("reveal_pk", |b| { b.iter_batched_ref( @@ -306,6 +319,7 @@ fn update_vp(c: &mut Criterion) { addr: defaults::albert_address(), vp_code_hash, }, + None, &defaults::albert_keypair(), ); @@ -335,6 +349,7 @@ fn init_account(c: &mut Criterion) { public_key: new_account.to_public(), vp_code_hash, }, + None, &defaults::albert_keypair(), ); @@ -368,6 +383,7 @@ fn init_proposal(c: &mut Criterion) { voting_end_epoch: 15.into(), grace_epoch: 18.into(), }, + None, &defaults::albert_keypair(), ), "complete_proposal" => { @@ -405,6 +421,7 @@ fn init_proposal(c: &mut Criterion) { voting_end_epoch: 15.into(), grace_epoch: 18.into(), }, + None, &defaults::albert_keypair(), ) } @@ -432,6 +449,7 @@ fn vote_proposal(c: &mut Criterion) { voter: defaults::albert_address(), delegations: vec![defaults::validator_address()], }, + None, &defaults::albert_keypair(), ); @@ -443,6 +461,7 @@ fn vote_proposal(c: &mut Criterion) { voter: defaults::validator_address(), delegations: vec![], }, + None, &defaults::validator_keypair(), ); @@ -497,6 +516,7 @@ fn init_validator(c: &mut Criterion) { max_commission_rate_change: Decimal::default(), validator_vp_code_hash, }, + None, &defaults::albert_keypair(), ); @@ -516,6 +536,7 @@ fn change_validator_commission(c: &mut Criterion) { validator: defaults::validator_address(), new_rate: Decimal::new(6, 2), }, + None, &defaults::validator_keypair(), ); diff --git a/benches/vps.rs b/benches/vps.rs index e11d8ebc69a..9f337a6a3a4 100644 --- a/benches/vps.rs +++ b/benches/vps.rs @@ -52,6 +52,7 @@ fn vp_user(c: &mut Criterion) { key: None, shielded: None, }, + None, &defaults::albert_keypair(), ); @@ -66,6 +67,7 @@ fn vp_user(c: &mut Criterion) { key: None, shielded: None, }, + None, &defaults::bertha_keypair(), ); @@ -79,6 +81,7 @@ fn vp_user(c: &mut Criterion) { addr: defaults::albert_address(), vp_code_hash: vp_validator_hash, }, + None, &defaults::albert_keypair(), ); @@ -90,6 +93,7 @@ fn vp_user(c: &mut Criterion) { voter: defaults::albert_address(), delegations: vec![defaults::validator_address()], }, + None, &defaults::albert_keypair(), ); @@ -100,6 +104,7 @@ fn vp_user(c: &mut Criterion) { amount: Amount::whole(1000), source: Some(defaults::albert_address()), }, + None, &defaults::albert_keypair(), ); @@ -132,23 +137,21 @@ fn vp_user(c: &mut Criterion) { group.bench_function(bench_name, |b| { b.iter(|| { - assert!( - run::vp( - &vp_code_hash, - signed_tx, - &TxIndex(0), - &defaults::albert_address(), - &shell.wl_storage.storage, - &shell.wl_storage.write_log, - &mut VpGasMeter::new(u64::MAX, 0), - &BTreeMap::default(), - &keys_changed, - &verifiers, - shell.vp_wasm_cache.clone(), - false, - ) - .unwrap() - ); + assert!(run::vp( + &vp_code_hash, + signed_tx, + &TxIndex(0), + &defaults::albert_address(), + &shell.wl_storage.storage, + &shell.wl_storage.write_log, + &mut VpGasMeter::new(u64::MAX, 0), + &BTreeMap::default(), + &keys_changed, + &verifiers, + shell.vp_wasm_cache.clone(), + false, + ) + .unwrap()); }) }); } @@ -179,6 +182,7 @@ fn vp_implicit(c: &mut Criterion) { key: None, shielded: None, }, + None, &implicit_account, ); @@ -193,15 +197,23 @@ fn vp_implicit(c: &mut Criterion) { key: None, shielded: None, }, + None, &defaults::bertha_keypair(), ); - let reveal_pk = Tx::new( + let mut reveal_pk = Tx::new(namada::types::transaction::TxType::Decrypted( + namada::types::transaction::DecryptedTx::Decrypted { + #[cfg(not(feature = "mainnet"))] + has_valid_pow: true, + }, + )); + //FIXME: need to chance the chain_id to bench? + reveal_pk.set_code(namada::proto::Code::new( wasm_loader::read_wasm_or_exit(WASM_DIR, TX_REVEAL_PK_WASM), - Some(implicit_account.to_public().try_to_vec().unwrap()), - ChainId("bench".to_string()), - None, - ); + )); + reveal_pk.set_data(namada::proto::Data::new( + Some(implicit_account.to_public()).try_to_vec().unwrap(), + )); let pos = generate_tx( TX_BOND_WASM, @@ -210,6 +222,7 @@ fn vp_implicit(c: &mut Criterion) { amount: Amount::whole(1000), source: Some(Address::from(&implicit_account.to_public())), }, + None, &implicit_account, ); @@ -222,6 +235,7 @@ fn vp_implicit(c: &mut Criterion) { delegations: vec![], /* NOTE: no need to bond tokens because the * implicit vp doesn't check that */ }, + None, &implicit_account, ); @@ -270,23 +284,21 @@ fn vp_implicit(c: &mut Criterion) { group.bench_function(bench_name, |b| { b.iter(|| { - assert!( - run::vp( - &vp_code_hash, - tx, - &TxIndex(0), - &Address::from(&implicit_account.to_public()), - &shell.wl_storage.storage, - &shell.wl_storage.write_log, - &mut VpGasMeter::new(u64::MAX, 0), - &BTreeMap::default(), - &keys_changed, - &verifiers, - shell.vp_wasm_cache.clone(), - false, - ) - .unwrap() + assert!(run::vp( + &vp_code_hash, + tx, + &TxIndex(0), + &Address::from(&implicit_account.to_public()), + &shell.wl_storage.storage, + &shell.wl_storage.write_log, + &mut VpGasMeter::new(u64::MAX, 0), + &BTreeMap::default(), + &keys_changed, + &verifiers, + shell.vp_wasm_cache.clone(), + false, ) + .unwrap()) }) }); } @@ -315,6 +327,7 @@ fn vp_validator(c: &mut Criterion) { key: None, shielded: None, }, + None, &defaults::validator_keypair(), ); @@ -329,6 +342,7 @@ fn vp_validator(c: &mut Criterion) { key: None, shielded: None, }, + None, &defaults::bertha_keypair(), ); @@ -338,6 +352,7 @@ fn vp_validator(c: &mut Criterion) { addr: defaults::validator_address(), vp_code_hash: vp_code_hash.clone(), }, + None, &defaults::validator_keypair(), ); @@ -347,6 +362,7 @@ fn vp_validator(c: &mut Criterion) { validator: defaults::validator_address(), new_rate: Decimal::new(6, 2), }, + None, &defaults::validator_keypair(), ); @@ -358,6 +374,7 @@ fn vp_validator(c: &mut Criterion) { voter: defaults::validator_address(), delegations: vec![], }, + None, &defaults::validator_keypair(), ); @@ -368,6 +385,7 @@ fn vp_validator(c: &mut Criterion) { amount: Amount::whole(1000), source: None, }, + None, &defaults::validator_keypair(), ); @@ -400,23 +418,21 @@ fn vp_validator(c: &mut Criterion) { group.bench_function(bench_name, |b| { b.iter(|| { - assert!( - run::vp( - &vp_code_hash, - signed_tx, - &TxIndex(0), - &defaults::validator_address(), - &shell.wl_storage.storage, - &shell.wl_storage.write_log, - &mut VpGasMeter::new(u64::MAX, 0), - &BTreeMap::default(), - &keys_changed, - &verifiers, - shell.vp_wasm_cache.clone(), - false, - ) - .unwrap() - ); + assert!(run::vp( + &vp_code_hash, + signed_tx, + &TxIndex(0), + &defaults::validator_address(), + &shell.wl_storage.storage, + &shell.wl_storage.write_log, + &mut VpGasMeter::new(u64::MAX, 0), + &BTreeMap::default(), + &keys_changed, + &verifiers, + shell.vp_wasm_cache.clone(), + false, + ) + .unwrap()); }) }); } @@ -441,6 +457,7 @@ fn vp_token(c: &mut Criterion) { key: None, shielded: None, }, + None, &defaults::albert_keypair(), ); @@ -460,23 +477,21 @@ fn vp_token(c: &mut Criterion) { group.bench_function(bench_name, |b| { b.iter(|| { - assert!( - run::vp( - &vp_code_hash, - signed_tx, - &TxIndex(0), - &defaults::albert_address(), - &shell.wl_storage.storage, - &shell.wl_storage.write_log, - &mut VpGasMeter::new(u64::MAX, 0), - &BTreeMap::default(), - &keys_changed, - &verifiers, - shell.vp_wasm_cache.clone(), - false, - ) - .unwrap() - ); + assert!(run::vp( + &vp_code_hash, + signed_tx, + &TxIndex(0), + &defaults::albert_address(), + &shell.wl_storage.storage, + &shell.wl_storage.write_log, + &mut VpGasMeter::new(u64::MAX, 0), + &BTreeMap::default(), + &keys_changed, + &verifiers, + shell.vp_wasm_cache.clone(), + false, + ) + .unwrap()); }) }); } @@ -496,19 +511,16 @@ fn vp_masp(c: &mut Criterion) { .unwrap(); let albert_spending_key = shielded_ctx - .ctx .wallet - .find_spending_key(ALBERT_SPENDING_KEY) + .find_spending_key(ALBERT_SPENDING_KEY, None) .unwrap() .to_owned(); let albert_payment_addr = shielded_ctx - .ctx .wallet .find_payment_addr(ALBERT_PAYMENT_ADDRESS) .unwrap() .to_owned(); let bertha_payment_addr = shielded_ctx - .ctx .wallet .find_payment_addr(BERTHA_PAYMENT_ADDRESS) .unwrap() @@ -550,23 +562,21 @@ fn vp_masp(c: &mut Criterion) { .verifiers_and_changed_keys(&BTreeSet::default()); b.iter(|| { - assert!( - run::vp( - &vp_code_hash, - &signed_tx, - &TxIndex(0), - &defaults::validator_address(), - &shielded_ctx.shell.wl_storage.storage, - &shielded_ctx.shell.wl_storage.write_log, - &mut VpGasMeter::new(u64::MAX, 0), - &BTreeMap::default(), - &keys_changed, - &verifiers, - shielded_ctx.shell.vp_wasm_cache.clone(), - false, - ) - .unwrap() - ); + assert!(run::vp( + &vp_code_hash, + &signed_tx, + &TxIndex(0), + &defaults::validator_address(), + &shielded_ctx.shell.wl_storage.storage, + &shielded_ctx.shell.wl_storage.write_log, + &mut VpGasMeter::new(u64::MAX, 0), + &BTreeMap::default(), + &keys_changed, + &verifiers, + shielded_ctx.shell.vp_wasm_cache.clone(), + false, + ) + .unwrap()); }) }); } diff --git a/core/src/ledger/testnet_pow.rs b/core/src/ledger/testnet_pow.rs index 601cc0c6390..238da8183a4 100644 --- a/core/src/ledger/testnet_pow.rs +++ b/core/src/ledger/testnet_pow.rs @@ -297,14 +297,10 @@ pub fn is_counter_key<'a>( faucet_address: &Address, ) -> Option<&'a Address> { match &key.segments[..] { - [ - DbKeySeg::AddressSeg(address), - DbKeySeg::StringSeg(sub_key), - DbKeySeg::StringSeg(data), - DbKeySeg::AddressSeg(owner), - ] if address == faucet_address - && sub_key.as_str() == Keys::VALUES.counters - && data.as_str() == lazy_map::DATA_SUBKEY => + [DbKeySeg::AddressSeg(address), DbKeySeg::StringSeg(sub_key), DbKeySeg::StringSeg(data), DbKeySeg::AddressSeg(owner)] + if address == faucet_address + && sub_key.as_str() == Keys::VALUES.counters + && data.as_str() == lazy_map::DATA_SUBKEY => { Some(owner) } @@ -417,7 +413,11 @@ pub struct Difficulty(u8); impl Difficulty { /// The value must be between `0..=9` (inclusive upper bound). pub fn try_new(raw: u8) -> Option { - if raw > 9 { None } else { Some(Self(raw)) } + if raw > 9 { + None + } else { + Some(Self(raw)) + } } } diff --git a/core/src/types/address.rs b/core/src/types/address.rs index 165952acd25..439adddc2df 100644 --- a/core/src/types/address.rs +++ b/core/src/types/address.rs @@ -616,6 +616,17 @@ pub fn masp() -> Address { Address::decode("atest1v4ehgw36xaryysfsx5unvve4g5my2vjz89p52sjxxgenzd348yuyyv3hg3pnjs35g5unvde4ca36y5").expect("The token address decoding shouldn't fail") } +/// Sentinel secret key to indicate a MASP source +pub fn masp_tx_key() -> crate::types::key::common::SecretKey { + use crate::types::key::common; + let bytes = [ + 0, 27, 238, 157, 32, 131, 242, 184, 142, 146, 189, 24, 249, 68, 165, + 205, 71, 213, 158, 25, 253, 52, 217, 87, 52, 171, 225, 110, 131, 238, + 58, 94, 56, + ]; + common::SecretKey::try_from_slice(bytes.as_ref()).unwrap() +} + /// Temporary helper for testing, a hash map of tokens addresses with their /// MASP XAN incentive schedules. If the reward is (a, b) then a rewarded tokens /// are dispensed for every b possessed tokens. diff --git a/core/src/types/transaction/mod.rs b/core/src/types/transaction/mod.rs index 8a98bf67311..848063ead15 100644 --- a/core/src/types/transaction/mod.rs +++ b/core/src/types/transaction/mod.rs @@ -357,7 +357,7 @@ mod test_process_tx { // the signed tx let mut tx = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( Fee { - amount: 10.into(), + amount_per_gas_unit: 10.into(), token: nam(), }, &keypair, @@ -365,6 +365,7 @@ mod test_process_tx { 0.into(), #[cfg(not(feature = "mainnet"))] None, + None, )))); tx.set_code(Code::new("wasm code".as_bytes().to_owned())); tx.set_data(Data::new("transaction data".as_bytes().to_owned())); @@ -393,7 +394,7 @@ mod test_process_tx { // the signed tx let mut tx = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( Fee { - amount: 10.into(), + amount_per_gas_unit: 10.into(), token: nam(), }, &keypair, @@ -401,6 +402,7 @@ mod test_process_tx { 0.into(), #[cfg(not(feature = "mainnet"))] None, + None, )))); tx.set_code(Code::new("wasm code".as_bytes().to_owned())); tx.set_data(Data::new("transaction data".as_bytes().to_owned())); diff --git a/core/src/types/transaction/wrapper.rs b/core/src/types/transaction/wrapper.rs index d65e07aa105..103fd1e6eac 100644 --- a/core/src/types/transaction/wrapper.rs +++ b/core/src/types/transaction/wrapper.rs @@ -14,7 +14,11 @@ pub mod wrapper_tx { use sha2::{Digest, Sha256}; use thiserror::Error; + use crate::proto::Tx; + use crate::proto::{Code, Data, Section}; use crate::types::address::{masp, Address}; + use crate::types::chain::ChainId; + use crate::types::hash::Hash; use crate::types::key::common::SecretKey; use crate::types::key::*; use crate::types::storage::Epoch; @@ -172,8 +176,9 @@ pub mod wrapper_tx { pub epoch: Epoch, /// Max amount of gas that can be used when executing the inner tx pub gas_limit: GasLimit, - /// The optional, unencrypted, unshielding transaction for fee payment - pub unshield: Option, + /// The hash of the optional, unencrypted, unshielding transaction for fee payment + pub unshield_hash: Option, //FIXME: rename to unshield_section_hash + //FIXME: write the Transaction in the section of the tx (no need to sign it though?) #[cfg(not(feature = "mainnet"))] /// A PoW solution can be used to allow zero-fee testnet transactions pub pow_solution: Option, @@ -193,14 +198,14 @@ pub mod wrapper_tx { #[cfg(not(feature = "mainnet"))] pow_solution: Option< crate::ledger::testnet_pow::Solution, >, - unshield: Option, + unshield_hash: Option, ) -> WrapperTx { Self { fee, pk: keypair.ref_to(), epoch, gas_limit, - unshield, + unshield_hash, #[cfg(not(feature = "mainnet"))] pow_solution, } @@ -225,103 +230,110 @@ pub mod wrapper_tx { pub fn check_and_generate_fee_unshielding( &self, transparent_balance: Amount, - transfer_code: Vec, + transfer_code_hash: Hash, descriptions_limit: u64, - ) -> Result, WrapperTxErr> { - // Check that the number of descriptions is within a certain limit - // to avoid a possible DoS vector - if let Some(ref unshield) = self.unshield { - let spends = unshield.shielded_spends.len(); - let converts = unshield.shielded_converts.len(); - let outs = unshield.shielded_outputs.len(); - - let descriptions = spends - .checked_add(converts) - .ok_or_else(|| { - WrapperTxErr::InvalidUnshield( - "Descriptions overflow".to_string(), - ) - })? - .checked_add(outs) - .ok_or_else(|| { - WrapperTxErr::InvalidUnshield( - "Descriptions overflow".to_string(), - ) - })?; - - if u64::try_from(descriptions) - .map_err(|e| WrapperTxErr::InvalidUnshield(e.to_string()))? - > descriptions_limit - { - return Err(WrapperTxErr::InvalidUnshield( - "Descriptions exceed the maximum amount allowed" - .to_string(), - )); - } - return self.generate_fee_unshielding( - transparent_balance, - transfer_code, - ); - } - - Ok(None) - } - - /// Generates the optional fee unshielding tx for execution. - pub fn generate_fee_unshielding( - &self, - transparent_balance: Amount, - transfer_code: Vec, - ) -> Result, WrapperTxErr> { - if self.unshield.is_some() { - let amount = self - .get_tx_fee()? - .checked_sub(transparent_balance) - .ok_or_else(|| { - WrapperTxErr::InvalidUnshield( + unshield: Transaction, + ) -> Result { + // Check that the unshield operation is actually needed. Note that unshielding 0 tokens is considered an error + //FIXME: check that the client does not attach a transaction to unshield 0 tokens + let amount = self + .get_tx_fee()? + .checked_sub(transparent_balance) + .and_then(|v| if v.is_zero() { None } else { Some(v) }) + .ok_or_else(|| { + WrapperTxErr::InvalidUnshield( "The transparent balance of the fee payer is enough \ to pay fees, no need for unshielding" .to_string(), ) - })?; - - if amount.is_zero() { - return Ok(None); - } - - let transfer = Transfer { - source: masp(), - target: self.fee_payer(), - token: self.fee.token.clone(), - sub_prefix: None, - amount, - key: None, - shielded: self.unshield.clone(), - }; - - let tx = Tx::new( - transfer_code, - Some(transfer.try_to_vec().map_err(|_| { - WrapperTxErr::InvalidUnshield( - "Error while serializing the unshield transfer \ - data" - .to_string(), - ) - })?), - // No need to correctly populate these field since we are constructing the tx in protocol - ChainId::default(), - None, - ); + })?; - // Mock a signature. The masp vp does not check it, it just checks the signature on the Transaction object - let mock_sigkey = SecretKey::Ed25519(ed25519::SecretKey( - Box::new([0; 32].into()), + // Check that the number of descriptions is within a certain limit + // to avoid a possible DoS vector + //FIXME: should check hash match Transaction here? Probably we already check it somewhere else + let sapling_bundle = unshield.sapling_bundle().ok_or( + WrapperTxErr::InvalidUnshield( + "Missing required sapling bundle".to_string(), + ), + )?; + let spends = sapling_bundle.shielded_spends.len(); + let converts = sapling_bundle.shielded_converts.len(); + let outs = sapling_bundle.shielded_outputs.len(); + + let descriptions = spends + .checked_add(converts) + .ok_or_else(|| { + WrapperTxErr::InvalidUnshield( + "Descriptions overflow".to_string(), + ) + })? + .checked_add(outs) + .ok_or_else(|| { + WrapperTxErr::InvalidUnshield( + "Descriptions overflow".to_string(), + ) + })?; + + if u64::try_from(descriptions) + .map_err(|e| WrapperTxErr::InvalidUnshield(e.to_string()))? + > descriptions_limit + { + return Err(WrapperTxErr::InvalidUnshield( + "Descriptions exceed the maximum amount allowed" + .to_string(), )); - - return Ok(Some(tx.sign(&mock_sigkey))); } + self.generate_fee_unshielding(amount, transfer_code_hash, unshield) + } - Ok(None) + /// Generates the fee unshielding tx for execution. + pub fn generate_fee_unshielding( + &self, + unshield_amount: Amount, + transfer_code_hash: Hash, + unshield: Transaction, + ) -> Result { + let mut tx = Tx::new(crate::types::transaction::TxType::Decrypted( + crate::types::transaction::DecryptedTx::Decrypted { + #[cfg(not(feature = "mainnet"))] + has_valid_pow: false, + }, + )); + let masp_section = tx.add_section(Section::MaspTx(unshield)); + let masp_hash = Hash( + masp_section + .hash(&mut Sha256::new()) + .finalize_reset() + .into(), + ); + + let transfer = Transfer { + source: masp(), + target: self.fee_payer(), + token: self.fee.token.clone(), + sub_prefix: None, + amount: unshield_amount, + key: None, + shielded: Some(masp_hash), + }; + let data = transfer.try_to_vec().map_err(|_| { + WrapperTxErr::InvalidUnshield( + "Error while serializing the unshield transfer data" + .to_string(), + ) + })?; + tx.set_data(Data::new(data)); + tx.set_code(Code::from_hash(transfer_code_hash)); + + // //FIXME: do I need to sign? Can I avoid this? If yes, remove this block + // // Mock a signature. The masp vp does not check it, it just checks the signature on the Transaction object + // let mock_sigkey = SecretKey::Ed25519(ed25519::SecretKey( + // Box::new([0; 32].into()), + // )); + + // return Ok(Some(tx.sign(&mock_sigkey))); + + Ok(tx) } /// Get the [`Amount`] of fees to be paid by the given wrapper. Returns an error if the amount overflows @@ -430,6 +442,7 @@ pub mod wrapper_tx { 0.into(), #[cfg(not(feature = "mainnet"))] None, + None, )))); wrapper.set_code(Code::new("wasm code".as_bytes().to_owned())); wrapper @@ -463,6 +476,7 @@ pub mod wrapper_tx { 0.into(), #[cfg(not(feature = "mainnet"))] None, + None, )))); wrapper.set_code(Code::new("wasm code".as_bytes().to_owned())); wrapper @@ -498,6 +512,7 @@ pub mod wrapper_tx { 0.into(), #[cfg(not(feature = "mainnet"))] None, + None, )))); tx.set_code(Code::new("wasm code".as_bytes().to_owned())); diff --git a/shared/src/ledger/args.rs b/shared/src/ledger/args.rs index f7f921928d3..fa8a9a72ce7 100644 --- a/shared/src/ledger/args.rs +++ b/shared/src/ledger/args.rs @@ -409,12 +409,12 @@ pub struct Tx { /// Whether to force overwrite the above alias, if it is provided, in the /// wallet. pub wallet_alias_force: bool, - /// The amount being payed to include the transaction - pub fee_amount: token::Amount, + /// The amount being payed (for gas unit) to include the transaction + pub fee_amount: Option, /// The token in which the fee is being paid pub fee_token: C::Address, /// The optional spending key for fee unshielding - pub fee_unshield: Option, + pub fee_unshield: Option, //FIXME: check in code that this is an extended spending key and not an address /// The max amount of gas used to process tx pub gas_limit: GasLimit, /// The optional expiration of the transaction diff --git a/shared/src/ledger/masp.rs b/shared/src/ledger/masp.rs index 36d65ad27ff..20d3109d22b 100644 --- a/shared/src/ledger/masp.rs +++ b/shared/src/ledger/masp.rs @@ -1214,15 +1214,9 @@ impl ShieldedContext { let (asset_type, amount) = convert_amount(epoch, &args.token, args.amount); // The fee to be paid for the transaction - let tx_fee; // 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, &args.tx.fee_token, args.tx.fee_amount); - tx_fee = fee.clone(); // Locate unspent notes that can help us meet the transaction amount let (_, unspent_notes, used_convs) = self .collect_unspent_notes( @@ -1251,9 +1245,6 @@ impl ShieldedContext { } } } else { - // No transfer fees come from the shielded transaction for non-MASP - // sources - tx_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 @@ -1313,12 +1304,11 @@ impl ShieldedContext { if let Some(sk) = spending_key { // Represents the amount of inputs we are short by let mut additional = Amount::zero(); - // The change left over from this transaction - let value_balance = builder + for (asset_type, amt) in builder .value_balance() - .expect("unable to compute value balance") - - tx_fee.clone(); - for (asset_type, amt) in value_balance.components() { + .expect("Unable to compute value balance") + .components() + { if *amt >= 0 { // Send the change in this asset type back to the sender builder @@ -1348,7 +1338,8 @@ impl ShieldedContext { .clone() .build( &self.utils.local_tx_prover(), - &FeeRule::non_standard(tx_fee), + // Fees are always paid outside of MASP + &FeeRule::non_standard(Amount::zero()), ) .map(|(tx, metadata)| { Some((builder.map_builder(WalletMap), tx, metadata, epoch)) diff --git a/shared/src/ledger/protocol/mod.rs b/shared/src/ledger/protocol/mod.rs index 1e616cec968..0a24c12753c 100644 --- a/shared/src/ledger/protocol/mod.rs +++ b/shared/src/ledger/protocol/mod.rs @@ -2,8 +2,10 @@ use std::collections::{BTreeMap, BTreeSet}; use std::panic; +use masp_primitives::transaction::Transaction; use namada_core::ledger::gas::TxGasMeter; use namada_core::types::storage::Key; +use namada_core::proto::Section; use namada_core::ledger::storage::TempWlStorage; use namada_core::ledger::storage_api::{StorageRead, StorageWrite}; use namada_core::types::hash::Hash; @@ -154,12 +156,16 @@ where } TxType::Wrapper(ref wrapper) => { let mut changed_keys = BTreeSet::default(); + // FIXME: this is only needed for ABCI? + // Propagate invalid masp transactions to try withdraw fees from transparent balance + let masp_transaction = wrapper.unshield_hash.map(|ref hash| tx.get_section(hash).map(|section| if let Section::MaspTx(transaction) = section { Some(transaction.to_owned()) } else { None }).flatten()).flatten(); apply_wrapper_tx( write_log, storage, &mut changed_keys, wrapper, + masp_transaction, tx_bytes, tx_gas_meter, gas_table, @@ -181,25 +187,19 @@ where } } - /// Load the wasm code for a transfer from storage. + /// Load the wasm hash for a transfer from storage. /// /// # Panics - /// If the transaction hash or code are not found in storage - pub fn load_transfer_code_from_storage(storage: &S) -> Vec + /// If the transaction hash is not found in storage + pub fn get_transfer_hash_from_storage(storage: &S) -> Hash where S: StorageRead{ let transfer_code_name_key = Key::wasm_code_name("tx_transfer.wasm".to_string()); - let transfer_hash: hash::Hash = storage .read(&transfer_code_name_key) .expect("Could not read the storage") - .expect("Expected tx transfer hash in storage"); + .expect("Expected tx transfer hash in storage") - let transfer_code_key = Key::wasm_code(&transfer_hash); - storage - .read_bytes(&transfer_code_key) - .expect("Could not read the storage") - .expect("Expected tx transfer code in storage") } /// Performs the required operation on a wrapper transaction: @@ -211,6 +211,7 @@ fn apply_wrapper_tx( storage: &Storage, changed_keys: &mut BTreeSet, wrapper: &WrapperTx, + masp_transaction: Option, tx_bytes: &[u8], gas_meter: &mut TxGasMeter, gas_table: &BTreeMap, @@ -248,6 +249,7 @@ where let mut temp_wl_storage = TempWlStorage::new(storage); charge_fee( wrapper, + masp_transaction, tx_bytes, &gas_table, #[cfg(not(feature = "mainnet"))] @@ -275,6 +277,7 @@ where /// - The accumulated fee amount to be credited to the block proposer overflows pub fn charge_fee( wrapper: &WrapperTx, + masp_transaction: Option, tx_bytes: &[u8], gas_table: &BTreeMap, #[cfg(not(feature = "mainnet"))] has_valid_pow: bool, @@ -290,7 +293,9 @@ where CA: 'static + WasmCacheAccess + Sync, { // Unshield funds if requested - if wrapper.unshield.is_some() { + if wrapper.unshield_hash.is_some() { + match masp_transaction { + Some(transaction ) => { // The unshielding tx does not charge gas, instantiate a // custom gas meter for this step let mut gas_meter = @@ -313,18 +318,16 @@ where // If it fails, do not return early // from this function but try to take the funds from the unshielded // balance + //FIXME: this logic is actually only needed in case of ABCI? match wrapper.generate_fee_unshielding( transparent_balance, - load_transfer_code_from_storage(wl_storage), + get_transfer_hash_from_storage(wl_storage), + transaction ) { - Ok(Some(fee_unshielding_tx)) => { + Ok(fee_unshielding_tx) => { // NOTE: A clean write log must be provided to this call for a correct vp validation match apply_tx( - TxType::Decrypted(DecryptedTx::Decrypted { - tx: fee_unshielding_tx, - #[cfg(not(feature = "mainnet"))] - has_valid_pow: false, - }), + fee_unshielding_tx, tx_bytes, TxIndex::default(), &mut gas_meter, @@ -358,12 +361,11 @@ where } } } - Ok(None) => { - tracing::error!("Missing expected fee unshielding tx") - } - Err(e) => tracing::error!("{}", e), + Err(e) => tracing::error!("{}", e), } - } + }, + None => tracing::error!("Missing expected fee unshielding tx") + }} // Charge or check fees match block_proposer { diff --git a/shared/src/ledger/queries/shell.rs b/shared/src/ledger/queries/shell.rs index 6454c3a2e39..4841a50ba3d 100644 --- a/shared/src/ledger/queries/shell.rs +++ b/shared/src/ledger/queries/shell.rs @@ -84,16 +84,14 @@ where use namada_core::ledger::gas::TxGasMeter; use namada_core::ledger::parameters; - use namada_core::types::transaction::process_tx; + use namada_core::types::transaction::DecryptedTx; use crate::ledger::protocol; use crate::ledger::storage::write_log::WriteLog; use crate::proto::Tx; use crate::types::storage::TxIndex; use crate::types::transaction::wrapper::wrapper_tx::PairingEngine; - use crate::types::transaction::{ - AffineCurve, , EllipticCurve, TxType, - }; + use crate::types::transaction::{AffineCurve, EllipticCurve, TxType}; let gas_table: BTreeMap = ctx .wl_storage @@ -101,15 +99,15 @@ where .expect("Error while reading storage") .expect("Missing gas table in storage"); - let tx = Tx::try_from(&request.data[..]).into_storage_result()?; - let mut tx = process_tx(tx.clone()).into_storage_result()?; + let mut tx = Tx::try_from(&request.data[..]).into_storage_result()?; + tx.validate_header().into_storage_result()?; let mut write_log = WriteLog::default(); let mut cumulated_gas = 0; // Wrapper dry run to allow estimating the gas cost of a transaction - let mut tx_gas_meter = match tx { - TxType::Wrapper(ref wrapper) => { + let mut tx_gas_meter = match tx.header().tx_type { + TxType::Wrapper(wrapper) => { let mut tx_gas_meter = TxGasMeter::new(wrapper.gas_limit.to_owned().into()); protocol::apply_tx( @@ -134,15 +132,12 @@ where // NOTE: the encryption key for a dry-run should always be an hardcoded, dummy one let privkey = ::G2Affine::prime_subgroup_generator(); - tx = TxType::Decrypted(DecryptedTx::Decrypted { - tx: wrapper - .decrypt(privkey) - .expect("Could not decrypt the inner tx"), - #[cfg(not(feature = "mainnet"))] + tx.update_header(TxType::Decrypted( + DecryptedTx::Decrypted { #[cfg(not(feature = "mainnet"))] // To be able to dry-run testnet faucet withdrawal, pretend // that we got a valid PoW - has_valid_pow: true, - }); + has_valid_pow: true }, + )); TxGasMeter::new( tx_gas_meter .tx_gas_limit @@ -159,14 +154,12 @@ where .expect("Missing parameter in storage"), ) } - TxType::Raw(raw) => { + TxType::Raw => { // Cast tx to a decrypted for execution - tx = TxType::Decrypted(DecryptedTx::Decrypted { - tx: raw, - + tx.update_header(TxType::Decrypted(DecryptedTx::Decrypted { #[cfg(not(feature = "mainnet"))] has_valid_pow: true, - }); + })); // If dry run only the inner tx, use the max block gas as the gas limit TxGasMeter::new( diff --git a/shared/src/ledger/rpc.rs b/shared/src/ledger/rpc.rs index f8f0ea1e861..70be12aee8b 100644 --- a/shared/src/ledger/rpc.rs +++ b/shared/src/ledger/rpc.rs @@ -446,8 +446,8 @@ pub async fn dry_run_tx( pub enum TxBroadcastData { /// Dry run broadcast data DryRun(Tx), - /// Wrapper broadcast data - Wrapper { + /// Live broadcast data + Live { /// Transaction to broadcast tx: Tx, /// Hash of the wrapper transaction diff --git a/shared/src/ledger/signing.rs b/shared/src/ledger/signing.rs index f9b4f4b3451..9c15499f49f 100644 --- a/shared/src/ledger/signing.rs +++ b/shared/src/ledger/signing.rs @@ -1,4 +1,5 @@ //! Functions to sign transactions +use std::borrow::Cow; use std::collections::{BTreeMap, HashMap}; #[cfg(feature = "std")] use std::env; @@ -7,6 +8,7 @@ use std::fs::File; use std::io::ErrorKind; #[cfg(feature = "std")] use std::io::Write; +use std::path::PathBuf; use borsh::{BorshDeserialize, BorshSerialize}; use data_encoding::HEXLOWER; @@ -17,9 +19,10 @@ use masp_primitives::transaction::components::sapling::fees::{ }; use namada_core::types::address::{masp, Address, ImplicitAddress}; use namada_core::types::token::{self, Amount}; -use namada_core::types::transaction::{pos, MIN_FEE}; +use namada_core::types::transaction::pos; use prost::Message; use serde::{Deserialize, Serialize}; +use sha2::Digest; use zeroize::Zeroizing; use crate::ibc::applications::transfer::msgs::transfer::{ @@ -51,6 +54,8 @@ use crate::types::transaction::{ Fee, InitAccount, InitValidator, TxType, UpdateVp, WrapperTx, }; +use super::masp::{ShieldedContext, ShieldedUtils}; + #[cfg(feature = "std")] /// Env. var specifying where to store signing test vectors const ENV_VAR_LEDGER_LOG_PATH: &str = "NAMADA_LEDGER_LOG_PATH"; @@ -129,9 +134,11 @@ pub enum TxSigningKey { pub async fn tx_signer< C: crate::ledger::queries::Client + Sync, U: WalletUtils, + V: ShieldedUtils, >( client: &C, wallet: &mut Wallet, + shielded: &mut ShieldedContext, args: &args::Tx, default: TxSigningKey, ) -> Result { @@ -159,8 +166,8 @@ 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, + super::tx::reveal_pk_if_needed::( + client, wallet, shielded, &pk, args, ) .await?; } @@ -188,16 +195,19 @@ pub async fn tx_signer< pub async fn sign_tx< C: crate::ledger::queries::Client + Sync, U: WalletUtils, + V: ShieldedUtils, >( client: &C, wallet: &mut Wallet, + shielded: &mut ShieldedContext, mut tx: Tx, args: &args::Tx, default: TxSigningKey, - mut updated_balance: Option, + updated_balance: Option, #[cfg(not(feature = "mainnet"))] requires_pow: bool, ) -> Result<(TxBroadcastData, Option), Error> { - let keypair = tx_signer::(client, wallet, args, default).await?; + let keypair = + tx_signer::(client, wallet, shielded, args, default).await?; // Sign over the transacttion data tx.add_section(Section::Signature(Signature::new( tx.data_sechash(), @@ -223,10 +233,11 @@ pub async fn sign_tx< sign_wrapper( client, wallet, + shielded, args, epoch, tx, - &keypair, + Cow::Borrowed(&keypair), updated_balance, #[cfg(not(feature = "mainnet"))] requires_pow, @@ -241,16 +252,18 @@ pub async fn sign_tx< /// wrapper and its payload which is needed for monitoring its /// progress on chain. pub async fn sign_wrapper< -'key, + 'key, C: crate::ledger::queries::Client + Sync, U: WalletUtils, + V: ShieldedUtils, >( client: &C, #[allow(unused_variables)] wallet: &mut Wallet, + shielded: &mut ShieldedContext, args: &args::Tx, epoch: Epoch, mut tx: Tx, - mut keypair: Cow<'key, &common::SecretKey>, + mut keypair: Cow<'key, common::SecretKey>, mut updated_balance: Option, #[cfg(not(feature = "mainnet"))] requires_pow: bool, ) -> (TxBroadcastData, Option) { @@ -265,57 +278,54 @@ pub async fn sign_wrapper< updated_balance = Some(Amount::default()); } - let fee_token = ctx.get(&args.fee_token); let fee_amount = match args.fee_amount { - Some(amount) => amount, + Some(amount) => amount, //FIXME: validate that this matches the minimum required? None => { let gas_cost_key = parameter_storage::get_gas_cost_key(); - match rpc::query_storage_value::>( - &client, + match rpc::query_storage_value::>( + client, &gas_cost_key, ) .await - .map(|map| map.get(&fee_token).map(ToOwned::to_owned)) + .map(|map| map.get(&args.fee_token).map(ToOwned::to_owned)) .flatten() { Some(amount) => amount, None => { if !args.force { - eprintln!( + panic!( "Could not retrieve the gas cost for token {}", - fee_token + args.fee_token ); - cli::safe_exit(1) } else { - 1.into() + token::Amount::default() } } } } }; let source = Address::from(&keypair.ref_to()); - let mut updated_balance = match updated_balance { + let mut updated_balance = match updated_balance { Some(balance) => balance, None => { - let balance_key = token::balance_key(fee_token, &source); + let balance_key = token::balance_key(&args.fee_token, &source); - rpc::query_storage_value::(client, &balance_key) + rpc::query_storage_value::(client, &balance_key) .await .unwrap_or_default() } }; - + let total_fee: Amount = u64::checked_mul(fee_amount.into(), u64::from(&args.gas_limit)) .expect("Fee computation shouldn't overflow") .into(); - let (unshield, unshielding_epoch) = match total_fee .checked_sub(updated_balance) { Some(diff) => { - if let Some(spending_key) = args.fee_unshield { + if let Some(spending_key) = args.fee_unshield.clone() { // Unshield funds for fee payment let tx_args = args::Tx { fee_amount: Some(0.into()), @@ -324,28 +334,46 @@ pub async fn sign_wrapper< }; let transfer_args = args::TxTransfer { tx: tx_args, - source: FromContext::new(spending_key.to_string()), - target: FromContext::new(source.to_string()), - token: args.fee_token.to_owned(), + source: spending_key, + target: namada_core::types::masp::TransferTarget::Address( + source.clone(), + ), + token: args.fee_token.clone(), sub_prefix: None, amount: diff, + // These last two fields are not used in the function, mock them + native_token: args.fee_token.clone(), + tx_code_path: PathBuf::new(), }; - match gen_shielded_transfer(ctx, &client, &transfer_args).await + match shielded + .gen_shielded_transfer(client, transfer_args) + .await { - Ok(Some((transaction, _data, unshielding_epoch))) => { - let spends = transaction.shielded_spends.len(); - let converts = transaction.shielded_converts.len(); - let outs = transaction.shielded_outputs.len(); + Ok(Some((_, transaction, _data, unshielding_epoch))) => { + let spends = transaction + .sapling_bundle() + .unwrap() + .shielded_spends + .len(); + let converts = transaction + .sapling_bundle() + .unwrap() + .shielded_converts + .len(); + let outs = transaction + .sapling_bundle() + .unwrap() + .shielded_outputs + .len(); let mut descriptions = spends.checked_add(converts).unwrap_or_else(|| { if !args.force && cfg!(feature = "mainnet") { - eprintln!( + panic!( "Overflow in fee unshielding \ descriptions" ); - cli::safe_exit(1) } else { usize::MAX } @@ -355,11 +383,10 @@ pub async fn sign_wrapper< .checked_add(outs) .unwrap_or_else(|| { if !args.force && cfg!(feature = "mainnet") { - eprintln!( + panic!( "Overflow in fee unshielding \ descriptions" ); - cli::safe_exit(1); } else { usize::MAX } @@ -367,8 +394,8 @@ pub async fn sign_wrapper< let descriptions_limit_key= parameter_storage::get_fee_unshielding_descriptions_limit_key(); let descriptions_limit = - rpc::query_storage_value::( - &client, + rpc::query_storage_value::( + client, &descriptions_limit_key, ) .await @@ -376,10 +403,9 @@ pub async fn sign_wrapper< if u64::try_from(descriptions).unwrap_or_else(|_| { if !args.force && cfg!(feature = "mainnet") { - eprintln!( + panic!( "Overflow in fee unshielding descriptions" ); - cli::safe_exit(1); } else { u64::MAX } @@ -387,10 +413,9 @@ pub async fn sign_wrapper< && !args.force && cfg!(feature = "mainnet") { - eprintln!( + panic!( "Fee unshielding descriptions exceed the limit" ); - cli::safe_exit(1); } updated_balance += diff; @@ -399,7 +424,7 @@ pub async fn sign_wrapper< Ok(None) => { eprintln!("Missing unshielding transaction"); if !args.force && cfg!(feature = "mainnet") { - cli::safe_exit(1); + panic!(); } (None, None) @@ -407,7 +432,7 @@ pub async fn sign_wrapper< Err(e) => { eprintln!("Error in fee unshielding generation: {}", e); if !args.force && cfg!(feature = "mainnet") { - cli::safe_exit(1); + panic!(); } (None, None) @@ -420,7 +445,7 @@ pub async fn sign_wrapper< {updated_balance}." ); if !args.force && cfg!(feature = "mainnet") { - cli::safe_exit(1); + panic!() } (None, None) @@ -451,24 +476,44 @@ pub async fn sign_wrapper< }; // This object governs how the payload will be processed + let (unshield_section_hash, unshield_section) = match unshield { + Some(masp_tx) => { + let section = Section::MaspTx(masp_tx); + let mut hasher = sha2::Sha256::new(); + section.hash(&mut hasher); + ( + Some(namada_core::types::hash::Hash(hasher.finalize().into())), + Some(section), + ) + } + None => (None, None), + }; + tx.update_header(TxType::Wrapper(Box::new(WrapperTx::new( Fee { - amount: fee_amount, - token: fee_token.clone(), + amount_per_gas_unit: fee_amount, + token: args.fee_token.clone(), }, - keypair, + keypair.as_ref(), epoch, args.gas_limit.clone(), #[cfg(not(feature = "mainnet"))] pow_solution, + unshield_section_hash, )))); tx.header.chain_id = args.chain_id.clone().unwrap(); tx.header.expiration = args.expiration; // Then sign over the bound wrapper tx.add_section(Section::Signature(Signature::new( &tx.header_hash(), - keypair, + keypair.as_ref(), ))); + if let Some(unshield) = unshield_section { + // NOTE: there's no need to sign this section + //FIXME: correct? + //FIXME: but still it must be tamper resistant, there must be a commitment in the header + tx.add_section(unshield); + } #[cfg(feature = "std")] // Attempt to decode the construction @@ -514,20 +559,22 @@ pub async fn sign_wrapper< let to_broadcast = if args.dry_run_wrapper { TxBroadcastData::DryRun(tx) } else { - // We use this to determine when the wrapper tx makes it on-chain - let wrapper_hash = tx.header_hash().to_string(); - // We use this to determine when the decrypted inner tx makes it - // on-chain - let decrypted_hash = tx - .clone() - .update_header(TxType::Raw) - .header_hash() - .to_string(); - TxBroadcastData::Live{ - tx, - wrapper_hash, - decrypted_hash, - }} + // We use this to determine when the wrapper tx makes it on-chain + let wrapper_hash = tx.header_hash().to_string(); + // We use this to determine when the decrypted inner tx makes it + // on-chain + let decrypted_hash = tx + .clone() + .update_header(TxType::Raw) + .header_hash() + .to_string(); + TxBroadcastData::Live { + tx, + wrapper_hash, + decrypted_hash, + } + }; + (to_broadcast, unshielding_epoch) } @@ -1193,15 +1240,19 @@ pub async fn to_ledger_vector< format!("Timestamp : {}", tx.header.timestamp.0), format!("PK : {}", wrapper.pk), format!("Epoch : {}", wrapper.epoch), - format!("Gas limit : {}", Amount::from(wrapper.gas_limit)), + format!("Gas limit : {}", u64::from(wrapper.gas_limit)), format!("Fee token : {}", wrapper.fee.token), ]); if let Some(token) = tokens.get(&wrapper.fee.token) { - tv.output_expert - .push(format!("Fee amount : {} {}", token, wrapper.fee.amount)); + tv.output_expert.push(format!( + "Fee amount : {} {}", + token, wrapper.fee.amount_per_gas_unit + )); } else { - tv.output_expert - .push(format!("Fee amount : {}", wrapper.fee.amount)); + tv.output_expert.push(format!( + "Fee amount : {}", + wrapper.fee.amount_per_gas_unit + )); } } diff --git a/shared/src/ledger/tx.rs b/shared/src/ledger/tx.rs index 4c8ce020339..ed36bf59314 100644 --- a/shared/src/ledger/tx.rs +++ b/shared/src/ledger/tx.rs @@ -94,7 +94,7 @@ pub enum Error { ExpectDryRun(Tx), /// Expect a wrapped encrypted running transaction #[error("Cannot broadcast a dry-run transaction")] - ExpectWrappedRun(Tx), + ExpectWrappedRun(Tx), //FIXME: rename to Live /// Error during broadcasting a transaction #[error("Encountered error while broadcasting transaction: {0}")] TxBroadcast(RpcError), @@ -230,85 +230,99 @@ impl ProcessTxResponse { pub async fn process_tx< C: crate::ledger::queries::Client + Sync, U: WalletUtils, + V: ShieldedUtils, >( client: &C, wallet: &mut Wallet, + shielded: &mut ShieldedContext, args: &args::Tx, tx: Tx, default_signer: TxSigningKey, - updated_balance: Option, + updated_balance: Option, #[cfg(not(feature = "mainnet"))] requires_pow: bool, ) -> Result { - // Loop twice in case the optional unshielding tx fails because of an epoch - // change - for _ in 0..2 { - let (to_broadcast, unshielding_tx_epoch) = sign_tx::( - client, - wallet, - tx, - args, - default_signer, - updated_balance, - #[cfg(not(feature = "mainnet"))] - requires_pow, - ) - .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 || args.dry_run_wrapper { - if let TxBroadcastData::DryRun(tx) = to_broadcast { - expect_dry_broadcast(to_broadcast, client).await - } else { - panic!("Expected a dry-tun transaction"); - } + let (to_broadcast, unshielding_tx_epoch) = sign_tx::( + client, + wallet, + shielded, + tx.clone(), + args, + default_signer.clone(), + updated_balance, + #[cfg(not(feature = "mainnet"))] + requires_pow, + ) + .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 || args.dry_run_wrapper { + if let TxBroadcastData::DryRun(_) = to_broadcast { + expect_dry_broadcast(to_broadcast, client).await } 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)) => { - // Query the epoch in which the transaction was probably - // submitted - let submission_epoch = rpc::query_epoch(client).await; - - // If wrapper tx requests the unshielding of tokens for - // fee payment - if let Some(unshield_epoch) = unshielding_tx_epoch { - // And the transaction was rejected by some vp and - // the epochs do not match - if result.code == 1.to_string() - && unshield_epoch != submission_epoch - { - // Retry the submission - eprintln!( - "Fee unshielding transaction rejected and \ + Err(Error::ExpectDryRun(tx)) + } + } 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)) => { + // Query the epoch in which the transaction was probably + // submitted + let submission_epoch = rpc::query_epoch(client).await; + + // If wrapper tx requests the unshielding of tokens for + // fee payment + if let Some(unshield_epoch) = unshielding_tx_epoch { + // And the transaction was rejected by some vp and + // the epochs do not match + if result.code == 1.to_string() + && unshield_epoch != submission_epoch + { + // Retry the submission in case the optional unshielding tx fails because of an epoch + // change + eprintln!( + "Fee unshielding transaction rejected and \ this may be due to the epoch changing. \ Attempting to resubmit transaction.", - ); - continue; - } + ); + let (to_broadcast, _) = sign_tx::( + client, + wallet, + shielded, + tx, + args, + default_signer, + updated_balance, + #[cfg(not(feature = "mainnet"))] + requires_pow, + ) + .await?; + return submit_tx(client, to_broadcast) + .await + .map(|result| ProcessTxResponse::Applied(result)); } - - Ok(ProcessTxResponse::Applied(result)) } - Left(Ok(result)) => Ok(ProcessTxResponse::Broadcast(result)), - Right(Err(err)) => Err(err), - Left(Err(err)) => Err(err), + + Ok(ProcessTxResponse::Applied(result)) } + Left(Ok(result)) => Ok(ProcessTxResponse::Broadcast(result)), + Right(Err(err)) => Err(err), + Left(Err(err)) => Err(err), } } } @@ -317,9 +331,11 @@ pub async fn process_tx< pub async fn submit_reveal_pk< C: crate::ledger::queries::Client + Sync, U: WalletUtils, + V: ShieldedUtils, >( client: &C, wallet: &mut Wallet, + shielded: &mut ShieldedContext, args: args::RevealPk, ) -> Result<(), Error> { let args::RevealPk { @@ -327,7 +343,15 @@ 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, + shielded, + &public_key, + &args, + ) + .await? + { let addr: Address = (&public_key).into(); println!("PK for {addr} is already revealed, nothing to do."); Ok(()) @@ -340,9 +364,11 @@ pub async fn submit_reveal_pk< pub async fn reveal_pk_if_needed< C: crate::ledger::queries::Client + Sync, U: WalletUtils, + V: ShieldedUtils, >( client: &C, wallet: &mut Wallet, + shielded: &mut ShieldedContext, public_key: &common::PublicKey, args: &args::Tx, ) -> Result { @@ -350,7 +376,10 @@ pub async fn reveal_pk_if_needed< // 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?; + submit_reveal_pk_aux::( + client, wallet, shielded, public_key, args, + ) + .await?; Ok(true) } else { Ok(false) @@ -369,9 +398,11 @@ pub async fn has_revealed_pk( pub async fn submit_reveal_pk_aux< C: crate::ledger::queries::Client + Sync, U: WalletUtils, + V: ShieldedUtils, >( client: &C, wallet: &mut Wallet, + shielded: &mut ShieldedContext, public_key: &common::PublicKey, args: &args::Tx, ) -> Result { @@ -415,11 +446,12 @@ pub async fn submit_reveal_pk_aux< super::signing::sign_wrapper( client, wallet, + shielded, args, epoch, tx, Cow::Borrowed(&keypair), - &keypair, + None, #[cfg(not(feature = "mainnet"))] false, ) @@ -427,7 +459,7 @@ pub async fn submit_reveal_pk_aux< }; if args.dry_run || args.dry_run_wrapper { - if let TxBroadcastData::DryRun(tx) = to_broadcast { + if let TxBroadcastData::DryRun(_) = to_broadcast { expect_dry_broadcast(to_broadcast, client).await } else { panic!("Expected a dry-run transaction"); @@ -621,9 +653,11 @@ pub async fn save_initialized_accounts( pub async fn submit_validator_commission_change< C: crate::ledger::queries::Client + Sync, U: WalletUtils, + V: ShieldedUtils, >( client: &C, wallet: &mut Wallet, + shielded: &mut ShieldedContext, args: args::TxCommissionRateChange, ) -> Result<(), Error> { let epoch = rpc::query_epoch(client).await; @@ -701,12 +735,14 @@ pub async fn submit_validator_commission_change< tx.set_code(Code::from_hash(tx_code_hash)); let default_signer = args.validator.clone(); - process_tx::( + process_tx::( client, wallet, + shielded, &args.tx, tx, TxSigningKey::WalletAddress(default_signer), + None, #[cfg(not(feature = "mainnet"))] false, ) @@ -718,9 +754,11 @@ pub async fn submit_validator_commission_change< pub async fn submit_unjail_validator< C: crate::ledger::queries::Client + Sync, U: WalletUtils, + V: ShieldedUtils, >( client: &C, wallet: &mut Wallet, + shielded: &mut ShieldedContext, args: args::TxUnjailValidator, ) -> Result<(), Error> { if !rpc::is_validator(client, &args.validator).await { @@ -750,9 +788,11 @@ pub async fn submit_unjail_validator< process_tx( client, wallet, + shielded, &args.tx, tx, TxSigningKey::WalletAddress(default_signer), + None, #[cfg(not(feature = "mainnet"))] false, ) @@ -764,9 +804,11 @@ pub async fn submit_unjail_validator< pub async fn submit_withdraw< C: crate::ledger::queries::Client + Sync, U: WalletUtils, + V: ShieldedUtils, >( client: &C, wallet: &mut Wallet, + shielded: &mut ShieldedContext, args: args::Withdraw, ) -> Result<(), Error> { let epoch = rpc::query_epoch(client).await; @@ -816,12 +858,14 @@ pub async fn submit_withdraw< tx.set_code(Code::from_hash(tx_code_hash)); let default_signer = args.source.unwrap_or(args.validator); - process_tx::( + process_tx::( client, wallet, + shielded, &args.tx, tx, TxSigningKey::WalletAddress(default_signer), + None, #[cfg(not(feature = "mainnet"))] false, ) @@ -833,9 +877,11 @@ pub async fn submit_withdraw< pub async fn submit_unbond< C: crate::ledger::queries::Client + Sync, U: WalletUtils, + V: ShieldedUtils, >( client: &C, wallet: &mut Wallet, + shielded: &mut ShieldedContext, args: args::Unbond, ) -> Result<(), Error> { let source = args.source.clone(); @@ -897,12 +943,14 @@ pub async fn submit_unbond< tx.set_code(Code::from_hash(tx_code_hash)); let default_signer = args.source.unwrap_or_else(|| args.validator.clone()); - process_tx::( + process_tx::( client, wallet, + shielded, &args.tx, tx, TxSigningKey::WalletAddress(default_signer), + None, #[cfg(not(feature = "mainnet"))] false, ) @@ -962,9 +1010,11 @@ pub async fn submit_unbond< pub async fn submit_bond< C: crate::ledger::queries::Client + Sync, U: WalletUtils, + V: ShieldedUtils, >( client: &C, wallet: &mut Wallet, + shielded: &mut ShieldedContext, args: args::Bond, ) -> Result<(), Error> { let validator = @@ -985,7 +1035,7 @@ pub async fn submit_bond< let balance_key = token::balance_key(&args.native_token, bond_source); // TODO Should we state the same error message for the native token? - check_balance_too_low_err( + let updated_balance = check_balance_too_low_err( &args.native_token, bond_source, args.amount, @@ -1014,12 +1064,14 @@ pub async fn submit_bond< tx.set_code(Code::from_hash(tx_code_hash)); let default_signer = args.source.unwrap_or(args.validator); - process_tx::( + process_tx::( client, wallet, + shielded, &args.tx, tx, TxSigningKey::WalletAddress(default_signer), + Some(updated_balance), #[cfg(not(feature = "mainnet"))] false, ) @@ -1061,9 +1113,11 @@ pub async fn is_safe_voting_window( pub async fn submit_ibc_transfer< C: crate::ledger::queries::Client + Sync, U: WalletUtils, + V: ShieldedUtils, >( client: &C, wallet: &mut Wallet, + shielded: &mut ShieldedContext, args: args::TxIbcTransfer, ) -> Result<(), Error> { // Check that the source address exists on chain @@ -1087,7 +1141,7 @@ pub async fn submit_ibc_transfer< None => (None, token::balance_key(&token, &source)), }; - check_balance_too_low_err( + let updated_balance = check_balance_too_low_err( &token, &source, args.amount, @@ -1152,12 +1206,14 @@ pub async fn submit_ibc_transfer< tx.set_data(Data::new(data)); tx.set_code(Code::from_hash(tx_code_hash)); - process_tx::( + process_tx::( client, wallet, + shielded, &args.tx, tx, TxSigningKey::WalletAddress(args.source), + Some(updated_balance), #[cfg(not(feature = "mainnet"))] false, ) @@ -1241,12 +1297,12 @@ async fn used_asset_types< /// Submit an ordinary transfer pub async fn submit_transfer< C: crate::ledger::queries::Client + Sync, - V: WalletUtils, - U: ShieldedUtils, + U: WalletUtils, + V: ShieldedUtils, >( client: &C, - wallet: &mut Wallet, - shielded: &mut ShieldedContext, + wallet: &mut Wallet, + shielded: &mut ShieldedContext, args: args::TxTransfer, ) -> Result<(), Error> { let source = args.source.effective_address(); @@ -1271,7 +1327,7 @@ pub async fn submit_transfer< } None => (None, token::balance_key(&token, &source)), }; - check_balance_too_low_err::( + let updated_balance = check_balance_too_low_err::( &token, &source, args.amount, @@ -1334,7 +1390,7 @@ pub async fn submit_transfer< source.clone(), args.amount, token.clone(), - args.tx.fee_amount, + token::Amount::default(), args.tx.fee_token.clone(), )) } @@ -1395,12 +1451,14 @@ pub async fn submit_transfer< tx.set_code(Code::from_hash(tx_code_hash)); // Dry-run/broadcast/submit the transaction - let result = process_tx::( + let result = process_tx::( client, wallet, + shielded, &args.tx, tx, default_signer.clone(), + Some(updated_balance), #[cfg(not(feature = "mainnet"))] is_source_faucet, ) @@ -1437,9 +1495,11 @@ pub async fn submit_transfer< pub async fn submit_init_account< C: crate::ledger::queries::Client + Sync, U: WalletUtils, + V: ShieldedUtils, >( client: &C, wallet: &mut Wallet, + shielded: &mut ShieldedContext, args: args::TxInitAccount, ) -> Result<(), Error> { let public_key = args.public_key; @@ -1470,12 +1530,14 @@ pub async fn submit_init_account< tx.set_code(Code::from_hash(tx_code_hash)); // TODO Move unwrap to an either - let initialized_accounts = process_tx::( + let initialized_accounts = process_tx::( client, wallet, + shielded, &args.tx, tx, TxSigningKey::WalletAddress(args.source), + None, #[cfg(not(feature = "mainnet"))] false, ) @@ -1491,9 +1553,11 @@ pub async fn submit_init_account< pub async fn submit_update_vp< C: crate::ledger::queries::Client + Sync, U: WalletUtils, + V: ShieldedUtils, >( client: &C, wallet: &mut Wallet, + shielded: &mut ShieldedContext, args: args::TxUpdateVp, ) -> Result<(), Error> { let addr = args.addr.clone(); @@ -1563,12 +1627,14 @@ pub async fn submit_update_vp< tx.set_data(Data::new(data)); tx.set_code(Code::from_hash(tx_code_hash)); - process_tx::( + process_tx::( client, wallet, + shielded, &args.tx, tx, TxSigningKey::WalletAddress(args.addr), + None, #[cfg(not(feature = "mainnet"))] false, ) @@ -1580,9 +1646,11 @@ pub async fn submit_update_vp< pub async fn submit_custom< C: crate::ledger::queries::Client + Sync, U: WalletUtils, + V: ShieldedUtils, >( client: &C, wallet: &mut Wallet, + shielded: &mut ShieldedContext, args: args::TxCustom, ) -> Result<(), Error> { let mut tx = Tx::new(TxType::Raw); @@ -1591,9 +1659,10 @@ pub async fn submit_custom< args.data_path.map(|data| tx.set_data(Data::new(data))); tx.set_code(Code::new(args.code_path)); - let initialized_accounts = process_tx::( + let initialized_accounts = process_tx::( client, wallet, + shielded, &args.tx, tx, TxSigningKey::None, @@ -1617,7 +1686,7 @@ async fn expect_dry_broadcast( rpc::dry_run_tx(client, tx.to_bytes()).await; Ok(ProcessTxResponse::DryRun) } - TxBroadcastData::Wrapper { + TxBroadcastData::Live { tx, wrapper_hash: _, decrypted_hash: _, @@ -1743,7 +1812,7 @@ async fn target_exists_or_err( /// checks the balance at the given address is enough to transfer the /// given amount, along with the balance even existing. force -/// overrides this +/// overrides this. Returns the updated balance for fee check async fn check_balance_too_low_err( token: &Address, source: &Address, @@ -1751,12 +1820,13 @@ async fn check_balance_too_low_err( balance_key: storage::Key, force: bool, client: &C, -) -> Result<(), Error> { +) -> Result { match rpc::query_storage_value::(client, &balance_key) .await { - Some(balance) => { - if balance < amount { + Some(balance) => match balance.checked_sub(amount) { + Some(diff) => Ok(diff), + None => { if force { eprintln!( "The balance of the source {} of token {} is lower \ @@ -1764,7 +1834,7 @@ async fn check_balance_too_low_err( transfer is {} and the balance is {}.", source, token, amount, balance ); - Ok(()) + Ok(token::Amount::default()) } else { Err(Error::BalanceTooLow( source.clone(), @@ -1773,17 +1843,15 @@ async fn check_balance_too_low_err( balance, )) } - } else { - Ok(()) } - } + }, None => { if force { eprintln!( "No balance found for the source {} of token {}", source, token ); - Ok(()) + Ok(token::Amount::default()) } else { Err(Error::NoBalanceForToken(source.clone(), token.clone())) } diff --git a/shared/src/ledger/vp_host_fns.rs b/shared/src/ledger/vp_host_fns.rs index e072422747b..cf26ca9c1e3 100644 --- a/shared/src/ledger/vp_host_fns.rs +++ b/shared/src/ledger/vp_host_fns.rs @@ -383,15 +383,3 @@ where } Ok(None) } - -/// Verify the signature of a transaction -pub fn verify_tx_signature( - gas_meter: &mut VpGasMeter, - tx: &Tx, - pk: &common::PublicKey, - sig: &common::Signature, -) -> EnvResult { - add_gas(gas_meter, VERIFY_TX_SIG_GAS_COST)?; - - Ok(tx.verify_sig(pk, sig).is_ok()) -} diff --git a/shared/src/vm/wasm/run.rs b/shared/src/vm/wasm/run.rs index 2a34168f355..7819851125f 100644 --- a/shared/src/vm/wasm/run.rs +++ b/shared/src/vm/wasm/run.rs @@ -114,11 +114,11 @@ where .ok_or(Error::MissingCode)?; let (tx_hash, code) = match tx_code.code { Commitment::Hash(code_hash) => (code_hash, None), - Commitment::Id(tx_code) => (Hash::sha256(tx_code), Some(tx_code)), + Commitment::Id(tx_code) => (Hash::sha256(&tx_code), Some(tx_code)), }; let code_or_hash = match code { - Some(code) => WasmPayload::Code(code), + Some(ref code) => WasmPayload::Code(code), None => WasmPayload::Hash(&tx_hash), }; diff --git a/tests/src/e2e/ledger_tests.rs b/tests/src/e2e/ledger_tests.rs index 68682030f6a..895e3fccde3 100644 --- a/tests/src/e2e/ledger_tests.rs +++ b/tests/src/e2e/ledger_tests.rs @@ -19,6 +19,7 @@ use std::time::{Duration, Instant}; use borsh::BorshSerialize; use color_eyre::eyre::Result; use data_encoding::HEXLOWER; +use namada::ledger::masp::{ShieldedContext, ShieldedUtils}; use namada::types::address::{btc, eth, masp_rewards, Address}; use namada::types::governance::ProposalType; use namada::types::storage::Epoch; @@ -555,7 +556,7 @@ fn ledger_txs_and_queries() -> Result<()> { /// 3. Test that a tx requesting a disposable signer with a correct unshielding operation is succesful fn wrapper_disposable_signer() -> 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. @@ -698,7 +699,7 @@ fn wrapper_disposable_signer() -> Result<()> { #[test] fn wrapper_fee_unshielding() -> 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.