diff --git a/.changelog/unreleased/bug-fixes/1607-fix-tx-malleability.md b/.changelog/unreleased/bug-fixes/1607-fix-tx-malleability.md new file mode 100644 index 0000000000..a6c4d78e1e --- /dev/null +++ b/.changelog/unreleased/bug-fixes/1607-fix-tx-malleability.md @@ -0,0 +1,2 @@ +- Fixed bug that allowed transactions to be modified without invalidating + transaction hash ([\#1607](https://github.com/anoma/namada/pull/1607)) \ No newline at end of file diff --git a/.changelog/unreleased/bug-fixes/1611-move-init-proposal-content.md b/.changelog/unreleased/bug-fixes/1611-move-init-proposal-content.md new file mode 100644 index 0000000000..fa84739c3a --- /dev/null +++ b/.changelog/unreleased/bug-fixes/1611-move-init-proposal-content.md @@ -0,0 +1,3 @@ +- Move the content and code of init proposal transactions + into separare section to reduce tx size for hardware wallets + ([\#1611](https://github.com/anoma/namada/pull/1611)) \ No newline at end of file diff --git a/.changelog/unreleased/improvements/1498-separate-signing.md b/.changelog/unreleased/improvements/1498-separate-signing.md new file mode 100644 index 0000000000..506b902672 --- /dev/null +++ b/.changelog/unreleased/improvements/1498-separate-signing.md @@ -0,0 +1,3 @@ +- Separate the transaction building, signing, and submission + actions in the SDKs API to enable hardware wallet usage + ([\#1498](https://github.com/anoma/namada/pull/1498)) \ No newline at end of file diff --git a/.changelog/unreleased/improvements/1636-disable-encryption.md b/.changelog/unreleased/improvements/1636-disable-encryption.md new file mode 100644 index 0000000000..7a407171d4 --- /dev/null +++ b/.changelog/unreleased/improvements/1636-disable-encryption.md @@ -0,0 +1,2 @@ +- Disable encryption when sending transactions + ([\#1636](https://github.com/anoma/namada/pull/1636)) \ No newline at end of file diff --git a/.changelog/unreleased/miscellaneous/cwgoes-remove-unused-named-address.md b/.changelog/unreleased/miscellaneous/cwgoes-remove-unused-named-address.md new file mode 100644 index 0000000000..3152915455 --- /dev/null +++ b/.changelog/unreleased/miscellaneous/cwgoes-remove-unused-named-address.md @@ -0,0 +1 @@ +Remove unused named address file diff --git a/.changelog/unreleased/unreleased/1444.md b/.changelog/unreleased/unreleased/1444.md new file mode 100644 index 0000000000..652f9ad334 --- /dev/null +++ b/.changelog/unreleased/unreleased/1444.md @@ -0,0 +1 @@ +Common sub-expression elimination in inflation calculation diff --git a/apps/src/bin/namada-client/cli.rs b/apps/src/bin/namada-client/cli.rs index bf9921a527..9e19841bfb 100644 --- a/apps/src/bin/namada-client/cli.rs +++ b/apps/src/bin/namada-client/cli.rs @@ -92,7 +92,7 @@ pub async fn main() -> Result<()> { .unwrap(); let args = args.to_sdk(&mut ctx); tx::submit_init_validator::(&client, ctx, args) - .await; + .await?; } Sub::TxInitProposal(TxInitProposal(args)) => { wait_until_node_is_synched(&args.tx.ledger_address).await; @@ -148,6 +148,17 @@ pub async fn main() -> Result<()> { tx::submit_withdraw::(&client, ctx, args) .await?; } + Sub::TxCommissionRateChange(TxCommissionRateChange(args)) => { + wait_until_node_is_synched(&args.tx.ledger_address).await; + let client = + HttpClient::new(args.tx.ledger_address.clone()) + .unwrap(); + let args = args.to_sdk(&mut ctx); + tx::submit_validator_commission_change::( + &client, ctx, args, + ) + .await?; + } // Ledger queries Sub::QueryEpoch(QueryEpoch(args)) => { wait_until_node_is_synched(&args.ledger_address).await; diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index 0a88306f18..a85c784b91 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -164,6 +164,7 @@ pub mod cmds { .subcommand(Bond::def().display_order(2)) .subcommand(Unbond::def().display_order(2)) .subcommand(Withdraw::def().display_order(2)) + .subcommand(TxCommissionRateChange::def().display_order(2)) // Queries .subcommand(QueryEpoch::def().display_order(3)) .subcommand(QueryTransfers::def().display_order(3)) @@ -198,6 +199,8 @@ pub mod cmds { Self::parse_with_ctx(matches, TxInitProposal); let tx_vote_proposal = Self::parse_with_ctx(matches, TxVoteProposal); + let tx_commission_rate_change = + Self::parse_with_ctx(matches, TxCommissionRateChange); let bond = Self::parse_with_ctx(matches, Bond); let unbond = Self::parse_with_ctx(matches, Unbond); let withdraw = Self::parse_with_ctx(matches, Withdraw); @@ -232,6 +235,7 @@ pub mod cmds { .or(tx_init_proposal) .or(tx_vote_proposal) .or(tx_init_validator) + .or(tx_commission_rate_change) .or(bond) .or(unbond) .or(withdraw) @@ -294,6 +298,7 @@ pub mod cmds { TxUpdateVp(TxUpdateVp), TxInitAccount(TxInitAccount), TxInitValidator(TxInitValidator), + TxCommissionRateChange(TxCommissionRateChange), TxInitProposal(TxInitProposal), TxVoteProposal(TxVoteProposal), TxRevealPk(TxRevealPk), @@ -1534,6 +1539,32 @@ pub mod cmds { } } + #[derive(Clone, Debug)] + pub struct TxCommissionRateChange( + pub args::CommissionRateChange, + ); + + impl SubCmd for TxCommissionRateChange { + const CMD: &'static str = "change-commission-rate"; + + fn parse(matches: &ArgMatches) -> Option + where + Self: Sized, + { + matches.subcommand_matches(Self::CMD).map(|matches| { + TxCommissionRateChange(args::CommissionRateChange::parse( + matches, + )) + }) + } + + fn def() -> App { + App::new(Self::CMD) + .about("Change commission raate.") + .add_args::>() + } + } + #[derive(Clone, Debug)] pub struct TxVoteProposal(pub args::VoteProposal); @@ -1935,6 +1966,8 @@ pub mod args { pub const VALIDATOR_CODE_PATH: ArgOpt = arg_opt("validator-code-path"); pub const VALUE: ArgOpt = arg_opt("value"); + pub const VERIFICATION_KEY: ArgOpt = + arg_opt("verification-key"); pub const VIEWING_KEY: Arg = arg("key"); pub const WALLET_ALIAS_FORCE: ArgFlag = flag("wallet-alias-force"); pub const WASM_CHECKSUMS_PATH: Arg = arg("wasm-checksums-path"); @@ -3101,11 +3134,11 @@ pub mod args { } } - impl CliToSdk> - for TxCommissionRateChange + impl CliToSdk> + for CommissionRateChange { - fn to_sdk(self, ctx: &mut Context) -> TxCommissionRateChange { - TxCommissionRateChange:: { + fn to_sdk(self, ctx: &mut Context) -> CommissionRateChange { + CommissionRateChange:: { tx: self.tx.to_sdk(ctx), validator: ctx.get(&self.validator), rate: self.rate, @@ -3114,7 +3147,7 @@ pub mod args { } } - impl Args for TxCommissionRateChange { + impl Args for CommissionRateChange { fn parse(matches: &ArgMatches) -> Self { let tx = Tx::parse(matches); let validator = VALIDATOR.parse(matches); @@ -3340,11 +3373,16 @@ pub mod args { fee_token: ctx.get(&self.fee_token), gas_limit: self.gas_limit, signing_key: self.signing_key.map(|x| ctx.get_cached(&x)), + verification_key: self + .verification_key + .map(|x| ctx.get_cached(&x)), signer: self.signer.map(|x| ctx.get(&x)), tx_reveal_code_path: self.tx_reveal_code_path, password: self.password, expiration: self.expiration, - chain_id: self.chain_id, + chain_id: self + .chain_id + .or_else(|| Some(ctx.config.ledger.chain_id.clone())), } } } @@ -3403,7 +3441,8 @@ pub mod args { public key, public key hash or alias from your \ wallet.", ) - .conflicts_with(SIGNER.name), + .conflicts_with(SIGNER.name) + .conflicts_with(VERIFICATION_KEY.name), ) .arg( SIGNER @@ -3412,6 +3451,18 @@ pub mod args { "Sign the transaction with the keypair of the public \ key of the given address.", ) + .conflicts_with(SIGNING_KEY_OPT.name) + .conflicts_with(VERIFICATION_KEY.name), + ) + .arg( + VERIFICATION_KEY + .def() + .help( + "Sign the transaction with the key for the given \ + public key, public key hash or alias from your \ + wallet.", + ) + .conflicts_with(SIGNER.name) .conflicts_with(SIGNING_KEY_OPT.name), ) .arg(CHAIN_ID_OPT.def().help("The chain ID.")) @@ -3431,6 +3482,7 @@ pub mod args { let gas_limit = GAS_LIMIT.parse(matches).amount.into(); let expiration = EXPIRATION_OPT.parse(matches); let signing_key = SIGNING_KEY_OPT.parse(matches); + let verification_key = VERIFICATION_KEY.parse(matches); let signer = SIGNER.parse(matches); let tx_reveal_code_path = PathBuf::from(TX_REVEAL_PK); let chain_id = CHAIN_ID_OPT.parse(matches); @@ -3448,6 +3500,7 @@ pub mod args { gas_limit, expiration, signing_key, + verification_key, signer, tx_reveal_code_path, password, diff --git a/apps/src/lib/client/signing.rs b/apps/src/lib/client/signing.rs index c44da88f9b..4ae12c6b17 100644 --- a/apps/src/lib/client/signing.rs +++ b/apps/src/lib/client/signing.rs @@ -14,16 +14,15 @@ use crate::cli::args; /// Find the public key for the given address and try to load the keypair /// for it from the wallet. Panics if the key cannot be found or loaded. -pub async fn find_keypair< +pub async fn find_pk< C: namada::ledger::queries::Client + Sync, U: WalletUtils, >( client: &C, wallet: &mut Wallet, addr: &Address, -) -> Result { - namada::ledger::signing::find_keypair::(client, wallet, addr, None) - .await +) -> Result { + namada::ledger::signing::find_pk(client, wallet, addr, None).await } /// Given CLI arguments and some defaults, determine the rightful transaction @@ -38,7 +37,7 @@ pub async fn tx_signer< wallet: &mut Wallet, args: &args::Tx, default: TxSigningKey, -) -> Result { +) -> Result<(Option
, common::PublicKey), tx::Error> { namada::ledger::signing::tx_signer::(client, wallet, args, default) .await } @@ -55,23 +54,12 @@ pub async fn sign_tx< C: namada::ledger::queries::Client + Sync, U: WalletUtils, >( - client: &C, wallet: &mut Wallet, - tx: Tx, + tx: &mut Tx, args: &args::Tx, - default: TxSigningKey, - #[cfg(not(feature = "mainnet"))] requires_pow: bool, -) -> Result { - namada::ledger::signing::sign_tx::( - client, - wallet, - tx, - args, - default, - #[cfg(not(feature = "mainnet"))] - requires_pow, - ) - .await + default: &common::PublicKey, +) -> Result<(), tx::Error> { + namada::ledger::signing::sign_tx(wallet, tx, args, default).await } /// Create a wrapper tx from a normal tx. Get the hash of the diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index be8936f6ee..93ca4479a6 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -5,8 +5,6 @@ use std::fs::{File, OpenOptions}; use std::io::{Read, Write}; use std::path::PathBuf; -use async_std::io; -use async_std::io::prelude::WriteExt; use async_trait::async_trait; use borsh::{BorshDeserialize, BorshSerialize}; use data_encoding::HEXLOWER_PERMISSIVE; @@ -15,7 +13,7 @@ use namada::ledger::governance::storage as gov_storage; use namada::ledger::rpc::{TxBroadcastData, TxResponse}; use namada::ledger::signing::TxSigningKey; use namada::ledger::wallet::{Wallet, WalletUtils}; -use namada::ledger::{masp, pos, tx}; +use namada::ledger::{masp, pos, signing, tx}; use namada::proof_of_stake::parameters::PosParams; use namada::proto::{Code, Data, Section, Tx}; use namada::types::address::Address; @@ -23,64 +21,93 @@ use namada::types::dec::Dec; use namada::types::governance::{ OfflineProposal, OfflineVote, Proposal, ProposalVote, VoteType, }; -use namada::types::hash::Hash; use namada::types::key::*; use namada::types::storage::{Epoch, Key}; use namada::types::token; -use namada::types::transaction::governance::{ - InitProposalData, ProposalType, VoteProposalData, -}; +use namada::types::transaction::governance::{ProposalType, VoteProposalData}; use namada::types::transaction::{InitValidator, TxType}; -use sha2::{Digest as Sha2Digest, Sha256}; use super::rpc; use crate::cli::context::WalletAddress; use crate::cli::{args, safe_exit, Context}; use crate::client::rpc::query_wasm_code_hash; -use crate::client::signing::find_keypair; +use crate::client::signing::find_pk; use crate::client::tx::tx::ProcessTxResponse; use crate::config::TendermintMode; use crate::facade::tendermint_rpc::endpoint::broadcast::tx_sync::Response; use crate::facade::tendermint_rpc::HttpClient; use crate::node::ledger::tendermint_node; -use crate::wallet::{ - gen_validator_keys, read_and_confirm_encryption_password, CliWalletUtils, -}; +use crate::wallet::{gen_validator_keys, read_and_confirm_encryption_password}; + +// Build a transaction to reveal the signer of the given transaction. +pub async fn submit_reveal_aux( + client: &C, + ctx: &mut Context, + args: &args::Tx, + addr: Option
, + pk: common::PublicKey, + tx: &mut Tx, +) -> Result<(), tx::Error> { + if let Some(Address::Implicit(_)) = addr { + let reveal_pk = tx::build_reveal_pk( + client, + &mut ctx.wallet, + args::RevealPk { + tx: args.clone(), + public_key: pk.clone(), + }, + ) + .await?; + if let Some((mut rtx, _, pk)) = reveal_pk { + // Sign the reveal public key transaction with the fee payer + signing::sign_tx(&mut ctx.wallet, &mut rtx, args, &pk).await?; + // Submit the reveal public key transaction first + tx::process_tx(client, &mut ctx.wallet, args, rtx).await?; + // Update the stateful PoW challenge of the outer transaction + #[cfg(not(feature = "mainnet"))] + signing::update_pow_challenge(client, args, tx, &pk, false).await; + } + } + Ok(()) +} pub async fn submit_custom( client: &C, ctx: &mut Context, - mut args: args::TxCustom, + args: args::TxCustom, ) -> Result<(), tx::Error> { - args.tx.chain_id = args - .tx - .chain_id - .or_else(|| Some(ctx.config.ledger.chain_id.clone())); - tx::submit_custom::(client, &mut ctx.wallet, args).await + let (mut tx, addr, pk) = + tx::build_custom(client, &mut ctx.wallet, args.clone()).await?; + submit_reveal_aux(client, ctx, &args.tx, addr, pk.clone(), &mut tx).await?; + signing::sign_tx(&mut ctx.wallet, &mut tx, &args.tx, &pk).await?; + tx::process_tx(client, &mut ctx.wallet, &args.tx, tx).await?; + Ok(()) } pub async fn submit_update_vp( client: &C, ctx: &mut Context, - mut args: args::TxUpdateVp, + args: args::TxUpdateVp, ) -> Result<(), tx::Error> { - args.tx.chain_id = args - .tx - .chain_id - .or_else(|| Some(ctx.config.ledger.chain_id.clone())); - tx::submit_update_vp::(client, &mut ctx.wallet, args).await + let (mut tx, addr, pk) = + tx::build_update_vp(client, &mut ctx.wallet, args.clone()).await?; + submit_reveal_aux(client, ctx, &args.tx, addr, pk.clone(), &mut tx).await?; + signing::sign_tx(&mut ctx.wallet, &mut tx, &args.tx, &pk).await?; + tx::process_tx(client, &mut ctx.wallet, &args.tx, tx).await?; + Ok(()) } pub async fn submit_init_account( client: &C, ctx: &mut Context, - mut args: args::TxInitAccount, + args: args::TxInitAccount, ) -> Result<(), tx::Error> { - args.tx.chain_id = args - .tx - .chain_id - .or_else(|| Some(ctx.config.ledger.chain_id.clone())); - tx::submit_init_account::(client, &mut ctx.wallet, args).await + let (mut tx, addr, pk) = + tx::build_init_account(client, &mut ctx.wallet, args.clone()).await?; + submit_reveal_aux(client, ctx, &args.tx, addr, pk.clone(), &mut tx).await?; + signing::sign_tx(&mut ctx.wallet, &mut tx, &args.tx, &pk).await?; + tx::process_tx(client, &mut ctx.wallet, &args.tx, tx).await?; + Ok(()) } pub async fn submit_init_validator< @@ -101,14 +128,7 @@ pub async fn submit_init_validator< unsafe_dont_encrypt, tx_code_path: _, }: args::TxInitValidator, -) { - let tx_args = args::Tx { - chain_id: tx_args - .clone() - .chain_id - .or_else(|| Some(ctx.config.ledger.chain_id.clone())), - ..tx_args.clone() - }; +) -> Result<(), tx::Error> { let alias = tx_args .initialized_account_alias .as_ref() @@ -213,8 +233,6 @@ pub async fn submit_init_validator< let extra = tx.add_section(Section::ExtraData(Code::from_hash( validator_vp_code_hash, ))); - let extra_hash = - Hash(extra.hash(&mut Sha256::new()).finalize_reset().into()); let data = InitValidator { account_key, consensus_key: consensus_key.ref_to(), @@ -222,7 +240,7 @@ pub async fn submit_init_validator< dkg_key, commission_rate, max_commission_rate_change, - validator_vp_code_hash: extra_hash, + validator_vp_code_hash: extra.get_hash(), }; let data = data.try_to_vec().expect("Encoding tx data shouldn't fail"); tx.header.chain_id = tx_args.chain_id.clone().unwrap(); @@ -230,56 +248,33 @@ pub async fn submit_init_validator< tx.set_data(Data::new(data)); tx.set_code(Code::from_hash(tx_code_hash)); - let (mut ctx, result) = process_tx( + let (mut tx, addr, pk) = tx::prepare_tx( client, - ctx, + &mut ctx.wallet, &tx_args, tx, TxSigningKey::WalletAddress(source), #[cfg(not(feature = "mainnet"))] false, ) - .await - .expect("expected process_tx to work"); + .await?; + submit_reveal_aux(client, &mut ctx, &tx_args, addr, pk.clone(), &mut tx) + .await?; + signing::sign_tx(&mut ctx.wallet, &mut tx, &tx_args, &pk).await?; + let result = tx::process_tx(client, &mut ctx.wallet, &tx_args, tx) + .await? + .initialized_accounts(); if !tx_args.dry_run { let (validator_address_alias, validator_address) = match &result[..] { // There should be 1 account for the validator itself [validator_address] => { - let validator_address_alias = match tx_args - .initialized_account_alias - { - Some(alias) => alias, - None => { - print!("Choose an alias for the validator address: "); - io::stdout().flush().await.unwrap(); - let mut alias = String::new(); - io::stdin().read_line(&mut alias).await.unwrap(); - alias.trim().to_owned() - } - }; - let validator_address_alias = - if validator_address_alias.is_empty() { - println!( - "Empty alias given, using {} as the alias.", - validator_address.encode() - ); - validator_address.encode() - } else { - validator_address_alias - }; - if let Some(new_alias) = ctx.wallet.add_address( - validator_address_alias.clone(), - validator_address.clone(), - tx_args.wallet_alias_force, - ) { - println!( - "Added alias {} for address {}.", - new_alias, - validator_address.encode() - ); + if let Some(alias) = ctx.wallet.find_alias(validator_address) { + (alias.clone(), validator_address.clone()) + } else { + eprintln!("Expected one account to be created"); + safe_exit(1) } - (validator_address_alias, validator_address.clone()) } _ => { eprintln!("Expected one account to be created"); @@ -329,8 +324,9 @@ pub async fn submit_init_validator< pos_params.pipeline_len ); } else { - println!("Transaction dry run. No addresses have been saved.") + println!("Transaction dry run. No addresses have been saved."); } + Ok(()) } /// Shielded context file name @@ -446,36 +442,71 @@ impl masp::ShieldedUtils for CLIShieldedUtils { pub async fn submit_transfer( client: &HttpClient, mut ctx: Context, - mut args: args::TxTransfer, + args: args::TxTransfer, ) -> Result<(), tx::Error> { - args.tx.chain_id = args - .tx - .chain_id - .or_else(|| Some(ctx.config.ledger.chain_id.clone())); - tx::submit_transfer(client, &mut ctx.wallet, &mut ctx.shielded, args).await + for _ in 0..2 { + let arg = args.clone(); + let (mut tx, addr, pk, tx_epoch, _isf) = + tx::build_transfer(client, &mut ctx.wallet, &mut ctx.shielded, arg) + .await?; + submit_reveal_aux( + client, + &mut ctx, + &args.tx, + addr, + pk.clone(), + &mut tx, + ) + .await?; + signing::sign_tx(&mut ctx.wallet, &mut tx, &args.tx, &pk).await?; + let result = + tx::process_tx(client, &mut ctx.wallet, &args.tx, tx).await?; + // Query the epoch in which the transaction was probably submitted + let submission_epoch = rpc::query_and_print_epoch(client).await; + + match result { + ProcessTxResponse::Applied(resp) if + // If a transaction is shielded + tx_epoch.is_some() && + // And it is rejected by a VP + resp.code == 1.to_string() && + // And the its submission epoch doesn't match construction epoch + tx_epoch.unwrap() != submission_epoch => + { + // Then we probably straddled an epoch boundary. Let's retry... + eprintln!( + "MASP transaction rejected and this may be due to the \ + epoch changing. Attempting to resubmit transaction.", + ); + continue; + }, + // Otherwise either the transaction was successful or it will not + // benefit from resubmission + _ => break, + } + } + Ok(()) } pub async fn submit_ibc_transfer( client: &C, mut ctx: Context, - mut args: args::TxIbcTransfer, + args: args::TxIbcTransfer, ) -> Result<(), tx::Error> { - args.tx.chain_id = args - .tx - .chain_id - .or_else(|| Some(ctx.config.ledger.chain_id.clone())); - tx::submit_ibc_transfer::(client, &mut ctx.wallet, args).await + let (mut tx, addr, pk) = + tx::build_ibc_transfer(client, &mut ctx.wallet, args.clone()).await?; + submit_reveal_aux(client, &mut ctx, &args.tx, addr, pk.clone(), &mut tx) + .await?; + signing::sign_tx(&mut ctx.wallet, &mut tx, &args.tx, &pk).await?; + tx::process_tx(client, &mut ctx.wallet, &args.tx, tx).await?; + Ok(()) } pub async fn submit_init_proposal( client: &C, mut ctx: Context, - mut args: args::InitProposal, + args: args::InitProposal, ) -> Result<(), tx::Error> { - args.tx.chain_id = args - .tx - .chain_id - .or_else(|| Some(ctx.config.ledger.chain_id.clone())); let file = File::open(&args.proposal_data).expect("File must exist."); let proposal: Proposal = serde_json::from_reader(file).expect("JSON was not well-formatted"); @@ -540,9 +571,9 @@ pub async fn submit_init_proposal( if args.offline { let signer = ctx.get(&signer); + let key = find_pk(client, &mut ctx.wallet, &signer).await?; let signing_key = - find_keypair::(client, &mut ctx.wallet, &signer) - .await?; + signing::find_key_by_pk(&mut ctx.wallet, &args.tx, &key)?; let offline_proposal = OfflineProposal::new(proposal, signer, &signing_key); let proposal_filename = args @@ -566,13 +597,14 @@ pub async fn submit_init_proposal( Ok(()) } else { let signer = ctx.get(&signer); - let tx_data: Result = proposal.clone().try_into(); - let init_proposal_data = if let Ok(data) = tx_data { - data - } else { - eprintln!("Invalid data for init proposal transaction."); - safe_exit(1) - }; + let tx_data = proposal.clone().try_into(); + let (mut init_proposal_data, init_proposal_content, init_proposal_code) = + if let Ok(data) = tx_data { + data + } else { + eprintln!("Invalid data for init proposal transaction."); + safe_exit(1) + }; let balance = rpc::get_token_balance(client, &ctx.native_token, &proposal.author) @@ -592,7 +624,7 @@ pub async fn submit_init_proposal( safe_exit(1); } - if init_proposal_data.content.len() + if init_proposal_content.len() > governance_parameters.max_proposal_content_size as usize { eprintln!("Proposal content size too big.",); @@ -600,20 +632,36 @@ pub async fn submit_init_proposal( } let mut tx = Tx::new(TxType::Raw); - let data = init_proposal_data - .try_to_vec() - .expect("Encoding proposal data shouldn't fail"); let tx_code_hash = query_wasm_code_hash(client, args::TX_INIT_PROPOSAL) .await .unwrap(); tx.header.chain_id = ctx.config.ledger.chain_id.clone(); tx.header.expiration = args.tx.expiration; + // Put the content of this proposal into an extra section + { + let content_sec = tx.add_section(Section::ExtraData(Code::new( + init_proposal_content, + ))); + let content_sec_hash = content_sec.get_hash(); + init_proposal_data.content = content_sec_hash; + } + // Put any proposal code into an extra section + if let Some(init_proposal_code) = init_proposal_code { + let code_sec = tx + .add_section(Section::ExtraData(Code::new(init_proposal_code))); + let code_sec_hash = code_sec.get_hash(); + init_proposal_data.r#type = + ProposalType::Default(Some(code_sec_hash)); + } + let data = init_proposal_data + .try_to_vec() + .expect("Encoding proposal data shouldn't fail"); tx.set_data(Data::new(data)); tx.set_code(Code::from_hash(tx_code_hash)); - process_tx::( + let (mut tx, addr, pk) = tx::prepare_tx( client, - ctx, + &mut ctx.wallet, &args.tx, tx, TxSigningKey::WalletAddress(signer), @@ -621,6 +669,17 @@ pub async fn submit_init_proposal( false, ) .await?; + submit_reveal_aux( + client, + &mut ctx, + &args.tx, + addr, + pk.clone(), + &mut tx, + ) + .await?; + signing::sign_tx(&mut ctx.wallet, &mut tx, &args.tx, &pk).await?; + tx::process_tx(client, &mut ctx.wallet, &args.tx, tx).await?; Ok(()) } } @@ -628,12 +687,8 @@ pub async fn submit_init_proposal( pub async fn submit_vote_proposal( client: &C, mut ctx: Context, - mut args: args::VoteProposal, + args: args::VoteProposal, ) -> Result<(), tx::Error> { - args.tx.chain_id = args - .tx - .chain_id - .or_else(|| Some(ctx.config.ledger.chain_id.clone())); let signer = if let Some(addr) = &args.tx.signer { addr } else { @@ -719,9 +774,9 @@ pub async fn submit_vote_proposal( safe_exit(1) } + let key = find_pk(client, &mut ctx.wallet, signer).await?; let signing_key = - find_keypair::(client, &mut ctx.wallet, signer) - .await?; + signing::find_key_by_pk(&mut ctx.wallet, &args.tx, &key)?; let offline_vote = OfflineVote::new( &proposal, proposal_vote, @@ -869,9 +924,9 @@ pub async fn submit_vote_proposal( tx.set_data(Data::new(data)); tx.set_code(Code::from_hash(tx_code_hash)); - process_tx::( + let (mut tx, addr, pk) = tx::prepare_tx( client, - ctx, + &mut ctx.wallet, &args.tx, tx, TxSigningKey::WalletAddress(signer.clone()), @@ -879,6 +934,18 @@ pub async fn submit_vote_proposal( false, ) .await?; + submit_reveal_aux( + client, + &mut ctx, + &args.tx, + addr, + pk.clone(), + &mut tx, + ) + .await?; + signing::sign_tx(&mut ctx.wallet, &mut tx, &args.tx, &pk) + .await?; + tx::process_tx(client, &mut ctx.wallet, &args.tx, tx).await?; Ok(()) } None => { @@ -895,54 +962,15 @@ pub async fn submit_vote_proposal( pub async fn submit_reveal_pk( client: &C, ctx: &mut Context, - mut args: args::RevealPk, + args: args::RevealPk, ) -> Result<(), tx::Error> { - args.tx.chain_id = args - .tx - .chain_id - .or_else(|| Some(ctx.config.ledger.chain_id.clone())); - tx::submit_reveal_pk::(client, &mut ctx.wallet, args).await -} - -pub async fn reveal_pk_if_needed( - client: &C, - ctx: &mut Context, - public_key: &common::PublicKey, - args: &args::Tx, -) -> Result { - let args = args::Tx { - chain_id: args - .clone() - .chain_id - .or_else(|| Some(ctx.config.ledger.chain_id.clone())), - ..args.clone() - }; - tx::reveal_pk_if_needed::(client, &mut ctx.wallet, public_key, &args) - .await -} - -pub async fn has_revealed_pk( - client: &C, - addr: &Address, -) -> bool { - tx::has_revealed_pk(client, addr).await -} - -pub async fn submit_reveal_pk_aux( - client: &C, - ctx: &mut Context, - public_key: &common::PublicKey, - args: &args::Tx, -) -> Result { - let args = args::Tx { - chain_id: args - .clone() - .chain_id - .or_else(|| Some(ctx.config.ledger.chain_id.clone())), - ..args.clone() - }; - tx::submit_reveal_pk_aux::(client, &mut ctx.wallet, public_key, &args) - .await + let reveal_tx = + tx::build_reveal_pk(client, &mut ctx.wallet, args.clone()).await?; + if let Some((mut tx, _, pk)) = reveal_tx { + signing::sign_tx(&mut ctx.wallet, &mut tx, &args.tx, &pk).await?; + tx::process_tx(client, &mut ctx.wallet, &args.tx, tx).await?; + } + Ok(()) } /// Check if current epoch is in the last third of the voting period of the @@ -998,37 +1026,42 @@ async fn filter_delegations( pub async fn submit_bond( client: &C, ctx: &mut Context, - mut args: args::Bond, + args: args::Bond, ) -> Result<(), tx::Error> { - args.tx.chain_id = args - .tx - .chain_id - .or_else(|| Some(ctx.config.ledger.chain_id.clone())); - tx::submit_bond::(client, &mut ctx.wallet, args).await + let (mut tx, addr, pk) = + tx::build_bond::(client, &mut ctx.wallet, args.clone()).await?; + submit_reveal_aux(client, ctx, &args.tx, addr, pk.clone(), &mut tx).await?; + signing::sign_tx(&mut ctx.wallet, &mut tx, &args.tx, &pk).await?; + tx::process_tx(client, &mut ctx.wallet, &args.tx, tx).await?; + Ok(()) } pub async fn submit_unbond( client: &C, ctx: &mut Context, - mut args: args::Unbond, + args: args::Unbond, ) -> Result<(), tx::Error> { - args.tx.chain_id = args - .tx - .chain_id - .or_else(|| Some(ctx.config.ledger.chain_id.clone())); - tx::submit_unbond::(client, &mut ctx.wallet, args).await + let (mut tx, addr, pk, latest_withdrawal_pre) = + tx::build_unbond(client, &mut ctx.wallet, args.clone()).await?; + submit_reveal_aux(client, ctx, &args.tx, addr, pk.clone(), &mut tx).await?; + signing::sign_tx(&mut ctx.wallet, &mut tx, &args.tx, &pk).await?; + tx::process_tx(client, &mut ctx.wallet, &args.tx, tx).await?; + tx::query_unbonds(client, args.clone(), latest_withdrawal_pre).await?; + Ok(()) } pub async fn submit_withdraw( client: &C, mut ctx: Context, - mut args: args::Withdraw, + args: args::Withdraw, ) -> Result<(), tx::Error> { - args.tx.chain_id = args - .tx - .chain_id - .or_else(|| Some(ctx.config.ledger.chain_id.clone())); - tx::submit_withdraw::(client, &mut ctx.wallet, args).await + let (mut tx, addr, pk) = + tx::build_withdraw(client, &mut ctx.wallet, args.clone()).await?; + submit_reveal_aux(client, &mut ctx, &args.tx, addr, pk.clone(), &mut tx) + .await?; + signing::sign_tx(&mut ctx.wallet, &mut tx, &args.tx, &pk).await?; + tx::process_tx(client, &mut ctx.wallet, &args.tx, tx).await?; + Ok(()) } pub async fn submit_validator_commission_change< @@ -1036,18 +1069,17 @@ pub async fn submit_validator_commission_change< >( client: &C, mut ctx: Context, - mut args: args::TxCommissionRateChange, + args: args::CommissionRateChange, ) -> Result<(), tx::Error> { - args.tx.chain_id = args - .tx - .chain_id - .or_else(|| Some(ctx.config.ledger.chain_id.clone())); - tx::submit_validator_commission_change::( - client, - &mut ctx.wallet, - args, - ) - .await + let arg = args.clone(); + let (mut tx, addr, pk) = + tx::build_validator_commission_change(client, &mut ctx.wallet, arg) + .await?; + submit_reveal_aux(client, &mut ctx, &args.tx, addr, pk.clone(), &mut tx) + .await?; + signing::sign_tx(&mut ctx.wallet, &mut tx, &args.tx, &pk).await?; + tx::process_tx(client, &mut ctx.wallet, &args.tx, tx).await?; + Ok(()) } pub async fn submit_unjail_validator< @@ -1055,44 +1087,16 @@ pub async fn submit_unjail_validator< >( client: &C, mut ctx: Context, - mut args: args::TxUnjailValidator, + args: args::TxUnjailValidator, ) -> Result<(), tx::Error> { - args.tx.chain_id = args - .tx - .chain_id - .or_else(|| Some(ctx.config.ledger.chain_id.clone())); - tx::submit_unjail_validator::(client, &mut ctx.wallet, args).await -} - -/// Submit transaction and wait for result. Returns a list of addresses -/// initialized in the transaction if any. In dry run, this is always empty. -async fn process_tx( - client: &C, - mut ctx: Context, - args: &args::Tx, - tx: Tx, - default_signer: TxSigningKey, - #[cfg(not(feature = "mainnet"))] requires_pow: bool, -) -> Result<(Context, Vec
), tx::Error> { - let args = args::Tx { - chain_id: args - .clone() - .chain_id - .or_else(|| Some(tx.header.chain_id.clone())), - ..args.clone() - }; - let res: Vec
= tx::process_tx::( - client, - &mut ctx.wallet, - &args, - tx, - default_signer, - #[cfg(not(feature = "mainnet"))] - requires_pow, - ) - .await? - .initialized_accounts(); - Ok((ctx, res)) + let (mut tx, addr, pk) = + tx::build_unjail_validator(client, &mut ctx.wallet, args.clone()) + .await?; + submit_reveal_aux(client, &mut ctx, &args.tx, addr, pk.clone(), &mut tx) + .await?; + signing::sign_tx(&mut ctx.wallet, &mut tx, &args.tx, &pk).await?; + tx::process_tx(client, &mut ctx.wallet, &args.tx, tx).await?; + Ok(()) } /// Save accounts initialized from a tx into the wallet, if any. diff --git a/apps/src/lib/node/ledger/shell/finalize_block.rs b/apps/src/lib/node/ledger/shell/finalize_block.rs index 2c2db986c6..1c4a78871c 100644 --- a/apps/src/lib/node/ledger/shell/finalize_block.rs +++ b/apps/src/lib/node/ledger/shell/finalize_block.rs @@ -159,7 +159,7 @@ where continue; } - let tx = if let Ok(()) = tx.validate_header() { + let tx = if tx.validate_tx().is_ok() { tx } else { tracing::error!( @@ -311,7 +311,7 @@ where DecryptedTx::Decrypted { has_valid_pow: _ } => { if let Some(code_sec) = tx .get_section(tx.code_sechash()) - .and_then(Section::code_sec) + .and_then(|x| Section::code_sec(x.as_ref())) { stats.increment_tx_type( code_sec.code.hash().to_string(), @@ -927,6 +927,7 @@ mod test_finalize_block { use namada::proto::{Code, Data, Section, Signature}; use namada::types::dec::POS_DECIMAL_PRECISION; use namada::types::governance::ProposalVote; + use namada::types::hash::Hash; use namada::types::key::tm_consensus_key_raw_hash; use namada::types::storage::Epoch; use namada::types::time::DurationSecs; @@ -980,7 +981,7 @@ mod test_finalize_block { amount: MIN_FEE.into(), token: shell.wl_storage.storage.native_token.clone(), }, - &keypair, + keypair.ref_to(), Epoch(0), Default::default(), #[cfg(not(feature = "mainnet"))] @@ -992,10 +993,9 @@ mod test_finalize_block { format!("transaction data: {}", i).as_bytes().to_owned(), )); wrapper.add_section(Section::Signature(Signature::new( - &wrapper.header_hash(), + wrapper.sechashes(), &keypair, ))); - wrapper.encrypt(&Default::default()); if i > 1 { processed_txs.push(ProcessedTx { tx: wrapper.to_bytes(), @@ -1055,7 +1055,7 @@ mod test_finalize_block { amount: Default::default(), token: shell.wl_storage.storage.native_token.clone(), }, - &keypair, + keypair.ref_to(), Epoch(0), Default::default(), #[cfg(not(feature = "mainnet"))] @@ -1066,7 +1066,6 @@ mod test_finalize_block { outer_tx.set_data(Data::new( String::from("transaction data").as_bytes().to_owned(), )); - outer_tx.encrypt(&Default::default()); shell.enqueue_tx(outer_tx.clone()); outer_tx.update_header(TxType::Decrypted(DecryptedTx::Decrypted { @@ -1179,7 +1178,7 @@ mod test_finalize_block { amount: MIN_FEE.into(), token: shell.wl_storage.storage.native_token.clone(), }, - &keypair, + keypair.ref_to(), Epoch(0), Default::default(), #[cfg(not(feature = "mainnet"))] @@ -1192,7 +1191,6 @@ mod test_finalize_block { .as_bytes() .to_owned(), )); - outer_tx.encrypt(&Default::default()); shell.enqueue_tx(outer_tx.clone()); outer_tx.update_header(TxType::Decrypted(DecryptedTx::Decrypted { #[cfg(not(feature = "mainnet"))] @@ -1216,7 +1214,7 @@ mod test_finalize_block { amount: MIN_FEE.into(), token: shell.wl_storage.storage.native_token.clone(), }, - &keypair, + keypair.ref_to(), Epoch(0), Default::default(), #[cfg(not(feature = "mainnet"))] @@ -1230,10 +1228,9 @@ mod test_finalize_block { .to_owned(), )); wrapper_tx.add_section(Section::Signature(Signature::new( - &wrapper_tx.header_hash(), + wrapper_tx.sechashes(), &keypair, ))); - wrapper_tx.encrypt(&Default::default()); valid_txs.push(wrapper_tx.clone()); processed_txs.push(ProcessedTx { tx: wrapper_tx.to_bytes(), @@ -1316,7 +1313,7 @@ mod test_finalize_block { let proposal = InitProposalData { id: Some(proposal_id), - content: vec![], + content: Hash::default(), author: validator.clone(), voting_start_epoch: Epoch::default(), voting_end_epoch: Epoch::default().next(), @@ -1327,6 +1324,8 @@ mod test_finalize_block { storage_api::governance::init_proposal( &mut shell.wl_storage, proposal, + vec![], + None, ) .unwrap(); @@ -1760,7 +1759,7 @@ mod test_finalize_block { amount: 0.into(), token: shell.wl_storage.storage.native_token.clone(), }, - &keypair, + keypair.ref_to(), Epoch(0), Default::default(), #[cfg(not(feature = "mainnet"))] @@ -1772,7 +1771,6 @@ mod test_finalize_block { "Encrypted transaction data".as_bytes().to_owned(), )); let mut decrypted_tx = wrapper_tx.clone(); - wrapper_tx.encrypt(&Default::default()); decrypted_tx.update_header(TxType::Decrypted(DecryptedTx::Decrypted { #[cfg(not(feature = "mainnet"))] diff --git a/apps/src/lib/node/ledger/shell/mod.rs b/apps/src/lib/node/ledger/shell/mod.rs index 50c809a850..29ddeaf7aa 100644 --- a/apps/src/lib/node/ledger/shell/mod.rs +++ b/apps/src/lib/node/ledger/shell/mod.rs @@ -793,8 +793,8 @@ where } // Tx signature check - let tx_type = match tx.validate_header() { - Ok(()) => tx.header(), + let tx_type = match tx.validate_tx() { + Ok(_) => tx.header(), Err(msg) => { response.code = ErrorCodes::InvalidSig.into(); response.log = msg.to_string(); @@ -1263,7 +1263,7 @@ mod test_utils { amount: Default::default(), token: native_token, }, - &keypair, + keypair.ref_to(), Epoch(0), Default::default(), #[cfg(not(feature = "mainnet"))] @@ -1272,7 +1272,6 @@ mod test_utils { 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()); shell.wl_storage.storage.tx_queue.push(TxInQueue { tx: wrapper, @@ -1331,7 +1330,7 @@ mod test_mempool_validate { amount: 100.into(), token: shell.wl_storage.storage.native_token.clone(), }, - &keypair, + keypair.ref_to(), Epoch(0), Default::default(), #[cfg(not(feature = "mainnet"))] @@ -1341,7 +1340,6 @@ mod test_mempool_validate { unsigned_wrapper.set_code(Code::new("wasm_code".as_bytes().to_owned())); unsigned_wrapper .set_data(Data::new("transaction data".as_bytes().to_owned())); - unsigned_wrapper.encrypt(&Default::default()); let mut result = shell.mempool_validate( unsigned_wrapper.to_bytes().as_ref(), @@ -1368,7 +1366,7 @@ mod test_mempool_validate { amount: 100.into(), token: shell.wl_storage.storage.native_token.clone(), }, - &keypair, + keypair.ref_to(), Epoch(0), Default::default(), #[cfg(not(feature = "mainnet"))] @@ -1379,10 +1377,9 @@ mod test_mempool_validate { invalid_wrapper .set_data(Data::new("transaction data".as_bytes().to_owned())); invalid_wrapper.add_section(Section::Signature(Signature::new( - &invalid_wrapper.header_hash(), + invalid_wrapper.sechashes(), &keypair, ))); - invalid_wrapper.encrypt(&Default::default()); // we mount a malleability attack to try and remove the fee let mut new_wrapper = @@ -1434,7 +1431,7 @@ mod test_mempool_validate { .expect("This can't fail"), token: shell.wl_storage.storage.native_token.clone(), }, - &keypair, + keypair.ref_to(), Epoch(0), Default::default(), #[cfg(not(feature = "mainnet"))] @@ -1444,10 +1441,9 @@ mod test_mempool_validate { 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(), + wrapper.sechashes(), &keypair, ))); - wrapper.encrypt(&Default::default()); // Write wrapper hash to storage let wrapper_hash = wrapper.header_hash(); @@ -1539,7 +1535,11 @@ mod test_mempool_validate { tx.set_code(Code::new("wasm_code".as_bytes().to_owned())); tx.set_data(Data::new("transaction data".as_bytes().to_owned())); tx.add_section(Section::Signature(Signature::new( - &tx.header_hash(), + vec![ + tx.header_hash(), + tx.sections[0].get_hash(), + tx.sections[1].get_hash(), + ], &keypair, ))); @@ -1570,7 +1570,11 @@ mod test_mempool_validate { tx.set_code(Code::new("wasm_code".as_bytes().to_owned())); tx.set_data(Data::new("transaction data".as_bytes().to_owned())); tx.add_section(Section::Signature(Signature::new( - &tx.header_hash(), + vec![ + tx.header_hash(), + tx.sections[0].get_hash(), + tx.sections[1].get_hash(), + ], &keypair, ))); diff --git a/apps/src/lib/node/ledger/shell/prepare_proposal.rs b/apps/src/lib/node/ledger/shell/prepare_proposal.rs index 5d655cb059..5db83745dd 100644 --- a/apps/src/lib/node/ledger/shell/prepare_proposal.rs +++ b/apps/src/lib/node/ledger/shell/prepare_proposal.rs @@ -138,7 +138,7 @@ where if let (Some(block_time), Some(exp)) = (block_time.as_ref(), &tx.header.expiration) { if block_time > exp { return None } } - if tx.validate_header().is_ok() && tx.header().wrapper().is_some() && self.replay_protection_checks(&tx, tx_bytes.as_slice(), &mut temp_wl_storage).is_ok() { + if tx.validate_tx().is_ok() && tx.header().wrapper().is_some() && self.replay_protection_checks(&tx, tx_bytes.as_slice(), &mut temp_wl_storage).is_ok() { return Some(tx_bytes.clone()); } } @@ -283,6 +283,7 @@ mod test_prepare_proposal { use namada::ledger::replay_protection; use namada::proof_of_stake::Epoch; use namada::proto::{Code, Data, Header, Section, Signature}; + use namada::types::key::RefTo; use namada::types::transaction::{Fee, WrapperTx}; use super::*; @@ -318,7 +319,7 @@ mod test_prepare_proposal { amount: 0.into(), token: shell.wl_storage.storage.native_token.clone(), }, - &keypair, + keypair.ref_to(), Epoch(0), Default::default(), #[cfg(not(feature = "mainnet"))] @@ -327,7 +328,6 @@ mod test_prepare_proposal { 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 wrapper = wrapper.to_bytes(); #[allow(clippy::redundant_clone)] let req = RequestPrepareProposal { @@ -359,7 +359,7 @@ mod test_prepare_proposal { amount: Default::default(), token: shell.wl_storage.storage.native_token.clone(), }, - &keypair, + keypair.ref_to(), Epoch(0), Default::default(), #[cfg(not(feature = "mainnet"))] @@ -371,10 +371,9 @@ mod test_prepare_proposal { format!("transaction data: {}", i).as_bytes().to_owned(), )); tx.add_section(Section::Signature(Signature::new( - &tx.header_hash(), + tx.sechashes(), &keypair, ))); - tx.encrypt(&Default::default()); shell.enqueue_tx(tx.clone()); expected_wrapper.push(tx.clone()); @@ -428,7 +427,7 @@ mod test_prepare_proposal { amount: 0.into(), token: shell.wl_storage.storage.native_token.clone(), }, - &keypair, + keypair.ref_to(), Epoch(0), Default::default(), #[cfg(not(feature = "mainnet"))] @@ -438,10 +437,9 @@ mod test_prepare_proposal { 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(), + wrapper.sechashes(), &keypair, ))); - wrapper.encrypt(&Default::default()); // Write wrapper hash to storage let wrapper_unsigned_hash = wrapper.header_hash(); @@ -480,7 +478,7 @@ mod test_prepare_proposal { amount: 0.into(), token: shell.wl_storage.storage.native_token.clone(), }, - &keypair, + keypair.ref_to(), Epoch(0), Default::default(), #[cfg(not(feature = "mainnet"))] @@ -490,10 +488,9 @@ mod test_prepare_proposal { 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(), + wrapper.sechashes(), &keypair, ))); - wrapper.encrypt(&Default::default()); let req = RequestPrepareProposal { txs: vec![wrapper.to_bytes(); 2], @@ -521,7 +518,7 @@ mod test_prepare_proposal { amount: 0.into(), token: shell.wl_storage.storage.native_token.clone(), }, - &keypair, + keypair.ref_to(), Epoch(0), Default::default(), #[cfg(not(feature = "mainnet"))] @@ -531,10 +528,9 @@ mod test_prepare_proposal { 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(), + wrapper.sechashes(), &keypair, ))); - wrapper.encrypt(&Default::default()); let inner_unsigned_hash = wrapper.clone().update_header(TxType::Raw).header_hash(); @@ -574,7 +570,7 @@ mod test_prepare_proposal { amount: 0.into(), token: shell.wl_storage.storage.native_token.clone(), }, - &keypair, + keypair.ref_to(), Epoch(0), Default::default(), #[cfg(not(feature = "mainnet"))] @@ -586,10 +582,9 @@ mod test_prepare_proposal { let tx_data = Data::new("transaction data".as_bytes().to_owned()); wrapper.set_data(tx_data.clone()); wrapper.add_section(Section::Signature(Signature::new( - &wrapper.header_hash(), + wrapper.sechashes(), &keypair, ))); - wrapper.encrypt(&Default::default()); let mut new_wrapper = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( @@ -597,7 +592,7 @@ mod test_prepare_proposal { amount: 0.into(), token: shell.wl_storage.storage.native_token.clone(), }, - &keypair_2, + keypair_2.ref_to(), Epoch(0), Default::default(), #[cfg(not(feature = "mainnet"))] @@ -608,10 +603,9 @@ mod test_prepare_proposal { new_wrapper.set_code(tx_code); new_wrapper.set_data(tx_data); new_wrapper.add_section(Section::Signature(Signature::new( - &new_wrapper.header_hash(), + wrapper.sechashes(), &keypair, ))); - new_wrapper.encrypt(&Default::default()); let req = RequestPrepareProposal { txs: vec![wrapper.to_bytes(), new_wrapper.to_bytes()], @@ -639,7 +633,7 @@ mod test_prepare_proposal { amount: 0.into(), token: shell.wl_storage.storage.native_token.clone(), }, - &keypair, + keypair.ref_to(), Epoch(0), Default::default(), #[cfg(not(feature = "mainnet"))] @@ -651,10 +645,9 @@ mod test_prepare_proposal { wrapper_tx .set_data(Data::new("transaction data".as_bytes().to_owned())); wrapper_tx.add_section(Section::Signature(Signature::new( - &wrapper_tx.header_hash(), + wrapper_tx.sechashes(), &keypair, ))); - wrapper_tx.encrypt(&Default::default()); let time = DateTimeUtc::now(); let block_time = diff --git a/apps/src/lib/node/ledger/shell/process_proposal.rs b/apps/src/lib/node/ledger/shell/process_proposal.rs index 7a6d8b660d..ced79d2485 100644 --- a/apps/src/lib/node/ledger/shell/process_proposal.rs +++ b/apps/src/lib/node/ledger/shell/process_proposal.rs @@ -231,7 +231,7 @@ where |tx| { let tx_chain_id = tx.header.chain_id.clone(); let tx_expiration = tx.header.expiration; - if let Err(err) = tx.validate_header() { + if let Err(err) = tx.validate_tx() { // This occurs if the wrapper / protocol tx signature is // invalid return Err(TxResult { @@ -250,7 +250,7 @@ where // TODO: This should not be hardcoded let privkey = ::G2Affine::prime_subgroup_generator(); - if let Err(err) = tx.validate_header() { + if let Err(err) = tx.validate_tx() { return TxResult { code: ErrorCodes::InvalidSig.into(), info: err.to_string(), @@ -540,7 +540,7 @@ mod test_process_proposal { amount: Default::default(), token: shell.wl_storage.storage.native_token.clone(), }, - &keypair, + keypair.ref_to(), Epoch(0), Default::default(), #[cfg(not(feature = "mainnet"))] @@ -549,7 +549,6 @@ mod test_process_proposal { 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("transaction data".as_bytes().to_owned())); - outer_tx.encrypt(&Default::default()); let tx = outer_tx.to_bytes(); #[allow(clippy::redundant_clone)] let request = ProcessProposal { @@ -585,7 +584,7 @@ mod test_process_proposal { amount: Amount::from_uint(100, 0).expect("Test failed"), token: shell.wl_storage.storage.native_token.clone(), }, - &keypair, + keypair.ref_to(), Epoch(0), Default::default(), #[cfg(not(feature = "mainnet"))] @@ -595,10 +594,9 @@ mod test_process_proposal { outer_tx.set_code(Code::new("wasm_code".as_bytes().to_owned())); outer_tx.set_data(Data::new("transaction data".as_bytes().to_owned())); outer_tx.add_section(Section::Signature(Signature::new( - &outer_tx.header_hash(), + outer_tx.sechashes(), &keypair, ))); - outer_tx.encrypt(&Default::default()); 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 @@ -649,7 +647,7 @@ mod test_process_proposal { amount: Amount::from_uint(1, 0).expect("Test failed"), token: shell.wl_storage.storage.native_token.clone(), }, - &keypair, + keypair.ref_to(), Epoch(0), Default::default(), #[cfg(not(feature = "mainnet"))] @@ -659,10 +657,9 @@ mod test_process_proposal { outer_tx.set_code(Code::new("wasm_code".as_bytes().to_owned())); outer_tx.set_data(Data::new("transaction data".as_bytes().to_owned())); outer_tx.add_section(Section::Signature(Signature::new( - &outer_tx.header_hash(), + outer_tx.sechashes(), &keypair, ))); - outer_tx.encrypt(&Default::default()); let request = ProcessProposal { txs: vec![outer_tx.to_bytes()], @@ -716,7 +713,7 @@ mod test_process_proposal { amount: Amount::native_whole(1_000_100), token: shell.wl_storage.storage.native_token.clone(), }, - &keypair, + keypair.ref_to(), Epoch(0), Default::default(), #[cfg(not(feature = "mainnet"))] @@ -726,10 +723,9 @@ mod test_process_proposal { outer_tx.set_code(Code::new("wasm_code".as_bytes().to_owned())); outer_tx.set_data(Data::new("transaction data".as_bytes().to_owned())); outer_tx.add_section(Section::Signature(Signature::new( - &outer_tx.header_hash(), + outer_tx.sechashes(), &keypair, ))); - outer_tx.encrypt(&Default::default()); let request = ProcessProposal { txs: vec![outer_tx.to_bytes()], @@ -767,7 +763,7 @@ mod test_process_proposal { amount: i.into(), token: shell.wl_storage.storage.native_token.clone(), }, - &keypair, + keypair.ref_to(), Epoch(0), Default::default(), #[cfg(not(feature = "mainnet"))] @@ -778,7 +774,6 @@ mod test_process_proposal { outer_tx.set_data(Data::new( format!("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 { @@ -826,7 +821,7 @@ mod test_process_proposal { amount: Default::default(), token: shell.wl_storage.storage.native_token.clone(), }, - &keypair, + keypair.ref_to(), Epoch(0), Default::default(), #[cfg(not(feature = "mainnet"))] @@ -835,7 +830,6 @@ mod test_process_proposal { 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()); tx.header.tx_type = TxType::Decrypted(DecryptedTx::Undecryptable); @@ -874,7 +868,7 @@ mod test_process_proposal { amount: Default::default(), token: shell.wl_storage.storage.native_token.clone(), }, - &keypair, + keypair.ref_to(), Epoch(0), Default::default(), #[cfg(not(feature = "mainnet"))] @@ -885,7 +879,6 @@ mod test_process_proposal { tx.set_data(Data::new("transaction data".as_bytes().to_owned())); tx.set_code_sechash(Hash([0u8; 32])); tx.set_data_sechash(Hash([0u8; 32])); - tx.encrypt(&Default::default()); shell.enqueue_tx(tx.clone()); @@ -1025,7 +1018,7 @@ mod test_process_proposal { amount: Amount::zero(), token: shell.wl_storage.storage.native_token.clone(), }, - &keypair, + keypair.ref_to(), Epoch(0), Default::default(), #[cfg(not(feature = "mainnet"))] @@ -1035,10 +1028,9 @@ mod test_process_proposal { wrapper.set_data(Data::new("transaction data".as_bytes().to_owned())); wrapper.set_code(Code::new("wasm_code".as_bytes().to_owned())); wrapper.add_section(Section::Signature(Signature::new( - &wrapper.header_hash(), + wrapper.sechashes(), &keypair, ))); - wrapper.encrypt(&Default::default()); // Write wrapper hash to storage let wrapper_unsigned_hash = wrapper.header_hash(); @@ -1100,7 +1092,7 @@ mod test_process_proposal { amount: Amount::zero(), token: shell.wl_storage.storage.native_token.clone(), }, - &keypair, + keypair.ref_to(), Epoch(0), Default::default(), #[cfg(not(feature = "mainnet"))] @@ -1110,10 +1102,9 @@ mod test_process_proposal { 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(), + wrapper.sechashes(), &keypair, ))); - wrapper.encrypt(&Default::default()); // Run validation let request = ProcessProposal { @@ -1158,7 +1149,7 @@ mod test_process_proposal { amount: Amount::zero(), token: shell.wl_storage.storage.native_token.clone(), }, - &keypair, + keypair.ref_to(), Epoch(0), Default::default(), #[cfg(not(feature = "mainnet"))] @@ -1168,10 +1159,9 @@ mod test_process_proposal { 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(), + wrapper.sechashes(), &keypair, ))); - wrapper.encrypt(&Default::default()); let inner_unsigned_hash = wrapper.clone().update_header(TxType::Raw).header_hash(); @@ -1248,7 +1238,7 @@ mod test_process_proposal { amount: Amount::zero(), token: shell.wl_storage.storage.native_token.clone(), }, - &keypair, + keypair.ref_to(), Epoch(0), Default::default(), #[cfg(not(feature = "mainnet"))] @@ -1259,10 +1249,9 @@ mod test_process_proposal { wrapper.set_data(Data::new("transaction data".as_bytes().to_owned())); let mut new_wrapper = wrapper.clone(); wrapper.add_section(Section::Signature(Signature::new( - &wrapper.header_hash(), + wrapper.sechashes(), &keypair, ))); - wrapper.encrypt(&Default::default()); let inner_unsigned_hash = wrapper.clone().update_header(TxType::Raw).header_hash(); @@ -1271,17 +1260,16 @@ mod test_process_proposal { amount: Amount::zero(), token: shell.wl_storage.storage.native_token.clone(), }, - &keypair_2, + keypair_2.ref_to(), Epoch(0), Default::default(), #[cfg(not(feature = "mainnet"))] None, )))); new_wrapper.add_section(Section::Signature(Signature::new( - &new_wrapper.header_hash(), + new_wrapper.sechashes(), &keypair, ))); - new_wrapper.encrypt(&Default::default()); // Run validation let request = ProcessProposal { @@ -1319,7 +1307,7 @@ mod test_process_proposal { amount: Amount::zero(), token: shell.wl_storage.storage.native_token.clone(), }, - &keypair, + keypair.ref_to(), Epoch(0), Default::default(), #[cfg(not(feature = "mainnet"))] @@ -1331,17 +1319,20 @@ mod test_process_proposal { wrapper.set_data(Data::new("transaction data".as_bytes().to_owned())); let mut protocol_tx = wrapper.clone(); wrapper.add_section(Section::Signature(Signature::new( - &wrapper.header_hash(), + wrapper.sechashes(), &keypair, ))); - wrapper.encrypt(&Default::default()); protocol_tx.update_header(TxType::Protocol(Box::new(ProtocolTx { pk: keypair.ref_to(), tx: ProtocolTxType::EthereumStateUpdate, }))); protocol_tx.add_section(Section::Signature(Signature::new( - &protocol_tx.header_hash(), + vec![ + protocol_tx.header_hash(), + protocol_tx.sections[0].get_hash(), + protocol_tx.sections[1].get_hash(), + ], &keypair, ))); @@ -1383,7 +1374,7 @@ mod test_process_proposal { amount: token::Amount::zero(), token: shell.wl_storage.storage.native_token.clone(), }, - &keypair, + keypair.ref_to(), Epoch(0), Default::default(), #[cfg(not(feature = "mainnet"))] @@ -1394,13 +1385,12 @@ mod test_process_proposal { wrapper .set_data(Data::new("new transaction data".as_bytes().to_owned())); let mut decrypted = wrapper.clone(); - wrapper.encrypt(&Default::default()); decrypted.update_header(TxType::Decrypted(DecryptedTx::Decrypted { has_valid_pow: false, })); decrypted.add_section(Section::Signature(Signature::new( - &decrypted.header_hash(), + decrypted.sechashes(), &keypair, ))); let wrapper_in_queue = TxInQueue { @@ -1444,7 +1434,7 @@ mod test_process_proposal { amount: token::Amount::zero(), token: shell.wl_storage.storage.native_token.clone(), }, - &keypair, + keypair.ref_to(), Epoch(0), Default::default(), #[cfg(not(feature = "mainnet"))] @@ -1455,10 +1445,9 @@ mod test_process_proposal { 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(), + wrapper.sechashes(), &keypair, ))); - wrapper.encrypt(&Default::default()); // Run validation let request = ProcessProposal { @@ -1487,7 +1476,7 @@ mod test_process_proposal { amount: token::Amount::zero(), token: shell.wl_storage.storage.native_token.clone(), }, - &keypair, + keypair.ref_to(), Epoch(0), Default::default(), #[cfg(not(feature = "mainnet"))] @@ -1499,13 +1488,12 @@ mod test_process_proposal { wrapper .set_data(Data::new("new transaction data".as_bytes().to_owned())); let mut decrypted = wrapper.clone(); - wrapper.encrypt(&Default::default()); decrypted.update_header(TxType::Decrypted(DecryptedTx::Decrypted { has_valid_pow: false, })); decrypted.add_section(Section::Signature(Signature::new( - &decrypted.header_hash(), + decrypted.sechashes(), &keypair, ))); let wrapper_in_queue = TxInQueue { diff --git a/core/src/ledger/storage_api/governance.rs b/core/src/ledger/storage_api/governance.rs index b71f4a6e40..c2316bffa7 100644 --- a/core/src/ledger/storage_api/governance.rs +++ b/core/src/ledger/storage_api/governance.rs @@ -11,6 +11,8 @@ use crate::types::transaction::governance::{ pub fn init_proposal( storage: &mut S, data: InitProposalData, + content: Vec, + code: Option>, ) -> storage_api::Result<()> where S: StorageRead + StorageWrite, @@ -23,18 +25,21 @@ where }; let content_key = storage::get_content_key(proposal_id); - storage.write_bytes(&content_key, data.content)?; + storage.write_bytes(&content_key, content)?; let author_key = storage::get_author_key(proposal_id); storage.write(&author_key, data.author.clone())?; let proposal_type_key = storage::get_proposal_type_key(proposal_id); match data.r#type { - ProposalType::Default(Some(ref code)) => { + ProposalType::Default(Some(_)) => { // Remove wasm code and write it under a different subkey storage.write(&proposal_type_key, ProposalType::Default(None))?; let proposal_code_key = storage::get_proposal_code_key(proposal_id); - storage.write_bytes(&proposal_code_key, code)? + let proposal_code = code.clone().ok_or( + storage_api::Error::new_const("Missing proposal code"), + )?; + storage.write_bytes(&proposal_code_key, proposal_code)? } _ => storage.write(&proposal_type_key, data.r#type.clone())?, } @@ -49,8 +54,10 @@ where let grace_epoch_key = storage::get_grace_epoch_key(proposal_id); storage.write(&grace_epoch_key, data.grace_epoch)?; - if let ProposalType::Default(Some(proposal_code)) = data.r#type { + if let ProposalType::Default(Some(_)) = data.r#type { let proposal_code_key = storage::get_proposal_code_key(proposal_id); + let proposal_code = + code.ok_or(storage_api::Error::new_const("Missing proposal code"))?; storage.write_bytes(&proposal_code_key, proposal_code)?; } diff --git a/core/src/proto/types.rs b/core/src/proto/types.rs index 37fb5b7286..d8f020def6 100644 --- a/core/src/proto/types.rs +++ b/core/src/proto/types.rs @@ -1,3 +1,4 @@ +use std::borrow::Cow; use std::collections::HashSet; use std::convert::TryFrom; @@ -206,25 +207,27 @@ pub struct Signature { /// Additional random data salt: [u8; 8], /// The hash of the section being signed - target: crate::types::hash::Hash, - /// The signature over the above has - pub signature: common::Signature, - /// The public key to verrify the above siggnature + targets: Vec, + /// The public key to verify the below signature pub_key: common::PublicKey, + /// The signature over the above hashes + pub signature: Option, } impl Signature { /// Sign the given section hash with the given key and return a section pub fn new( - target: &crate::types::hash::Hash, + targets: Vec, sec_key: &common::SecretKey, ) -> Self { - Self { + let mut sec = Self { salt: DateTimeUtc::now().0.timestamp_millis().to_le_bytes(), - target: *target, - signature: common::SigScheme::sign(sec_key, target), + targets, pub_key: sec_key.ref_to(), - } + signature: None, + }; + sec.signature = Some(common::SigScheme::sign(sec_key, sec.get_hash())); + sec } /// Hash this signature section @@ -235,6 +238,29 @@ impl Signature { ); hasher } + + /// Get the hash of this section + pub fn get_hash(&self) -> crate::types::hash::Hash { + crate::types::hash::Hash( + self.hash(&mut Sha256::new()).finalize_reset().into(), + ) + } + + /// Verify that the signature contained in this section is valid + pub fn verify_signature(&self) -> std::result::Result<(), VerifySigError> { + let signature = + self.signature.as_ref().ok_or(VerifySigError::MissingData)?; + common::SigScheme::verify_signature_raw( + &self.pub_key, + &Self { + signature: None, + ..self.clone() + } + .get_hash() + .0, + signature, + ) + } } /// Represents a section obtained by encrypting another section @@ -550,6 +576,8 @@ pub enum Section { /// A section providing the auxiliary inputs used to construct a MASP /// transaction. Only send to wallet, never send to protocol. MaspBuilder(MaspBuilder), + /// Wrap a header with a section for the purposes of computing hashes + Header(Header), } impl Section { @@ -572,17 +600,14 @@ impl Section { hasher.update(tx.txid().as_ref()); hasher } + Self::Header(header) => header.hash(hasher), } } - /// Sign over the hash of this section and return a signature section that - /// can be added to the container transaction - pub fn sign(&self, sec_key: &common::SecretKey) -> Signature { - let mut hasher = Sha256::new(); - self.hash(&mut hasher); - Signature::new( - &crate::types::hash::Hash(hasher.finalize().into()), - sec_key, + /// Get the hash of this section + pub fn get_hash(&self) -> crate::types::hash::Hash { + crate::types::hash::Hash( + self.hash(&mut Sha256::new()).finalize_reset().into(), ) } @@ -803,9 +828,16 @@ impl Tx { /// Get the transaction header hash pub fn header_hash(&self) -> crate::types::hash::Hash { - crate::types::hash::Hash( - self.header.hash(&mut Sha256::new()).finalize_reset().into(), - ) + Section::Header(self.header.clone()).get_hash() + } + + /// Get hashes of all the sections in this transaction + pub fn sechashes(&self) -> Vec { + let mut hashes = vec![self.header_hash()]; + for sec in &self.sections { + hashes.push(sec.get_hash()); + } + hashes } /// Update the header whilst maintaining existing cross-references @@ -818,13 +850,13 @@ impl Tx { pub fn get_section( &self, hash: &crate::types::hash::Hash, - ) -> Option<&Section> { + ) -> Option> { + if self.header_hash() == *hash { + return Some(Cow::Owned(Section::Header(self.header.clone()))); + } for section in &self.sections { - let sechash = crate::types::hash::Hash( - section.hash(&mut Sha256::new()).finalize_reset().into(), - ); - if sechash == *hash { - return Some(section); + if section.get_hash() == *hash { + return Some(Cow::Borrowed(section)); } } None @@ -848,7 +880,11 @@ impl Tx { /// Get the code designated by the transaction code hash in the header pub fn code(&self) -> Option> { - match self.get_section(self.code_sechash()) { + match self + .get_section(self.code_sechash()) + .as_ref() + .map(Cow::as_ref) + { Some(Section::Code(section)) => section.code.id(), _ => None, } @@ -857,10 +893,7 @@ impl Tx { /// Add the given code to the transaction and set code hash in the header pub fn set_code(&mut self, code: Code) -> &mut Section { let sec = Section::Code(code); - let mut hasher = Sha256::new(); - sec.hash(&mut hasher); - let hash = crate::types::hash::Hash(hasher.finalize().into()); - self.set_code_sechash(hash); + self.set_code_sechash(sec.get_hash()); self.sections.push(sec); self.sections.last_mut().unwrap() } @@ -878,17 +911,18 @@ impl Tx { /// Add the given code to the transaction and set the hash in the header pub fn set_data(&mut self, data: Data) -> &mut Section { let sec = Section::Data(data); - let mut hasher = Sha256::new(); - sec.hash(&mut hasher); - let hash = crate::types::hash::Hash(hasher.finalize().into()); - self.set_data_sechash(hash); + self.set_data_sechash(sec.get_hash()); self.sections.push(sec); self.sections.last_mut().unwrap() } /// Get the data designated by the transaction data hash in the header pub fn data(&self) -> Option> { - match self.get_section(self.data_sechash()) { + match self + .get_section(self.data_sechash()) + .as_ref() + .map(Cow::as_ref) + { Some(Section::Data(data)) => Some(data.data.clone()), _ => None, } @@ -905,21 +939,32 @@ impl Tx { bytes } - /// Verify that the section with the given hash has been signed by the given - /// public key + /// Verify that the sections with the given hashes have been signed together + /// by the given public key. I.e. this function looks for one signature that + /// covers over the given slice of hashes. pub fn verify_signature( &self, pk: &common::PublicKey, - hash: &crate::types::hash::Hash, - ) -> std::result::Result<(), VerifySigError> { + hashes: &[crate::types::hash::Hash], + ) -> std::result::Result<&Signature, VerifySigError> { for section in &self.sections { if let Section::Signature(sig_sec) = section { - if sig_sec.pub_key == *pk && sig_sec.target == *hash { - return common::SigScheme::verify_signature_raw( - pk, - &hash.0, - &sig_sec.signature, - ); + // Check that the signer is matched and that the hashes being + // checked are a subset of those in this section + if sig_sec.pub_key == *pk + && hashes.iter().all(|x| { + sig_sec.targets.contains(x) || section.get_hash() == *x + }) + { + // Ensure that all the sections the signature signs over are + // present + for target in &sig_sec.targets { + if self.get_section(target).is_none() { + return Err(VerifySigError::MissingData); + } + } + // Finally verify that the signature itself is valid + return sig_sec.verify_signature().map(|_| sig_sec); } } } @@ -975,7 +1020,8 @@ impl Tx { // Iterate backwrds to sidestep the effects of deletion on indexing for i in (0..self.sections.len()).rev() { match &self.sections[i] { - Section::Signature(sig) if sig.target == header_hash => {} + Section::Signature(sig) + if sig.targets.contains(&header_hash) => {} // Add eligible section to the list of sections to encrypt _ => plaintexts.push(self.sections.remove(i)), } @@ -999,35 +1045,35 @@ impl Tx { /// the Tx and verify it is of the appropriate form. This means /// 1. The wrapper tx is indeed signed /// 2. The signature is valid - pub fn validate_header(&self) -> std::result::Result<(), TxError> { + pub fn validate_tx( + &self, + ) -> std::result::Result, TxError> { match &self.header.tx_type { // verify signature and extract signed data - TxType::Wrapper(wrapper) => { - self.verify_signature(&wrapper.pk, &self.header_hash()) - .map_err(|err| { - TxError::SigError(format!( - "WrapperTx signature verification failed: {}", - err - )) - })?; - Ok(()) - } + TxType::Wrapper(wrapper) => self + .verify_signature(&wrapper.pk, &self.sechashes()) + .map(Option::Some) + .map_err(|err| { + TxError::SigError(format!( + "WrapperTx signature verification failed: {}", + err + )) + }), // verify signature and extract signed data #[cfg(feature = "ferveo-tpke")] - TxType::Protocol(protocol) => { - self.verify_signature(&protocol.pk, &self.header_hash()) - .map_err(|err| { - TxError::SigError(format!( - "ProtocolTx signature verification failed: {}", - err - )) - })?; - Ok(()) - } + TxType::Protocol(protocol) => self + .verify_signature(&protocol.pk, &self.sechashes()) + .map(Option::Some) + .map_err(|err| { + TxError::SigError(format!( + "ProtocolTx signature verification failed: {}", + err + )) + }), // we extract the signed data, but don't check the signature - TxType::Decrypted(_) => Ok(()), + TxType::Decrypted(_) => Ok(None), // return as is - TxType::Raw => Ok(()), + TxType::Raw => Ok(None), } } diff --git a/core/src/types/transaction/governance.rs b/core/src/types/transaction/governance.rs index 3b1f183eaa..bfc17eeaef 100644 --- a/core/src/types/transaction/governance.rs +++ b/core/src/types/transaction/governance.rs @@ -7,6 +7,7 @@ use crate::types::address::Address; use crate::types::governance::{ self, Proposal, ProposalError, ProposalVote, VoteType, }; +use crate::types::hash::Hash; use crate::types::storage::Epoch; /// The type of a Proposal @@ -21,7 +22,7 @@ use crate::types::storage::Epoch; )] pub enum ProposalType { /// Default governance proposal with the optional wasm code - Default(Option>), + Default(Option), /// PGF council proposal PGFCouncil, /// ETH proposal @@ -54,7 +55,7 @@ impl PartialEq for ProposalType { } } -impl TryFrom for ProposalType { +impl TryFrom for (ProposalType, Option>) { type Error = ProposalError; fn try_from(value: governance::ProposalType) -> Result { @@ -62,15 +63,22 @@ impl TryFrom for ProposalType { governance::ProposalType::Default(path) => { if let Some(p) = path { match std::fs::read(p) { - Ok(code) => Ok(Self::Default(Some(code))), + Ok(code) => Ok(( + ProposalType::Default(Some(Hash::default())), + Some(code), + )), Err(_) => Err(Self::Error::InvalidProposalData), } } else { - Ok(Self::Default(None)) + Ok((ProposalType::Default(None), None)) } } - governance::ProposalType::PGFCouncil => Ok(Self::PGFCouncil), - governance::ProposalType::ETHBridge => Ok(Self::ETHBridge), + governance::ProposalType::PGFCouncil => { + Ok((ProposalType::PGFCouncil, None)) + } + governance::ProposalType::ETHBridge => { + Ok((ProposalType::ETHBridge, None)) + } } } } @@ -89,7 +97,7 @@ pub struct InitProposalData { /// The proposal id pub id: Option, /// The proposal content - pub content: Vec, + pub content: Hash, /// The proposal author address pub author: Address, /// The proposal type @@ -123,18 +131,23 @@ pub struct VoteProposalData { pub delegations: Vec
, } -impl TryFrom for InitProposalData { +impl TryFrom for (InitProposalData, Vec, Option>) { type Error = ProposalError; fn try_from(proposal: Proposal) -> Result { - Ok(InitProposalData { - id: proposal.id, - content: proposal.content.try_to_vec().unwrap(), - author: proposal.author, - r#type: proposal.r#type.try_into()?, - voting_start_epoch: proposal.voting_start_epoch, - voting_end_epoch: proposal.voting_end_epoch, - grace_epoch: proposal.grace_epoch, - }) + let (r#type, code) = proposal.r#type.try_into()?; + Ok(( + InitProposalData { + id: proposal.id, + content: Hash::default(), + author: proposal.author, + r#type, + voting_start_epoch: proposal.voting_start_epoch, + voting_end_epoch: proposal.voting_end_epoch, + grace_epoch: proposal.grace_epoch, + }, + proposal.content.try_to_vec().unwrap(), + code, + )) } } diff --git a/core/src/types/transaction/mod.rs b/core/src/types/transaction/mod.rs index d4104920ab..fc7c09de4e 100644 --- a/core/src/types/transaction/mod.rs +++ b/core/src/types/transaction/mod.rs @@ -254,12 +254,11 @@ mod test_process_tx { let code_sec = outer_tx .set_code(Code::new("wasm code".as_bytes().to_owned())) .clone(); - outer_tx.validate_header().expect("Test failed"); + outer_tx.validate_tx().expect("Test failed"); match outer_tx.header().tx_type { - TxType::Raw => assert_eq!( - Hash(code_sec.hash(&mut Sha256::new()).finalize_reset().into()), - outer_tx.header.code_hash, - ), + TxType::Raw => { + assert_eq!(code_sec.get_hash(), outer_tx.header.code_hash,) + } _ => panic!("Test failed: Expected Raw Tx"), } } @@ -277,27 +276,11 @@ mod test_process_tx { .set_data(Data::new("transaction data".as_bytes().to_owned())) .clone(); - tx.validate_header().expect("Test failed"); + tx.validate_tx().expect("Test failed"); match tx.header().tx_type { TxType::Raw => { - assert_eq!( - Hash( - code_sec - .hash(&mut Sha256::new()) - .finalize_reset() - .into() - ), - tx.header().code_hash, - ); - assert_eq!( - Hash( - data_sec - .hash(&mut Sha256::new()) - .finalize_reset() - .into() - ), - tx.header().data_hash, - ); + assert_eq!(code_sec.get_hash(), tx.header().code_hash,); + assert_eq!(data_sec.get_hash(), tx.header().data_hash,); } _ => panic!("Test failed: Expected Raw Tx"), } @@ -315,35 +298,15 @@ mod test_process_tx { .set_data(Data::new("transaction data".as_bytes().to_owned())) .clone(); tx.add_section(Section::Signature(Signature::new( - tx.code_sechash(), - &gen_keypair(), - ))); - tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), + vec![*tx.code_sechash(), *tx.data_sechash()], &gen_keypair(), ))); - tx.validate_header().expect("Test failed"); + tx.validate_tx().expect("Test failed"); match tx.header().tx_type { TxType::Raw => { - assert_eq!( - Hash( - code_sec - .hash(&mut Sha256::new()) - .finalize_reset() - .into() - ), - tx.header().code_hash, - ); - assert_eq!( - Hash( - data_sec - .hash(&mut Sha256::new()) - .finalize_reset() - .into() - ), - tx.header().data_hash, - ); + assert_eq!(code_sec.get_hash(), tx.header().code_hash,); + assert_eq!(data_sec.get_hash(), tx.header().data_hash,); } _ => panic!("Test failed: Expected Raw Tx"), } @@ -360,7 +323,7 @@ mod test_process_tx { amount: 10.into(), token: nam(), }, - &keypair, + keypair.ref_to(), Epoch(0), Default::default(), #[cfg(not(feature = "mainnet"))] @@ -369,12 +332,11 @@ mod test_process_tx { tx.set_code(Code::new("wasm code".as_bytes().to_owned())); tx.set_data(Data::new("transaction data".as_bytes().to_owned())); tx.add_section(Section::Signature(Signature::new( - &tx.header_hash(), + tx.sechashes(), &keypair, ))); - tx.encrypt(&Default::default()); - tx.validate_header().expect("Test failed"); + tx.validate_tx().expect("Test failed"); match tx.header().tx_type { TxType::Wrapper(_) => { tx.decrypt(::G2Affine::prime_subgroup_generator()) @@ -396,7 +358,7 @@ mod test_process_tx { amount: 10.into(), token: nam(), }, - &keypair, + keypair.ref_to(), Epoch(0), Default::default(), #[cfg(not(feature = "mainnet"))] @@ -404,8 +366,7 @@ mod test_process_tx { )))); 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()); - let result = tx.validate_header().expect_err("Test failed"); + let result = tx.validate_tx().expect_err("Test failed"); assert_matches!(result, TxError::SigError(_)); } } @@ -425,20 +386,14 @@ fn test_process_tx_decrypted_unsigned() { let data_sec = tx .set_data(Data::new("transaction data".as_bytes().to_owned())) .clone(); - tx.validate_header().expect("Test failed"); + tx.validate_tx().expect("Test failed"); match tx.header().tx_type { TxType::Decrypted(DecryptedTx::Decrypted { #[cfg(not(feature = "mainnet"))] has_valid_pow: _, }) => { - assert_eq!( - tx.header().code_hash, - Hash(code_sec.hash(&mut Sha256::new()).finalize_reset().into()), - ); - assert_eq!( - tx.header().data_hash, - Hash(data_sec.hash(&mut Sha256::new()).finalize_reset().into()), - ); + assert_eq!(tx.header().code_hash, code_sec.get_hash(),); + assert_eq!(tx.header().data_hash, data_sec.get_hash(),); } _ => panic!("Test failed"), } @@ -466,8 +421,9 @@ fn test_process_tx_decrypted_signed() { // Invalid signed data let ed_sig = ed25519::Signature::try_from_slice([0u8; 64].as_ref()).unwrap(); - let mut sig_sec = Signature::new(&decrypted.header_hash(), &gen_keypair()); - sig_sec.signature = common::Signature::try_from_sig(&ed_sig).unwrap(); + let mut sig_sec = + Signature::new(vec![decrypted.header_hash()], &gen_keypair()); + sig_sec.signature = Some(common::Signature::try_from_sig(&ed_sig).unwrap()); decrypted.add_section(Section::Signature(sig_sec)); // create the tx with signed decrypted data let code_sec = decrypted @@ -476,20 +432,14 @@ fn test_process_tx_decrypted_signed() { let data_sec = decrypted .set_data(Data::new("transaction data".as_bytes().to_owned())) .clone(); - decrypted.validate_header().expect("Test failed"); + decrypted.validate_tx().expect("Test failed"); match decrypted.header().tx_type { TxType::Decrypted(DecryptedTx::Decrypted { #[cfg(not(feature = "mainnet"))] has_valid_pow: _, }) => { - assert_eq!( - decrypted.header.code_hash, - Hash(code_sec.hash(&mut Sha256::new()).finalize_reset().into()), - ); - assert_eq!( - decrypted.header.data_hash, - Hash(data_sec.hash(&mut Sha256::new()).finalize_reset().into()), - ); + assert_eq!(decrypted.header.code_hash, code_sec.get_hash()); + assert_eq!(decrypted.header.data_hash, data_sec.get_hash()); } _ => panic!("Test failed"), } diff --git a/core/src/types/transaction/protocol.rs b/core/src/types/transaction/protocol.rs index 910f401a66..e1edcf2417 100644 --- a/core/src/types/transaction/protocol.rs +++ b/core/src/types/transaction/protocol.rs @@ -136,7 +136,11 @@ mod protocol_txs { .expect("Serializing request should not fail"), )); outer_tx.add_section(Section::Signature(Signature::new( - &outer_tx.header_hash(), + vec![ + outer_tx.header_hash(), + *outer_tx.code_sechash(), + *outer_tx.data_sechash(), + ], signing_key, ))); outer_tx diff --git a/core/src/types/transaction/wrapper.rs b/core/src/types/transaction/wrapper.rs index 08e8f7d2aa..1bbfceadc2 100644 --- a/core/src/types/transaction/wrapper.rs +++ b/core/src/types/transaction/wrapper.rs @@ -230,7 +230,7 @@ pub mod wrapper_tx { /// transaction pub fn new( fee: Fee, - keypair: &common::SecretKey, + pk: common::PublicKey, epoch: Epoch, gas_limit: GasLimit, #[cfg(not(feature = "mainnet"))] pow_solution: Option< @@ -239,7 +239,7 @@ pub mod wrapper_tx { ) -> WrapperTx { Self { fee, - pk: keypair.ref_to(), + pk, epoch, gas_limit, #[cfg(not(feature = "mainnet"))] @@ -369,7 +369,7 @@ pub mod wrapper_tx { amount: 10.into(), token: nam(), }, - &keypair, + keypair.ref_to(), Epoch(0), Default::default(), #[cfg(not(feature = "mainnet"))] @@ -378,12 +378,12 @@ pub mod wrapper_tx { wrapper.set_code(Code::new("wasm code".as_bytes().to_owned())); wrapper .set_data(Data::new("transaction data".as_bytes().to_owned())); + let mut encrypted_tx = wrapper.clone(); + encrypted_tx.encrypt(&Default::default()); wrapper.add_section(Section::Signature(Signature::new( - &wrapper.header_hash(), + vec![wrapper.header_hash(), wrapper.sections[0].get_hash()], &keypair, ))); - let mut encrypted_tx = wrapper.clone(); - encrypted_tx.encrypt(&Default::default()); assert!(encrypted_tx.validate_ciphertext()); let privkey = ::G2Affine::prime_subgroup_generator(); encrypted_tx.decrypt(privkey).expect("Test failed"); @@ -402,7 +402,7 @@ pub mod wrapper_tx { amount: 10.into(), token: nam(), }, - &keypair, + keypair.ref_to(), Epoch(0), Default::default(), #[cfg(not(feature = "mainnet"))] @@ -414,11 +414,11 @@ pub mod wrapper_tx { // give a incorrect commitment to the decrypted contents of the tx wrapper.set_code_sechash(Hash([0u8; 32])); wrapper.set_data_sechash(Hash([0u8; 32])); + wrapper.encrypt(&Default::default()); wrapper.add_section(Section::Signature(Signature::new( - &wrapper.header_hash(), + vec![wrapper.header_hash(), wrapper.sections[0].get_hash()], &keypair, ))); - wrapper.encrypt(&Default::default()); assert!(wrapper.validate_ciphertext()); let privkey = ::G2Affine::prime_subgroup_generator(); let err = wrapper.decrypt(privkey).expect_err("Test failed"); @@ -437,7 +437,7 @@ pub mod wrapper_tx { amount: Amount::from_uint(10, 0).expect("Test failed"), token: nam(), }, - &keypair, + keypair.ref_to(), Epoch(0), Default::default(), #[cfg(not(feature = "mainnet"))] @@ -447,7 +447,7 @@ pub mod wrapper_tx { tx.set_code(Code::new("wasm code".as_bytes().to_owned())); tx.set_data(Data::new("transaction data".as_bytes().to_owned())); tx.add_section(Section::Signature(Signature::new( - &tx.header_hash(), + tx.sechashes(), &keypair, ))); @@ -457,7 +457,6 @@ pub mod wrapper_tx { // We change the commitment appropriately let malicious = "Give me all the money".as_bytes().to_owned(); tx.set_data(Data::new(malicious.clone())); - tx.encrypt(&Default::default()); // we check ciphertext validity still passes assert!(tx.validate_ciphertext()); @@ -469,10 +468,10 @@ pub mod wrapper_tx { assert_eq!(tx.data(), Some(malicious)); // check that the signature is not valid - tx.verify_signature(&keypair.ref_to(), &tx.header_hash()) + tx.verify_signature(&keypair.ref_to(), &tx.sechashes()) .expect_err("Test failed"); // check that the try from method also fails - let err = tx.validate_header().expect_err("Test failed"); + let err = tx.validate_tx().expect_err("Test failed"); assert_matches!(err, TxError::SigError(_)); } } diff --git a/proof_of_stake/src/parameters.rs b/proof_of_stake/src/parameters.rs index 8501aff379..d6d7a59fa0 100644 --- a/proof_of_stake/src/parameters.rs +++ b/proof_of_stake/src/parameters.rs @@ -211,7 +211,7 @@ pub mod testing { (pipeline_len in Just(2)) (max_validator_slots in 3..num_max_validator_slots.unwrap_or(128), // `unbonding_len` > `pipeline_len` - unbonding_len in pipeline_len + 1..pipeline_len + 8, + unbonding_len in Just(4), pipeline_len in Just(pipeline_len), tm_votes_per_token in 1..10_001_i128) -> PosParams { diff --git a/proof_of_stake/src/tests/state_machine.rs b/proof_of_stake/src/tests/state_machine.rs index def808af43..1c3005483c 100644 --- a/proof_of_stake/src/tests/state_machine.rs +++ b/proof_of_stake/src/tests/state_machine.rs @@ -636,6 +636,7 @@ impl ConcretePosState { .checked_sub(amount) .unwrap_or_default() ); + println!("Check bond+unbond post-conds"); self.check_bond_and_unbond_post_conditions( submit_epoch, diff --git a/scripts/generator.sh b/scripts/generator.sh index 9c8a2de352..16ced33298 100755 --- a/scripts/generator.sh +++ b/scripts/generator.sh @@ -12,26 +12,6 @@ NAMADA_DIR="$(pwd)" export NAMADA_LEDGER_LOG_PATH="$(pwd)/vectors.json" export NAMADA_TX_LOG_PATH="$(pwd)/debugs.txt" -echo '{ - "content": { - "title": "TheTitle", - "authors": "test@test.com", - "discussions-to": "www.github.com/anoma/aip/1", - "created": "2022-03-10T08:54:37Z", - "license": "MIT", - "abstract": "Ut convallis eleifend orci vel venenatis. Duis vulputate metus in lacus sollicitudin vestibulum. Suspendisse vel velit ac est consectetur feugiat nec ac urna. Ut faucibus ex nec dictum fermentum. Morbi aliquet purus at sollicitudin ultrices. Quisque viverra varius cursus. Praesent sed mauris gravida, pharetra turpis non, gravida eros. Nullam sed ex justo. Ut at placerat ipsum, sit amet rhoncus libero. Sed blandit non purus non suscipit. Phasellus sed quam nec augue bibendum bibendum ut vitae urna. Sed odio diam, ornare nec sapien eget, congue viverra enim.", - "motivation": "Ut convallis eleifend orci vel venenatis. Duis vulputate metus in lacus sollicitudin vestibulum. Suspendisse vel velit ac est consectetur feugiat nec ac urna. Ut faucibus ex nec dictum fermentum. Morbi aliquet purus at sollicitudin ultrices.", - "details": "Ut convallis eleifend orci vel venenatis. Duis vulputate metus in lacus sollicitudin vestibulum. Suspendisse vel velit ac est consectetur feugiat nec ac urna. Ut faucibus ex nec dictum fermentum. Morbi aliquet purus at sollicitudin ultrices. Quisque viverra varius cursus. Praesent sed mauris gravida, pharetra turpis non, gravida eros.", - "requires": "2" - }, - "author": "atest1v4ehgw36xvcyyvejgvenxs34g3zygv3jxqunjd6rxyeyys3sxy6rwvfkx4qnj33hg9qnvse4lsfctw", - "voting_start_epoch": 12, - "voting_end_epoch": 24, - "grace_epoch": 30, - "type": { "Default": "'"$NAMADA_DIR"'/wasm_for_tests/tx_no_op.wasm" } -} -' > valid_proposal.json - if [ "$#" -ne 1 ]; then echo "Illegal number of parameters" elif [ "$1" = "server" ]; then @@ -56,11 +36,240 @@ elif [ "$1" = "client" ]; then echo > $NAMADA_TX_LOG_PATH echo $'[' > $NAMADA_LEDGER_LOG_PATH + + ALBERT_ADDRESS=$(cargo run --bin namadaw -- address find --alias albert | sed 's/^Found address Established: //') + + echo '{ + "author":"'$ALBERT_ADDRESS'", + "content":{ + "abstract":"Ut convallis eleifend orci vel venenatis. Duis vulputate metus in lacus sollicitudin vestibulum. Suspendisse vel velit ac est consectetur feugiat nec ac urna. Ut faucibus ex nec dictum fermentum. Morbi aliquet purus at sollicitudin ultrices. Quisque viverra varius cursus. Praesent sed mauris gravida, pharetra turpis non, gravida eros. Nullam sed ex justo. Ut at placerat ipsum, sit amet rhoncus libero. Sed blandit non purus non suscipit. Phasellus sed quam nec augue bibendum bibendum ut vitae urna. Sed odio diam, ornare nec sapien eget, congue viverra enim.", + "authors":"test@test.com", + "created":"2022-03-10T08:54:37Z", + "details":"Ut convallis eleifend orci vel venenatis. Duis vulputate metus in lacus sollicitudin vestibulum. Suspendisse vel velit ac est consectetur feugiat nec ac urna. Ut faucibus ex nec dictum fermentum. Morbi aliquet purus at sollicitudin ultrices. Quisque viverra varius cursus. Praesent sed mauris gravida, pharetra turpis non, gravida eros.", + "discussions-to":"www.github.com/anoma/aip/1", + "license":"MIT", + "motivation":"Ut convallis eleifend orci vel venenatis. Duis vulputate metus in lacus sollicitudin vestibulum. Suspendisse vel velit ac est consectetur feugiat nec ac urna. Ut faucibus ex nec dictum fermentum. Morbi aliquet purus at sollicitudin ultrices.", + "requires":"2", + "title":"TheTitle" + }, + "grace_epoch":30, + "type":{ + "Default":"'$NAMADA_DIR'/wasm_for_tests/tx_proposal_code.wasm" + }, + "voting_end_epoch":24, + "voting_start_epoch":12 +} +' > proposal_submission_valid_proposal.json + + echo '{ + "content": { + "abstract": "Ut convallis eleifend orci vel venenatis. Duis vulputate metus in lacus sollicitudin vestibulum. Suspendisse vel velit ac est consectetur feugiat nec ac urna. Ut faucibus ex nec dictum fermentum. Morbi aliquet purus at sollicitudin ultrices. Quisque viverra varius cursus. Praesent sed mauris gravida, pharetra turpis non, gravida eros. Nullam sed ex justo. Ut at placerat ipsum, sit amet rhoncus libero. Sed blandit non purus non suscipit. Phasellus sed quam nec augue bibendum bibendum ut vitae urna. Sed odio diam, ornare nec sapien eget, congue viverra enim.", + "authors": "test@test.com", + "created": "2022-03-10T08:54:37Z", + "details": "Ut convallis eleifend orci vel venenatis. Duis vulputate metus in lacus sollicitudin vestibulum. Suspendisse vel velit ac est consectetur feugiat nec ac urna. Ut faucibus ex nec dictum fermentum. Morbi aliquet purus at sollicitudin ultrices. Quisque viverra varius cursus. Praesent sed mauris gravida, pharetra turpis non, gravida eros.", + "discussions-to": "www.github.com/anoma/aip/1", + "license": "MIT", + "motivation": "Ut convallis eleifend orci vel venenatis. Duis vulputate metus in lacus sollicitudin vestibulum. Suspendisse vel velit ac est consectetur feugiat nec ac urna. Ut faucibus ex nec dictum fermentum. Morbi aliquet purus at sollicitudin ultrices.", + "requires": "2", + "title": "TheTitle" + }, + "author": "'$ALBERT_ADDRESS'", + "tally_epoch": 18, + "signature": { + "Ed25519": { + "R_bytes": [ + 113, + 196, + 231, + 134, + 101, + 191, + 75, + 17, + 245, + 19, + 50, + 231, + 183, + 80, + 162, + 38, + 108, + 72, + 72, + 2, + 116, + 112, + 121, + 33, + 197, + 67, + 64, + 116, + 21, + 250, + 196, + 121 + ], + "s_bytes": [ + 87, + 163, + 134, + 87, + 42, + 156, + 121, + 211, + 189, + 19, + 255, + 5, + 23, + 178, + 143, + 39, + 118, + 249, + 37, + 53, + 121, + 136, + 59, + 103, + 190, + 91, + 121, + 95, + 46, + 54, + 168, + 9 + ] + } + }, + "address": "'$ALBERT_ADDRESS'" +} +' > proposal_offline_proposal + + echo '{ + "author":"'$ALBERT_ADDRESS'", + "content":{ + "abstract":"Ut convallis eleifend orci vel venenatis. Duis vulputate metus in lacus sollicitudin vestibulum. Suspendisse vel velit ac est consectetur feugiat nec ac urna. Ut faucibus ex nec dictum fermentum. Morbi aliquet purus at sollicitudin ultrices. Quisque viverra varius cursus. Praesent sed mauris gravida, pharetra turpis non, gravida eros. Nullam sed ex justo. Ut at placerat ipsum, sit amet rhoncus libero. Sed blandit non purus non suscipit. Phasellus sed quam nec augue bibendum bibendum ut vitae urna. Sed odio diam, ornare nec sapien eget, congue viverra enim.", + "authors":"test@test.com", + "created":"2022-03-10T08:54:37Z", + "details":"Ut convallis eleifend orci vel venenatis. Duis vulputate metus in lacus sollicitudin vestibulum. Suspendisse vel velit ac est consectetur feugiat nec ac urna. Ut faucibus ex nec dictum fermentum. Morbi aliquet purus at sollicitudin ultrices. Quisque viverra varius cursus. Praesent sed mauris gravida, pharetra turpis non, gravida eros.", + "discussions-to":"www.github.com/anoma/aip/1", + "license":"MIT", + "motivation":"Ut convallis eleifend orci vel venenatis. Duis vulputate metus in lacus sollicitudin vestibulum. Suspendisse vel velit ac est consectetur feugiat nec ac urna. Ut faucibus ex nec dictum fermentum. Morbi aliquet purus at sollicitudin ultrices.", + "requires":"2", + "title":"TheTitle" + }, + "grace_epoch":18, + "type":{ + "Default":null + }, + "voting_end_epoch":9, + "voting_start_epoch":3 +}' > proposal_offline_valid_proposal.json + + echo '{ + "author":"'$ALBERT_ADDRESS'", + "content":{ + "abstract":"Ut convallis eleifend orci vel venenatis. Duis vulputate metus in lacus sollicitudin vestibulum. Suspendisse vel velit ac est consectetur feugiat nec ac urna. Ut faucibus ex nec dictum fermentum. Morbi aliquet purus at sollicitudin ultrices. Quisque viverra varius cursus. Praesent sed mauris gravida, pharetra turpis non, gravida eros. Nullam sed ex justo. Ut at placerat ipsum, sit amet rhoncus libero. Sed blandit non purus non suscipit. Phasellus sed quam nec augue bibendum bibendum ut vitae urna. Sed odio diam, ornare nec sapien eget, congue viverra enim.", + "authors":"test@test.com", + "created":"2022-03-10T08:54:37Z", + "details":"Ut convallis eleifend orci vel venenatis. Duis vulputate metus in lacus sollicitudin vestibulum. Suspendisse vel velit ac est consectetur feugiat nec ac urna. Ut faucibus ex nec dictum fermentum. Morbi aliquet purus at sollicitudin ultrices. Quisque viverra varius cursus. Praesent sed mauris gravida, pharetra turpis non, gravida eros.", + "discussions-to":"www.github.com/anoma/aip/1", + "license":"MIT", + "motivation":"Ut convallis eleifend orci vel venenatis. Duis vulputate metus in lacus sollicitudin vestibulum. Suspendisse vel velit ac est consectetur feugiat nec ac urna. Ut faucibus ex nec dictum fermentum. Morbi aliquet purus at sollicitudin ultrices.", + "requires":"2", + "title":"TheTitle" + }, + "grace_epoch":30, + "type":"ETHBridge", + "voting_end_epoch":24, + "voting_start_epoch":12 +}' > eth_governance_proposal_valid_proposal.json + + echo '{ + "author":"'$ALBERT_ADDRESS'", + "content":{ + "abstract":"Ut convallis eleifend orci vel venenatis. Duis vulputate metus in lacus sollicitudin vestibulum. Suspendisse vel velit ac est consectetur feugiat nec ac urna. Ut faucibus ex nec dictum fermentum. Morbi aliquet purus at sollicitudin ultrices. Quisque viverra varius cursus. Praesent sed mauris gravida, pharetra turpis non, gravida eros. Nullam sed ex justo. Ut at placerat ipsum, sit amet rhoncus libero. Sed blandit non purus non suscipit. Phasellus sed quam nec augue bibendum bibendum ut vitae urna. Sed odio diam, ornare nec sapien eget, congue viverra enim.", + "authors":"test@test.com", + "created":"2022-03-10T08:54:37Z", + "details":"Ut convallis eleifend orci vel venenatis. Duis vulputate metus in lacus sollicitudin vestibulum. Suspendisse vel velit ac est consectetur feugiat nec ac urna. Ut faucibus ex nec dictum fermentum. Morbi aliquet purus at sollicitudin ultrices. Quisque viverra varius cursus. Praesent sed mauris gravida, pharetra turpis non, gravida eros.", + "discussions-to":"www.github.com/anoma/aip/1", + "license":"MIT", + "motivation":"Ut convallis eleifend orci vel venenatis. Duis vulputate metus in lacus sollicitudin vestibulum. Suspendisse vel velit ac est consectetur feugiat nec ac urna. Ut faucibus ex nec dictum fermentum. Morbi aliquet purus at sollicitudin ultrices.", + "requires":"2", + "title":"TheTitle" + }, + "grace_epoch":30, + "type":"PGFCouncil", + "voting_end_epoch":24, + "voting_start_epoch":12 +}' > pgf_governance_proposal_valid_proposal.json + + # proposal_submission + + cargo run --bin namadac --features std -- --mode full bond --validator validator-0 --source Bertha --amount 900 --gas-amount 0 --gas-limit 0 --gas-token NAM --node 127.0.0.1:27657 + + cargo run --bin namadac --features std -- --mode full change-commission-rate --validator Bertha --commission-rate 0.02 --gas-amount 0 --gas-limit 0 --gas-token NAM --force --node 127.0.0.1:27657 + + PROPOSAL_ID_0=$(cargo run --bin namadac --features std -- --mode full init-proposal --force --data-path proposal_submission_valid_proposal.json --node 127.0.0.1:27657 | grep -o -P '(?<=/proposal/).*(?=/author)') + + cargo run --bin namadac --features std -- --base-dir $NAMADA_BASE_DIR/setup/validator-0/.namada --mode validator vote-proposal --force --proposal-id $PROPOSAL_ID_0 --vote yay --signer validator-0 --node 127.0.0.1:27657 + + cargo run --bin namadac --features std -- --mode full vote-proposal --force --proposal-id $PROPOSAL_ID_0 --vote nay --signer Bertha --node 127.0.0.1:27657 + + cargo run --bin namadac --features std -- --mode full vote-proposal --force --proposal-id $PROPOSAL_ID_0 --vote yay --signer Albert --node 127.0.0.1:27657 + + # proposal_offline + + cargo run --bin namadac --features std -- --mode full bond --validator validator-0 --source Albert --amount 900 --gas-amount 0 --gas-limit 0 --gas-token NAM --node 127.0.0.1:27657 + + cargo run --bin namadac --features std -- --mode full change-commission-rate --validator Albert --commission-rate 0.05 --gas-amount 0 --gas-limit 0 --gas-token NAM --force --node 127.0.0.1:27657 + + cargo run --bin namadac --features std -- --mode full init-proposal --force --data-path proposal_offline_valid_proposal.json --offline --node 127.0.0.1:27657 + + cargo run --bin namadac --features std -- --mode full vote-proposal --data-path proposal_offline_proposal --vote yay --signer Albert --offline --node 127.0.0.1:27657 + + # eth_governance_proposal + + cargo run --bin namadac --features std -- --mode full bond --validator validator-0 --source Bertha --amount 900 --gas-amount 0 --gas-limit 0 --gas-token NAM --ledger-address 127.0.0.1:27657 + + cargo run --bin namadac --features std -- --mode full change-commission-rate --validator Bertha --commission-rate 0.07 --gas-amount 0 --gas-limit 0 --gas-token NAM --force --node 127.0.0.1:27657 + + PROPOSAL_ID_0=$(cargo run --bin namadac --features std -- --mode full init-proposal --force --data-path eth_governance_proposal_valid_proposal.json --ledger-address 127.0.0.1:27657 | grep -o -P '(?<=/proposal/).*(?=/author)') + + cargo run --bin namadac --features std -- --mode full vote-proposal --force --proposal-id 0 --vote yay --eth '011586062748ba53bc53155e817ec1ea708de75878dcb9a5713bf6986d87fe14e7 fd34672ab5' --signer Bertha --ledger-address 127.0.0.1:27657 + + cargo run --bin namadac --features std -- --base-dir $NAMADA_BASE_DIR/setup/validator-0/.namada --mode validator vote-proposal --force --proposal-id $PROPOSAL_ID_0 --vote yay --eth '011586062748ba53bc53155e817ec1ea708de75878dcb9a5713bf6986d87fe14e7 fd34672ab5' --signer validator-0 --ledger-address 127.0.0.1:27657 + + # pgf_governance_proposal + + cargo run --bin namadac --features std -- --mode full bond --validator validator-0 --source Bertha --amount 900 --gas-amount 0 --gas-limit 0 --gas-token NAM --ledger-address 127.0.0.1:27657 + + cargo run --bin namadac --features std -- --mode full change-commission-rate --validator Bertha --commission-rate 0.09 --gas-amount 0 --gas-limit 0 --gas-token NAM --force --node 127.0.0.1:27657 + + PROPOSAL_ID_0=$(cargo run --bin namadac --features std -- --mode full init-proposal --force --data-path pgf_governance_proposal_valid_proposal.json --ledger-address 127.0.0.1:27657 | grep -o -P '(?<=/proposal/).*(?=/author)') + + PROPOSAL_ID_1=$(cargo run --bin namadac --features std -- --mode full init-proposal --force --data-path pgf_governance_proposal_valid_proposal.json --ledger-address 127.0.0.1:27657 | grep -o -P '(?<=/proposal/).*(?=/author)') + + cargo run --bin namadac --features std -- --base-dir $NAMADA_BASE_DIR/setup/validator-0/.namada --mode validator vote-proposal --force --proposal-id $PROPOSAL_ID_0 --vote yay --pgf "$ALBERT_ADDRESS 1000" --signer validator-0 --ledger-address 127.0.0.1:27657 + + cargo run --bin namadac --features std -- --mode full vote-proposal --force --proposal-id $PROPOSAL_ID_0 --vote yay --pgf "$ALBERT_ADDRESS 900" --signer Bertha --ledger-address 127.0.0.1:27657 + + cargo run --bin namadac --features std -- --mode full vote-proposal --force --proposal-id $PROPOSAL_ID_1 --vote yay --pgf "$ALBERT_ADDRESS 900" --signer Bertha --ledger-address 127.0.0.1:27657 + + # non-proposal tests cargo run --bin namadac --features std -- transfer --source bertha --target christel --token btc --amount 23 --force --signing-key bertha-key --ledger-address 127.0.0.1:27657 cargo run --bin namadac --features std -- bond --validator bertha --amount 25 --signing-key bertha-key --force --ledger-address 127.0.0.1:27657 + cargo run --bin namadac --features std -- --mode full change-commission-rate --validator Bertha --commission-rate 0.11 --gas-amount 0 --gas-limit 0 --gas-token NAM --force --node 127.0.0.1:27657 + cargo run --bin namadac --features std -- reveal-pk --public-key albert-key --force --ledger-address 127.0.0.1:27657 cargo run --bin namadac --features std -- update --code-path vp_user.wasm --address bertha --signing-key bertha-key --force --ledger-address 127.0.0.1:27657 @@ -77,8 +286,6 @@ elif [ "$1" = "client" ]; then cargo run --bin namadac --features std -- ibc-transfer --source bertha --receiver christel --token btc --amount 24 --channel-id channel-141 --signing-key bertha-key --force --ledger-address 127.0.0.1:27657 - #cargo run --bin namadac -- init-proposal --data-path valid_proposal.json --epoch 2 --force --signing-key bertha-key --ledger-address 127.0.0.1:27657 - cargo run --bin namadaw -- masp add --alias a_spending_key --value xsktest1qqqqqqqqqqqqqq9v0sls5r5de7njx8ehu49pqgmqr9ygelg87l5x8y4s9r0pjlvu69au6gn3su5ewneas486hdccyayx32hxvt64p3d0hfuprpgcgv2q9gdx3jvxrn02f0nnp3jtdd6f5vwscfuyum083cvfv4jun75ak5sdgrm2pthzj3sflxc0jx0edrakx3vdcngrfjmru8ywkguru8mxss2uuqxdlglaz6undx5h8w7g70t2es850g48xzdkqay5qs0yw06rtxcvedhsv --unsafe-dont-encrypt cargo run --bin namadaw -- masp add --alias b_spending_key --value xsktest1qqqqqqqqqqqqqqpagte43rsza46v55dlz8cffahv0fnr6eqacvnrkyuf9lmndgal7c2k4r7f7zu2yr5rjwr374unjjeuzrh6mquzy6grfdcnnu5clzaq2llqhr70a8yyx0p62aajqvrqjxrht3myuyypsvm725uyt5vm0fqzrzuuedtf6fala4r4nnazm9y9hq5yu6pq24arjskmpv4mdgfn3spffxxv8ugvym36kmnj45jcvvmm227vqjm5fq8882yhjsq97p7xrwqqd82s0 --unsafe-dont-encrypt @@ -101,9 +308,17 @@ elif [ "$1" = "client" ]; then cargo run --bin namadac --features std -- --mode full transfer --source b_spending_key --target bb_payment_address --token btc --amount 6 --force --ledger-address 127.0.0.1:27657 + rm proposal_submission_valid_proposal.json + + rm proposal_offline_proposal + + rm proposal_offline_valid_proposal.json + + rm eth_governance_proposal_valid_proposal.json + + rm pgf_governance_proposal_valid_proposal.json + perl -0777 -i.original -pe 's/,\s*$//igs' $NAMADA_LEDGER_LOG_PATH echo $'\n]' >> $NAMADA_LEDGER_LOG_PATH fi - -rm valid_proposal.json diff --git a/scripts/get_tendermint.sh b/scripts/get_tendermint.sh index 024299ec93..5842d2c646 100755 --- a/scripts/get_tendermint.sh +++ b/scripts/get_tendermint.sh @@ -5,10 +5,10 @@ set -Eo pipefail # an examplary download-url # https://github.com/tendermint/tendermint/releases/download/v0.34.13/tendermint_0.34.13_linux_amd64.tar.gz # https://github.com/heliaxdev/tendermint/releases/download/v0.1.1-abcipp/tendermint_0.1.0-abcipp_darwin_amd64.tar.gz -TM_MAJORMINOR="0.1" -TM_PATCH="4" -TM_SUFFIX="-abciplus" -TM_REPO="https://github.com/heliaxdev/tendermint" +TM_MAJORMINOR="0.37" +TM_PATCH="1" +TM_SUFFIX="" +TM_REPO="https://github.com/cometbft/cometbft" TM_VERSION="${TM_MAJORMINOR}.${TM_PATCH}${TM_SUFFIX}" @@ -22,14 +22,14 @@ error_exit() } # check for existence -TM_EXECUTABLE=$(which tendermint) +TM_EXECUTABLE=$(which cometbft) if [ -x "$TM_EXECUTABLE" ]; then TM_EXISTS_VER=$(${TM_EXECUTABLE} version) fi if [[ $TM_EXISTS_VER == "${TM_MAJORMINOR}" ]]; then echo "tendermint already exists in your current PATH with a sufficient version = $TM_EXISTS_VER" - echo "tendermint is located at = $(which tendermint)" + echo "tendermint is located at = $(which cometbft)" exit fi @@ -40,10 +40,10 @@ if [[ $MACHINE = "aarch64" ]] || [[ $MACHINE = "arm64" ]]; then ARCH="arm64" fi -RELEASE_URL="${TM_REPO}/releases/download/v${TM_VERSION}/tendermint_${TM_VERSION}_$(echo "${SYSTEM}" | tr '[:upper:]' '[:lower:]')_${ARCH}.tar.gz" +RELEASE_URL="${TM_REPO}/releases/download/v${TM_VERSION}/cometbft_${TM_VERSION}_$(echo "${SYSTEM}" | tr '[:upper:]' '[:lower:]')_${ARCH}.tar.gz" echo "$RELEASE_URL" -curl -LsSfo "$TMP_PATH"/tendermint.tar.gz "$RELEASE_URL" || error_exit "tendermint release download failed" +curl -LsSfo "$TMP_PATH"/cometbft.tar.gz "$RELEASE_URL" || error_exit "tendermint release download failed" cd $TARGET_PATH -sudo tar -xvzf $TMP_PATH/tendermint.tar.gz tendermint || error_exit "tendermint release extraction failed" +sudo tar -xvzf $TMP_PATH/cometbft.tar.gz tendermint || error_exit "tendermint release extraction failed" diff --git a/shared/src/ledger/args.rs b/shared/src/ledger/args.rs index 0ad90faf0a..7ad213f327 100644 --- a/shared/src/ledger/args.rs +++ b/shared/src/ledger/args.rs @@ -332,7 +332,7 @@ pub struct QueryBondedStake { #[derive(Clone, Debug)] /// Commission rate change args -pub struct TxCommissionRateChange { +pub struct CommissionRateChange { /// Common tx arguments pub tx: Tx, /// Validator address (should be self) @@ -436,6 +436,8 @@ pub struct Tx { pub signer: Option, /// Path to the TX WASM code file to reveal PK pub tx_reveal_code_path: PathBuf, + /// Sign the tx with the public key for the given alias from your wallet + pub verification_key: Option, /// Password to decrypt key pub password: Option>, } diff --git a/shared/src/ledger/ibc/vp/mod.rs b/shared/src/ledger/ibc/vp/mod.rs index dae26cf4e0..7ea826c686 100644 --- a/shared/src/ledger/ibc/vp/mod.rs +++ b/shared/src/ledger/ibc/vp/mod.rs @@ -720,11 +720,7 @@ mod tests { outer_tx.set_code(Code::new(tx_code)); outer_tx.set_data(Data::new(tx_data)); outer_tx.add_section(Section::Signature(Signature::new( - outer_tx.code_sechash(), - &keypair_1(), - ))); - outer_tx.add_section(Section::Signature(Signature::new( - outer_tx.data_sechash(), + vec![*outer_tx.code_sechash(), *outer_tx.data_sechash()], &keypair_1(), ))); let ctx = Ctx::new( @@ -798,11 +794,7 @@ mod tests { tx.set_code(Code::new(tx_code)); tx.set_data(Data::new(tx_data)); tx.add_section(Section::Signature(Signature::new( - tx.code_sechash(), - &keypair_1(), - ))); - tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), + vec![*tx.code_sechash(), *tx.data_sechash()], &keypair_1(), ))); @@ -940,11 +932,7 @@ mod tests { tx.set_code(Code::new(tx_code)); tx.set_data(Data::new(tx_data)); tx.add_section(Section::Signature(Signature::new( - tx.code_sechash(), - &keypair_1(), - ))); - tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), + vec![*tx.code_sechash(), *tx.data_sechash()], &keypair_1(), ))); @@ -1052,11 +1040,7 @@ mod tests { outer_tx.set_code(Code::new(tx_code)); outer_tx.set_data(Data::new(tx_data)); outer_tx.add_section(Section::Signature(Signature::new( - outer_tx.code_sechash(), - &keypair_1(), - ))); - outer_tx.add_section(Section::Signature(Signature::new( - outer_tx.data_sechash(), + vec![*outer_tx.code_sechash(), *outer_tx.data_sechash()], &keypair_1(), ))); let gas_meter = VpGasMeter::new(0); @@ -1152,11 +1136,7 @@ mod tests { tx.set_code(Code::new(tx_code)); tx.set_data(Data::new(tx_data)); tx.add_section(Section::Signature(Signature::new( - tx.code_sechash(), - &keypair_1(), - ))); - tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), + vec![*tx.code_sechash(), *tx.data_sechash()], &keypair_1(), ))); @@ -1284,11 +1264,7 @@ mod tests { tx.set_code(Code::new(tx_code)); tx.set_data(Data::new(tx_data)); tx.add_section(Section::Signature(Signature::new( - tx.code_sechash(), - &keypair_1(), - ))); - tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), + vec![*tx.code_sechash(), *tx.data_sechash()], &keypair_1(), ))); let gas_meter = VpGasMeter::new(0); @@ -1396,11 +1372,7 @@ mod tests { outer_tx.set_code(Code::new(tx_code)); outer_tx.set_data(Data::new(tx_data)); outer_tx.add_section(Section::Signature(Signature::new( - outer_tx.code_sechash(), - &keypair_1(), - ))); - outer_tx.add_section(Section::Signature(Signature::new( - outer_tx.data_sechash(), + vec![*outer_tx.code_sechash(), *outer_tx.data_sechash()], &keypair_1(), ))); let gas_meter = VpGasMeter::new(0); @@ -1485,11 +1457,7 @@ mod tests { outer_tx.set_code(Code::new(tx_code)); outer_tx.set_data(Data::new(tx_data)); outer_tx.add_section(Section::Signature(Signature::new( - outer_tx.code_sechash(), - &keypair_1(), - ))); - outer_tx.add_section(Section::Signature(Signature::new( - outer_tx.data_sechash(), + vec![*outer_tx.code_sechash(), *outer_tx.data_sechash()], &keypair_1(), ))); let gas_meter = VpGasMeter::new(0); @@ -1610,11 +1578,7 @@ mod tests { outer_tx.set_code(Code::new(tx_code)); outer_tx.set_data(Data::new(tx_data)); outer_tx.add_section(Section::Signature(Signature::new( - outer_tx.code_sechash(), - &keypair_1(), - ))); - outer_tx.add_section(Section::Signature(Signature::new( - outer_tx.data_sechash(), + vec![*outer_tx.code_sechash(), *outer_tx.data_sechash()], &keypair_1(), ))); let gas_meter = VpGasMeter::new(0); @@ -1736,11 +1700,7 @@ mod tests { outer_tx.set_code(Code::new(tx_code)); outer_tx.set_data(Data::new(tx_data)); outer_tx.add_section(Section::Signature(Signature::new( - outer_tx.code_sechash(), - &keypair_1(), - ))); - outer_tx.add_section(Section::Signature(Signature::new( - outer_tx.data_sechash(), + vec![*outer_tx.code_sechash(), *outer_tx.data_sechash()], &keypair_1(), ))); let gas_meter = VpGasMeter::new(0); @@ -1846,11 +1806,7 @@ mod tests { outer_tx.set_code(Code::new(tx_code)); outer_tx.set_data(Data::new(tx_data)); outer_tx.add_section(Section::Signature(Signature::new( - outer_tx.code_sechash(), - &keypair_1(), - ))); - outer_tx.add_section(Section::Signature(Signature::new( - outer_tx.data_sechash(), + vec![*outer_tx.code_sechash(), *outer_tx.data_sechash()], &keypair_1(), ))); let gas_meter = VpGasMeter::new(0); @@ -1954,11 +1910,7 @@ mod tests { tx.set_code(Code::new(tx_code)); tx.set_data(Data::new(tx_data)); tx.add_section(Section::Signature(Signature::new( - tx.code_sechash(), - &keypair_1(), - ))); - tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), + vec![*tx.code_sechash(), *tx.data_sechash()], &keypair_1(), ))); let gas_meter = VpGasMeter::new(0); @@ -2095,11 +2047,7 @@ mod tests { tx.set_code(Code::new(tx_code)); tx.set_data(Data::new(tx_data)); tx.add_section(Section::Signature(Signature::new( - tx.code_sechash(), - &keypair_1(), - ))); - tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), + vec![*tx.code_sechash(), *tx.data_sechash()], &keypair_1(), ))); let gas_meter = VpGasMeter::new(0); @@ -2274,11 +2222,7 @@ mod tests { tx.set_code(Code::new(tx_code)); tx.set_data(Data::new(tx_data)); tx.add_section(Section::Signature(Signature::new( - tx.code_sechash(), - &keypair_1(), - ))); - tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), + vec![*tx.code_sechash(), *tx.data_sechash()], &keypair_1(), ))); let gas_meter = VpGasMeter::new(0); @@ -2422,11 +2366,7 @@ mod tests { tx.set_code(Code::new(tx_code)); tx.set_data(Data::new(tx_data)); tx.add_section(Section::Signature(Signature::new( - tx.code_sechash(), - &keypair_1(), - ))); - tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), + vec![*tx.code_sechash(), *tx.data_sechash()], &keypair_1(), ))); let gas_meter = VpGasMeter::new(0); @@ -2575,11 +2515,7 @@ mod tests { tx.set_code(Code::new(tx_code)); tx.set_data(Data::new(tx_data)); tx.add_section(Section::Signature(Signature::new( - tx.code_sechash(), - &keypair_1(), - ))); - tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), + vec![*tx.code_sechash(), *tx.data_sechash()], &keypair_1(), ))); let gas_meter = VpGasMeter::new(0); @@ -2728,11 +2664,7 @@ mod tests { tx.set_code(Code::new(tx_code)); tx.set_data(Data::new(tx_data)); tx.add_section(Section::Signature(Signature::new( - tx.code_sechash(), - &keypair_1(), - ))); - tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), + vec![*tx.code_sechash(), *tx.data_sechash()], &keypair_1(), ))); let gas_meter = VpGasMeter::new(0); diff --git a/shared/src/ledger/masp.rs b/shared/src/ledger/masp.rs index 7ae4f142bb..05e5783f0a 100644 --- a/shared/src/ledger/masp.rs +++ b/shared/src/ledger/masp.rs @@ -1594,7 +1594,6 @@ impl ShieldedContext { return Err(builder::Error::InsufficientFunds(additional)); } } - // Build and return the constructed transaction builder .clone() diff --git a/shared/src/ledger/signing.rs b/shared/src/ledger/signing.rs index 30bdb12d34..b6b06f0dae 100644 --- a/shared/src/ledger/signing.rs +++ b/shared/src/ledger/signing.rs @@ -1,5 +1,5 @@ //! Functions to sign transactions -use std::collections::{BTreeMap, HashMap}; +use std::collections::HashMap; #[cfg(feature = "std")] use std::env; #[cfg(feature = "std")] @@ -15,7 +15,9 @@ use masp_primitives::asset_type::AssetType; use masp_primitives::transaction::components::sapling::fees::{ InputView, OutputView, }; -use namada_core::types::address::{masp, Address, ImplicitAddress}; +use namada_core::types::address::{ + masp, masp_tx_key, Address, ImplicitAddress, +}; use namada_core::types::storage::Key; use namada_core::types::token::{ self, Amount, DenominatedAmount, MaspDenom, TokenAddress, @@ -43,12 +45,11 @@ use crate::ledger::tx::{ pub use crate::ledger::wallet::store::AddressVpType; use crate::ledger::wallet::{Wallet, WalletUtils}; use crate::ledger::{args, rpc}; -use crate::proto::{Section, Signature, Tx}; +use crate::proto::{MaspBuilder, Section, Signature, Tx}; use crate::types::key::*; use crate::types::masp::{ExtendedViewingKey, PaymentAddress}; use crate::types::storage::Epoch; use crate::types::token::Transfer; -use crate::types::transaction::decrypted::DecryptedTx; use crate::types::transaction::governance::{ InitProposalData, VoteProposalData, }; @@ -67,7 +68,7 @@ const ENV_VAR_TX_LOG_PATH: &str = "NAMADA_TX_LOG_PATH"; /// for it from the wallet. If the keypair is encrypted but a password is not /// supplied, then it is interactively prompted. Errors if the key cannot be /// found or loaded. -pub async fn find_keypair< +pub async fn find_pk< C: crate::ledger::queries::Client + Sync, U: WalletUtils, >( @@ -75,37 +76,31 @@ pub async fn find_keypair< wallet: &mut Wallet, addr: &Address, password: Option>, -) -> Result { +) -> Result { match addr { Address::Established(_) => { println!( "Looking-up public key of {} from the ledger...", addr.encode() ); - let public_key = rpc::get_public_key(client, addr).await.ok_or( - Error::Other(format!( + rpc::get_public_key(client, addr).await.ok_or(Error::Other( + format!( "No public key found for the address {}", addr.encode() - )), - )?; - wallet.find_key_by_pk(&public_key, password).map_err(|err| { - Error::Other(format!( - "Unable to load the keypair from the wallet for public \ - key {}. Failed with: {}", - public_key, err - )) - }) + ), + )) } - Address::Implicit(ImplicitAddress(pkh)) => { - wallet.find_key_by_pkh(pkh, password).map_err(|err| { + Address::Implicit(ImplicitAddress(pkh)) => Ok(wallet + .find_key_by_pkh(pkh, password) + .map_err(|err| { Error::Other(format!( "Unable to load the keypair from the wallet for the \ implicit address {}. Failed with: {}", addr.encode(), err )) - }) - } + })? + .ref_to()), Address::Internal(_) => other_err(format!( "Internal address {} doesn't have any signing keys.", addr @@ -113,18 +108,47 @@ pub async fn find_keypair< } } +/// Load the secret key corresponding to the given public key from the wallet. +/// If the keypair is encrypted but a password is not supplied, then it is +/// interactively prompted. Errors if the key cannot be found or loaded. +pub fn find_key_by_pk( + wallet: &mut Wallet, + args: &args::Tx, + keypair: &common::PublicKey, +) -> Result { + if *keypair == masp_tx_key().ref_to() { + // We already know the secret key corresponding to the MASP sentinal key + Ok(masp_tx_key()) + } else if args + .signing_key + .as_ref() + .map(|x| x.ref_to() == *keypair) + .unwrap_or(false) + { + // We can lookup the secret key from the CLI arguments in this case + Ok(args.signing_key.clone().unwrap()) + } else { + // Otherwise we need to search the wallet for the secret key + wallet + .find_key_by_pk(keypair, args.password.clone()) + .map_err(|err| { + Error::Other(format!( + "Unable to load the keypair from the wallet for public \ + key {}. Failed with: {}", + keypair, err + )) + }) + } +} + /// Carries types that can be directly/indirectly used to sign a transaction. #[allow(clippy::large_enum_variant)] #[derive(Clone)] pub enum TxSigningKey { /// Do not sign any transaction None, - /// Obtain the actual keypair from wallet and use that to sign - WalletKeypair(common::SecretKey), /// Obtain the keypair corresponding to given address from wallet and sign WalletAddress(Address), - /// Directly use the given secret key to sign transactions - SecretKey(common::SecretKey), } /// Given CLI arguments and some defaults, determine the rightful transaction @@ -139,39 +163,32 @@ pub async fn tx_signer< wallet: &mut Wallet, args: &args::Tx, default: TxSigningKey, -) -> Result { - // Override the default signing key source if possible - let default = if let Some(signing_key) = &args.signing_key { - TxSigningKey::WalletKeypair(signing_key.clone()) +) -> Result<(Option
, common::PublicKey), Error> { + let signer = if args.dry_run { + // We cannot override the signer if we're doing a dry run + default + } else if let Some(signing_key) = &args.signing_key { + // Otherwise use the signing key override provided by user + return Ok((None, signing_key.ref_to())); + } else if let Some(verification_key) = &args.verification_key { + return Ok((None, verification_key.clone())); } else if let Some(signer) = &args.signer { + // Otherwise use the signer address provided by user TxSigningKey::WalletAddress(signer.clone()) } else { + // Otherwise use the signer determined by the caller default }; // Now actually fetch the signing key and apply it - match default { - TxSigningKey::WalletKeypair(signing_key) => Ok(signing_key), - TxSigningKey::WalletAddress(signer) => { - let signer = signer; - let signing_key = find_keypair::( - client, - wallet, - &signer, - args.password.clone(), - ) - .await?; - // Check if the signer is implicit account that needs to reveal its - // PK first - if matches!(signer, Address::Implicit(_)) { - let pk: common::PublicKey = signing_key.ref_to(); - super::tx::reveal_pk_if_needed::( - client, wallet, &pk, args, - ) - .await?; - } - Ok(signing_key) + match signer { + TxSigningKey::WalletAddress(signer) if signer == masp() => { + Ok((None, masp_tx_key().ref_to())) } - TxSigningKey::SecretKey(signing_key) => Ok(signing_key), + TxSigningKey::WalletAddress(signer) => Ok(( + Some(signer.clone()), + find_pk::(client, wallet, &signer, args.password.clone()) + .await?, + )), TxSigningKey::None => other_err( "All transactions must be signed; please either specify the key \ or the address from which to look up the signing key." @@ -188,54 +205,171 @@ pub async fn tx_signer< /// hashes needed for monitoring the tx on chain. /// /// If it is a dry run, it is not put in a wrapper, but returned as is. -pub async fn sign_tx< - C: crate::ledger::queries::Client + Sync, - U: WalletUtils, ->( - client: &C, +pub async fn sign_tx( wallet: &mut Wallet, - mut tx: Tx, + tx: &mut Tx, args: &args::Tx, - default: TxSigningKey, - #[cfg(not(feature = "mainnet"))] requires_pow: bool, -) -> Result { - let keypair = tx_signer::(client, wallet, args, default).await?; + keypair: &common::PublicKey, +) -> Result<(), Error> { + let keypair = find_key_by_pk(wallet, args, keypair)?; // Sign over the transacttion data tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), + vec![*tx.data_sechash(), *tx.code_sechash()], &keypair, ))); - // Sign over the transaction code + // Remove all the sensitive sections + tx.protocol_filter(); + // Then sign over the bound wrapper tx.add_section(Section::Signature(Signature::new( - tx.code_sechash(), + tx.sechashes(), &keypair, ))); + Ok(()) +} - let epoch = rpc::query_epoch(client).await; - - let broadcast_data = if args.dry_run { - 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, - })); - TxBroadcastData::DryRun(tx) - } else { - sign_wrapper( - client, - wallet, - args, - epoch, - tx, - &keypair, - #[cfg(not(feature = "mainnet"))] - requires_pow, - ) - .await +#[cfg(not(feature = "mainnet"))] +/// Solve the PoW challenge if balance is insufficient to pay transaction fees +/// or if solution is explicitly requested. +pub async fn solve_pow_challenge( + client: &C, + args: &args::Tx, + keypair: &common::PublicKey, + requires_pow: bool, +) -> (Option, Fee) { + let wrapper_tx_fees_key = parameter_storage::get_wrapper_tx_fees_key(); + let fee_amount = rpc::query_storage_value::( + client, + &wrapper_tx_fees_key, + ) + .await + .unwrap_or_default(); + let fee_token = &args.fee_token; + let source = Address::from(keypair); + let balance_key = token::balance_key(fee_token, &source); + let balance = + rpc::query_storage_value::(client, &balance_key) + .await + .unwrap_or_default(); + let is_bal_sufficient = fee_amount <= balance; + if !is_bal_sufficient { + let token_addr = TokenAddress { + address: args.fee_token.clone(), + sub_prefix: None, + }; + let err_msg = format!( + "The wrapper transaction source doesn't have enough balance to \ + pay fee {}, got {}.", + format_denominated_amount(client, &token_addr, fee_amount).await, + format_denominated_amount(client, &token_addr, balance).await, + ); + if !args.force && cfg!(feature = "mainnet") { + panic!("{}", err_msg); + } + } + let fee = Fee { + amount: fee_amount, + token: fee_token.clone(), }; + // A PoW solution can be used to allow zero-fee testnet transactions + // If the address derived from the keypair doesn't have enough balance + // to pay for the fee, allow to find a PoW solution instead. + if requires_pow || !is_bal_sufficient { + println!("The transaction requires the completion of a PoW challenge."); + // Obtain a PoW challenge for faucet withdrawal + let challenge = rpc::get_testnet_pow_challenge(client, source).await; + + // Solve the solution, this blocks until a solution is found + let solution = challenge.solve(); + (Some(solution), fee) + } else { + (None, fee) + } +} + +#[cfg(not(feature = "mainnet"))] +/// Update the PoW challenge inside the given transaction +pub async fn update_pow_challenge( + client: &C, + args: &args::Tx, + tx: &mut Tx, + keypair: &common::PublicKey, + requires_pow: bool, +) { + if let TxType::Wrapper(wrapper) = &mut tx.header.tx_type { + let (pow_solution, fee) = + solve_pow_challenge(client, args, keypair, requires_pow).await; + wrapper.fee = fee; + wrapper.pow_solution = pow_solution; + } +} + +/// Create a wrapper tx from a normal tx. Get the hash of the +/// wrapper and its payload which is needed for monitoring its +/// progress on chain. +pub async fn wrap_tx< + C: crate::ledger::queries::Client + Sync, + U: WalletUtils, +>( + client: &C, + #[allow(unused_variables)] wallet: &mut Wallet, + args: &args::Tx, + epoch: Epoch, + mut tx: Tx, + keypair: &common::PublicKey, + #[cfg(not(feature = "mainnet"))] requires_pow: bool, +) -> Tx { + #[cfg(not(feature = "mainnet"))] + let (pow_solution, fee) = + solve_pow_challenge(client, args, keypair, requires_pow).await; + // This object governs how the payload will be processed + tx.update_header(TxType::Wrapper(Box::new(WrapperTx::new( + fee, + keypair.clone(), + epoch, + args.gas_limit.clone(), + #[cfg(not(feature = "mainnet"))] + pow_solution, + )))); + tx.header.chain_id = args.chain_id.clone().unwrap(); + tx.header.expiration = args.expiration; - Ok(broadcast_data) + #[cfg(feature = "std")] + // Attempt to decode the construction + if let Ok(path) = env::var(ENV_VAR_LEDGER_LOG_PATH) { + let mut tx = tx.clone(); + // Contract the large data blobs in the transaction + tx.wallet_filter(); + // Convert the transaction to Ledger format + let decoding = to_ledger_vector(client, wallet, &tx) + .await + .expect("unable to decode transaction"); + let output = serde_json::to_string(&decoding) + .expect("failed to serialize decoding"); + // Record the transaction at the identified path + let mut f = File::options() + .append(true) + .create(true) + .open(path) + .expect("failed to open test vector file"); + writeln!(f, "{},", output) + .expect("unable to write test vector to file"); + } + #[cfg(feature = "std")] + // Attempt to decode the construction + if let Ok(path) = env::var(ENV_VAR_TX_LOG_PATH) { + let mut tx = tx.clone(); + // Contract the large data blobs in the transaction + tx.wallet_filter(); + // Record the transaction at the identified path + let mut f = File::options() + .append(true) + .create(true) + .open(path) + .expect("failed to open test vector file"); + writeln!(f, "{:x?},", tx).expect("unable to write test vector to file"); + } + + tx } /// Create a wrapper tx from a normal tx. Get the hash of the @@ -316,7 +450,7 @@ pub async fn sign_wrapper< amount: fee_amount, token: fee_token.clone(), }, - keypair, + keypair.ref_to(), epoch, args.gas_limit.clone(), #[cfg(not(feature = "mainnet"))] @@ -324,11 +458,6 @@ pub async fn sign_wrapper< )))); 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, - ))); #[cfg(feature = "std")] // Attempt to decode the construction @@ -368,8 +497,8 @@ pub async fn sign_wrapper< // Remove all the sensitive sections tx.protocol_filter(); - // Encrypt all sections not relating to the header - tx.encrypt(&Default::default()); + // Then sign over the bound wrapper committing to all other sections + tx.add_section(Section::Signature(Signature::new(tx.sechashes(), keypair))); // 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 @@ -532,6 +661,86 @@ fn format_outputs(output: &mut Vec) { } } +/// Adds a Ledger output for the sender and destination for transparent and MASP +/// transactions +pub async fn make_ledger_masp_endpoints< + C: crate::ledger::queries::Client + Sync, +>( + client: &C, + tokens: &HashMap, + output: &mut Vec, + transfer: &Transfer, + builder: Option<&MaspBuilder>, + assets: &HashMap, MaspDenom, Epoch)>, +) { + if transfer.source != masp() { + output.push(format!("Sender : {}", transfer.source)); + if transfer.target == masp() { + make_ledger_amount_addr( + tokens, + output, + transfer.amount, + &transfer.token, + &transfer.sub_prefix, + "Sending ", + ); + } + } else if let Some(builder) = builder { + for sapling_input in builder.builder.sapling_inputs() { + let vk = ExtendedViewingKey::from(*sapling_input.key()); + output.push(format!("Sender : {}", vk)); + make_ledger_amount_asset( + client, + tokens, + output, + sapling_input.value(), + &sapling_input.asset_type(), + assets, + "Sending ", + ) + .await; + } + } + if transfer.target != masp() { + output.push(format!("Destination : {}", transfer.target)); + if transfer.source == masp() { + make_ledger_amount_addr( + tokens, + output, + transfer.amount, + &transfer.token, + &transfer.sub_prefix, + "Receiving ", + ); + } + } else if let Some(builder) = builder { + for sapling_output in builder.builder.sapling_outputs() { + let pa = PaymentAddress::from(sapling_output.address()); + output.push(format!("Destination : {}", pa)); + make_ledger_amount_asset( + client, + tokens, + output, + sapling_output.value(), + &sapling_output.asset_type(), + assets, + "Receiving ", + ) + .await; + } + } + if transfer.source != masp() && transfer.target != masp() { + make_ledger_amount_addr( + tokens, + output, + transfer.amount, + &transfer.token, + &transfer.sub_prefix, + "", + ); + } +} + /// Converts the given transaction to the form that is displayed on the Ledger /// device pub async fn to_ledger_vector< @@ -618,7 +827,7 @@ pub async fn to_ledger_vector< let extra = tx .get_section(&init_account.vp_code_hash) - .and_then(Section::extra_data_sec) + .and_then(|x| Section::extra_data_sec(x.as_ref())) .expect("unable to load vp code") .code .hash(); @@ -648,7 +857,7 @@ pub async fn to_ledger_vector< let extra = tx .get_section(&init_validator.validator_vp_code_hash) - .and_then(Section::extra_data_sec) + .and_then(|x| Section::extra_data_sec(x.as_ref())) .expect("unable to load vp code") .code .hash(); @@ -711,15 +920,8 @@ pub async fn to_ledger_vector< ), format!("Grace epoch : {}", init_proposal_data.grace_epoch), ]); - let content: BTreeMap = - BorshDeserialize::try_from_slice(&init_proposal_data.content)?; - if !content.is_empty() { - for (key, value) in &content { - tv.output.push(format!("Content {} : {}", key, value)); - } - } else { - tv.output.push("Content : (none)".to_string()); - } + tv.output + .push(format!("Content: {}", init_proposal_data.content)); tv.output_expert.extend(vec![ format!("ID : {}", init_proposal_data_id), @@ -734,14 +936,8 @@ pub async fn to_ledger_vector< ), format!("Grace epoch : {}", init_proposal_data.grace_epoch), ]); - if !content.is_empty() { - for (key, value) in content { - tv.output_expert - .push(format!("Content {} : {}", key, value)); - } - } else { - tv.output_expert.push("Content : none".to_string()); - } + tv.output + .push(format!("Content: {}", init_proposal_data.content)); } else if code_hash == vote_proposal_hash { let vote_proposal = VoteProposalData::try_from_slice(&tx.data().ok_or_else(|| { @@ -794,7 +990,7 @@ pub async fn to_ledger_vector< let extra = tx .get_section(&transfer.vp_code_hash) - .and_then(Section::extra_data_sec) + .and_then(|x| Section::extra_data_sec(x.as_ref())) .expect("unable to load vp code") .code .hash(); @@ -849,79 +1045,24 @@ pub async fn to_ledger_vector< tv.name = "Transfer 0".to_string(); tv.output.push("Type : Transfer".to_string()); - if transfer.source != masp() { - tv.output.push(format!("Sender : {}", transfer.source)); - if transfer.target == masp() { - make_ledger_amount_addr( - &tokens, - &mut tv.output, - transfer.amount, - &transfer.token, - &transfer.sub_prefix, - "Sending ", - ); - } - } else if let Some(builder) = builder { - for input in builder.builder.sapling_inputs() { - let vk = ExtendedViewingKey::from(*input.key()); - tv.output.push(format!("Sender : {}", vk)); - make_ledger_amount_asset( - client, - &tokens, - &mut tv.output, - input.value(), - &input.asset_type(), - &asset_types, - "Sending ", - ) - .await; - } - } - if transfer.target != masp() { - tv.output.push(format!("Destination : {}", transfer.target)); - if transfer.source == masp() { - make_ledger_amount_addr( - &tokens, - &mut tv.output, - transfer.amount, - &transfer.token, - &transfer.sub_prefix, - "Receiving ", - ); - } - } else if let Some(builder) = builder { - for output in builder.builder.sapling_outputs() { - let pa = PaymentAddress::from(output.address()); - tv.output.push(format!("Destination : {}", pa)); - make_ledger_amount_asset( - client, - &tokens, - &mut tv.output, - output.value(), - &output.asset_type(), - &asset_types, - "Receiving ", - ) - .await; - } - } - if transfer.source != masp() && transfer.target != masp() { - make_ledger_amount_addr( - &tokens, - &mut tv.output, - transfer.amount, - &transfer.token, - &transfer.sub_prefix, - "", - ); - } - - tv.output_expert.extend(vec![ - format!("Source : {}", transfer.source), - format!("Target : {}", transfer.target), - format!("Token : {}", transfer.token), - format!("Amount : {}", transfer.amount), - ]); + make_ledger_masp_endpoints( + client, + &tokens, + &mut tv.output, + &transfer, + builder, + &asset_types, + ) + .await; + make_ledger_masp_endpoints( + client, + &tokens, + &mut tv.output_expert, + &transfer, + builder, + &asset_types, + ) + .await; } else if code_hash == ibc_hash { let msg = Any::decode( tx.data() diff --git a/shared/src/ledger/tx.rs b/shared/src/ledger/tx.rs index 27245ecaed..f130a6ee5a 100644 --- a/shared/src/ledger/tx.rs +++ b/shared/src/ledger/tx.rs @@ -4,7 +4,6 @@ use std::collections::{BTreeMap, HashMap, HashSet}; use std::str::FromStr; use borsh::BorshSerialize; -use itertools::Either::*; use masp_primitives::asset_type::AssetType; use masp_primitives::transaction::builder; use masp_primitives::transaction::builder::Builder; @@ -22,7 +21,6 @@ use namada_core::types::token::MaspDenom; use namada_proof_of_stake::parameters::PosParams; use namada_proof_of_stake::types::CommissionPair; use prost::EncodeError; -use sha2::{Digest as Sha2Digest, Sha256}; use thiserror::Error; use tokio::time::Duration; @@ -38,12 +36,11 @@ use crate::ledger::args::{self, InputAmount}; use crate::ledger::governance::storage as gov_storage; use crate::ledger::masp::{ShieldedContext, ShieldedUtils}; use crate::ledger::rpc::{self, validate_amount, TxBroadcastData, TxResponse}; -use crate::ledger::signing::{find_keypair, sign_tx, tx_signer, TxSigningKey}; +use crate::ledger::signing::{tx_signer, wrap_tx, TxSigningKey}; use crate::ledger::wallet::{Wallet, WalletUtils}; -use crate::proto::{Code, Data, MaspBuilder, Section, Signature, Tx}; +use crate::proto::{Code, Data, MaspBuilder, Section, Tx}; use crate::tendermint_rpc::endpoint::broadcast::tx_sync::Response; use crate::tendermint_rpc::error::Error as RpcError; -use crate::types::hash::Hash; use crate::types::key::*; use crate::types::masp::TransferTarget; use crate::types::storage::{Epoch, RESERVED_ADDRESS_PREFIX}; @@ -160,7 +157,13 @@ pub enum Error { transferred and fees. Amount to transfer is {1} {2} and fees are {3} \ {4}." )] - NegativeBalanceAfterTransfer(Address, String, Address, String, Address), + NegativeBalanceAfterTransfer( + Box
, + String, + Box
, + String, + Box
, + ), /// No Balance found for token #[error("{0}")] MaspError(builder::Error), @@ -221,9 +224,9 @@ impl ProcessTxResponse { } } -/// Submit transaction and wait for result. Returns a list of addresses -/// initialized in the transaction if any. In dry run, this is always empty. -pub async fn process_tx< +/// Prepare a transaction for signing and submission by adding a wrapper header +/// to it. +pub async fn prepare_tx< C: crate::ledger::queries::Client + Sync, U: WalletUtils, >( @@ -233,17 +236,44 @@ pub async fn process_tx< tx: Tx, default_signer: TxSigningKey, #[cfg(not(feature = "mainnet"))] requires_pow: bool, +) -> Result<(Tx, Option
, common::PublicKey), Error> { + let (signer_addr, signer_pk) = + tx_signer::(client, wallet, args, default_signer.clone()).await?; + if args.dry_run { + Ok((tx, signer_addr, signer_pk)) + } else { + let epoch = rpc::query_epoch(client).await; + Ok(( + wrap_tx( + client, + wallet, + args, + epoch, + tx.clone(), + &signer_pk, + #[cfg(not(feature = "mainnet"))] + requires_pow, + ) + .await, + signer_addr, + signer_pk, + )) + } +} + +/// Submit transaction and wait for result. Returns a list of addresses +/// initialized in the transaction if any. In dry run, this is always empty. +pub async fn process_tx< + C: crate::ledger::queries::Client + Sync, + U: WalletUtils, +>( + client: &C, + wallet: &mut Wallet, + args: &args::Tx, + mut tx: Tx, ) -> Result { - let to_broadcast = sign_tx::( - client, - wallet, - tx, - args, - default_signer, - #[cfg(not(feature = "mainnet"))] - requires_pow, - ) - .await?; + // Remove all the sensitive sections + tx.protocol_filter(); // NOTE: use this to print the request JSON body: // let request = @@ -255,68 +285,84 @@ pub async fn process_tx< // println!("HTTP request body: {}", request_body); if args.dry_run { - expect_dry_broadcast(to_broadcast, client).await + expect_dry_broadcast(TxBroadcastData::DryRun(tx), client).await } 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(); + let to_broadcast = TxBroadcastData::Wrapper { + tx, + wrapper_hash, + decrypted_hash, + }; // Either broadcast or submit transaction and collect result into // sum type - let result = if args.broadcast_only { - Left(broadcast_tx(client, &to_broadcast).await) + if args.broadcast_only { + broadcast_tx(client, &to_broadcast) + .await + .map(ProcessTxResponse::Broadcast) } 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)) => Ok(ProcessTxResponse::Applied(result)), - Left(Ok(result)) => Ok(ProcessTxResponse::Broadcast(result)), - Right(Err(err)) => Err(err), - Left(Err(err)) => Err(err), + match submit_tx(client, to_broadcast).await { + Ok(x) => { + save_initialized_accounts::( + wallet, + args, + x.initialized_accounts.clone(), + ) + .await; + Ok(ProcessTxResponse::Applied(x)) + } + Err(x) => Err(x), + } } } } /// Submit transaction to reveal public key -pub async fn submit_reveal_pk< +pub async fn build_reveal_pk< C: crate::ledger::queries::Client + Sync, U: WalletUtils, >( client: &C, wallet: &mut Wallet, args: args::RevealPk, -) -> Result<(), Error> { +) -> Result, common::PublicKey)>, Error> { let args::RevealPk { tx: args, public_key, } = args; let public_key = public_key; - if !reveal_pk_if_needed::(client, wallet, &public_key, &args).await? { + if !is_reveal_pk_needed::(client, &public_key, &args).await? { let addr: Address = (&public_key).into(); println!("PK for {addr} is already revealed, nothing to do."); - Ok(()) + Ok(None) } else { - Ok(()) + // If not, submit it + Ok(Some( + build_reveal_pk_aux::(client, wallet, &public_key, &args) + .await?, + )) } } /// Submit transaction to rveeal public key if needed -pub async fn reveal_pk_if_needed< +pub async fn is_reveal_pk_needed< C: crate::ledger::queries::Client + Sync, U: WalletUtils, >( client: &C, - wallet: &mut Wallet, public_key: &common::PublicKey, args: &args::Tx, ) -> Result { let addr: Address = public_key.into(); // Check if PK revealed - if args.force || !has_revealed_pk(client, &addr).await { - // If not, submit it - submit_reveal_pk_aux::(client, wallet, public_key, args).await?; - Ok(true) - } else { - Ok(false) - } + Ok(args.force || !has_revealed_pk(client, &addr).await) } /// Check if the public key for the given address has been revealed @@ -328,7 +374,7 @@ pub async fn has_revealed_pk( } /// Submit transaction to reveal the given public key -pub async fn submit_reveal_pk_aux< +pub async fn build_reveal_pk_aux< C: crate::ledger::queries::Client + Sync, U: WalletUtils, >( @@ -336,7 +382,7 @@ pub async fn submit_reveal_pk_aux< wallet: &mut Wallet, public_key: &common::PublicKey, args: &args::Tx, -) -> Result { +) -> Result<(Tx, Option
, common::PublicKey), Error> { let addr: Address = public_key.into(); println!("Submitting a tx to reveal the public key for address {addr}..."); let tx_data = public_key.try_to_vec().map_err(Error::EncodeKeyFailure)?; @@ -353,59 +399,16 @@ pub async fn submit_reveal_pk_aux< tx.header.expiration = args.expiration; tx.set_data(Data::new(tx_data)); tx.set_code(Code::from_hash(tx_code_hash)); - - // submit_tx without signing the inner tx - let keypair = if let Some(signing_key) = &args.signing_key { - Ok(signing_key.clone()) - } else if let Some(signer) = args.signer.as_ref() { - find_keypair(client, wallet, signer, args.password.clone()).await - } else { - find_keypair(client, wallet, &addr, args.password.clone()).await - }?; - tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), - &keypair, - ))); - tx.add_section(Section::Signature(Signature::new( - tx.code_sechash(), - &keypair, - ))); - let epoch = rpc::query_epoch(client).await; - let to_broadcast = if args.dry_run { - TxBroadcastData::DryRun(tx) - } else { - super::signing::sign_wrapper( - client, - wallet, - args, - epoch, - tx, - &keypair, - #[cfg(not(feature = "mainnet"))] - false, - ) - .await - }; - - if args.dry_run { - 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(Err(err)) => Err(err), - Left(Err(err)) => Err(err), - Right(Ok(response)) => Ok(ProcessTxResponse::Applied(response)), - Left(Ok(response)) => Ok(ProcessTxResponse::Broadcast(response)), - } - } + prepare_tx::( + client, + wallet, + args, + tx, + TxSigningKey::WalletAddress(addr), + #[cfg(not(feature = "mainnet"))] + false, + ) + .await } /// Broadcast a transaction to be included in the blockchain and checks that @@ -593,14 +596,14 @@ pub async fn save_initialized_accounts( } /// Submit validator comission rate change -pub async fn submit_validator_commission_change< +pub async fn build_validator_commission_change< C: crate::ledger::queries::Client + Sync, U: WalletUtils, >( client: &C, wallet: &mut Wallet, - args: args::TxCommissionRateChange, -) -> Result<(), Error> { + args: args::CommissionRateChange, +) -> Result<(Tx, Option
, common::PublicKey), Error> { let epoch = rpc::query_epoch(client).await; let tx_code_hash = @@ -674,7 +677,7 @@ pub async fn submit_validator_commission_change< tx.set_code(Code::from_hash(tx_code_hash)); let default_signer = args.validator.clone(); - process_tx::( + prepare_tx::( client, wallet, &args.tx, @@ -683,8 +686,52 @@ pub async fn submit_validator_commission_change< #[cfg(not(feature = "mainnet"))] false, ) - .await?; - Ok(()) + .await +} + +/// Submit transaction to unjail a jailed validator +pub async fn build_unjail_validator< + C: crate::ledger::queries::Client + Sync, + U: WalletUtils, +>( + client: &C, + wallet: &mut Wallet, + args: args::TxUnjailValidator, +) -> Result<(Tx, Option
, common::PublicKey), Error> { + if !rpc::is_validator(client, &args.validator).await { + eprintln!("The given address {} is not a validator.", &args.validator); + if !args.tx.force { + return Err(Error::InvalidValidatorAddress(args.validator.clone())); + } + } + + let tx_code_path = String::from_utf8(args.tx_code_path).unwrap(); + let tx_code_hash = + query_wasm_code_hash(client, tx_code_path).await.unwrap(); + + let data = args + .validator + .clone() + .try_to_vec() + .map_err(Error::EncodeTxFailure)?; + + let mut tx = Tx::new(TxType::Raw); + tx.header.chain_id = args.tx.chain_id.clone().unwrap(); + tx.header.expiration = args.tx.expiration; + tx.set_data(Data::new(data)); + tx.set_code(Code::from_hash(tx_code_hash)); + + let default_signer = args.validator; + prepare_tx( + client, + wallet, + &args.tx, + tx, + TxSigningKey::WalletAddress(default_signer), + #[cfg(not(feature = "mainnet"))] + false, + ) + .await } /// Submit transaction to unjail a jailed validator @@ -720,7 +767,7 @@ pub async fn submit_unjail_validator< tx.set_code(Code::from_hash(tx_code_hash)); let default_signer = args.validator; - process_tx( + prepare_tx( client, wallet, &args.tx, @@ -734,14 +781,14 @@ pub async fn submit_unjail_validator< } /// Submit transaction to withdraw an unbond -pub async fn submit_withdraw< +pub async fn build_withdraw< C: crate::ledger::queries::Client + Sync, U: WalletUtils, >( client: &C, wallet: &mut Wallet, args: args::Withdraw, -) -> Result<(), Error> { +) -> Result<(Tx, Option
, common::PublicKey), Error> { let epoch = rpc::query_epoch(client).await; let validator = @@ -792,7 +839,7 @@ 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::( + prepare_tx::( client, wallet, &args.tx, @@ -801,19 +848,26 @@ pub async fn submit_withdraw< #[cfg(not(feature = "mainnet"))] false, ) - .await?; - Ok(()) + .await } /// Submit a transaction to unbond -pub async fn submit_unbond< +pub async fn build_unbond< C: crate::ledger::queries::Client + Sync, U: WalletUtils, >( client: &C, wallet: &mut Wallet, args: args::Unbond, -) -> Result<(), Error> { +) -> Result< + ( + Tx, + Option
, + common::PublicKey, + Option<(Epoch, token::Amount)>, + ), + Error, +> { let source = args.source.clone(); // Check the source's current bond amount let bond_source = source.clone().unwrap_or_else(|| args.validator.clone()); @@ -878,7 +932,7 @@ 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::( + let (tx, signer_addr, default_signer) = prepare_tx::( client, wallet, &args.tx, @@ -889,6 +943,19 @@ pub async fn submit_unbond< ) .await?; + Ok((tx, signer_addr, default_signer, latest_withdrawal_pre)) +} + +/// Query the unbonds post-tx +pub async fn query_unbonds( + client: &C, + args: args::Unbond, + latest_withdrawal_pre: Option<(Epoch, token::Amount)>, +) -> Result<(), Error> { + let source = args.source.clone(); + // Check the source's current bond amount + let bond_source = source.clone().unwrap_or_else(|| args.validator.clone()); + // Query the unbonds post-tx let unbonds = rpc::query_unbond_with_slashing(client, &bond_source, &args.validator) @@ -938,19 +1005,18 @@ pub async fn submit_unbond< latest_withdraw_epoch_post, ); } - Ok(()) } /// Submit a transaction to bond -pub async fn submit_bond< +pub async fn build_bond< C: crate::ledger::queries::Client + Sync, U: WalletUtils, >( client: &C, wallet: &mut Wallet, args: args::Bond, -) -> Result<(), Error> { +) -> Result<(Tx, Option
, common::PublicKey), Error> { let validator = known_validator_or_err(args.validator.clone(), args.tx.force, client) .await?; @@ -998,7 +1064,7 @@ 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::( + prepare_tx::( client, wallet, &args.tx, @@ -1007,8 +1073,7 @@ pub async fn submit_bond< #[cfg(not(feature = "mainnet"))] false, ) - .await?; - Ok(()) + .await } /// Check if current epoch is in the last third of the voting period of the @@ -1042,14 +1107,14 @@ pub async fn is_safe_voting_window( } /// Submit an IBC transfer -pub async fn submit_ibc_transfer< +pub async fn build_ibc_transfer< C: crate::ledger::queries::Client + Sync, U: WalletUtils, >( client: &C, wallet: &mut Wallet, args: args::TxIbcTransfer, -) -> Result<(), Error> { +) -> Result<(Tx, Option
, common::PublicKey), Error> { // Check that the source address exists on chain let source = source_exists_or_err(args.source.clone(), args.tx.force, client) @@ -1140,7 +1205,7 @@ pub async fn submit_ibc_transfer< tx.set_data(Data::new(data)); tx.set_code(Code::from_hash(tx_code_hash)); - process_tx::( + prepare_tx::( client, wallet, &args.tx, @@ -1149,8 +1214,7 @@ pub async fn submit_ibc_transfer< #[cfg(not(feature = "mainnet"))] false, ) - .await?; - Ok(()) + .await } /// Try to decode the given asset type and add its decoding to the supplied set. @@ -1227,7 +1291,7 @@ async fn used_asset_types< } /// Submit an ordinary transfer -pub async fn submit_transfer< +pub async fn build_transfer< C: crate::ledger::queries::Client + Sync, V: WalletUtils, U: ShieldedUtils, @@ -1236,7 +1300,8 @@ pub async fn submit_transfer< wallet: &mut Wallet, shielded: &mut ShieldedContext, mut args: args::TxTransfer, -) -> Result<(), Error> { +) -> Result<(Tx, Option
, common::PublicKey, Option, bool), Error> +{ let source = args.source.effective_address(); let target = args.target.effective_address(); let token = args.token.clone(); @@ -1299,33 +1364,20 @@ pub async fn submit_transfer< // signer. Also, if the transaction is shielded, redact the amount and token // types by setting the transparent value to 0 and token type to a constant. // This has no side-effect because transaction is to self. - let (default_signer, _amount, token) = - if source == masp_addr && target == masp_addr { - // TODO Refactor me, we shouldn't rely on any specific token here. - ( - TxSigningKey::SecretKey(masp_tx_key()), - token::Amount::default(), - args.native_token.clone(), - ) - } else if source == masp_addr { - ( - TxSigningKey::SecretKey(masp_tx_key()), - validated_amount.amount, - token.clone(), - ) - } else { - ( - TxSigningKey::WalletAddress(args.source.effective_address()), - validated_amount.amount, - token.clone(), - ) - }; + let (_amount, token) = if source == masp_addr && target == masp_addr { + // TODO Refactor me, we shouldn't rely on any specific token here. + (token::Amount::default(), args.native_token.clone()) + } else { + (validated_amount.amount, token) + }; + let default_signer = + TxSigningKey::WalletAddress(args.source.effective_address()); // If our chosen signer is the MASP sentinel key, then our shielded inputs // will need to cover the gas fees. let chosen_signer = tx_signer::(client, wallet, &args.tx, default_signer.clone()) .await? - .ref_to(); + .1; let shielded_gas = masp_tx_key().ref_to() == chosen_signer; // Determine whether to pin this transaction to a storage key let key = match &args.target { @@ -1341,128 +1393,105 @@ pub async fn submit_transfer< .await .unwrap(); - // Loop twice in case the first submission attempt fails - for _ in 0..2 { - // Construct the shielded part of the transaction, if any - let stx_result = shielded - .gen_shielded_transfer(client, &args, shielded_gas) - .await; + // Construct the shielded part of the transaction, if any + let stx_result = shielded + .gen_shielded_transfer(client, &args, shielded_gas) + .await; - let shielded_parts = match stx_result { - Ok(stx) => Ok(stx), - Err(builder::Error::InsufficientFunds(_)) => { - Err(Error::NegativeBalanceAfterTransfer( - source.clone(), - validated_amount.amount.to_string_native(), - token.clone(), - validate_fee.amount.to_string_native(), - args.tx.fee_token.clone(), - )) - } - Err(err) => Err(Error::MaspError(err)), - }?; - - let mut tx = Tx::new(TxType::Raw); - tx.header.chain_id = args.tx.chain_id.clone().unwrap(); - tx.header.expiration = args.tx.expiration; - // Add the MASP Transaction and its Builder to facilitate validation - let (masp_hash, shielded_tx_epoch) = if let Some(shielded_parts) = - shielded_parts - { - // Add a MASP Transaction section to the Tx - let masp_tx = tx.add_section(Section::MaspTx(shielded_parts.1)); - // Get the hash of the MASP Transaction section - let masp_hash = - Hash(masp_tx.hash(&mut Sha256::new()).finalize_reset().into()); - // Get the decoded asset types used in the transaction to give - // offline wallet users more information - let asset_types = - used_asset_types(shielded, client, &shielded_parts.0) - .await - .unwrap_or_default(); - // Add the MASP Transaction's Builder to the Tx - tx.add_section(Section::MaspBuilder(MaspBuilder { - asset_types, - // Store how the Info objects map to Descriptors/Outputs - metadata: shielded_parts.2, - // Store the data that was used to construct the Transaction - builder: shielded_parts.0, - // Link the Builder to the Transaction by hash code - target: masp_hash, - })); - // The MASP Transaction section hash will be used in Transfer - (Some(masp_hash), Some(shielded_parts.3)) - } else { - (None, None) - }; - // Construct the corresponding transparent Transfer object - let transfer = token::Transfer { - source: source.clone(), - target: target.clone(), - token: token.clone(), - sub_prefix: sub_prefix.clone(), - amount: validated_amount, - key: key.clone(), - // Link the Transfer to the MASP Transaction by hash code - shielded: masp_hash, - }; - tracing::debug!("Transfer data {:?}", transfer); - // Encode the Transfer and store it beside the MASP transaction - let data = transfer - .try_to_vec() - .expect("Encoding tx data shouldn't fail"); - tx.set_data(Data::new(data)); - // Finally store the Traansfer WASM code in the Tx - tx.set_code(Code::from_hash(tx_code_hash)); - - // Dry-run/broadcast/submit the transaction - let result = process_tx::( - client, - wallet, - &args.tx, - tx, - default_signer.clone(), - #[cfg(not(feature = "mainnet"))] - is_source_faucet, - ) - .await?; - - // Query the epoch in which the transaction was probably submitted - let submission_epoch = rpc::query_epoch(client).await; - - match result { - ProcessTxResponse::Applied(resp) if - // If a transaction is shielded - shielded_tx_epoch.is_some() && - // And it is rejected by a VP - resp.code == 1.to_string() && - // And the its submission epoch doesn't match construction epoch - shielded_tx_epoch.unwrap() != submission_epoch => - { - // Then we probably straddled an epoch boundary. Let's retry... - eprintln!( - "MASP transaction rejected and this may be due to the \ - epoch changing. Attempting to resubmit transaction.", - ); - continue; - }, - // Otherwise either the transaction was successful or it will not - // benefit from resubmission - _ => break, + let shielded_parts = match stx_result { + Ok(stx) => Ok(stx), + Err(builder::Error::InsufficientFunds(_)) => { + Err(Error::NegativeBalanceAfterTransfer( + Box::new(source.clone()), + validated_amount.amount.to_string_native(), + Box::new(token.clone()), + validate_fee.amount.to_string_native(), + Box::new(args.tx.fee_token.clone()), + )) } - } - Ok(()) + Err(err) => Err(Error::MaspError(err)), + }?; + + let mut tx = Tx::new(TxType::Raw); + tx.header.chain_id = args.tx.chain_id.clone().unwrap(); + tx.header.expiration = args.tx.expiration; + // Add the MASP Transaction and its Builder to facilitate validation + let (masp_hash, shielded_tx_epoch) = if let Some(shielded_parts) = + shielded_parts + { + // Add a MASP Transaction section to the Tx + let masp_tx = tx.add_section(Section::MaspTx(shielded_parts.1)); + // Get the hash of the MASP Transaction section + let masp_hash = masp_tx.get_hash(); + // Get the decoded asset types used in the transaction to give + // offline wallet users more information + let asset_types = used_asset_types(shielded, client, &shielded_parts.0) + .await + .unwrap_or_default(); + // Add the MASP Transaction's Builder to the Tx + tx.add_section(Section::MaspBuilder(MaspBuilder { + asset_types, + // Store how the Info objects map to Descriptors/Outputs + metadata: shielded_parts.2, + // Store the data that was used to construct the Transaction + builder: shielded_parts.0, + // Link the Builder to the Transaction by hash code + target: masp_hash, + })); + // The MASP Transaction section hash will be used in Transfer + (Some(masp_hash), Some(shielded_parts.3)) + } else { + (None, None) + }; + // Construct the corresponding transparent Transfer object + let transfer = token::Transfer { + source: source.clone(), + target: target.clone(), + token: token.clone(), + sub_prefix: sub_prefix.clone(), + amount: validated_amount, + key: key.clone(), + // Link the Transfer to the MASP Transaction by hash code + shielded: masp_hash, + }; + tracing::debug!("Transfer data {:?}", transfer); + // Encode the Transfer and store it beside the MASP transaction + let data = transfer + .try_to_vec() + .expect("Encoding tx data shouldn't fail"); + tx.set_data(Data::new(data)); + // Finally store the Traansfer WASM code in the Tx + tx.set_code(Code::from_hash(tx_code_hash)); + + // Dry-run/broadcast/submit the transaction + let (tx, signer_addr, def_key) = prepare_tx::( + client, + wallet, + &args.tx, + tx, + default_signer.clone(), + #[cfg(not(feature = "mainnet"))] + is_source_faucet, + ) + .await?; + Ok(( + tx, + signer_addr, + def_key, + shielded_tx_epoch, + is_source_faucet, + )) } /// Submit a transaction to initialize an account -pub async fn submit_init_account< +pub async fn build_init_account< C: crate::ledger::queries::Client + Sync, U: WalletUtils, >( client: &C, wallet: &mut Wallet, args: args::TxInitAccount, -) -> Result<(), Error> { +) -> Result<(Tx, Option
, common::PublicKey), Error> { let public_key = args.public_key; let vp_code_hash = @@ -1480,18 +1509,15 @@ pub async fn submit_init_account< tx.header.expiration = args.tx.expiration; let extra = tx.add_section(Section::ExtraData(Code::from_hash(vp_code_hash))); - let extra_hash = - Hash(extra.hash(&mut Sha256::new()).finalize_reset().into()); let data = InitAccount { public_key, - vp_code_hash: extra_hash, + vp_code_hash: extra.get_hash(), }; let data = data.try_to_vec().map_err(Error::EncodeTxFailure)?; tx.set_data(Data::new(data)); tx.set_code(Code::from_hash(tx_code_hash)); - // TODO Move unwrap to an either - let initialized_accounts = process_tx::( + prepare_tx::( client, wallet, &args.tx, @@ -1501,22 +1527,17 @@ pub async fn submit_init_account< false, ) .await - .unwrap() - .initialized_accounts(); - save_initialized_accounts::(wallet, &args.tx, initialized_accounts) - .await; - Ok(()) } /// Submit a transaction to update a VP -pub async fn submit_update_vp< +pub async fn build_update_vp< C: crate::ledger::queries::Client + Sync, U: WalletUtils, >( client: &C, wallet: &mut Wallet, args: args::TxUpdateVp, -) -> Result<(), Error> { +) -> Result<(Tx, Option
, common::PublicKey), Error> { let addr = args.addr.clone(); // Check that the address is established and exists on chain @@ -1574,17 +1595,15 @@ pub async fn submit_update_vp< tx.header.expiration = args.tx.expiration; let extra = tx.add_section(Section::ExtraData(Code::from_hash(vp_code_hash))); - let extra_hash = - Hash(extra.hash(&mut Sha256::new()).finalize_reset().into()); let data = UpdateVp { addr, - vp_code_hash: extra_hash, + vp_code_hash: extra.get_hash(), }; let data = data.try_to_vec().map_err(Error::EncodeTxFailure)?; tx.set_data(Data::new(data)); tx.set_code(Code::from_hash(tx_code_hash)); - process_tx::( + prepare_tx::( client, wallet, &args.tx, @@ -1593,26 +1612,25 @@ pub async fn submit_update_vp< #[cfg(not(feature = "mainnet"))] false, ) - .await?; - Ok(()) + .await } /// Submit a custom transaction -pub async fn submit_custom< +pub async fn build_custom< C: crate::ledger::queries::Client + Sync, U: WalletUtils, >( client: &C, wallet: &mut Wallet, args: args::TxCustom, -) -> Result<(), Error> { +) -> Result<(Tx, Option
, common::PublicKey), Error> { let mut tx = Tx::new(TxType::Raw); tx.header.chain_id = args.tx.chain_id.clone().unwrap(); tx.header.expiration = args.tx.expiration; args.data_path.map(|data| tx.set_data(Data::new(data))); tx.set_code(Code::new(args.code_path)); - let initialized_accounts = process_tx::( + prepare_tx::( client, wallet, &args.tx, @@ -1621,11 +1639,7 @@ pub async fn submit_custom< #[cfg(not(feature = "mainnet"))] false, ) - .await? - .initialized_accounts(); - save_initialized_accounts::(wallet, &args.tx, initialized_accounts) - .await; - Ok(()) + .await } async fn expect_dry_broadcast( diff --git a/shared/src/ledger/vp_host_fns.rs b/shared/src/ledger/vp_host_fns.rs index 5d89e4006d..92c8e19e1c 100644 --- a/shared/src/ledger/vp_host_fns.rs +++ b/shared/src/ledger/vp_host_fns.rs @@ -289,7 +289,7 @@ pub fn get_tx_code_hash( ) -> EnvResult> { let hash = tx .get_section(tx.code_sechash()) - .and_then(Section::code_sec) + .and_then(|x| Section::code_sec(x.as_ref())) .map(|x| x.code.hash()); add_gas(gas_meter, MIN_STORAGE_GAS)?; Ok(hash) diff --git a/shared/src/vm/wasm/run.rs b/shared/src/vm/wasm/run.rs index 6e7282ff7b..c006cb2f16 100644 --- a/shared/src/vm/wasm/run.rs +++ b/shared/src/vm/wasm/run.rs @@ -96,7 +96,7 @@ where { let tx_code = tx .get_section(tx.code_sechash()) - .and_then(Section::code_sec) + .and_then(|x| Section::code_sec(x.as_ref())) .ok_or(Error::MissingCode)?; let (module, store) = match tx_code.code { Commitment::Hash(code_hash) => { diff --git a/tests/src/e2e/ledger_tests.rs b/tests/src/e2e/ledger_tests.rs index 70900abf7d..4593bf50e1 100644 --- a/tests/src/e2e/ledger_tests.rs +++ b/tests/src/e2e/ledger_tests.rs @@ -527,7 +527,9 @@ fn ledger_txs_and_queries() -> Result<()> { for tx_args in &txs_args { for &dry_run in &[true, false] { - let tx_args = if dry_run { + let tx_args = if dry_run && tx_args[0] == "tx" { + continue; + } else if dry_run { vec![tx_args.clone(), vec!["--dry-run"]].concat() } else { tx_args.clone() @@ -707,8 +709,6 @@ fn masp_txs_and_queries() -> Result<()> { ETH, "--amount", "10", - "--signer", - ALBERT, "--node", &validator_one_rpc, ], @@ -726,8 +726,6 @@ fn masp_txs_and_queries() -> Result<()> { BTC, "--amount", "7", - "--signer", - ALBERT, "--node", &validator_one_rpc, ], @@ -745,8 +743,6 @@ fn masp_txs_and_queries() -> Result<()> { BTC, "--amount", "7", - "--signer", - ALBERT, "--node", &validator_one_rpc, ], @@ -764,8 +760,6 @@ fn masp_txs_and_queries() -> Result<()> { BTC, "--amount", "7", - "--signer", - ALBERT, "--node", &validator_one_rpc, ], @@ -783,8 +777,6 @@ fn masp_txs_and_queries() -> Result<()> { BTC, "--amount", "6", - "--signer", - ALBERT, "--node", &validator_one_rpc, ], @@ -839,8 +831,6 @@ fn masp_txs_and_queries() -> Result<()> { BTC, "--amount", "20", - "--signer", - BERTHA, "--node", &validator_one_rpc, ], diff --git a/tests/src/vm_host_env/mod.rs b/tests/src/vm_host_env/mod.rs index 9fa53dd3f2..1766d7bad3 100644 --- a/tests/src/vm_host_env/mod.rs +++ b/tests/src/vm_host_env/mod.rs @@ -461,11 +461,7 @@ mod tests { tx.set_code(Code::new(code.clone())); tx.set_data(Data::new(data.clone())); tx.add_section(Section::Signature(Signature::new( - tx.code_sechash(), - &keypair, - ))); - tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), + vec![*tx.code_sechash(), *tx.data_sechash()], &keypair, ))); env.tx = tx; @@ -474,7 +470,13 @@ mod tests { assert_eq!(signed_tx_data.data().as_ref(), Some(data)); assert!( signed_tx_data - .verify_signature(&pk, signed_tx_data.data_sechash()) + .verify_signature( + &pk, + &[ + *signed_tx_data.data_sechash(), + *signed_tx_data.code_sechash(), + ], + ) .is_ok() ); @@ -483,7 +485,10 @@ mod tests { signed_tx_data .verify_signature( &other_keypair.ref_to(), - signed_tx_data.data_sechash() + &[ + *signed_tx_data.data_sechash(), + *signed_tx_data.code_sechash(), + ], ) .is_err() ); @@ -541,11 +546,7 @@ mod tests { tx.set_code(Code::new(vec![])); tx.set_data(Data::new(input_data)); tx.add_section(Section::Signature(Signature::new( - tx.code_sechash(), - &key::testing::keypair_1(), - ))); - tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), + vec![*tx.code_sechash(), *tx.data_sechash()], &key::testing::keypair_1(), ))); let result = vp::CTX.eval(empty_code, tx).unwrap(); @@ -564,11 +565,7 @@ mod tests { tx.set_code(Code::new(vec![])); tx.set_data(Data::new(input_data)); tx.add_section(Section::Signature(Signature::new( - tx.code_sechash(), - &key::testing::keypair_1(), - ))); - tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), + vec![*tx.code_sechash(), *tx.data_sechash()], &key::testing::keypair_1(), ))); let result = vp::CTX.eval(code_hash, tx).unwrap(); @@ -588,11 +585,7 @@ mod tests { tx.set_code(Code::new(vec![])); tx.set_data(Data::new(input_data)); tx.add_section(Section::Signature(Signature::new( - tx.code_sechash(), - &key::testing::keypair_1(), - ))); - tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), + vec![*tx.code_sechash(), *tx.data_sechash()], &key::testing::keypair_1(), ))); let result = vp::CTX.eval(code_hash, tx).unwrap(); @@ -614,11 +607,7 @@ mod tests { tx.set_code(Code::new(vec![])); tx.set_data(Data::new(tx_data.clone())); tx.add_section(Section::Signature(Signature::new( - tx.code_sechash(), - &key::testing::keypair_1(), - ))); - tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), + vec![*tx.code_sechash(), *tx.data_sechash()], &key::testing::keypair_1(), ))); @@ -654,11 +643,7 @@ mod tests { tx.set_code(Code::new(vec![])); tx.set_data(Data::new(tx_data.clone())); tx.add_section(Section::Signature(Signature::new( - tx.code_sechash(), - &key::testing::keypair_1(), - ))); - tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), + vec![*tx.code_sechash(), *tx.data_sechash()], &key::testing::keypair_1(), ))); // update the client with the message @@ -697,11 +682,7 @@ mod tests { tx.set_code(Code::new(vec![])); tx.set_data(Data::new(tx_data.clone())); tx.add_section(Section::Signature(Signature::new( - tx.code_sechash(), - &key::testing::keypair_1(), - ))); - tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), + vec![*tx.code_sechash(), *tx.data_sechash()], &key::testing::keypair_1(), ))); // init a connection with the message @@ -736,11 +717,7 @@ mod tests { tx.set_code(Code::new(vec![])); tx.set_data(Data::new(tx_data.clone())); tx.add_section(Section::Signature(Signature::new( - tx.code_sechash(), - &key::testing::keypair_1(), - ))); - tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), + vec![*tx.code_sechash(), *tx.data_sechash()], &key::testing::keypair_1(), ))); // open the connection with the message @@ -780,11 +757,7 @@ mod tests { tx.set_code(Code::new(vec![])); tx.set_data(Data::new(tx_data.clone())); tx.add_section(Section::Signature(Signature::new( - tx.code_sechash(), - &key::testing::keypair_1(), - ))); - tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), + vec![*tx.code_sechash(), *tx.data_sechash()], &key::testing::keypair_1(), ))); // open try a connection with the message @@ -819,11 +792,7 @@ mod tests { tx.set_code(Code::new(vec![])); tx.set_data(Data::new(tx_data.clone())); tx.add_section(Section::Signature(Signature::new( - tx.code_sechash(), - &key::testing::keypair_1(), - ))); - tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), + vec![*tx.code_sechash(), *tx.data_sechash()], &key::testing::keypair_1(), ))); // open the connection with the mssage @@ -865,11 +834,7 @@ mod tests { tx.set_code(Code::new(vec![])); tx.set_data(Data::new(tx_data.clone())); tx.add_section(Section::Signature(Signature::new( - tx.code_sechash(), - &key::testing::keypair_1(), - ))); - tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), + vec![*tx.code_sechash(), *tx.data_sechash()], &key::testing::keypair_1(), ))); // init a channel with the message @@ -904,11 +869,7 @@ mod tests { tx.set_code(Code::new(vec![])); tx.set_data(Data::new(tx_data.clone())); tx.add_section(Section::Signature(Signature::new( - tx.code_sechash(), - &key::testing::keypair_1(), - ))); - tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), + vec![*tx.code_sechash(), *tx.data_sechash()], &key::testing::keypair_1(), ))); // open the channle with the message @@ -950,11 +911,7 @@ mod tests { tx.set_code(Code::new(vec![])); tx.set_data(Data::new(tx_data.clone())); tx.add_section(Section::Signature(Signature::new( - tx.code_sechash(), - &key::testing::keypair_1(), - ))); - tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), + vec![*tx.code_sechash(), *tx.data_sechash()], &key::testing::keypair_1(), ))); // try open a channel with the message @@ -990,11 +947,7 @@ mod tests { tx.set_code(Code::new(vec![])); tx.set_data(Data::new(tx_data.clone())); tx.add_section(Section::Signature(Signature::new( - tx.code_sechash(), - &key::testing::keypair_1(), - ))); - tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), + vec![*tx.code_sechash(), *tx.data_sechash()], &key::testing::keypair_1(), ))); // open a channel with the message @@ -1039,11 +992,7 @@ mod tests { tx.set_code(Code::new(vec![])); tx.set_data(Data::new(tx_data.clone())); tx.add_section(Section::Signature(Signature::new( - tx.code_sechash(), - &key::testing::keypair_1(), - ))); - tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), + vec![*tx.code_sechash(), *tx.data_sechash()], &key::testing::keypair_1(), ))); // close the channel with the message @@ -1096,11 +1045,7 @@ mod tests { tx.set_code(Code::new(vec![])); tx.set_data(Data::new(tx_data.clone())); tx.add_section(Section::Signature(Signature::new( - tx.code_sechash(), - &key::testing::keypair_1(), - ))); - tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), + vec![*tx.code_sechash(), *tx.data_sechash()], &key::testing::keypair_1(), ))); @@ -1150,11 +1095,7 @@ mod tests { tx.set_code(Code::new(vec![])); tx.set_data(Data::new(tx_data.clone())); tx.add_section(Section::Signature(Signature::new( - tx.code_sechash(), - &key::testing::keypair_1(), - ))); - tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), + vec![*tx.code_sechash(), *tx.data_sechash()], &key::testing::keypair_1(), ))); // send the token and a packet with the data @@ -1203,11 +1144,7 @@ mod tests { tx.set_code(Code::new(vec![])); tx.set_data(Data::new(tx_data.clone())); tx.add_section(Section::Signature(Signature::new( - tx.code_sechash(), - &key::testing::keypair_1(), - ))); - tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), + vec![*tx.code_sechash(), *tx.data_sechash()], &key::testing::keypair_1(), ))); // ack the packet with the message @@ -1283,11 +1220,7 @@ mod tests { tx.set_code(Code::new(vec![])); tx.set_data(Data::new(tx_data.clone())); tx.add_section(Section::Signature(Signature::new( - tx.code_sechash(), - &key::testing::keypair_1(), - ))); - tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), + vec![*tx.code_sechash(), *tx.data_sechash()], &key::testing::keypair_1(), ))); // send the token and a packet with the data @@ -1363,11 +1296,7 @@ mod tests { tx.set_code(Code::new(vec![])); tx.set_data(Data::new(tx_data.clone())); tx.add_section(Section::Signature(Signature::new( - tx.code_sechash(), - &key::testing::keypair_1(), - ))); - tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), + vec![*tx.code_sechash(), *tx.data_sechash()], &key::testing::keypair_1(), ))); // receive a packet with the message @@ -1455,11 +1384,7 @@ mod tests { tx.set_code(Code::new(vec![])); tx.set_data(Data::new(tx_data.clone())); tx.add_section(Section::Signature(Signature::new( - tx.code_sechash(), - &key::testing::keypair_1(), - ))); - tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), + vec![*tx.code_sechash(), *tx.data_sechash()], &key::testing::keypair_1(), ))); // receive a packet with the message @@ -1551,11 +1476,7 @@ mod tests { tx.set_code(Code::new(vec![])); tx.set_data(Data::new(tx_data.clone())); tx.add_section(Section::Signature(Signature::new( - tx.code_sechash(), - &key::testing::keypair_1(), - ))); - tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), + vec![*tx.code_sechash(), *tx.data_sechash()], &key::testing::keypair_1(), ))); // receive a packet with the message @@ -1650,11 +1571,7 @@ mod tests { tx.set_code(Code::new(vec![])); tx.set_data(Data::new(tx_data.clone())); tx.add_section(Section::Signature(Signature::new( - tx.code_sechash(), - &key::testing::keypair_1(), - ))); - tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), + vec![*tx.code_sechash(), *tx.data_sechash()], &key::testing::keypair_1(), ))); @@ -1739,11 +1656,7 @@ mod tests { tx.set_code(Code::new(vec![])); tx.set_data(Data::new(tx_data.clone())); tx.add_section(Section::Signature(Signature::new( - tx.code_sechash(), - &key::testing::keypair_1(), - ))); - tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), + vec![*tx.code_sechash(), *tx.data_sechash()], &key::testing::keypair_1(), ))); diff --git a/wasm/checksums.json b/wasm/checksums.json index 4dc880d270..68000e2f7d 100644 --- a/wasm/checksums.json +++ b/wasm/checksums.json @@ -1,21 +1,21 @@ { - "tx_bond.wasm": "tx_bond.22ae29557b12fcd402add2041b0616b02ec517b255f57fe2a2b42f8e8b122d6a.wasm", - "tx_change_validator_commission.wasm": "tx_change_validator_commission.bc2221a98b0a7434ee10fc60e90c6da75722d18233bb20b878959ca739dde3a3.wasm", - "tx_ibc.wasm": "tx_ibc.6a67b61b4b749498f5f11d667d41bad841781089bf104f53843675569cbead06.wasm", - "tx_init_account.wasm": "tx_init_account.8d0853e9b8c2c453ebf18d32ba7268150318d64efd93d93bcfbb1f1e21789dfd.wasm", - "tx_init_proposal.wasm": "tx_init_proposal.6fb695273206ac9a0666e6d25ed002cd335673b6f444e01df9fcc8b4a0bf5967.wasm", - "tx_init_validator.wasm": "tx_init_validator.d57519588d95903437d987157d293e25f2e919fa4da4b9e998b95f4b6c8e098b.wasm", - "tx_reveal_pk.wasm": "tx_reveal_pk.24a641072eb7aba949f0d4f927dc156f8096d3e4da6fad046363a3fc1cf0cb01.wasm", - "tx_transfer.wasm": "tx_transfer.60a7dbe38bad2a86f52f6cd109cd30002b46de4cd7ee82045fd3302da6deb424.wasm", - "tx_unbond.wasm": "tx_unbond.64ce8a1181e993c8cea75ec52c8a103d98d946e633ccd1c134041ba5473ffbce.wasm", - "tx_unjail_validator.wasm": "tx_unjail_validator.fdb92288ddf746c8e94fdd3d1cef8eb0498a576da6d080bce9a0c6ed32dde909.wasm", - "tx_update_vp.wasm": "tx_update_vp.5ede9d5a56d8ebe7075f9a9bb8a988fcd629e5ca1f800ca738a10d8b31d56ae7.wasm", - "tx_vote_proposal.wasm": "tx_vote_proposal.2f7176863cec7379cbf2c27e17dc6475e2a4b5a772e4142bb3cefa5783cb710f.wasm", - "tx_withdraw.wasm": "tx_withdraw.f30cc0a7d015cdc141cb3d68865006486f0cdb40140ddafec11528fd3e9ceb3c.wasm", - "vp_implicit.wasm": "vp_implicit.ee1caec6e233c899dd6c0f7106ae493304394a7a0774db0cd0714afdee8d26bd.wasm", - "vp_masp.wasm": "vp_masp.8f2e84eeacf76c17892d7cd4e327a071ce399ab4cdd48fb709bb66324e82bea1.wasm", - "vp_testnet_faucet.wasm": "vp_testnet_faucet.9cd90e26b1d2660ad1add04e65c302325de7210fca5cce2f520a08ad83f9cba7.wasm", - "vp_token.wasm": "vp_token.94be4a19c2b3e4b78f61d213ea5d070e81f6a79348bc6b05ff6cc36291a2dbcb.wasm", - "vp_user.wasm": "vp_user.595d69f43189d651c7fddbb9106683522ae5128194b382ff4193952930faa142.wasm", - "vp_validator.wasm": "vp_validator.285dc9030008de641312269bb4e707a2882ff7daeb18ddfc70a238eddf00af57.wasm" + "tx_bond.wasm": "tx_bond.cc32955f1d4db4a3da181422d18a7f957f9621b6741f89833368c4401f906a4b.wasm", + "tx_change_validator_commission.wasm": "tx_change_validator_commission.204e819544ae9ea6a3b1da4737eb7d17c4cdf2b9a13a2ebca0437abaee8aa93c.wasm", + "tx_ibc.wasm": "tx_ibc.e4900783fd0432da76a4d8670e6fb4c8ffbbbbafcd6f7c297470b1fb2101e89c.wasm", + "tx_init_account.wasm": "tx_init_account.804728adb12afd263bb2e85bee2d54b6075fc5dbac960cfc0bd515d1885d1953.wasm", + "tx_init_proposal.wasm": "tx_init_proposal.f59e980d3d6318f55bb9b415696810b7ea74e1561c79e5535cf921b86a732469.wasm", + "tx_init_validator.wasm": "tx_init_validator.a4a46d9d8260c2ae6fa0457b94e9d68226e36082a5e40e224a2543ad8b2dee7b.wasm", + "tx_reveal_pk.wasm": "tx_reveal_pk.0c739b1bb6e27476d81e662816b61abdaa4096dc44d3125351fec6c2334ac8bf.wasm", + "tx_transfer.wasm": "tx_transfer.aa377c8949f3248f6497d520fd14ba877211eec328866bc2189cc777c18c1402.wasm", + "tx_unbond.wasm": "tx_unbond.e87561880b0dc53b44de1464e5ef2ac3d87f9d88458888d99fc2934416edad51.wasm", + "tx_unjail_validator.wasm": "tx_unjail_validator.fc0f2633868c70e650ed6c98fdc7b6c29059346bca2580f9b6bbc191fe31c892.wasm", + "tx_update_vp.wasm": "tx_update_vp.c264b31144dec6ca24624351c3a37ba7d47f618dd7b3f6b3d0b679b0e0fb9c47.wasm", + "tx_vote_proposal.wasm": "tx_vote_proposal.ee75560b69a2566ecfd2fe8eb2fb166aeb8528048ee11003cf427939f765f3ad.wasm", + "tx_withdraw.wasm": "tx_withdraw.01dccfcac196e7df1d0e1a4abb9162c551d4d5350764d0130fa4b40f4d2a92fd.wasm", + "vp_implicit.wasm": "vp_implicit.a3292269ba4e807ea9ed0601bdee00957eb3105448587c2faf2e46fded7b3895.wasm", + "vp_masp.wasm": "vp_masp.2b0fa9b4666ee36d9e5cee481b24e5d9946c566ccb0db7cca4f066448be86a3a.wasm", + "vp_testnet_faucet.wasm": "vp_testnet_faucet.304915e0f5188937529ee8163b25dcc175a580062f60c256277db72cd5296555.wasm", + "vp_token.wasm": "vp_token.8b36f44e51ff3d73c06a06f5cd4aa38a358d2bfe7a7b5c146c515878c254932f.wasm", + "vp_user.wasm": "vp_user.054d109bc1415e8bc8f9df23281b10a1b844d70150b224ba34ae699fed9568e3.wasm", + "vp_validator.wasm": "vp_validator.87d47ebcf7141cff0399d5ee797c213ea1c29e031c840a5b09c248d8c02deec2.wasm" } \ No newline at end of file diff --git a/wasm/wasm_source/src/tx_bond.rs b/wasm/wasm_source/src/tx_bond.rs index b7a0bdb4ca..29546f98e5 100644 --- a/wasm/wasm_source/src/tx_bond.rs +++ b/wasm/wasm_source/src/tx_bond.rs @@ -108,11 +108,7 @@ mod tests { tx.set_code(Code::new(tx_code)); tx.set_data(Data::new(tx_data)); tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), - &key, - ))); - tx.add_section(Section::Signature(Signature::new( - tx.code_sechash(), + vec![*tx.data_sechash(), *tx.code_sechash()], &key, ))); let signed_tx = tx.clone(); diff --git a/wasm/wasm_source/src/tx_change_validator_commission.rs b/wasm/wasm_source/src/tx_change_validator_commission.rs index 4c9410c748..06efc9bbf7 100644 --- a/wasm/wasm_source/src/tx_change_validator_commission.rs +++ b/wasm/wasm_source/src/tx_change_validator_commission.rs @@ -83,11 +83,7 @@ mod tests { tx.set_data(Data::new(tx_data)); tx.set_code(Code::new(tx_code)); tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), - &key, - ))); - tx.add_section(Section::Signature(Signature::new( - tx.code_sechash(), + vec![*tx.data_sechash(), *tx.code_sechash()], &key, ))); let signed_tx = tx.clone(); diff --git a/wasm/wasm_source/src/tx_init_proposal.rs b/wasm/wasm_source/src/tx_init_proposal.rs index 2507a18d21..c0da1d9316 100644 --- a/wasm/wasm_source/src/tx_init_proposal.rs +++ b/wasm/wasm_source/src/tx_init_proposal.rs @@ -3,13 +3,28 @@ use namada_tx_prelude::*; #[transaction] -fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { - let signed = tx_data; - let data = signed.data().ok_or_err_msg("Missing data")?; +fn apply_tx(ctx: &mut Ctx, tx: Tx) -> TxResult { + let data = tx.data().ok_or_err_msg("Missing data")?; let tx_data = transaction::governance::InitProposalData::try_from_slice(&data[..]) .wrap_err("failed to decode InitProposalData")?; + // Get the content from the referred to section + let content = tx + .get_section(&tx_data.content) + .ok_or_err_msg("Missing proposal content")? + .extra_data() + .ok_or_err_msg("Missing full proposal content")?; + // Get the code from the referred to section + let code = match tx_data.r#type { + transaction::governance::ProposalType::Default(Some(hash)) => Some( + tx.get_section(&hash) + .ok_or_err_msg("Missing proposal code")? + .extra_data() + .ok_or_err_msg("Missing full proposal code")?, + ), + _ => None, + }; log_string("apply_tx called to create a new governance proposal"); - governance::init_proposal(ctx, tx_data) + governance::init_proposal(ctx, tx_data, content, code) } diff --git a/wasm/wasm_source/src/tx_transfer.rs b/wasm/wasm_source/src/tx_transfer.rs index 1727035d2f..d2e3dc314f 100644 --- a/wasm/wasm_source/src/tx_transfer.rs +++ b/wasm/wasm_source/src/tx_transfer.rs @@ -25,7 +25,7 @@ fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { .map(|hash| { signed .get_section(hash) - .and_then(Section::masp_tx) + .and_then(|x| x.as_ref().masp_tx()) .ok_or_err_msg("unable to find shielded section") }) .transpose()?; diff --git a/wasm/wasm_source/src/tx_unbond.rs b/wasm/wasm_source/src/tx_unbond.rs index 8608282966..9d2787bee5 100644 --- a/wasm/wasm_source/src/tx_unbond.rs +++ b/wasm/wasm_source/src/tx_unbond.rs @@ -132,11 +132,7 @@ mod tests { tx.set_data(Data::new(tx_data)); tx.set_code(Code::new(tx_code)); tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), - &key, - ))); - tx.add_section(Section::Signature(Signature::new( - tx.code_sechash(), + vec![*tx.data_sechash(), *tx.code_sechash()], &key, ))); let signed_tx = tx.clone(); diff --git a/wasm/wasm_source/src/tx_withdraw.rs b/wasm/wasm_source/src/tx_withdraw.rs index c476173f7c..32e56be462 100644 --- a/wasm/wasm_source/src/tx_withdraw.rs +++ b/wasm/wasm_source/src/tx_withdraw.rs @@ -176,11 +176,7 @@ mod tests { tx.set_code(Code::new(tx_code)); tx.set_data(Data::new(tx_data)); tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), - &key, - ))); - tx.add_section(Section::Signature(Signature::new( - tx.code_sechash(), + vec![*tx.data_sechash(), *tx.code_sechash()], &key, ))); let signed_tx = tx.clone(); diff --git a/wasm/wasm_source/src/vp_implicit.rs b/wasm/wasm_source/src/vp_implicit.rs index 73e5bf54b3..ec31020017 100644 --- a/wasm/wasm_source/src/vp_implicit.rs +++ b/wasm/wasm_source/src/vp_implicit.rs @@ -70,7 +70,10 @@ fn validate_tx( let pk = key::get(ctx, &addr); match pk { Ok(Some(pk)) => tx_data - .verify_signature(&pk, tx_data.data_sechash()) + .verify_signature( + &pk, + &[*tx_data.data_sechash(), *tx_data.code_sechash()], + ) .is_ok(), _ => false, } @@ -524,8 +527,10 @@ mod tests { let mut vp_env = vp_host_env::take(); let mut tx = vp_env.tx.clone(); + tx.set_data(Data::new(vec![])); + tx.set_code(Code::new(vec![])); tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), + vec![*tx.data_sechash(), *tx.code_sechash()], &secret_key, ))); let signed_tx = tx.clone(); @@ -656,8 +661,10 @@ mod tests { let mut vp_env = vp_host_env::take(); let mut tx = vp_env.tx.clone(); + tx.set_data(Data::new(vec![])); + tx.set_code(Code::new(vec![])); tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), + vec![*tx.data_sechash(), *tx.code_sechash()], &secret_key, ))); let signed_tx = tx.clone(); @@ -823,7 +830,9 @@ mod tests { let mut vp_env = vp_host_env::take(); let mut tx = vp_env.tx.clone(); - tx.add_section(Section::Signature(Signature::new(tx.data_sechash(), &secret_key))); + tx.set_data(Data::new(vec![])); + tx.set_code(Code::new(vec![])); + tx.add_section(Section::Signature(Signature::new(vec![*tx.data_sechash(), *tx.code_sechash()], &secret_key))); let signed_tx = tx.clone(); vp_env.tx = signed_tx.clone(); let keys_changed: BTreeSet = @@ -909,8 +918,9 @@ mod tests { let mut vp_env = vp_host_env::take(); let mut tx = vp_env.tx.clone(); tx.set_data(Data::new(vec![])); + tx.set_code(Code::new(vec![])); tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), + vec![*tx.data_sechash(), *tx.code_sechash()], &secret_key, ))); let signed_tx = tx.clone(); @@ -938,8 +948,8 @@ mod tests { // for the update tx_env.store_wasm_code(vp_code); - // hardcoded hash of VP_ALWAYS_TRUE_WASM - tx_env.init_parameters(None, None, Some(vec![vp_hash.to_string()])); + let empty_sha256 = sha256(&[]).to_string(); + tx_env.init_parameters(None, None, Some(vec![empty_sha256])); // Spawn the accounts to be able to modify their storage tx_env.spawn_accounts([&vp_owner]); @@ -956,10 +966,10 @@ mod tests { let mut vp_env = vp_host_env::take(); let mut tx = vp_env.tx.clone(); - tx.set_code(Code::from_hash(vp_hash)); + tx.set_code(Code::new(vec![])); tx.set_data(Data::new(vec![])); tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), + vec![*tx.data_sechash(), *tx.code_sechash()], &secret_key, ))); let signed_tx = tx.clone(); diff --git a/wasm/wasm_source/src/vp_masp.rs b/wasm/wasm_source/src/vp_masp.rs index 99d756e237..6fa86b510c 100644 --- a/wasm/wasm_source/src/vp_masp.rs +++ b/wasm/wasm_source/src/vp_masp.rs @@ -109,7 +109,7 @@ fn validate_tx( .map(|hash| { signed .get_section(hash) - .and_then(Section::masp_tx) + .and_then(|x| x.as_ref().masp_tx()) .ok_or_err_msg("unable to find shielded section") }) .transpose()?; diff --git a/wasm/wasm_source/src/vp_testnet_faucet.rs b/wasm/wasm_source/src/vp_testnet_faucet.rs index 95077d3cc5..8e002663ee 100644 --- a/wasm/wasm_source/src/vp_testnet_faucet.rs +++ b/wasm/wasm_source/src/vp_testnet_faucet.rs @@ -29,7 +29,10 @@ fn validate_tx( let pk = key::get(ctx, &addr); match pk { Ok(Some(pk)) => tx_data - .verify_signature(&pk, tx_data.data_sechash()) + .verify_signature( + &pk, + &[*tx_data.data_sechash(), *tx_data.code_sechash()], + ) .is_ok(), _ => false, } @@ -101,7 +104,7 @@ fn validate_tx( #[cfg(test)] mod tests { use address::testing::arb_non_internal_address; - use namada::proto::{Data, Signature}; + use namada::proto::{Code, Data, Signature}; use namada::types::transaction::TxType; use namada_test_utils::TestWasms; // Use this as `#[test]` annotation to enable logging @@ -257,8 +260,9 @@ mod tests { let mut vp_env = vp_host_env::take(); let mut tx = vp_env.tx.clone(); tx.set_data(Data::new(vec![])); + tx.set_code(Code::new(vec![])); tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), + vec![*tx.data_sechash(), *tx.code_sechash()], &keypair, ))); let signed_tx = tx.clone(); @@ -387,7 +391,8 @@ mod tests { vp_env.has_valid_pow = true; let mut tx_data = Tx::new(TxType::Raw); tx_data.set_data(Data::new(solution_bytes)); - tx_data.add_section(Section::Signature(Signature::new(tx_data.data_sechash(), &target_key))); + tx_data.set_code(Code::new(vec![])); + tx_data.add_section(Section::Signature(Signature::new(vec![*tx_data.data_sechash(), *tx_data.code_sechash()], &target_key))); let keys_changed: BTreeSet = vp_env.all_touched_storage_keys(); let verifiers: BTreeSet
= BTreeSet::default(); @@ -434,7 +439,8 @@ mod tests { let mut vp_env = vp_host_env::take(); let mut tx = vp_env.tx.clone(); tx.set_data(Data::new(vec![])); - tx.add_section(Section::Signature(Signature::new(tx.data_sechash(), &keypair))); + tx.set_code(Code::new(vec![])); + tx.add_section(Section::Signature(Signature::new(vec![*tx.data_sechash(), *tx.code_sechash()], &keypair))); let signed_tx = tx.clone(); vp_env.tx = signed_tx.clone(); let keys_changed: BTreeSet = diff --git a/wasm/wasm_source/src/vp_user.rs b/wasm/wasm_source/src/vp_user.rs index 4c71180128..12a5d229b6 100644 --- a/wasm/wasm_source/src/vp_user.rs +++ b/wasm/wasm_source/src/vp_user.rs @@ -68,7 +68,10 @@ fn validate_tx( let pk = key::get(ctx, &addr); match pk { Ok(Some(pk)) => tx_data - .verify_signature(&pk, tx_data.data_sechash()) + .verify_signature( + &pk, + &[*tx_data.data_sechash(), *tx_data.code_sechash()], + ) .is_ok(), _ => false, } @@ -395,8 +398,9 @@ mod tests { let mut vp_env = vp_host_env::take(); let mut tx = vp_env.tx.clone(); tx.set_data(Data::new(vec![])); + tx.set_code(Code::new(vec![])); tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), + vec![*tx.data_sechash(), *tx.code_sechash()], &keypair, ))); let signed_tx = tx.clone(); @@ -549,8 +553,9 @@ mod tests { let mut vp_env = vp_host_env::take(); let mut tx = vp_env.tx.clone(); tx.set_data(Data::new(vec![])); + tx.set_code(Code::new(vec![])); tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), + vec![*tx.data_sechash(), *tx.code_sechash()], &secret_key, ))); let signed_tx = tx.clone(); @@ -708,8 +713,9 @@ mod tests { let mut vp_env = vp_host_env::take(); let mut tx = vp_env.tx.clone(); + tx.set_code(Code::new(vec![])); tx.set_data(Data::new(vec![])); - tx.add_section(Section::Signature(Signature::new(tx.data_sechash(), &keypair))); + tx.add_section(Section::Signature(Signature::new(vec![*tx.data_sechash(), *tx.code_sechash()], &keypair))); let signed_tx = tx.clone(); vp_env.tx = signed_tx.clone(); let keys_changed: BTreeSet = @@ -747,6 +753,7 @@ mod tests { let vp_env = vp_host_env::take(); let mut tx_data = Tx::new(TxType::Raw); tx_data.set_data(Data::new(vec![])); + tx_data.set_code(Code::new(vec![])); let keys_changed: BTreeSet = vp_env.all_touched_storage_keys(); let verifiers: BTreeSet
= BTreeSet::default(); @@ -789,8 +796,9 @@ mod tests { let mut vp_env = vp_host_env::take(); let mut tx = vp_env.tx.clone(); tx.set_data(Data::new(vec![])); + tx.set_code(Code::new(vec![])); tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), + vec![*tx.data_sechash(), *tx.code_sechash()], &keypair, ))); let signed_tx = tx.clone(); @@ -836,8 +844,9 @@ mod tests { let mut vp_env = vp_host_env::take(); let mut tx = vp_env.tx.clone(); tx.set_data(Data::new(vec![])); + tx.set_code(Code::new(vec![])); tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), + vec![*tx.data_sechash(), *tx.code_sechash()], &keypair, ))); let signed_tx = tx.clone(); @@ -884,8 +893,9 @@ mod tests { let mut vp_env = vp_host_env::take(); let mut tx = vp_env.tx.clone(); tx.set_data(Data::new(vec![])); + tx.set_code(Code::new(vec![])); tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), + vec![*tx.data_sechash(), *tx.code_sechash()], &keypair, ))); let signed_tx = tx.clone(); @@ -936,8 +946,9 @@ mod tests { let mut vp_env = vp_host_env::take(); let mut tx = vp_env.tx.clone(); tx.set_data(Data::new(vec![])); + tx.set_code(Code::new(vec![])); tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), + vec![*tx.data_sechash(), *tx.code_sechash()], &keypair, ))); let signed_tx = tx.clone(); @@ -965,8 +976,8 @@ mod tests { // for the update tx_env.store_wasm_code(vp_code); - // hardcoded hash of VP_ALWAYS_TRUE_WASM - tx_env.init_parameters(None, None, Some(vec![vp_hash.to_string()])); + let empty_sha256 = sha256(&[]).to_string(); + tx_env.init_parameters(None, None, Some(vec![empty_sha256])); // Spawn the accounts to be able to modify their storage tx_env.spawn_accounts([&vp_owner]); @@ -983,10 +994,10 @@ mod tests { let mut vp_env = vp_host_env::take(); let mut tx = vp_env.tx.clone(); - tx.set_code(Code::from_hash(vp_hash)); + tx.set_code(Code::new(vec![])); tx.set_data(Data::new(vec![])); tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), + vec![*tx.data_sechash(), *tx.code_sechash()], &keypair, ))); let signed_tx = tx.clone(); diff --git a/wasm/wasm_source/src/vp_validator.rs b/wasm/wasm_source/src/vp_validator.rs index cf76cbc98c..aeb069bc56 100644 --- a/wasm/wasm_source/src/vp_validator.rs +++ b/wasm/wasm_source/src/vp_validator.rs @@ -68,7 +68,10 @@ fn validate_tx( let pk = key::get(ctx, &addr); match pk { Ok(Some(pk)) => tx_data - .verify_signature(&pk, tx_data.data_sechash()) + .verify_signature( + &pk, + &[*tx_data.data_sechash(), *tx_data.code_sechash()], + ) .is_ok(), _ => false, } @@ -401,8 +404,9 @@ mod tests { let mut vp_env = vp_host_env::take(); let mut tx = vp_env.tx.clone(); tx.set_data(Data::new(vec![])); + tx.set_code(Code::new(vec![])); tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), + vec![*tx.data_sechash(), *tx.code_sechash()], &keypair, ))); let signed_tx = tx.clone(); @@ -567,8 +571,9 @@ mod tests { let mut vp_env = vp_host_env::take(); let mut tx = vp_env.tx.clone(); tx.set_data(Data::new(vec![])); + tx.set_code(Code::new(vec![])); tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), + vec![*tx.data_sechash(), *tx.code_sechash()], &secret_key, ))); let signed_tx = tx.clone(); @@ -726,7 +731,8 @@ mod tests { let mut vp_env = vp_host_env::take(); let mut tx = vp_env.tx.clone(); tx.set_data(Data::new(vec![])); - tx.add_section(Section::Signature(Signature::new(tx.data_sechash(), &keypair))); + tx.set_code(Code::new(vec![])); + tx.add_section(Section::Signature(Signature::new(vec![*tx.data_sechash(), *tx.code_sechash()], &keypair))); let signed_tx = tx.clone(); vp_env.tx = signed_tx.clone(); let keys_changed: BTreeSet = @@ -806,8 +812,9 @@ mod tests { let mut vp_env = vp_host_env::take(); let mut tx = vp_env.tx.clone(); tx.set_data(Data::new(vec![])); + tx.set_code(Code::new(vec![])); tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), + vec![*tx.data_sechash(), *tx.code_sechash()], &keypair, ))); let signed_tx = tx.clone(); @@ -853,8 +860,9 @@ mod tests { let mut vp_env = vp_host_env::take(); let mut tx = vp_env.tx.clone(); tx.set_data(Data::new(vec![])); + tx.set_code(Code::new(vec![])); tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), + vec![*tx.data_sechash(), *tx.code_sechash()], &keypair, ))); let signed_tx = tx.clone(); @@ -901,8 +909,9 @@ mod tests { let mut vp_env = vp_host_env::take(); let mut tx = vp_env.tx.clone(); tx.set_data(Data::new(vec![])); + tx.set_code(Code::new(vec![])); tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), + vec![*tx.data_sechash(), *tx.code_sechash()], &keypair, ))); let signed_tx = tx.clone(); @@ -953,8 +962,9 @@ mod tests { let mut vp_env = vp_host_env::take(); let mut tx = vp_env.tx.clone(); tx.set_data(Data::new(vec![])); + tx.set_code(Code::new(vec![])); tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), + vec![*tx.data_sechash(), *tx.code_sechash()], &keypair, ))); let signed_tx = tx.clone(); @@ -982,8 +992,8 @@ mod tests { // for the update tx_env.store_wasm_code(vp_code); - // hardcoded hash of VP_ALWAYS_TRUE_WASM - tx_env.init_parameters(None, None, Some(vec![vp_hash.to_string()])); + let empty_sha256 = sha256(&[]).to_string(); + tx_env.init_parameters(None, None, Some(vec![empty_sha256])); // Spawn the accounts to be able to modify their storage tx_env.spawn_accounts([&vp_owner]); @@ -1000,10 +1010,10 @@ mod tests { let mut vp_env = vp_host_env::take(); let mut tx = vp_env.tx.clone(); - tx.set_code(Code::from_hash(vp_hash)); + tx.set_code(Code::new(vec![])); tx.set_data(Data::new(vec![])); tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), + vec![*tx.data_sechash(), *tx.code_sechash()], &keypair, ))); let signed_tx = tx.clone(); diff --git a/wasm_for_tests/tx_memory_limit.wasm b/wasm_for_tests/tx_memory_limit.wasm index a347676c78..7d46c64e74 100755 Binary files a/wasm_for_tests/tx_memory_limit.wasm and b/wasm_for_tests/tx_memory_limit.wasm differ diff --git a/wasm_for_tests/tx_mint_tokens.wasm b/wasm_for_tests/tx_mint_tokens.wasm index 55619c33f5..f413ead2ce 100755 Binary files a/wasm_for_tests/tx_mint_tokens.wasm and b/wasm_for_tests/tx_mint_tokens.wasm differ diff --git a/wasm_for_tests/tx_no_op.wasm b/wasm_for_tests/tx_no_op.wasm index 7cfb49d910..868d8a79ae 100755 Binary files a/wasm_for_tests/tx_no_op.wasm and b/wasm_for_tests/tx_no_op.wasm differ diff --git a/wasm_for_tests/tx_proposal_code.wasm b/wasm_for_tests/tx_proposal_code.wasm index b2d35b39c8..fcc9a12508 100755 Binary files a/wasm_for_tests/tx_proposal_code.wasm and b/wasm_for_tests/tx_proposal_code.wasm differ diff --git a/wasm_for_tests/tx_read_storage_key.wasm b/wasm_for_tests/tx_read_storage_key.wasm index e79ae72cb9..9f11f81631 100755 Binary files a/wasm_for_tests/tx_read_storage_key.wasm and b/wasm_for_tests/tx_read_storage_key.wasm differ diff --git a/wasm_for_tests/tx_write.wasm b/wasm_for_tests/tx_write.wasm index 2d0ce34be7..8ce26b7e8c 100755 Binary files a/wasm_for_tests/tx_write.wasm and b/wasm_for_tests/tx_write.wasm differ diff --git a/wasm_for_tests/vp_always_false.wasm b/wasm_for_tests/vp_always_false.wasm index 389acc29b1..3392480db2 100755 Binary files a/wasm_for_tests/vp_always_false.wasm and b/wasm_for_tests/vp_always_false.wasm differ diff --git a/wasm_for_tests/vp_always_true.wasm b/wasm_for_tests/vp_always_true.wasm index 47460e2f57..43cef29306 100755 Binary files a/wasm_for_tests/vp_always_true.wasm and b/wasm_for_tests/vp_always_true.wasm differ diff --git a/wasm_for_tests/vp_eval.wasm b/wasm_for_tests/vp_eval.wasm index 218a125101..11a20bbb02 100755 Binary files a/wasm_for_tests/vp_eval.wasm and b/wasm_for_tests/vp_eval.wasm differ diff --git a/wasm_for_tests/vp_memory_limit.wasm b/wasm_for_tests/vp_memory_limit.wasm index cfe4d441cc..67b546edf7 100755 Binary files a/wasm_for_tests/vp_memory_limit.wasm and b/wasm_for_tests/vp_memory_limit.wasm differ diff --git a/wasm_for_tests/vp_read_storage_key.wasm b/wasm_for_tests/vp_read_storage_key.wasm index 7c7ebc8c81..0a7709eba5 100755 Binary files a/wasm_for_tests/vp_read_storage_key.wasm and b/wasm_for_tests/vp_read_storage_key.wasm differ