diff --git a/.changelog/unreleased/improvements/1884-multisig-fixes.md b/.changelog/unreleased/improvements/1884-multisig-fixes.md new file mode 100644 index 0000000000..3c380fb3f4 --- /dev/null +++ b/.changelog/unreleased/improvements/1884-multisig-fixes.md @@ -0,0 +1,2 @@ +- Enable hardware wallets to participate in nondegenerate multisignature + transactions. ([\#1884](https://github.com/anoma/namada/pull/1884)) \ No newline at end of file diff --git a/apps/src/lib/cli/client.rs b/apps/src/lib/cli/client.rs index d61401560c..e136430abc 100644 --- a/apps/src/lib/cli/client.rs +++ b/apps/src/lib/cli/client.rs @@ -261,7 +261,7 @@ impl CliApi { &client, &mut ctx.wallet, &args.tx, - &Some(args.sender.clone()), + Some(args.sender.clone()), default_signer, ) .await?; diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index 6d80c84a36..35c1722198 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -47,7 +47,7 @@ pub async fn aux_signing_data< client: &C, wallet: &mut Wallet, args: &args::Tx, - owner: &Option
, + owner: Option
, default_signer: Option
, ) -> Result { let signing_data = signing::aux_signing_data::<_, _, IO>( @@ -105,7 +105,7 @@ pub async fn submit_reveal_aux< client, &mut ctx.wallet, &args, - &None, + None, None, ) .await?; @@ -152,7 +152,7 @@ where client, &mut ctx.wallet, &args.tx, - &Some(args.owner.clone()), + Some(args.owner.clone()), default_signer, ) .await?; @@ -197,7 +197,7 @@ where client, &mut ctx.wallet, &args.tx, - &Some(args.addr.clone()), + Some(args.addr.clone()), default_signer, ) .await?; @@ -238,7 +238,7 @@ where client, &mut ctx.wallet, &args.tx, - &None, + None, None, ) .await?; @@ -476,7 +476,7 @@ where client, &mut ctx.wallet, &tx_args, - &None, + None, None, ) .await?; @@ -719,7 +719,7 @@ pub async fn submit_transfer< client, &mut ctx.wallet, &args.tx, - &Some(args.source.effective_address()), + Some(args.source.effective_address()), default_signer, ) .await?; @@ -800,7 +800,7 @@ where client, &mut ctx.wallet, &args.tx, - &Some(args.source.clone()), + Some(args.source.clone()), default_signer, ) .await?; @@ -859,7 +859,7 @@ where client, &mut ctx.wallet, &args.tx, - &Some(proposal.author.clone()), + Some(proposal.author.clone()), default_signer, ) .await?; @@ -894,7 +894,7 @@ where client, &mut ctx.wallet, &args.tx, - &Some(proposal.proposal.author.clone()), + Some(proposal.proposal.author.clone()), default_signer, ) .await?; @@ -946,7 +946,7 @@ where client, &mut ctx.wallet, &args.tx, - &Some(proposal.proposal.author.clone()), + Some(proposal.proposal.author.clone()), default_signer, ) .await?; @@ -996,7 +996,7 @@ where client, &mut ctx.wallet, &args.tx, - &Some(proposal.proposal.author.clone()), + Some(proposal.proposal.author.clone()), default_signer, ) .await?; @@ -1066,7 +1066,7 @@ where client, &mut ctx.wallet, &args.tx, - &Some(args.voter.clone()), + Some(args.voter.clone()), default_signer.clone(), ) .await?; @@ -1176,7 +1176,7 @@ where client, &mut ctx.wallet, &tx_args, - &Some(owner), + Some(owner.clone()), default_signer, ) .await?; @@ -1203,14 +1203,17 @@ where if let Some(account_public_keys_map) = signing_data.account_public_keys_map { - let signatures = - tx.compute_section_signature(secret_keys, &account_public_keys_map); + let signatures = tx.compute_section_signature( + secret_keys, + &account_public_keys_map, + Some(owner), + ); for signature in &signatures { let filename = format!( "offline_signature_{}_{}.tx", tx.header_hash(), - signature.index + signature.pubkey, ); let output_path = match &tx_args.output_folder { Some(path) => path.join(filename), @@ -1228,9 +1231,7 @@ where display_line!( IO, "Signature for {} serialized at {}", - &account_public_keys_map - .get_public_key_from_index(signature.index) - .unwrap(), + signature.pubkey, output_path.display() ); } @@ -1273,7 +1274,7 @@ where client, &mut ctx.wallet, &args.tx, - &Some(default_address.clone()), + Some(default_address.clone()), default_signer, ) .await?; @@ -1319,7 +1320,7 @@ where client, &mut ctx.wallet, &args.tx, - &Some(default_address), + Some(default_address), default_signer, ) .await?; @@ -1366,7 +1367,7 @@ where client, &mut ctx.wallet, &args.tx, - &Some(default_address), + Some(default_address), default_signer, ) .await?; @@ -1407,7 +1408,7 @@ where client, &mut ctx.wallet, &args.tx, - &Some(args.validator.clone()), + Some(args.validator.clone()), default_signer, ) .await?; @@ -1452,7 +1453,7 @@ where client, &mut ctx.wallet, &args.tx, - &Some(args.validator.clone()), + Some(args.validator.clone()), default_signer, ) .await?; @@ -1498,7 +1499,7 @@ where client, &mut ctx.wallet, &args.tx, - &Some(args.steward.clone()), + Some(args.steward.clone()), default_signer, ) .await?; @@ -1541,7 +1542,7 @@ where client, &mut ctx.wallet, &args.tx, - &Some(args.steward.clone()), + Some(args.steward.clone()), default_signer, ) .await?; diff --git a/apps/src/lib/node/ledger/shell/finalize_block.rs b/apps/src/lib/node/ledger/shell/finalize_block.rs index 51a745f6e1..b08e3ce770 100644 --- a/apps/src/lib/node/ledger/shell/finalize_block.rs +++ b/apps/src/lib/node/ledger/shell/finalize_block.rs @@ -1126,7 +1126,8 @@ mod test_finalize_block { )); wrapper_tx.add_section(Section::Signature(Signature::new( wrapper_tx.sechashes(), - keypair, + [(0, keypair.clone())].into_iter().collect(), + None, ))); let tx = wrapper_tx.to_bytes(); ( @@ -2440,7 +2441,8 @@ mod test_finalize_block { )); wrapper.add_section(Section::Signature(Signature::new( wrapper.sechashes(), - &keypair, + [(0, keypair)].into_iter().collect(), + None, ))); let wrapper_hash_key = replay_protection::get_replay_protection_key( @@ -2511,7 +2513,8 @@ mod test_finalize_block { )); wrapper.add_section(Section::Signature(Signature::new( wrapper.sechashes(), - &keypair, + [(0, keypair.clone())].into_iter().collect(), + None, ))); let processed_tx = ProcessedTx { @@ -2593,7 +2596,10 @@ mod test_finalize_block { )); wrapper.add_section(Section::Signature(Signature::new( wrapper.sechashes(), - &crate::wallet::defaults::albert_keypair(), + [(0, crate::wallet::defaults::albert_keypair())] + .into_iter() + .collect(), + None, ))); let fee_amount = wrapper.header().wrapper().unwrap().get_tx_fee().unwrap(); diff --git a/apps/src/lib/node/ledger/shell/mod.rs b/apps/src/lib/node/ledger/shell/mod.rs index 4e9cae744f..5f592f13b9 100644 --- a/apps/src/lib/node/ledger/shell/mod.rs +++ b/apps/src/lib/node/ledger/shell/mod.rs @@ -2233,7 +2233,8 @@ mod abciplus_mempool_tests { tx.set_data(Data::new(ext.try_to_vec().expect("Test falied"))); tx.add_section(Section::Signature(Signature::new( tx.sechashes(), - &protocol_key, + [(0, protocol_key)].into_iter().collect(), + None, ))); tx } @@ -2314,7 +2315,8 @@ mod test_mempool_validate { .set_data(Data::new("transaction data".as_bytes().to_owned())); invalid_wrapper.add_section(Section::Signature(Signature::new( invalid_wrapper.sechashes(), - &keypair, + [(0, keypair)].into_iter().collect(), + None, ))); // we mount a malleability attack to try and remove the fee @@ -2380,7 +2382,8 @@ mod test_mempool_validate { wrapper.set_data(Data::new("transaction data".as_bytes().to_owned())); wrapper.add_section(Section::Signature(Signature::new( wrapper.sechashes(), - &keypair, + [(0, keypair)].into_iter().collect(), + None, ))); // Write wrapper hash to storage @@ -2537,7 +2540,8 @@ mod test_mempool_validate { wrapper.set_data(Data::new("transaction data".as_bytes().to_owned())); wrapper.add_section(Section::Signature(Signature::new( wrapper.sechashes(), - &keypair, + [(0, keypair)].into_iter().collect(), + None, ))); let result = shell.mempool_validate( @@ -2569,7 +2573,8 @@ mod test_mempool_validate { wrapper.set_data(Data::new("transaction data".as_bytes().to_owned())); wrapper.add_section(Section::Signature(Signature::new( wrapper.sechashes(), - &keypair, + [(0, keypair)].into_iter().collect(), + None, ))); let result = shell.mempool_validate( @@ -2601,7 +2606,10 @@ mod test_mempool_validate { wrapper.set_data(Data::new("transaction data".as_bytes().to_owned())); wrapper.add_section(Section::Signature(Signature::new( wrapper.sechashes(), - &crate::wallet::defaults::albert_keypair(), + [(0, crate::wallet::defaults::albert_keypair())] + .into_iter() + .collect(), + None, ))); let result = shell.mempool_validate( @@ -2633,7 +2641,10 @@ mod test_mempool_validate { wrapper.set_data(Data::new("transaction data".as_bytes().to_owned())); wrapper.add_section(Section::Signature(Signature::new( wrapper.sechashes(), - &crate::wallet::defaults::albert_keypair(), + [(0, crate::wallet::defaults::albert_keypair())] + .into_iter() + .collect(), + None, ))); let result = shell.mempool_validate( @@ -2664,7 +2675,10 @@ mod test_mempool_validate { wrapper.set_data(Data::new("transaction data".as_bytes().to_owned())); wrapper.add_section(Section::Signature(Signature::new( wrapper.sechashes(), - &crate::wallet::defaults::albert_keypair(), + [(0, crate::wallet::defaults::albert_keypair())] + .into_iter() + .collect(), + None, ))); let result = shell.mempool_validate( @@ -2695,7 +2709,10 @@ mod test_mempool_validate { wrapper.set_data(Data::new("transaction data".as_bytes().to_owned())); wrapper.add_section(Section::Signature(Signature::new( wrapper.sechashes(), - &crate::wallet::defaults::albert_keypair(), + [(0, crate::wallet::defaults::albert_keypair())] + .into_iter() + .collect(), + None, ))); let result = shell.mempool_validate( diff --git a/apps/src/lib/node/ledger/shell/prepare_proposal.rs b/apps/src/lib/node/ledger/shell/prepare_proposal.rs index df7a540522..fb205e902f 100644 --- a/apps/src/lib/node/ledger/shell/prepare_proposal.rs +++ b/apps/src/lib/node/ledger/shell/prepare_proposal.rs @@ -1125,7 +1125,8 @@ mod test_prepare_proposal { )); tx.add_section(Section::Signature(Signature::new( tx.sechashes(), - &keypair, + [(0, keypair.clone())].into_iter().collect(), + None, ))); let gas = Gas::from( @@ -1193,7 +1194,8 @@ mod test_prepare_proposal { wrapper.set_data(Data::new("transaction data".as_bytes().to_owned())); wrapper.add_section(Section::Signature(Signature::new( wrapper.sechashes(), - &keypair, + [(0, keypair)].into_iter().collect(), + None, ))); // Write wrapper hash to storage @@ -1245,7 +1247,8 @@ mod test_prepare_proposal { wrapper.set_data(Data::new("transaction data".as_bytes().to_owned())); wrapper.add_section(Section::Signature(Signature::new( wrapper.sechashes(), - &keypair, + [(0, keypair)].into_iter().collect(), + None, ))); let req = RequestPrepareProposal { @@ -1285,7 +1288,8 @@ mod test_prepare_proposal { wrapper.set_data(Data::new("transaction data".as_bytes().to_owned())); wrapper.add_section(Section::Signature(Signature::new( wrapper.sechashes(), - &keypair, + [(0, keypair)].into_iter().collect(), + None, ))); let inner_unsigned_hash = wrapper.clone().update_header(TxType::Raw).header_hash(); @@ -1341,7 +1345,8 @@ mod test_prepare_proposal { let mut new_wrapper = wrapper.clone(); wrapper.add_section(Section::Signature(Signature::new( wrapper.sechashes(), - &keypair, + [(0, keypair)].into_iter().collect(), + None, ))); new_wrapper.update_header(TxType::Wrapper(Box::new(WrapperTx::new( @@ -1356,7 +1361,8 @@ mod test_prepare_proposal { )))); new_wrapper.add_section(Section::Signature(Signature::new( wrapper.sechashes(), - &keypair_2, + [(0, keypair_2)].into_iter().collect(), + None, ))); let req = RequestPrepareProposal { @@ -1396,7 +1402,8 @@ mod test_prepare_proposal { .set_data(Data::new("transaction data".as_bytes().to_owned())); wrapper_tx.add_section(Section::Signature(Signature::new( wrapper_tx.sechashes(), - &keypair, + [(0, keypair)].into_iter().collect(), + None, ))); let time = DateTimeUtc::now(); @@ -1444,7 +1451,8 @@ mod test_prepare_proposal { .set_data(Data::new("transaction data".as_bytes().to_owned())); wrapper_tx.add_section(Section::Signature(Signature::new( wrapper_tx.sechashes(), - &keypair, + [(0, keypair)].into_iter().collect(), + None, ))); let req = RequestPrepareProposal { @@ -1483,7 +1491,8 @@ mod test_prepare_proposal { .set_data(Data::new("transaction data".as_bytes().to_owned())); wrapper_tx.add_section(Section::Signature(Signature::new( wrapper_tx.sechashes(), - &keypair, + [(0, keypair)].into_iter().collect(), + None, ))); let req = RequestPrepareProposal { @@ -1521,7 +1530,10 @@ mod test_prepare_proposal { .set_data(Data::new("transaction data".as_bytes().to_owned())); wrapper_tx.add_section(Section::Signature(Signature::new( wrapper_tx.sechashes(), - &crate::wallet::defaults::albert_keypair(), + [(0, crate::wallet::defaults::albert_keypair())] + .into_iter() + .collect(), + None, ))); let req = RequestPrepareProposal { @@ -1558,7 +1570,10 @@ mod test_prepare_proposal { .set_data(Data::new("transaction data".as_bytes().to_owned())); wrapper_tx.add_section(Section::Signature(Signature::new( wrapper_tx.sechashes(), - &crate::wallet::defaults::albert_keypair(), + [(0, crate::wallet::defaults::albert_keypair())] + .into_iter() + .collect(), + None, ))); let req = RequestPrepareProposal { @@ -1594,7 +1609,10 @@ mod test_prepare_proposal { .set_data(Data::new("transaction data".as_bytes().to_owned())); wrapper_tx.add_section(Section::Signature(Signature::new( wrapper_tx.sechashes(), - &crate::wallet::defaults::albert_keypair(), + [(0, crate::wallet::defaults::albert_keypair())] + .into_iter() + .collect(), + None, ))); let req = RequestPrepareProposal { @@ -1630,7 +1648,10 @@ mod test_prepare_proposal { .set_data(Data::new("transaction data".as_bytes().to_owned())); wrapper_tx.add_section(Section::Signature(Signature::new( wrapper_tx.sechashes(), - &crate::wallet::defaults::albert_keypair(), + [(0, crate::wallet::defaults::albert_keypair())] + .into_iter() + .collect(), + None, ))); let req = RequestPrepareProposal { diff --git a/apps/src/lib/node/ledger/shell/process_proposal.rs b/apps/src/lib/node/ledger/shell/process_proposal.rs index bb2c4ec939..145626e5ce 100644 --- a/apps/src/lib/node/ledger/shell/process_proposal.rs +++ b/apps/src/lib/node/ledger/shell/process_proposal.rs @@ -1656,7 +1656,8 @@ mod test_process_proposal { outer_tx.set_data(Data::new("transaction data".as_bytes().to_owned())); outer_tx.add_section(Section::Signature(Signature::new( outer_tx.sechashes(), - &keypair, + [(0, keypair)].into_iter().collect(), + None, ))); let mut new_tx = outer_tx.clone(); if let TxType::Wrapper(wrapper) = &mut new_tx.header.tx_type { @@ -1727,7 +1728,8 @@ mod test_process_proposal { outer_tx.set_data(Data::new("transaction data".as_bytes().to_owned())); outer_tx.add_section(Section::Signature(Signature::new( outer_tx.sechashes(), - &keypair, + [(0, keypair)].into_iter().collect(), + None, ))); let response = { @@ -1791,7 +1793,8 @@ mod test_process_proposal { outer_tx.set_data(Data::new("transaction data".as_bytes().to_owned())); outer_tx.add_section(Section::Signature(Signature::new( outer_tx.sechashes(), - &keypair, + [(0, keypair)].into_iter().collect(), + None, ))); let response = { @@ -2140,7 +2143,8 @@ mod test_process_proposal { wrapper.set_code(Code::new("wasm_code".as_bytes().to_owned())); wrapper.add_section(Section::Signature(Signature::new( wrapper.sechashes(), - &keypair, + [(0, keypair)].into_iter().collect(), + None, ))); // Write wrapper hash to storage @@ -2215,7 +2219,8 @@ mod test_process_proposal { wrapper.set_data(Data::new("transaction data".as_bytes().to_owned())); wrapper.add_section(Section::Signature(Signature::new( wrapper.sechashes(), - &keypair, + [(0, keypair)].into_iter().collect(), + None, ))); // Run validation @@ -2272,7 +2277,8 @@ mod test_process_proposal { wrapper.set_data(Data::new("transaction data".as_bytes().to_owned())); wrapper.add_section(Section::Signature(Signature::new( wrapper.sechashes(), - &keypair, + [(0, keypair)].into_iter().collect(), + None, ))); let inner_unsigned_hash = wrapper.clone().update_header(TxType::Raw).header_hash(); @@ -2335,7 +2341,8 @@ mod test_process_proposal { let mut new_wrapper = wrapper.clone(); wrapper.add_section(Section::Signature(Signature::new( wrapper.sechashes(), - &keypair, + [(0, keypair)].into_iter().collect(), + None, ))); let inner_unsigned_hash = wrapper.clone().update_header(TxType::Raw).header_hash(); @@ -2352,7 +2359,8 @@ mod test_process_proposal { )))); new_wrapper.add_section(Section::Signature(Signature::new( new_wrapper.sechashes(), - &keypair_2, + [(0, keypair_2)].into_iter().collect(), + None, ))); // Run validation @@ -2403,7 +2411,8 @@ mod test_process_proposal { wrapper.set_data(Data::new("transaction data".as_bytes().to_owned())); wrapper.add_section(Section::Signature(Signature::new( wrapper.sechashes(), - &keypair, + [(0, keypair)].into_iter().collect(), + None, ))); let protocol_key = shell.mode.get_protocol_key().expect("Test failed"); @@ -2468,7 +2477,8 @@ mod test_process_proposal { decrypted.update_header(TxType::Decrypted(DecryptedTx::Decrypted)); decrypted.add_section(Section::Signature(Signature::new( decrypted.sechashes(), - &keypair, + [(0, keypair)].into_iter().collect(), + None, ))); let gas_limit = Gas::from(wrapper.header.wrapper().unwrap().gas_limit) .checked_sub(Gas::from(wrapper.to_bytes().len() as u64)) @@ -2526,7 +2536,8 @@ mod test_process_proposal { wrapper.set_data(Data::new("transaction data".as_bytes().to_owned())); wrapper.add_section(Section::Signature(Signature::new( wrapper.sechashes(), - &keypair, + [(0, keypair)].into_iter().collect(), + None, ))); // Run validation @@ -2572,7 +2583,8 @@ mod test_process_proposal { decrypted.update_header(TxType::Decrypted(DecryptedTx::Decrypted)); decrypted.add_section(Section::Signature(Signature::new( decrypted.sechashes(), - &keypair, + [(0, keypair)].into_iter().collect(), + None, ))); let gas_limit = Gas::from(wrapper.header.wrapper().unwrap().gas_limit) .checked_sub(Gas::from(wrapper.to_bytes().len() as u64)) @@ -2626,7 +2638,8 @@ mod test_process_proposal { wrapper.set_data(Data::new("transaction data".as_bytes().to_owned())); wrapper.add_section(Section::Signature(Signature::new( wrapper.sechashes(), - &keypair, + [(0, keypair)].into_iter().collect(), + None, ))); // Run validation @@ -2667,7 +2680,8 @@ mod test_process_proposal { wrapper.set_data(Data::new("transaction data".as_bytes().to_owned())); wrapper.add_section(Section::Signature(Signature::new( wrapper.sechashes(), - &keypair, + [(0, keypair)].into_iter().collect(), + None, ))); // Run validation @@ -2707,7 +2721,10 @@ mod test_process_proposal { wrapper.set_data(Data::new("transaction data".as_bytes().to_owned())); wrapper.add_section(Section::Signature(Signature::new( wrapper.sechashes(), - &crate::wallet::defaults::albert_keypair(), + [(0, crate::wallet::defaults::albert_keypair())] + .into_iter() + .collect(), + None, ))); // Run validation @@ -2747,7 +2764,10 @@ mod test_process_proposal { wrapper.set_data(Data::new("transaction data".as_bytes().to_owned())); wrapper.add_section(Section::Signature(Signature::new( wrapper.sechashes(), - &crate::wallet::defaults::albert_keypair(), + [(0, crate::wallet::defaults::albert_keypair())] + .into_iter() + .collect(), + None, ))); // Run validation @@ -2787,7 +2807,10 @@ mod test_process_proposal { wrapper.set_data(Data::new("transaction data".as_bytes().to_owned())); wrapper.add_section(Section::Signature(Signature::new( wrapper.sechashes(), - &crate::wallet::defaults::albert_keypair(), + [(0, crate::wallet::defaults::albert_keypair())] + .into_iter() + .collect(), + None, ))); // Run validation @@ -2827,7 +2850,10 @@ mod test_process_proposal { wrapper.set_data(Data::new("transaction data".as_bytes().to_owned())); wrapper.add_section(Section::Signature(Signature::new( wrapper.sechashes(), - &crate::wallet::defaults::albert_keypair(), + [(0, crate::wallet::defaults::albert_keypair())] + .into_iter() + .collect(), + None, ))); // Run validation @@ -2870,7 +2896,8 @@ mod test_process_proposal { wrapper.set_data(Data::new("transaction data".as_bytes().to_owned())); wrapper.add_section(Section::Signature(Signature::new( wrapper.sechashes(), - &keypair, + [(0, keypair)].into_iter().collect(), + None, ))); let wrapper = wrapper.to_bytes(); for height in [1u64, 2] { diff --git a/benches/host_env.rs b/benches/host_env.rs index 6a3cc6e37b..6f385b93bc 100644 --- a/benches/host_env.rs +++ b/benches/host_env.rs @@ -1,9 +1,11 @@ +use std::collections::HashSet; + use borsh::BorshSerialize; use criterion::{criterion_group, criterion_main, Criterion}; use namada::core::types::account::AccountPublicKeysMap; use namada::core::types::address; use namada::core::types::token::{Amount, Transfer}; -use namada::proto::{Data, MultiSignature, Section}; +use namada::proto::{Data, Section, Signature}; use namada_apps::wallet::defaults; /// Benchmarks the validation of a single signature on a single `Section` of a @@ -24,17 +26,16 @@ fn tx_section_signature_validation(c: &mut Criterion) { defaults::albert_keypair().to_public() ]); - let multisig = MultiSignature::new( + let multisig = Signature::new( vec![section_hash], - &[defaults::albert_keypair()], - &pkim, + pkim.index_secret_keys(vec![defaults::albert_keypair()]), + None, ); - let signature_index = multisig.signatures.first().unwrap().clone(); c.bench_function("tx_section_signature_validation", |b| { b.iter(|| { - signature_index - .verify(&pkim, &multisig.get_raw_hash()) + multisig + .verify_signature(&mut HashSet::new(), &pkim, &None) .unwrap() }) }); diff --git a/benches/lib.rs b/benches/lib.rs index 0d9bb63759..a723048e87 100644 --- a/benches/lib.rs +++ b/benches/lib.rs @@ -433,7 +433,8 @@ pub fn generate_tx( if let Some(signer) = signer { tx.add_section(Section::Signature(Signature::new( tx.sechashes(), - signer, + [(0, signer.clone())].into_iter().collect(), + None, ))); } @@ -473,7 +474,11 @@ pub fn generate_foreign_key_tx(signer: &SecretKey) -> Tx { .try_to_vec() .unwrap(), )); - tx.add_section(Section::Signature(Signature::new(tx.sechashes(), signer))); + tx.add_section(Section::Signature(Signature::new( + tx.sechashes(), + [(0, signer.clone())].into_iter().collect(), + None, + ))); tx } diff --git a/benches/process_wrapper.rs b/benches/process_wrapper.rs index c050d83600..fc09d6f65e 100644 --- a/benches/process_wrapper.rs +++ b/benches/process_wrapper.rs @@ -47,7 +47,8 @@ fn process_tx(c: &mut Criterion) { ))); tx.add_section(namada::proto::Section::Signature(Signature::new( tx.sechashes(), - &defaults::albert_keypair(), + [(0, defaults::albert_keypair())].into_iter().collect(), + None, ))); let wrapper = tx.to_bytes(); diff --git a/core/src/ledger/governance/cli/offline.rs b/core/src/ledger/governance/cli/offline.rs index 3f9feeb1dc..fb56a1270a 100644 --- a/core/src/ledger/governance/cli/offline.rs +++ b/core/src/ledger/governance/cli/offline.rs @@ -315,18 +315,13 @@ fn compute_signatures_index( account_public_keys_map: &AccountPublicKeysMap, hashed_data: &Hash, ) -> BTreeSet { - keys.iter() - .filter_map(|signing_key| { + account_public_keys_map + .index_secret_keys(keys.to_vec()) + .values() + .map(|signing_key| { let public_key = signing_key.ref_to(); - let public_key_index = - account_public_keys_map.get_index_from_public_key(&public_key); - if public_key_index.is_some() { - let signature = - common::SigScheme::sign(signing_key, hashed_data); - Some(SignatureIndex::from_single_signature(signature)) - } else { - None - } + let signature = common::SigScheme::sign(signing_key, hashed_data); + SignatureIndex::from_single_signature(public_key, signature) }) .collect::>() } @@ -338,11 +333,12 @@ fn compute_total_valid_signatures( hashed_data: &Hash, ) -> u8 { signatures.iter().fold(0_u8, |acc, signature_index| { - let public_key = account_public_keys_map - .get_public_key_from_index(signature_index.index); - if let Some(pk) = public_key { + if account_public_keys_map + .get_index_from_public_key(&signature_index.pubkey) + .is_some() + { let sig_check = common::SigScheme::verify_signature( - &pk, + &signature_index.pubkey, hashed_data, &signature_index.signature, ); diff --git a/core/src/proto/mod.rs b/core/src/proto/mod.rs index e8411a41d2..9dfe32e644 100644 --- a/core/src/proto/mod.rs +++ b/core/src/proto/mod.rs @@ -4,9 +4,9 @@ pub mod generated; mod types; pub use types::{ - Code, Commitment, Data, Dkg, Error, Header, MaspBuilder, MultiSignature, - Section, Signable, SignableEthMessage, Signature, SignatureIndex, Signed, - Tx, TxError, + Code, Commitment, CompressedSignature, Data, Dkg, Error, Header, + MaspBuilder, Section, Signable, SignableEthMessage, Signature, + SignatureIndex, Signed, Signer, Tx, TxError, }; #[cfg(test)] diff --git a/core/src/proto/types.rs b/core/src/proto/types.rs index 29ca643473..f9cd9d82be 100644 --- a/core/src/proto/types.rs +++ b/core/src/proto/types.rs @@ -1,6 +1,6 @@ use std::borrow::Cow; use std::cmp::Ordering; -use std::collections::{BTreeSet, HashMap, HashSet}; +use std::collections::{BTreeMap, HashMap, HashSet}; use std::convert::TryFrom; use std::hash::{Hash, Hasher}; use std::marker::PhantomData; @@ -388,15 +388,20 @@ impl Code { PartialEq, )] pub struct SignatureIndex { + pub pubkey: common::PublicKey, + pub index: Option<(Address, u8)>, pub signature: common::Signature, - pub index: u8, } impl SignatureIndex { - pub fn from_single_signature(signature: common::Signature) -> Self { + pub fn from_single_signature( + pubkey: common::PublicKey, + signature: common::Signature, + ) -> Self { Self { + pubkey, signature, - index: 0, + index: None, } } @@ -404,24 +409,6 @@ impl SignatureIndex { vec![self.clone()] } - pub fn verify( - &self, - public_key_index_map: &AccountPublicKeysMap, - data: &impl SignableBytes, - ) -> std::result::Result<(), VerifySigError> { - let public_key = - public_key_index_map.get_public_key_from_index(self.index); - if let Some(public_key) = public_key { - common::SigScheme::verify_signature( - &public_key, - data, - &self.signature, - ) - } else { - Err(VerifySigError::MissingData) - } - } - pub fn serialize(&self) -> String { let signature_bytes = self.try_to_vec().expect("Signature should be serializable"); @@ -443,7 +430,7 @@ impl SignatureIndex { impl Ord for SignatureIndex { fn cmp(&self, other: &Self) -> Ordering { - self.index.cmp(&other.index) + self.pubkey.cmp(&other.pubkey) } } @@ -453,6 +440,23 @@ impl PartialOrd for SignatureIndex { } } +/// Indicates the list of public keys against which signatures will be verified +#[derive( + Clone, + Debug, + BorshSerialize, + BorshDeserialize, + BorshSchema, + Serialize, + Deserialize, +)] +pub enum Signer { + /// The address of a multisignature account + Address(Address), + /// The public keys that constitute a signer + PubKeys(Vec), +} + /// A section representing a multisig over another section #[derive( Clone, @@ -463,45 +467,54 @@ impl PartialOrd for SignatureIndex { Serialize, Deserialize, )] -pub struct MultiSignature { +pub struct Signature { /// The hash of the section being signed pub targets: Vec, + /// The public keys against which the signatures should be verified + pub signer: Signer, /// The signature over the above hash - pub signatures: BTreeSet, + pub signatures: BTreeMap, } -impl MultiSignature { +impl Signature { /// Sign the given section hash with the given key and return a section pub fn new( targets: Vec, - secret_keys: &[common::SecretKey], - public_keys_index_map: &AccountPublicKeysMap, + secret_keys: BTreeMap, + signer: Option
, ) -> Self { - let target = Self { - targets: targets.clone(), - signatures: BTreeSet::new(), - } - .get_hash(); - - let signatures_public_keys_map = - secret_keys.iter().map(|secret_key: &common::SecretKey| { - let signature = common::SigScheme::sign(secret_key, target); - let public_key = secret_key.ref_to(); - (public_key, signature) - }); - - let signatures = signatures_public_keys_map - .filter_map(|(public_key, signature)| { - let public_key_index = public_keys_index_map - .get_index_from_public_key(&public_key); - public_key_index - .map(|index| SignatureIndex { signature, index }) - }) - .collect::>(); + // If no signer address is given, then derive the signer's public keys + // from the given secret keys. + let signer = if let Some(addr) = signer { + Signer::Address(addr) + } else { + // Make sure the corresponding public keys can be represented by a + // vector instead of a map + assert!( + secret_keys.keys().cloned().eq(0..(secret_keys.len() as u8)), + "secret keys must be enumerateed when signer address is absent" + ); + Signer::PubKeys(secret_keys.values().map(RefTo::ref_to).collect()) + }; - Self { + // Commit to the given targets + let partial = Self { targets, + signer, + signatures: BTreeMap::new(), + }; + let target = partial.get_raw_hash(); + // Turn the map of secret keys into a map of signatures over the + // commitment made above + let signatures = secret_keys + .iter() + .map(|(index, secret_key)| { + (*index, common::SigScheme::sign(secret_key, target)) + }) + .collect(); + Self { signatures, + ..partial } } @@ -527,14 +540,66 @@ impl MultiSignature { pub fn get_raw_hash(&self) -> crate::types::hash::Hash { Self { - signatures: BTreeSet::new(), + signer: Signer::PubKeys(vec![]), + signatures: BTreeMap::new(), ..self.clone() } .get_hash() } + + /// Verify that the signature contained in this section is valid + pub fn verify_signature( + &self, + verified_pks: &mut HashSet, + public_keys_index_map: &AccountPublicKeysMap, + signer: &Option
, + ) -> std::result::Result { + // Records whether there are any successful verifications + let mut verifications = 0; + match &self.signer { + // Verify the signatures against the given public keys if the + // account addresses match + Signer::Address(addr) if Some(addr) == signer.as_ref() => { + for (idx, sig) in &self.signatures { + if let Some(pk) = + public_keys_index_map.get_public_key_from_index(*idx) + { + common::SigScheme::verify_signature( + &pk, + &self.get_raw_hash(), + sig, + )?; + verified_pks.insert(*idx); + verifications += 1; + } + } + } + // If the account addresses do not match, then there is no efficient + // way to map signatures to the given public keys + Signer::Address(_) => {} + // Verify the signatures against the subset of this section's public + // keys that are also in the given map + Signer::PubKeys(pks) => { + for (idx, pk) in pks.iter().enumerate() { + if let Some(map_idx) = + public_keys_index_map.get_index_from_public_key(pk) + { + common::SigScheme::verify_signature( + pk, + &self.get_raw_hash(), + &self.signatures[&(idx as u8)], + )?; + verified_pks.insert(map_idx); + verifications += 1; + } + } + } + } + Ok(verifications) + } } -/// A section representing the signature over another section +/// A section representing a multisig over another section #[derive( Clone, Debug, @@ -544,58 +609,34 @@ impl MultiSignature { Serialize, Deserialize, )] -pub struct Signature { +pub struct CompressedSignature { /// The hash of the section being signed - targets: Vec, - /// The signature over the above hashes - pub signature: Option, -} - -impl Signature { - pub fn new( - targets: Vec, - sec_key: &common::SecretKey, - ) -> Self { - let mut sec = Self { - targets, - signature: None, - }; - sec.signature = Some(common::SigScheme::sign(sec_key, sec.get_hash())); - sec - } - - /// Hash this signature section - pub fn hash<'a>(&self, hasher: &'a mut Sha256) -> &'a mut Sha256 { - hasher.update( - self.try_to_vec() - .expect("unable to serialize signature section"), - ); - 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, - public_key: &common::PublicKey, - ) -> std::result::Result<(), VerifySigError> { - let signature = - self.signature.as_ref().ok_or(VerifySigError::MissingData)?; - common::SigScheme::verify_signature( - public_key, - &Self { - signature: None, - ..self.clone() + pub targets: Vec, + /// The public keys against which the signatures should be verified + pub signer: Signer, + /// The signature over the above hash + pub signatures: BTreeMap, +} + +impl CompressedSignature { + /// Decompress this signature object with respect to the given transaction + /// by looking up the necessary section hashes. Used by constrained hardware + /// wallets. + pub fn expand(self, tx: &Tx) -> Signature { + let mut targets = Vec::new(); + for idx in self.targets { + if idx == 0 { + // The "zeroth" section is the header + targets.push(tx.header_hash()); + } else { + targets.push(tx.sections[idx as usize - 1].get_hash()); } - .get_hash(), - signature, - ) + } + Signature { + targets, + signer: self.signer, + signatures: self.signatures, + } } } @@ -899,8 +940,6 @@ pub enum Section { ExtraData(Code), /// Transaction code. Sending to hardware wallets optional Code(Code), - /// A transaction signature. Often produced by hardware wallets - SectionSignature(MultiSignature), /// A transaction header/protocol signature Signature(Signature), /// Ciphertext obtained by encrypting arbitrary transaction sections @@ -932,7 +971,6 @@ impl Section { Self::ExtraData(extra) => extra.hash(hasher), Self::Code(code) => code.hash(hasher), Self::Signature(signature) => signature.hash(hasher), - Self::SectionSignature(signatures) => signatures.hash(hasher), Self::Ciphertext(ct) => ct.hash(hasher), Self::MaspBuilder(mb) => mb.hash(hasher), Self::MaspTx(tx) => { @@ -1004,15 +1042,6 @@ impl Section { } } - /// Extract the section signature from this section if possible - pub fn section_signature(&self) -> Option { - if let Self::SectionSignature(data) = self { - Some(data.clone()) - } else { - None - } - } - /// Extract the ciphertext from this section if possible pub fn ciphertext(&self) -> Option { if let Self::Ciphertext(data) = self { @@ -1345,68 +1374,73 @@ impl Tx { /// Verify that the section with the given hash has been signed by the given /// public key - pub fn verify_section_signatures( + pub fn verify_signatures( &self, hashes: &[crate::types::hash::Hash], public_keys_index_map: AccountPublicKeysMap, + signer: &Option
, threshold: u8, max_signatures: Option, - gas_meter: &mut VpGasMeter, - ) -> std::result::Result<(), Error> { + mut gas_meter: Option<&mut VpGasMeter>, + ) -> std::result::Result, Error> { let max_signatures = max_signatures.unwrap_or(u8::MAX); - let mut valid_signatures = 0; + // Records the public key indices used in successful signatures + let mut verified_pks = HashSet::new(); + // Records the sections instrumental in verifying signatures + let mut witnesses = Vec::new(); for section in &self.sections { - if let Section::SectionSignature(signatures) = section { - if !hashes.iter().all(|x| { + if let Section::Signature(signatures) = section { + // Check that the hashes being checked are a subset of those in + // this section. Also ensure that all the sections the signature + // signs over are present. + if hashes.iter().all(|x| { signatures.targets.contains(x) || section.get_hash() == *x - }) { - return Err(Error::InvalidSectionSignature( - "missing target hash.".to_string(), - )); - } - - for target in &signatures.targets { - if self.get_section(target).is_none() { + }) && signatures + .targets + .iter() + .all(|x| self.get_section(x).is_some()) + { + if signatures.total_signatures() > max_signatures { return Err(Error::InvalidSectionSignature( - "Missing target section.".to_string(), + "too many signatures.".to_string(), )); } - } - if signatures.total_signatures() > max_signatures { - return Err(Error::InvalidSectionSignature( - "too many signatures.".to_string(), - )); - } - - if signatures.total_signatures() < threshold { - return Err(Error::InvalidSectionSignature( - "too few signatures.".to_string(), - )); - } - - for signature_index in &signatures.signatures { - let is_valid_signature = signature_index - .verify( + // Finally verify that the signature itself is valid + let prev_verifieds = verified_pks.len(); + let amt_verifieds = signatures + .verify_signature( + &mut verified_pks, &public_keys_index_map, - &signatures.get_raw_hash(), + signer, ) - .is_ok(); - gas_meter - .consume(VERIFY_TX_SIG_GAS_COST) - .map_err(|_| Error::OutOfGas)?; - if is_valid_signature { - valid_signatures += 1; + .map_err(|_| { + Error::InvalidSectionSignature( + "found invalid signature.".to_string(), + ) + }); + // Compute the cost of the signature verifications + if let Some(x) = gas_meter.as_mut() { + let amt_verified = usize::from(amt_verifieds.is_err()) + + verified_pks.len() + - prev_verifieds; + x.consume(VERIFY_TX_SIG_GAS_COST * amt_verified as u64) + .map_err(|_| Error::OutOfGas)?; } - if valid_signatures >= threshold { - return Ok(()); + // Record the section witnessing these signatures + if amt_verifieds? > 0 { + witnesses.push(signatures); + } + // Short-circuit these checks if the threshold is exceeded + if verified_pks.len() >= threshold.into() { + return Ok(witnesses); } } } } Err(Error::InvalidSectionSignature( - "invalid signatures.".to_string(), + "signature threshold not met.".to_string(), )) } @@ -1418,31 +1452,16 @@ impl Tx { public_key: &common::PublicKey, hashes: &[crate::types::hash::Hash], ) -> Result<&Signature> { - for section in &self.sections { - if let Section::Signature(signature) = section { - // Check that the hashes being - // checked are a subset of those in this section - if hashes.iter().all(|x| { - signature.targets.contains(x) || section.get_hash() == *x - }) { - // Ensure that all the sections the signature signs over are - // present - for target in &signature.targets { - if self.get_section(target).is_none() { - return Err(Error::InvalidSectionSignature( - "Target section is missing.".to_string(), - )); - } - } - // Finally verify that the signature itself is valid - return signature - .verify_signature(public_key) - .map(|_| signature) - .map_err(|_| Error::InvalidWrapperSignature); - } - } - } - Err(Error::InvalidWrapperSignature) + self.verify_signatures( + hashes, + AccountPublicKeysMap::from_iter([public_key.clone()].into_iter()), + &None, + 1, + None, + None, + ) + .map(|x| *x.first().unwrap()) + .map_err(|_| Error::InvalidWrapperSignature) } /// Validate any and all ciphertexts stored in this transaction @@ -1465,10 +1484,38 @@ impl Tx { &self, secret_keys: &[common::SecretKey], public_keys_index_map: &AccountPublicKeysMap, - ) -> BTreeSet { + signer: Option
, + ) -> Vec { let targets = self.inner_section_targets(); - MultiSignature::new(targets, secret_keys, public_keys_index_map) - .signatures + let mut signatures = Vec::new(); + let section = Signature::new( + targets, + public_keys_index_map.index_secret_keys(secret_keys.to_vec()), + signer, + ); + match section.signer { + Signer::Address(addr) => { + for (idx, signature) in section.signatures { + signatures.push(SignatureIndex { + pubkey: public_keys_index_map + .get_public_key_from_index(idx) + .unwrap(), + index: Some((addr.clone(), idx)), + signature, + }); + } + } + Signer::PubKeys(pub_keys) => { + for (idx, signature) in section.signatures { + signatures.push(SignatureIndex { + pubkey: pub_keys[idx as usize].clone(), + index: None, + signature, + }); + } + } + } + signatures } /// Decrypt any and all ciphertexts stored in this transaction use the @@ -1712,7 +1759,8 @@ impl Tx { self.protocol_filter(); self.add_section(Section::Signature(Signature::new( self.sechashes(), - &keypair, + [(0, keypair)].into_iter().collect(), + None, ))); self } @@ -1722,27 +1770,53 @@ impl Tx { &mut self, keypairs: Vec, account_public_keys_map: AccountPublicKeysMap, + signer: Option
, ) -> &mut Self { self.protocol_filter(); let hashes = self.inner_section_targets(); - self.add_section(Section::SectionSignature(MultiSignature::new( + self.add_section(Section::Signature(Signature::new( hashes, - &keypairs, - &account_public_keys_map, + account_public_keys_map.index_secret_keys(keypairs), + signer, ))); self } - /// Add signature + /// Add signatures pub fn add_signatures( &mut self, - signatures: BTreeSet, + signatures: Vec, ) -> &mut Self { self.protocol_filter(); - self.add_section(Section::SectionSignature(MultiSignature { + let mut pk_section = Signature { targets: self.inner_section_targets(), - signatures, - })); + signatures: BTreeMap::new(), + signer: Signer::PubKeys(vec![]), + }; + let mut sections = HashMap::new(); + // Put the supplied signatures into the correct sections + for signature in signatures { + if let Some((addr, idx)) = &signature.index { + // Add the signature under the given multisig address + let section = + sections.entry(addr.clone()).or_insert_with(|| Signature { + targets: self.inner_section_targets(), + signatures: BTreeMap::new(), + signer: Signer::Address(addr.clone()), + }); + section.signatures.insert(*idx, signature.signature); + } else if let Signer::PubKeys(pks) = &mut pk_section.signer { + // Add the signature under its corresponding public key + pk_section + .signatures + .insert(pks.len() as u8, signature.signature); + pks.push(signature.pubkey); + } + } + for section in std::iter::once(pk_section).chain(sections.into_values()) + { + self.add_section(Section::Signature(section)); + } self } } diff --git a/core/src/types/account.rs b/core/src/types/account.rs index d66876ba37..83f0b0aafe 100644 --- a/core/src/types/account.rs +++ b/core/src/types/account.rs @@ -1,12 +1,12 @@ //! Helper structures to manage accounts -use std::collections::HashMap; +use std::collections::{BTreeMap, HashMap}; use borsh::{BorshDeserialize, BorshSerialize}; use serde::{Deserialize, Serialize}; use super::address::Address; -use super::key::common; +use super::key::{common, RefTo}; #[derive( Debug, Clone, BorshSerialize, BorshDeserialize, Serialize, Deserialize, @@ -89,4 +89,18 @@ impl AccountPublicKeysMap { ) -> Option { self.pk_to_idx.get(public_key).cloned() } + + /// Index the given set of secret keys + pub fn index_secret_keys( + &self, + secret_keys: Vec, + ) -> BTreeMap { + secret_keys + .into_iter() + .filter_map(|secret_key: common::SecretKey| { + self.get_index_from_public_key(&secret_key.ref_to()) + .map(|index| (index, secret_key)) + }) + .collect() + } } diff --git a/core/src/types/transaction/mod.rs b/core/src/types/transaction/mod.rs index c83e62ddc4..8acb9e6c7e 100644 --- a/core/src/types/transaction/mod.rs +++ b/core/src/types/transaction/mod.rs @@ -235,7 +235,8 @@ mod test_process_tx { .clone(); tx.add_section(Section::Signature(Signature::new( vec![*tx.code_sechash(), *tx.data_sechash()], - &gen_keypair(), + [(0, gen_keypair())].into_iter().collect(), + None, ))); tx.validate_tx().expect("Test failed"); @@ -269,7 +270,8 @@ mod test_process_tx { tx.set_data(Data::new("transaction data".as_bytes().to_owned())); tx.add_section(Section::Signature(Signature::new( tx.sechashes(), - &keypair, + [(0, keypair)].into_iter().collect(), + None, ))); tx.validate_tx().expect("Test failed"); @@ -351,9 +353,14 @@ 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(vec![decrypted.header_hash()], &gen_keypair()); - sig_sec.signature = Some(common::Signature::try_from_sig(&ed_sig).unwrap()); + let mut sig_sec = Signature::new( + vec![decrypted.header_hash()], + [(0, gen_keypair())].into_iter().collect(), + None, + ); + sig_sec + .signatures + .insert(0, 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 diff --git a/core/src/types/transaction/protocol.rs b/core/src/types/transaction/protocol.rs index aa9abe8cec..1a51434b29 100644 --- a/core/src/types/transaction/protocol.rs +++ b/core/src/types/transaction/protocol.rs @@ -201,7 +201,8 @@ mod protocol_txs { outer_tx.set_data(Data::new(tx_data)); outer_tx.add_section(Section::Signature(Signature::new( outer_tx.sechashes(), - signing_key, + [(0, signing_key.clone())].into_iter().collect(), + None, ))); outer_tx } @@ -339,7 +340,8 @@ mod protocol_txs { *outer_tx.code_sechash(), *outer_tx.data_sechash(), ], - signing_key, + [(0, signing_key.clone())].into_iter().collect(), + None, ))); outer_tx } diff --git a/core/src/types/transaction/wrapper.rs b/core/src/types/transaction/wrapper.rs index 58deb6f093..e9b49b0c07 100644 --- a/core/src/types/transaction/wrapper.rs +++ b/core/src/types/transaction/wrapper.rs @@ -438,7 +438,8 @@ pub mod wrapper_tx { encrypted_tx.encrypt(&Default::default()); wrapper.add_section(Section::Signature(Signature::new( vec![wrapper.header_hash(), wrapper.sections[0].get_hash()], - &keypair, + [(0, keypair)].into_iter().collect(), + None, ))); assert!(encrypted_tx.validate_ciphertext()); let privkey = ::G2Affine::prime_subgroup_generator(); @@ -473,7 +474,8 @@ pub mod wrapper_tx { wrapper.encrypt(&Default::default()); wrapper.add_section(Section::Signature(Signature::new( vec![wrapper.header_hash(), wrapper.sections[0].get_hash()], - &keypair, + [(0, keypair)].into_iter().collect(), + None, ))); assert!(wrapper.validate_ciphertext()); let privkey = ::G2Affine::prime_subgroup_generator(); @@ -505,7 +507,8 @@ pub mod wrapper_tx { tx.set_data(Data::new("transaction data".as_bytes().to_owned())); tx.add_section(Section::Signature(Signature::new( tx.sechashes(), - &keypair, + [(0, keypair.clone())].into_iter().collect(), + None, ))); // we now try to alter the inner tx maliciously diff --git a/shared/src/ledger/ibc/vp/mod.rs b/shared/src/ledger/ibc/vp/mod.rs index 4e02cb229a..2d7ebf18f7 100644 --- a/shared/src/ledger/ibc/vp/mod.rs +++ b/shared/src/ledger/ibc/vp/mod.rs @@ -725,7 +725,8 @@ mod tests { outer_tx.set_data(Data::new(tx_data)); outer_tx.add_section(Section::Signature(Signature::new( vec![*outer_tx.code_sechash(), *outer_tx.data_sechash()], - &keypair_1(), + [(0, keypair_1())].into_iter().collect(), + None, ))); let ctx = Ctx::new( &ADDRESS, @@ -1037,7 +1038,8 @@ mod tests { outer_tx.set_data(Data::new(tx_data)); outer_tx.add_section(Section::Signature(Signature::new( vec![*outer_tx.code_sechash(), *outer_tx.data_sechash()], - &keypair_1(), + [(0, keypair_1())].into_iter().collect(), + None, ))); let gas_meter = VpGasMeter::new_from_tx_meter( &TxGasMeter::new_from_sub_limit(TX_GAS_LIMIT.into()), @@ -1370,7 +1372,8 @@ mod tests { outer_tx.set_data(Data::new(tx_data)); outer_tx.add_section(Section::Signature(Signature::new( vec![*outer_tx.code_sechash(), *outer_tx.data_sechash()], - &keypair_1(), + [(0, keypair_1())].into_iter().collect(), + None, ))); let gas_meter = VpGasMeter::new_from_tx_meter( &TxGasMeter::new_from_sub_limit(TX_GAS_LIMIT.into()), @@ -1457,7 +1460,8 @@ mod tests { outer_tx.set_data(Data::new(tx_data)); outer_tx.add_section(Section::Signature(Signature::new( vec![*outer_tx.code_sechash(), *outer_tx.data_sechash()], - &keypair_1(), + [(0, keypair_1())].into_iter().collect(), + None, ))); let gas_meter = VpGasMeter::new_from_tx_meter( &TxGasMeter::new_from_sub_limit(TX_GAS_LIMIT.into()), @@ -1581,7 +1585,8 @@ mod tests { outer_tx.set_data(Data::new(tx_data)); outer_tx.add_section(Section::Signature(Signature::new( vec![*outer_tx.code_sechash(), *outer_tx.data_sechash()], - &keypair_1(), + [(0, keypair_1())].into_iter().collect(), + None, ))); let gas_meter = VpGasMeter::new_from_tx_meter( &TxGasMeter::new_from_sub_limit(TX_GAS_LIMIT.into()), @@ -1704,7 +1709,8 @@ mod tests { outer_tx.set_data(Data::new(tx_data)); outer_tx.add_section(Section::Signature(Signature::new( vec![*outer_tx.code_sechash(), *outer_tx.data_sechash()], - &keypair_1(), + [(0, keypair_1())].into_iter().collect(), + None, ))); let gas_meter = VpGasMeter::new_from_tx_meter( &TxGasMeter::new_from_sub_limit(TX_GAS_LIMIT.into()), @@ -1812,7 +1818,8 @@ mod tests { outer_tx.set_data(Data::new(tx_data)); outer_tx.add_section(Section::Signature(Signature::new( vec![*outer_tx.code_sechash(), *outer_tx.data_sechash()], - &keypair_1(), + [(0, keypair_1())].into_iter().collect(), + None, ))); let gas_meter = VpGasMeter::new_from_tx_meter( &TxGasMeter::new_from_sub_limit(TX_GAS_LIMIT.into()), diff --git a/shared/src/ledger/native_vp/multitoken.rs b/shared/src/ledger/native_vp/multitoken.rs index 0d42e86eaa..564024fd8f 100644 --- a/shared/src/ledger/native_vp/multitoken.rs +++ b/shared/src/ledger/native_vp/multitoken.rs @@ -171,7 +171,8 @@ mod tests { tx.set_data(Data::new(tx_data)); tx.add_section(Section::Signature(Signature::new( tx.sechashes(), - &keypair_1(), + [(0, keypair_1())].into_iter().collect(), + None, ))); tx } diff --git a/shared/src/sdk/signing.rs b/shared/src/sdk/signing.rs index 9a906026ae..145daa8213 100644 --- a/shared/src/sdk/signing.rs +++ b/shared/src/sdk/signing.rs @@ -66,6 +66,8 @@ const ENV_VAR_TX_LOG_PATH: &str = "NAMADA_TX_LOG_PATH"; /// A struture holding the signing data to craft a transaction #[derive(Clone)] pub struct SigningTxData { + /// The address owning the transaction + pub owner: Option
, /// The public keys associated to an account pub public_keys: Vec, /// The threshold associated to an account @@ -225,7 +227,11 @@ pub fn sign_tx( } }) .collect::>(); - tx.sign_raw(signing_tx_keypairs, account_public_keys_map); + tx.sign_raw( + signing_tx_keypairs, + account_public_keys_map, + signing_data.owner, + ); } let fee_payer_keypair = @@ -244,7 +250,7 @@ pub async fn aux_signing_data< client: &C, wallet: &mut Wallet, args: &args::Tx, - owner: &Option
, + owner: Option
, default_signer: Option
, ) -> Result { let public_keys = if owner.is_some() || args.wrapper_fee_payer.is_none() { @@ -254,7 +260,7 @@ pub async fn aux_signing_data< vec![] }; - let (account_public_keys_map, threshold) = match owner { + let (account_public_keys_map, threshold) = match &owner { Some(owner @ Address::Established(_)) => { let account = rpc::get_account_info::(client, owner).await?; if let Some(account) = account { @@ -292,6 +298,7 @@ pub async fn aux_signing_data< } Ok(SigningTxData { + owner, public_keys, threshold, account_public_keys_map, diff --git a/shared/src/vm/host_env.rs b/shared/src/vm/host_env.rs index 62cedf795e..ac35b837d4 100644 --- a/shared/src/vm/host_env.rs +++ b/shared/src/vm/host_env.rs @@ -1784,6 +1784,8 @@ pub fn vp_verify_tx_section_signature( hash_list_len: u64, public_keys_map_ptr: u64, public_keys_map_len: u64, + signer_ptr: u64, + signer_len: u64, threshold: u8, max_signatures_ptr: u64, max_signatures_len: u64, @@ -1816,6 +1818,14 @@ where ) .map_err(vp_host_fns::RuntimeError::EncodingError)?; + let (signer, gas) = env + .memory + .read_bytes(signer_ptr, signer_len as _) + .map_err(|e| vp_host_fns::RuntimeError::MemoryError(Box::new(e)))?; + vp_host_fns::add_gas(gas_meter, gas)?; + let signer = Address::try_from_slice(&signer) + .map_err(vp_host_fns::RuntimeError::EncodingError)?; + let (max_signatures, gas) = env .memory .read_bytes(max_signatures_ptr, max_signatures_len as _) @@ -1827,12 +1837,13 @@ where let tx = unsafe { env.ctx.tx.get() }; Ok(HostEnvResult::from( - tx.verify_section_signatures( + tx.verify_signatures( &hashes, public_keys_map, + &Some(signer), threshold, max_signatures, - gas_meter, + Some(gas_meter), ) .is_ok(), ) diff --git a/tests/src/vm_host_env/mod.rs b/tests/src/vm_host_env/mod.rs index fe61570fb8..27545c644e 100644 --- a/tests/src/vm_host_env/mod.rs +++ b/tests/src/vm_host_env/mod.rs @@ -466,7 +466,7 @@ mod tests { let mut tx = Tx::new(chain_id, expiration); tx.add_code(code.clone()) .add_serialized_data(data.to_vec()) - .sign_raw(keypairs.clone(), pks_map.clone()) + .sign_raw(keypairs.clone(), pks_map.clone(), None) .sign_wrapper(keypair.clone()); env.tx = tx; env.tx.clone() @@ -474,17 +474,18 @@ mod tests { assert_eq!(signed_tx_data.data().as_ref(), Some(data)); assert!( signed_tx_data - .verify_section_signatures( + .verify_signatures( &[ *signed_tx_data.data_sechash(), *signed_tx_data.code_sechash(), ], pks_map, + &None, 1, None, - &mut VpGasMeter::new_from_tx_meter( + Some(&mut VpGasMeter::new_from_tx_meter( &TxGasMeter::new_from_sub_limit(u64::MAX.into()) - ) + )) ) .is_ok() ); @@ -492,7 +493,7 @@ mod tests { let other_keypair = key::testing::keypair_2(); assert!( signed_tx_data - .verify_section_signatures( + .verify_signatures( &[ *signed_tx_data.data_sechash(), *signed_tx_data.code_sechash(), @@ -500,11 +501,12 @@ mod tests { AccountPublicKeysMap::from_iter([ other_keypair.ref_to() ]), + &None, 1, None, - &mut VpGasMeter::new_from_tx_meter( + Some(&mut VpGasMeter::new_from_tx_meter( &TxGasMeter::new_from_sub_limit(u64::MAX.into()) - ) + )) ) .is_err() ); @@ -567,7 +569,7 @@ mod tests { let mut tx = Tx::new(ChainId::default(), None); tx.add_code(vec![]) .add_serialized_data(input_data.clone()) - .sign_raw(keypairs.clone(), pks_map.clone()) + .sign_raw(keypairs.clone(), pks_map.clone(), None) .sign_wrapper(keypair.clone()); let result = vp::CTX.eval(empty_code, tx).unwrap(); assert!(!result); @@ -589,7 +591,7 @@ mod tests { let mut tx = Tx::new(ChainId::default(), None); tx.add_code_from_hash(code_hash) .add_serialized_data(input_data.clone()) - .sign_raw(keypairs.clone(), pks_map.clone()) + .sign_raw(keypairs.clone(), pks_map.clone(), None) .sign_wrapper(keypair.clone()); let result = vp::CTX.eval(code_hash, tx).unwrap(); assert!(result); @@ -612,7 +614,7 @@ mod tests { let mut tx = Tx::new(ChainId::default(), None); tx.add_code_from_hash(code_hash) .add_serialized_data(input_data) - .sign_raw(keypairs, pks_map) + .sign_raw(keypairs, pks_map, None) .sign_wrapper(keypair); let result = vp::CTX.eval(code_hash, tx).unwrap(); assert!(!result); @@ -637,7 +639,7 @@ mod tests { let mut tx = Tx::new(ChainId::default(), None); tx.add_code(vec![]) .add_serialized_data(tx_data.clone()) - .sign_raw(keypairs.clone(), pks_map.clone()) + .sign_raw(keypairs.clone(), pks_map.clone(), None) .sign_wrapper(keypair.clone()); // create a client with the message @@ -671,7 +673,7 @@ mod tests { let mut tx = Tx::new(ChainId::default(), None); tx.add_code(vec![]) .add_serialized_data(tx_data.clone()) - .sign_raw(keypairs, pks_map) + .sign_raw(keypairs, pks_map, None) .sign_wrapper(keypair); // update the client with the message tx_host_env::ibc::ibc_actions(tx::ctx()) @@ -714,7 +716,7 @@ mod tests { let mut tx = Tx::new(ChainId::default(), None); tx.add_code(vec![]) .add_serialized_data(tx_data.clone()) - .sign_raw(keypairs.clone(), pks_map.clone()) + .sign_raw(keypairs.clone(), pks_map.clone(), None) .sign_wrapper(keypair.clone()); // init a connection with the message tx_host_env::ibc::ibc_actions(tx::ctx()) @@ -747,7 +749,7 @@ mod tests { let mut tx = Tx::new(ChainId::default(), None); tx.add_code(vec![]) .add_serialized_data(tx_data.clone()) - .sign_raw(keypairs, pks_map) + .sign_raw(keypairs, pks_map, None) .sign_wrapper(keypair); // open the connection with the message tx_host_env::ibc::ibc_actions(tx::ctx()) @@ -791,7 +793,7 @@ mod tests { let mut tx = Tx::new(ChainId::default(), None); tx.add_code(vec![]) .add_serialized_data(tx_data.clone()) - .sign_raw(keypairs.clone(), pks_map.clone()) + .sign_raw(keypairs.clone(), pks_map.clone(), None) .sign_wrapper(keypair.clone()); // open try a connection with the message tx_host_env::ibc::ibc_actions(tx::ctx()) @@ -824,7 +826,7 @@ mod tests { let mut tx = Tx::new(ChainId::default(), None); tx.add_code(vec![]) .add_serialized_data(tx_data.clone()) - .sign_raw(keypairs, pks_map) + .sign_raw(keypairs, pks_map, None) .sign_wrapper(keypair); // open the connection with the mssage tx_host_env::ibc::ibc_actions(tx::ctx()) @@ -870,7 +872,7 @@ mod tests { let mut tx = Tx::new(ChainId::default(), None); tx.add_code(vec![]) .add_serialized_data(tx_data.clone()) - .sign_raw(keypairs.clone(), pks_map.clone()) + .sign_raw(keypairs.clone(), pks_map.clone(), None) .sign_wrapper(keypair.clone()); // init a channel with the message tx_host_env::ibc::ibc_actions(tx::ctx()) @@ -903,7 +905,7 @@ mod tests { let mut tx = Tx::new(ChainId::default(), None); tx.add_code(vec![]) .add_serialized_data(tx_data.clone()) - .sign_raw(keypairs, pks_map) + .sign_raw(keypairs, pks_map, None) .sign_wrapper(keypair); // open the channle with the message tx_host_env::ibc::ibc_actions(tx::ctx()) @@ -949,7 +951,7 @@ mod tests { let mut tx = Tx::new(ChainId::default(), None); tx.add_code(vec![]) .add_serialized_data(tx_data.clone()) - .sign_raw(keypairs.clone(), pks_map.clone()) + .sign_raw(keypairs.clone(), pks_map.clone(), None) .sign_wrapper(keypair.clone()); // try open a channel with the message tx_host_env::ibc::ibc_actions(tx::ctx()) @@ -983,7 +985,7 @@ mod tests { let mut tx = Tx::new(ChainId::default(), None); tx.add_code(vec![]) .add_serialized_data(tx_data.clone()) - .sign_raw(keypairs, pks_map) + .sign_raw(keypairs, pks_map, None) .sign_wrapper(keypair); // open a channel with the message tx_host_env::ibc::ibc_actions(tx::ctx()) @@ -1032,7 +1034,7 @@ mod tests { let mut tx = Tx::new(ChainId::default(), None); tx.add_code(vec![]) .add_serialized_data(tx_data.clone()) - .sign_raw(keypairs, pks_map) + .sign_raw(keypairs, pks_map, None) .sign_wrapper(keypair); // close the channel with the message let mut actions = tx_host_env::ibc::ibc_actions(tx::ctx()); @@ -1089,7 +1091,7 @@ mod tests { let mut tx = Tx::new(ChainId::default(), None); tx.add_code(vec![]) .add_serialized_data(tx_data.clone()) - .sign_raw(keypairs, pks_map) + .sign_raw(keypairs, pks_map, None) .sign_wrapper(keypair); // close the channel with the message @@ -1143,7 +1145,7 @@ mod tests { let mut tx = Tx::new(ChainId::default(), None); tx.add_code(vec![]) .add_serialized_data(tx_data.clone()) - .sign_raw(keypairs.clone(), pks_map.clone()) + .sign_raw(keypairs.clone(), pks_map.clone(), None) .sign_wrapper(keypair.clone()); // send the token and a packet with the data tx_host_env::ibc::ibc_actions(tx::ctx()) @@ -1190,7 +1192,7 @@ mod tests { let mut tx = Tx::new(ChainId::default(), None); tx.add_code(vec![]) .add_serialized_data(tx_data.clone()) - .sign_raw(keypairs, pks_map) + .sign_raw(keypairs, pks_map, None) .sign_wrapper(keypair); // ack the packet with the message tx_host_env::ibc::ibc_actions(tx::ctx()) @@ -1280,7 +1282,7 @@ mod tests { let mut tx = Tx::new(ChainId::default(), None); tx.add_code(vec![]) .add_serialized_data(tx_data.clone()) - .sign_raw(keypairs, pks_map) + .sign_raw(keypairs, pks_map, None) .sign_wrapper(keypair); // send the token and a packet with the data tx_host_env::ibc::ibc_actions(tx::ctx()) @@ -1353,7 +1355,7 @@ mod tests { let mut tx = Tx::new(ChainId::default(), None); tx.add_code(vec![]) .add_serialized_data(tx_data.clone()) - .sign_raw(keypairs, pks_map) + .sign_raw(keypairs, pks_map, None) .sign_wrapper(keypair); // receive a packet with the message tx_host_env::ibc::ibc_actions(tx::ctx()) @@ -1432,7 +1434,7 @@ mod tests { let mut tx = Tx::new(ChainId::default(), None); tx.add_code(vec![]) .add_serialized_data(tx_data.clone()) - .sign_raw(keypairs, pks_map) + .sign_raw(keypairs, pks_map, None) .sign_wrapper(keypair); // Receive the packet, but no token is received tx_host_env::ibc::ibc_actions(tx::ctx()) @@ -1536,7 +1538,7 @@ mod tests { let mut tx = Tx::new(ChainId::default(), None); tx.add_code(vec![]) .add_serialized_data(tx_data.clone()) - .sign_raw(keypairs, pks_map) + .sign_raw(keypairs, pks_map, None) .sign_wrapper(keypair); // receive a packet with the message tx_host_env::ibc::ibc_actions(tx::ctx()) @@ -1641,7 +1643,7 @@ mod tests { let mut tx = Tx::new(ChainId::default(), None); tx.add_code(vec![]) .add_serialized_data(tx_data.clone()) - .sign_raw(keypairs, pks_map) + .sign_raw(keypairs, pks_map, None) .sign_wrapper(keypair); // receive a packet with the message tx_host_env::ibc::ibc_actions(tx::ctx()) @@ -1741,7 +1743,7 @@ mod tests { let mut tx = Tx::new(ChainId::default(), None); tx.add_code(vec![]) .add_serialized_data(tx_data.clone()) - .sign_raw(keypairs, pks_map) + .sign_raw(keypairs, pks_map, None) .sign_wrapper(keypair); // timeout the packet @@ -1830,7 +1832,7 @@ mod tests { let mut tx = Tx::new(ChainId::default(), None); tx.add_code(vec![]) .add_serialized_data(tx_data.clone()) - .sign_raw(keypairs, pks_map) + .sign_raw(keypairs, pks_map, None) .sign_wrapper(keypair); // timeout the packet diff --git a/tests/src/vm_host_env/vp.rs b/tests/src/vm_host_env/vp.rs index 296a32d384..69d7af9bb1 100644 --- a/tests/src/vm_host_env/vp.rs +++ b/tests/src/vm_host_env/vp.rs @@ -366,6 +366,8 @@ mod native_vp_host_env { hash_list_len: u64, public_keys_map_ptr: u64, public_keys_map_len: u64, + signer_ptr: u64, + signer_len: u64, threshold: u8, max_signatures_ptr: u64, max_signatures_len: u64,) diff --git a/vm_env/src/lib.rs b/vm_env/src/lib.rs index d2ea27cbc6..9a540d5808 100644 --- a/vm_env/src/lib.rs +++ b/vm_env/src/lib.rs @@ -202,6 +202,8 @@ pub mod vp { hash_list_len: u64, public_keys_map_ptr: u64, public_keys_map_len: u64, + signer_ptr: u64, + signer_len: u64, threshold: u8, max_signatures_ptr: u64, max_signatures_len: u64, diff --git a/vp_prelude/src/lib.rs b/vp_prelude/src/lib.rs index 9a7e891624..0962628363 100644 --- a/vp_prelude/src/lib.rs +++ b/vp_prelude/src/lib.rs @@ -94,6 +94,7 @@ pub fn verify_signatures(ctx: &Ctx, tx: &Tx, owner: &Address) -> VpResult { let max_signatures = max_signatures_per_transaction.try_to_vec().unwrap(); let public_keys_map = public_keys_index_map.try_to_vec().unwrap(); let targets = targets.try_to_vec().unwrap(); + let signer = owner.try_to_vec().unwrap(); let valid = unsafe { namada_vp_verify_tx_section_signature( @@ -101,6 +102,8 @@ pub fn verify_signatures(ctx: &Ctx, tx: &Tx, owner: &Address) -> VpResult { targets.len() as _, public_keys_map.as_ptr() as _, public_keys_map.len() as _, + signer.as_ptr() as _, + signer.len() as _, threshold, max_signatures.as_ptr() as _, max_signatures.len() as _, diff --git a/wasm/checksums.json b/wasm/checksums.json index adf06b5afe..193353b640 100644 --- a/wasm/checksums.json +++ b/wasm/checksums.json @@ -1,21 +1,22 @@ { - "tx_bond.wasm": "tx_bond.3d9341191f3059b478435c52ec0db4b19fe9d2b7a60ac7371287fb15552dbe3b.wasm", - "tx_bridge_pool.wasm": "tx_bridge_pool.0d8d4de6222aeb5b0cb80fa39b1ce1ee9e2f50115ffdaa2a1e79eabdddeb6bfe.wasm", - "tx_change_validator_commission.wasm": "tx_change_validator_commission.349170accac147e8b4b77eb10ded0421936176b09d8913246235db0c5a4fac83.wasm", - "tx_ibc.wasm": "tx_ibc.5ddb7f3032d8b59446486a7fb20bac8c43c5a002f6e9b3309ff2e01048b98c7f.wasm", - "tx_init_account.wasm": "tx_init_account.f254fa819c85694d01f440132690ac54b3a8911a5eca9ccb49bc6171cf219017.wasm", - "tx_init_proposal.wasm": "tx_init_proposal.1cf4ac19672bc368ff1e8ea0fd31507ac4a9a33c1b21ff80d85de8b36c6bce66.wasm", - "tx_init_validator.wasm": "tx_init_validator.bd5aa6513bf890e4591413fe6a4345dc28a0767ebc2fad2279e851fb1373091a.wasm", - "tx_reveal_pk.wasm": "tx_reveal_pk.8f252e6fd0ab74be7f7afb4dbe4ba2b3d441011fd57ce5ccd06fe24109728019.wasm", - "tx_transfer.wasm": "tx_transfer.cdb2edbaa152608e2ae6d96a586955b911b74cc097228fee2bb5768f5f6db0ea.wasm", - "tx_unbond.wasm": "tx_unbond.0e6319063d74311a42a405435c15aa867894fc0a7ae209c08fc5fa9eee4013c5.wasm", - "tx_unjail_validator.wasm": "tx_unjail_validator.24e345f69f62fc8b75c475eee66e3252dc06a5468411f6770096b1b8ced8f022.wasm", - "tx_update_vp.wasm": "tx_update_vp.86123368b829488aface2804afc397a8c26873e9f319e3183fb211b292239e1d.wasm", - "tx_vote_proposal.wasm": "tx_vote_proposal.80586902bae368088edfdef22a6fcc25aee8779620fd48eabbe1f3f11754d701.wasm", - "tx_withdraw.wasm": "tx_withdraw.5648dac90b0f25c94d60fb39dfed6120f6eed8794e4c3dbf554af36adf7bafc3.wasm", - "vp_implicit.wasm": "vp_implicit.18f2b20fa776493a0182b05e95b3eb0676be702dc0a4d39a09cb9ab51d116146.wasm", - "vp_masp.wasm": "vp_masp.b07f344c2d760ee69dfcef1ae4e703f63c7c6594a377a48550ef576f9fcaca96.wasm", - "vp_testnet_faucet.wasm": "vp_testnet_faucet.9e3c9d133e52d4b762b9fc7d3bcc1e7d0ad2bc4d1754d390ed31555471f184d5.wasm", - "vp_user.wasm": "vp_user.df279909cb2de9d5125bbfe8b34b14ff3b924ed7db61dcc93cbb06f2f3f29509.wasm", - "vp_validator.wasm": "vp_validator.e3ed90196c676b73bcfa2f4142e2831b306eef56bde73243b2d50ddc5ab8e2b2.wasm" + "tx_bond.wasm": "tx_bond.a0631898c43c82add1a5df8102c8a4e3967b2d6dae99f5d77d7cb7aff314a5e9.wasm", + "tx_bridge_pool.wasm": "tx_bridge_pool.6c1a3fb49a25adcfca928a3902a858547a57d9696366b674b9f0d8a327ea8a12.wasm", + "tx_change_validator_commission.wasm": "tx_change_validator_commission.8dc88e362ad2dea3039c0451bc654e804e28cd8a3c6761237f7d016f621e942b.wasm", + "tx_ibc.wasm": "tx_ibc.0491c038523456c0f23bc9d5aa8d0257e64d6cc63bf6dd3bbac3f729feaf668a.wasm", + "tx_init_account.wasm": "tx_init_account.957be876dc7a865c7b0acff2368195ad4bc786a9beb14837e28f1789cab0eff3.wasm", + "tx_init_proposal.wasm": "tx_init_proposal.161c79ec6fbd6262e80896c0901a629de7d5e2888d56b8025d8dfa5d06c97440.wasm", + "tx_init_validator.wasm": "tx_init_validator.2fd11c1339476a9a154dff5782182e49383641ed53b997638983f05d318ac69b.wasm", + "tx_resign_steward.wasm": "tx_resign_steward.78792e7977322f2dc163258169978039fd571bd0c1b726ff6f78759e9a5ebb06.wasm", + "tx_reveal_pk.wasm": "tx_reveal_pk.9eb149f4360fe7e37be5214a6cb01110757565a4c9b36de5bd7122cadab3c095.wasm", + "tx_transfer.wasm": "tx_transfer.7e9835463134a998d66071d44ac9580c8b70203ebe38641a72e4ad6e12fb70fc.wasm", + "tx_unbond.wasm": "tx_unbond.7fbae71401bc73610918457f0b52477706eaeaff86b83f8f0099882929a5884c.wasm", + "tx_unjail_validator.wasm": "tx_unjail_validator.ffdd901a188ba1bcedb9a1e34afa8b14ee99406c4599f8d1e76ccb02e9ab53ed.wasm", + "tx_update_account.wasm": "tx_update_account.a43bc2c2be95ab7c140f038a4c5889857e27cb6071a1357c9e6b314d6c9da7f9.wasm", + "tx_update_steward_commission.wasm": "tx_update_steward_commission.34b5b47690f95ce5d4591ca5470dbcd1fe1a9e7add85b665c23f3f7d810e824a.wasm", + "tx_vote_proposal.wasm": "tx_vote_proposal.fa68528173e5ce09507b96825f59fe870485080d103c0387408d7a4443c18989.wasm", + "tx_withdraw.wasm": "tx_withdraw.d7512dfbe8eaad71bba1d98a9e2f3115777806006cb1a8ef29bf05337936c268.wasm", + "vp_implicit.wasm": "vp_implicit.ac88e968f6e0f9a50798425e45dc0c654abe9371234502d20670c8029e706b64.wasm", + "vp_masp.wasm": "vp_masp.f009a84a78edf4d6174bf82e649784bb121d064d0d577e75b63f3a5e3719a845.wasm", + "vp_user.wasm": "vp_user.69cc290c6824557e7cc233b4287e301d4a402ede3af66c186016a135bb423115.wasm", + "vp_validator.wasm": "vp_validator.c03262cfbfe57bc0c798aeb54257c493dbbf6f6fea7f4239215fed8bcc68a476.wasm" } \ No newline at end of file diff --git a/wasm/wasm_source/src/vp_implicit.rs b/wasm/wasm_source/src/vp_implicit.rs index a67b40d357..215dccf421 100644 --- a/wasm/wasm_source/src/vp_implicit.rs +++ b/wasm/wasm_source/src/vp_implicit.rs @@ -187,7 +187,7 @@ fn validate_tx( mod tests { // Use this as `#[test]` annotation to enable logging use namada::ledger::pos::{GenesisValidator, PosParams}; - use namada::proto::{Code, Data, MultiSignature}; + use namada::proto::{Code, Data, Signature}; use namada::types::dec::Dec; use namada::types::storage::Epoch; use namada::types::transaction::TxType; @@ -535,10 +535,10 @@ mod tests { let mut tx = vp_env.tx.clone(); tx.set_data(Data::new(vec![])); tx.set_code(Code::new(vec![])); - tx.add_section(Section::SectionSignature(MultiSignature::new( + tx.add_section(Section::Signature(Signature::new( vec![*tx.data_sechash(), *tx.code_sechash()], - &[secret_key], - &pks_map, + pks_map.index_secret_keys(vec![secret_key]), + None, ))); let signed_tx = tx.clone(); @@ -671,10 +671,10 @@ mod tests { let mut tx = vp_env.tx.clone(); tx.set_data(Data::new(vec![])); tx.set_code(Code::new(vec![])); - tx.add_section(Section::SectionSignature(MultiSignature::new( + tx.add_section(Section::Signature(Signature::new( vec![*tx.data_sechash(), *tx.code_sechash()], - &[secret_key], - &pks_map, + pks_map.index_secret_keys(vec![secret_key]), + None, ))); let signed_tx = tx.clone(); @@ -839,10 +839,10 @@ mod tests { let mut tx = vp_env.tx.clone(); tx.set_data(Data::new(vec![])); tx.set_code(Code::new(vec![])); - tx.add_section(Section::SectionSignature(MultiSignature::new( + tx.add_section(Section::Signature(Signature::new( vec![*tx.data_sechash(), *tx.code_sechash()], - &[secret_key], - &pks_map, + pks_map.index_secret_keys(vec![secret_key]), + None, ))); let signed_tx = tx.clone(); vp_env.tx = signed_tx.clone(); @@ -932,10 +932,10 @@ mod tests { let mut tx = vp_env.tx.clone(); tx.set_data(Data::new(vec![])); tx.set_code(Code::new(vec![])); - tx.add_section(Section::SectionSignature(MultiSignature::new( + tx.add_section(Section::Signature(Signature::new( vec![*tx.data_sechash(), *tx.code_sechash()], - &[secret_key], - &pks_map, + pks_map.index_secret_keys(vec![secret_key]), + None, ))); let signed_tx = tx.clone(); vp_env.tx = signed_tx.clone(); @@ -987,10 +987,10 @@ mod tests { let mut tx = vp_env.tx.clone(); tx.set_code(Code::new(vec![])); tx.set_data(Data::new(vec![])); - tx.add_section(Section::SectionSignature(MultiSignature::new( + tx.add_section(Section::Signature(Signature::new( vec![*tx.data_sechash(), *tx.code_sechash()], - &[secret_key], - &pks_map, + pks_map.index_secret_keys(vec![secret_key]), + None, ))); let signed_tx = tx.clone(); vp_env.tx = signed_tx.clone(); diff --git a/wasm/wasm_source/src/vp_testnet_faucet.rs b/wasm/wasm_source/src/vp_testnet_faucet.rs new file mode 100644 index 0000000000..7298c0b126 --- /dev/null +++ b/wasm/wasm_source/src/vp_testnet_faucet.rs @@ -0,0 +1,470 @@ +//! A "faucet" account for testnet. +//! +//! This VP allows anyone to withdraw up to +//! [`testnet_pow::read_withdrawal_limit`] tokens without the faucet's +//! signature, but with a valid PoW challenge solution that cannot be replayed. +//! +//! Any other storage key changes are allowed only with a valid signature. + +use namada_vp_prelude::*; +use once_cell::unsync::Lazy; + +#[validity_predicate(gas = 0)] +fn validate_tx( + ctx: &Ctx, + tx_data: Tx, + addr: Address, + keys_changed: BTreeSet, + verifiers: BTreeSet
, +) -> VpResult { + debug_log!( + "vp_testnet_faucet called with user addr: {}, key_changed: {:?}, \ + verifiers: {:?}", + addr, + keys_changed, + verifiers + ); + + let valid_sig = Lazy::new(|| { + matches!(verify_signatures(ctx, &tx_data, &addr), Ok(true)) + }); + + if !is_valid_tx(ctx, &tx_data)? { + return reject(); + } + + for key in keys_changed.iter() { + let is_valid = if let Some([token, owner]) = + token::is_any_token_balance_key(key) + { + if owner == &addr { + let pre: token::Amount = ctx.read_pre(key)?.unwrap_or_default(); + let post: token::Amount = + ctx.read_post(key)?.unwrap_or_default(); + let change = post.change() - pre.change(); + let maybe_denom = + storage_api::token::read_denom(&ctx.pre(), token)?; + if maybe_denom.is_none() { + debug_log!( + "A denomination for token address {} does not exist \ + in storage", + token, + ); + return reject(); + } + let denom = maybe_denom.unwrap(); + if !change.non_negative() { + // Allow to withdraw without a sig if there's a valid PoW + if ctx.has_valid_pow() { + let max_free_debit = + testnet_pow::read_withdrawal_limit( + &ctx.pre(), + &addr, + )?; + + token::Amount::from_uint(change.abs(), 0).unwrap() + <= token::Amount::from_uint(max_free_debit, denom) + .unwrap() + } else { + debug_log!("No PoW solution, a signature is required"); + // Debit without a solution has to signed + *valid_sig + } + } else { + // credit is permissive + true + } + } else { + // balance changes of other accounts + true + } + } else if let Some(owner) = key.is_validity_predicate() { + let has_post: bool = ctx.has_key_post(key)?; + if owner == &addr { + if has_post { + let vp_hash: Vec = ctx.read_bytes_post(key)?.unwrap(); + return Ok(*valid_sig && is_vp_whitelisted(ctx, &vp_hash)?); + } else { + return reject(); + } + } else { + let vp_hash: Vec = ctx.read_bytes_post(key)?.unwrap(); + return is_vp_whitelisted(ctx, &vp_hash); + } + } else { + // Allow any other key change if authorized by a signature + *valid_sig + }; + + if !is_valid { + debug_log!("key {} modification failed vp", key); + return reject(); + } + } + + accept() +} + +#[cfg(test)] +mod tests { + use address::testing::arb_non_internal_address; + use namada::proto::{Code, Data, Signature}; + use namada::types::transaction::TxType; + use namada_test_utils::TestWasms; + // Use this as `#[test]` annotation to enable logging + use namada_tests::log::test; + use namada_tests::tx::{self, tx_host_env, TestTxEnv}; + use namada_tests::vp::vp_host_env::storage::Key; + use namada_tests::vp::*; + use namada_tx_prelude::{StorageWrite, TxEnv}; + use namada_vp_prelude::account::AccountPublicKeysMap; + use namada_vp_prelude::key::RefTo; + use proptest::prelude::*; + use storage::testing::arb_account_storage_key_no_vp; + + use super::*; + + /// Allows anyone to withdraw up to 1_000 tokens in a single tx + pub const MAX_FREE_DEBIT: i128 = 1_000_000_000; // in micro units + + /// Test that no-op transaction (i.e. no storage modifications) accepted. + #[test] + fn test_no_op_transaction() { + let mut tx_data = Tx::from_type(TxType::Raw); + tx_data.set_data(Data::new(vec![])); + let addr: Address = address::testing::established_address_1(); + let keys_changed: BTreeSet = BTreeSet::default(); + let verifiers: BTreeSet
= BTreeSet::default(); + + // The VP env must be initialized before calling `validate_tx` + vp_host_env::init(); + + assert!( + validate_tx(&CTX, tx_data, addr, keys_changed, verifiers).unwrap() + ); + } + + /// Test that a credit transfer is accepted. + #[test] + fn test_credit_transfer_accepted() { + // Initialize a tx environment + let mut tx_env = TestTxEnv::default(); + + let vp_owner = address::testing::established_address_1(); + let source = address::testing::established_address_2(); + let token = address::nam(); + let amount = token::Amount::from_uint(10_098_123, 0).unwrap(); + + // Spawn the accounts to be able to modify their storage + tx_env.spawn_accounts([&vp_owner, &source, &token]); + + // Credit the tokens to the source before running the transaction to be + // able to transfer from it + tx_env.credit_tokens(&source, &token, amount); + + let amount = token::DenominatedAmount { + amount, + denom: token::NATIVE_MAX_DECIMAL_PLACES.into(), + }; + + // Initialize VP environment from a transaction + vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |address| { + // Apply transfer in a transaction + tx_host_env::token::transfer( + tx_host_env::ctx(), + &source, + address, + &token, + amount, + &None, + &None, + &None, + ) + .unwrap(); + }); + + let vp_env = vp_host_env::take(); + let mut tx_data = Tx::from_type(TxType::Raw); + tx_data.set_data(Data::new(vec![])); + let keys_changed: BTreeSet = + vp_env.all_touched_storage_keys(); + let verifiers: BTreeSet
= BTreeSet::default(); + vp_host_env::set(vp_env); + assert!( + validate_tx(&CTX, tx_data, vp_owner, keys_changed, verifiers) + .unwrap() + ); + } + + /// Test that a validity predicate update without a valid signature is + /// rejected. + #[test] + fn test_unsigned_vp_update_rejected() { + // Initialize a tx environment + let mut tx_env = TestTxEnv::default(); + + let vp_owner = address::testing::established_address_1(); + let vp_code = TestWasms::VpAlwaysTrue.read_bytes(); + let vp_hash = sha256(&vp_code); + // for the update + tx_env.store_wasm_code(vp_code); + + // Spawn the accounts to be able to modify their storage + tx_env.spawn_accounts([&vp_owner]); + + // Initialize VP environment from a transaction + vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |address| { + // Update VP in a transaction + tx::ctx() + .update_validity_predicate(address, vp_hash) + .unwrap(); + }); + + let vp_env = vp_host_env::take(); + let mut tx_data = Tx::from_type(TxType::Raw); + tx_data.set_data(Data::new(vec![])); + let keys_changed: BTreeSet = + vp_env.all_touched_storage_keys(); + let verifiers: BTreeSet
= BTreeSet::default(); + vp_host_env::set(vp_env); + assert!( + !validate_tx(&CTX, tx_data, vp_owner, keys_changed, verifiers) + .unwrap() + ); + } + + /// Test that a validity predicate update with a valid signature is + /// accepted. + #[test] + fn test_signed_vp_update_accepted() { + // Initialize a tx environment + let mut tx_env = TestTxEnv::default(); + + let vp_owner = address::testing::established_address_1(); + let keypair = key::testing::keypair_1(); + let public_key = &keypair.ref_to(); + let vp_code = TestWasms::VpAlwaysTrue.read_bytes(); + let vp_hash = sha256(&vp_code); + // for the update + tx_env.store_wasm_code(vp_code); + + // Spawn the accounts to be able to modify their storage + tx_env.spawn_accounts([&vp_owner]); + tx_env.init_account_storage(&vp_owner, vec![public_key.clone()], 1); + + // Initialize VP environment from a transaction + vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |address| { + // Update VP in a transaction + tx::ctx() + .update_validity_predicate(address, vp_hash) + .unwrap(); + }); + + let pks_map = AccountPublicKeysMap::from_iter(vec![public_key.clone()]); + + 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( + vec![*tx.data_sechash(), *tx.code_sechash()], + pks_map.index_secret_keys(vec![keypair]), + None, + ))); + let signed_tx = tx.clone(); + vp_env.tx = signed_tx.clone(); + let keys_changed: BTreeSet = + vp_env.all_touched_storage_keys(); + let verifiers: BTreeSet
= BTreeSet::default(); + vp_host_env::set(vp_env); + assert!( + validate_tx(&CTX, signed_tx, vp_owner, keys_changed, verifiers) + .unwrap() + ); + } + + prop_compose! { + /// Generates an account address and a storage key inside its storage. + fn arb_account_storage_subspace_key() + // Generate an address + (address in arb_non_internal_address()) + // Generate a storage key other than its VP key (VP cannot be + // modified directly via `write`, it has to be modified via + // `tx::update_validity_predicate`. + (storage_key in arb_account_storage_key_no_vp(address.clone()), + // Use the generated address too + address in Just(address)) + -> (Address, Key) { + (address, storage_key) + } + } + + proptest! { + /// Test that a debit of more than [`MAX_FREE_DEBIT`] tokens without a valid signature is rejected. + #[test] + fn test_unsigned_debit_over_limit_rejected(amount in (MAX_FREE_DEBIT as u64 + 1..)) { + // Initialize a tx environment + let mut tx_env = TestTxEnv::default(); + + // Init the VP + let vp_owner = address::testing::established_address_1(); + let difficulty = testnet_pow::Difficulty::try_new(0).unwrap(); + let withdrawal_limit = token::Amount::from_uint(MAX_FREE_DEBIT as u64, 0).unwrap(); + testnet_pow::init_faucet_storage(&mut tx_env.wl_storage, &vp_owner, difficulty, withdrawal_limit.into()).unwrap(); + + let target = address::testing::established_address_2(); + let token = address::nam(); + let amount = token::Amount::from_uint(amount, 0).unwrap(); + + // Spawn the accounts to be able to modify their storage + tx_env.spawn_accounts([&vp_owner, &target, &token]); + + // Credit the tokens to the VP owner before running the transaction to + // be able to transfer from it + tx_env.credit_tokens(&vp_owner, &token, amount); + tx_env.commit_genesis(); + let amount = token::DenominatedAmount { + amount, + denom: token::NATIVE_MAX_DECIMAL_PLACES.into() + }; + + // Initialize VP environment from a transaction + vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |address| { + // Apply transfer in a transaction + tx_host_env::token::transfer(tx::ctx(), address, &target, &token, amount, &None, &None, &None).unwrap(); + }); + + let vp_env = vp_host_env::take(); + let mut tx_data = Tx::from_type(TxType::Raw); + tx_data.set_data(Data::new(vec![])); + let keys_changed: BTreeSet = + vp_env.all_touched_storage_keys(); + let verifiers: BTreeSet
= BTreeSet::default(); + vp_host_env::set(vp_env); + assert!(!validate_tx(&CTX, tx_data, vp_owner, keys_changed, verifiers).unwrap()); + } + + /// Test that a debit of less than or equal to [`MAX_FREE_DEBIT`] tokens + /// without a valid signature but with a valid PoW solution is accepted. + #[test] + fn test_unsigned_debit_under_limit_accepted(amount in (..MAX_FREE_DEBIT as u64 + 1)) { + // Initialize a tx environment + let mut tx_env = TestTxEnv::default(); + + // Init the VP + let vp_owner = address::testing::established_address_1(); + let difficulty = testnet_pow::Difficulty::try_new(0).unwrap(); + let withdrawal_limit = token::Amount::from_uint(MAX_FREE_DEBIT as u64, 0).unwrap(); + testnet_pow::init_faucet_storage(&mut tx_env.wl_storage, &vp_owner, difficulty, withdrawal_limit.into()).unwrap(); + + let target = address::testing::established_address_2(); + let target_key = key::testing::keypair_1(); + let _public_key = target_key.ref_to(); + let token = address::nam(); + let amount = token::Amount::from_uint(amount, 0).unwrap(); + + // Spawn the accounts to be able to modify their storage + tx_env.spawn_accounts([&vp_owner, &target, &token]); + + // Credit the tokens to the VP owner before running the transaction to + // be able to transfer from it + tx_env.credit_tokens(&vp_owner, &token, amount); + // write the denomination of NAM into storage + storage_api::token::write_denom(&mut tx_env.wl_storage, &token, token::NATIVE_MAX_DECIMAL_PLACES.into()).unwrap(); + tx_env.commit_genesis(); + + // Construct a PoW solution like a client would + let challenge = testnet_pow::Challenge::new(&mut tx_env.wl_storage, &vp_owner, target.clone()).unwrap(); + let solution = challenge.solve(); + let solution_bytes = solution.try_to_vec().unwrap(); + + let amount = token::DenominatedAmount { + amount, + denom: token::NATIVE_MAX_DECIMAL_PLACES.into(), + }; + + // Initialize VP environment from a transaction + vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |address| { + // Don't call `Solution::invalidate_if_valid` - this is done by the + // shell's finalize_block. + let valid = solution.validate(tx::ctx(), address, target.clone()).unwrap(); + assert!(valid); + // Apply transfer in a transaction + tx_host_env::token::transfer(tx::ctx(), address, &target, &token, amount, &None, &None, &None).unwrap(); + }); + + let mut vp_env = vp_host_env::take(); + // This is set by the protocol when the wrapper tx has a valid PoW + vp_env.has_valid_pow = true; + let mut tx_data = Tx::from_type(TxType::Raw); + tx_data.set_data(Data::new(solution_bytes)); + tx_data.set_code(Code::new(vec![])); + tx_data.add_section(Section::Signature(Signature::new( + vec![*tx_data.data_sechash(), *tx_data.code_sechash()], + [(0, target_key)].into_iter().collect(), + None, + ))); + let keys_changed: BTreeSet = + vp_env.all_touched_storage_keys(); + let verifiers: BTreeSet
= BTreeSet::default(); + vp_host_env::set(vp_env); + assert!(validate_tx(&CTX, tx_data, vp_owner, keys_changed, verifiers).unwrap()); + } + + /// Test that a signed tx that performs arbitrary storage writes or + /// deletes to the account is accepted. + #[test] + fn test_signed_arb_storage_write( + (vp_owner, storage_key) in arb_account_storage_subspace_key(), + // Generate bytes to write. If `None`, delete from the key instead + storage_value in any::>>(), + ) { + // Initialize a tx environment + let mut tx_env = TestTxEnv::default(); + + // Init the VP + let difficulty = testnet_pow::Difficulty::try_new(0).unwrap(); + let withdrawal_limit = token::Amount::from_uint(MAX_FREE_DEBIT as u64, 0).unwrap(); + testnet_pow::init_faucet_storage(&mut tx_env.wl_storage, &vp_owner, difficulty, withdrawal_limit.into()).unwrap(); + + let keypair = key::testing::keypair_1(); + let public_key = &keypair.ref_to(); + + // Spawn all the accounts in the storage key to be able to modify + // their storage + let storage_key_addresses = storage_key.find_addresses(); + tx_env.spawn_accounts(storage_key_addresses); + + tx_env.init_account_storage(&vp_owner, vec![public_key.clone()], 1); + + // Initialize VP environment from a transaction + vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |_address| { + // Write or delete some data in the transaction + if let Some(value) = &storage_value { + tx::ctx().write(&storage_key, value).unwrap(); + } else { + tx::ctx().delete(&storage_key).unwrap(); + } + }); + + let pks_map = AccountPublicKeysMap::from_iter(vec![public_key.clone()]); + + 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( + vec![*tx.data_sechash(), *tx.code_sechash()], + pks_map.index_secret_keys(vec![keypair]), + None, + ))); + let signed_tx = tx.clone(); + vp_env.tx = signed_tx.clone(); + let keys_changed: BTreeSet = + vp_env.all_touched_storage_keys(); + let verifiers: BTreeSet
= BTreeSet::default(); + vp_host_env::set(vp_env); + assert!(validate_tx(&CTX, signed_tx, vp_owner, keys_changed, verifiers).unwrap()); + } + } +} diff --git a/wasm/wasm_source/src/vp_user.rs b/wasm/wasm_source/src/vp_user.rs index 5f0b70b506..a334576b53 100644 --- a/wasm/wasm_source/src/vp_user.rs +++ b/wasm/wasm_source/src/vp_user.rs @@ -185,7 +185,7 @@ fn validate_tx( mod tests { use address::testing::arb_non_internal_address; use namada::ledger::pos::{GenesisValidator, PosParams}; - use namada::proto::{Code, Data, MultiSignature}; + use namada::proto::{Code, Data, Signature}; use namada::types::dec::Dec; use namada::types::storage::Epoch; use namada::types::transaction::TxType; @@ -392,10 +392,10 @@ mod tests { let mut tx = vp_env.tx.clone(); tx.set_data(Data::new(vec![])); tx.set_code(Code::new(vec![])); - tx.add_section(Section::SectionSignature(MultiSignature::new( + tx.add_section(Section::Signature(Signature::new( vec![*tx.data_sechash(), *tx.code_sechash()], - &[keypair], - &pks_map, + pks_map.index_secret_keys(vec![keypair]), + None, ))); let signed_tx = tx.clone(); vp_env.tx = signed_tx.clone(); @@ -561,10 +561,10 @@ mod tests { let mut tx = vp_env.tx.clone(); tx.set_data(Data::new(vec![])); tx.set_code(Code::new(vec![])); - tx.add_section(Section::SectionSignature(MultiSignature::new( + tx.add_section(Section::Signature(Signature::new( vec![*tx.data_sechash(), *tx.code_sechash()], - &[secret_key], - &pks_map, + pks_map.index_secret_keys(vec![secret_key]), + None, ))); let signed_tx = tx.clone(); vp_env.tx = signed_tx.clone(); @@ -723,7 +723,11 @@ mod tests { let mut tx = vp_env.tx.clone(); tx.set_code(Code::new(vec![])); tx.set_data(Data::new(vec![])); - tx.add_section(Section::SectionSignature(MultiSignature::new(vec![*tx.data_sechash(), *tx.code_sechash()], &[keypair], &pks_map))); + tx.add_section(Section::Signature(Signature::new( + vec![*tx.data_sechash(), *tx.code_sechash()], + pks_map.index_secret_keys(vec![keypair]), + None, + ))); let signed_tx = tx.clone(); vp_env.tx = signed_tx.clone(); let keys_changed: BTreeSet = @@ -806,10 +810,10 @@ mod tests { let mut tx = vp_env.tx.clone(); tx.set_data(Data::new(vec![])); tx.set_code(Code::new(vec![])); - tx.add_section(Section::SectionSignature(MultiSignature::new( + tx.add_section(Section::Signature(Signature::new( vec![*tx.data_sechash(), *tx.code_sechash()], - &[keypair], - &pks_map, + pks_map.index_secret_keys(vec![keypair]), + None, ))); let signed_tx = tx.clone(); vp_env.tx = signed_tx.clone(); @@ -861,10 +865,10 @@ mod tests { let mut tx = vp_env.tx.clone(); tx.set_data(Data::new(vec![])); tx.set_code(Code::new(vec![])); - tx.add_section(Section::SectionSignature(MultiSignature::new( + tx.add_section(Section::Signature(Signature::new( vec![*tx.data_sechash(), *tx.code_sechash()], - &[keypair], - &pks_map, + pks_map.index_secret_keys(vec![keypair]), + None, ))); let signed_tx = tx.clone(); vp_env.tx = signed_tx.clone(); @@ -917,10 +921,10 @@ mod tests { let mut tx = vp_env.tx.clone(); tx.set_data(Data::new(vec![])); tx.set_code(Code::new(vec![])); - tx.add_section(Section::SectionSignature(MultiSignature::new( + tx.add_section(Section::Signature(Signature::new( vec![*tx.data_sechash(), *tx.code_sechash()], - &[keypair], - &pks_map, + pks_map.index_secret_keys(vec![keypair]), + None, ))); let signed_tx = tx.clone(); vp_env.tx = signed_tx.clone(); @@ -973,10 +977,10 @@ mod tests { let mut tx = vp_env.tx.clone(); tx.set_data(Data::new(vec![])); tx.set_code(Code::new(vec![])); - tx.add_section(Section::SectionSignature(MultiSignature::new( + tx.add_section(Section::Signature(Signature::new( vec![*tx.data_sechash(), *tx.code_sechash()], - &[keypair], - &pks_map, + pks_map.index_secret_keys(vec![keypair]), + None, ))); let signed_tx = tx.clone(); vp_env.tx = signed_tx.clone(); @@ -1029,10 +1033,10 @@ mod tests { let mut tx = vp_env.tx.clone(); tx.set_code(Code::new(vec![])); tx.set_data(Data::new(vec![])); - tx.add_section(Section::SectionSignature(MultiSignature::new( + tx.add_section(Section::Signature(Signature::new( vec![*tx.data_sechash(), *tx.code_sechash()], - &[keypair], - &pks_map, + pks_map.index_secret_keys(vec![keypair]), + None, ))); let signed_tx = tx.clone(); vp_env.tx = signed_tx.clone(); diff --git a/wasm/wasm_source/src/vp_validator.rs b/wasm/wasm_source/src/vp_validator.rs index f1418e38e1..f929a8a0d1 100644 --- a/wasm/wasm_source/src/vp_validator.rs +++ b/wasm/wasm_source/src/vp_validator.rs @@ -192,7 +192,7 @@ fn validate_tx( mod tests { use address::testing::arb_non_internal_address; use namada::ledger::pos::{GenesisValidator, PosParams}; - use namada::proto::{Code, Data, MultiSignature}; + use namada::proto::{Code, Data, Signature}; use namada::types::dec::Dec; use namada::types::storage::Epoch; use namada::types::transaction::TxType; @@ -399,10 +399,10 @@ mod tests { let mut tx = vp_env.tx.clone(); tx.set_data(Data::new(vec![])); tx.set_code(Code::new(vec![])); - tx.add_section(Section::SectionSignature(MultiSignature::new( + tx.add_section(Section::Signature(Signature::new( vec![*tx.data_sechash(), *tx.code_sechash()], - &[keypair], - &pks_map, + pks_map.index_secret_keys(vec![keypair]), + None, ))); let signed_tx = tx.clone(); vp_env.tx = signed_tx.clone(); @@ -579,10 +579,10 @@ mod tests { let mut tx = vp_env.tx.clone(); tx.set_data(Data::new(vec![])); tx.set_code(Code::new(vec![])); - tx.add_section(Section::SectionSignature(MultiSignature::new( + tx.add_section(Section::Signature(Signature::new( vec![*tx.data_sechash(), *tx.code_sechash()], - &[secret_key], - &pks_map, + pks_map.index_secret_keys(vec![secret_key]), + None, ))); let signed_tx = tx.clone(); vp_env.tx = signed_tx.clone(); @@ -741,7 +741,11 @@ mod tests { let mut tx = vp_env.tx.clone(); tx.set_data(Data::new(vec![])); tx.set_code(Code::new(vec![])); - tx.add_section(Section::SectionSignature(MultiSignature::new(vec![*tx.data_sechash(), *tx.code_sechash()], &[keypair], &pks_map))); + tx.add_section(Section::Signature(Signature::new( + vec![*tx.data_sechash(), *tx.code_sechash()], + pks_map.index_secret_keys(vec![keypair]), + None, + ))); let signed_tx = tx.clone(); vp_env.tx = signed_tx.clone(); let keys_changed: BTreeSet = @@ -823,10 +827,10 @@ mod tests { let mut tx = vp_env.tx.clone(); tx.set_data(Data::new(vec![])); tx.set_code(Code::new(vec![])); - tx.add_section(Section::SectionSignature(MultiSignature::new( + tx.add_section(Section::Signature(Signature::new( vec![*tx.data_sechash(), *tx.code_sechash()], - &[keypair], - &pks_map, + pks_map.index_secret_keys(vec![keypair]), + None, ))); let signed_tx = tx.clone(); vp_env.tx = signed_tx.clone(); @@ -878,10 +882,10 @@ mod tests { let mut tx = vp_env.tx.clone(); tx.set_data(Data::new(vec![])); tx.set_code(Code::new(vec![])); - tx.add_section(Section::SectionSignature(MultiSignature::new( + tx.add_section(Section::Signature(Signature::new( vec![*tx.data_sechash(), *tx.code_sechash()], - &[keypair], - &pks_map, + pks_map.index_secret_keys(vec![keypair]), + None, ))); let signed_tx = tx.clone(); vp_env.tx = signed_tx.clone(); @@ -934,10 +938,10 @@ mod tests { let mut tx = vp_env.tx.clone(); tx.set_data(Data::new(vec![])); tx.set_code(Code::new(vec![])); - tx.add_section(Section::SectionSignature(MultiSignature::new( + tx.add_section(Section::Signature(Signature::new( vec![*tx.data_sechash(), *tx.code_sechash()], - &[keypair], - &pks_map, + pks_map.index_secret_keys(vec![keypair]), + None, ))); let signed_tx = tx.clone(); vp_env.tx = signed_tx.clone(); @@ -990,10 +994,10 @@ mod tests { let mut tx = vp_env.tx.clone(); tx.set_data(Data::new(vec![])); tx.set_code(Code::new(vec![])); - tx.add_section(Section::SectionSignature(MultiSignature::new( + tx.add_section(Section::Signature(Signature::new( vec![*tx.data_sechash(), *tx.code_sechash()], - &[keypair], - &pks_map, + pks_map.index_secret_keys(vec![keypair]), + None, ))); let signed_tx = tx.clone(); vp_env.tx = signed_tx.clone(); @@ -1046,10 +1050,10 @@ mod tests { let mut tx = vp_env.tx.clone(); tx.set_code(Code::new(vec![])); tx.set_data(Data::new(vec![])); - tx.add_section(Section::SectionSignature(MultiSignature::new( + tx.add_section(Section::Signature(Signature::new( vec![*tx.data_sechash(), *tx.code_sechash()], - &[keypair], - &pks_map, + pks_map.index_secret_keys(vec![keypair]), + None, ))); let signed_tx = tx.clone(); vp_env.tx = signed_tx.clone(); diff --git a/wasm_for_tests/tx_memory_limit.wasm b/wasm_for_tests/tx_memory_limit.wasm index 10933a03a2..5f74591650 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 adb1e921ec..753a67c6d8 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 490b84969a..8f45d26b7d 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 729d9e9f50..32e8e618ba 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 3a6da643bf..f5003d12c7 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 cffb764ed0..a3505dec71 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/tx_write_storage_key.wasm b/wasm_for_tests/tx_write_storage_key.wasm index 5d600d185f..a0fb758ae9 100755 Binary files a/wasm_for_tests/tx_write_storage_key.wasm and b/wasm_for_tests/tx_write_storage_key.wasm differ diff --git a/wasm_for_tests/vp_always_false.wasm b/wasm_for_tests/vp_always_false.wasm index 7ae138c050..ab62bcc6a7 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 c2058b5cd6..020b8e4879 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 4b388a3984..455338da47 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 e735ba11b7..5ae22bb0f0 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 771165a1d5..4972a7b164 100755 Binary files a/wasm_for_tests/vp_read_storage_key.wasm and b/wasm_for_tests/vp_read_storage_key.wasm differ