diff --git a/.changelog/unreleased/improvements/3494-expand-correct-test-vectors.md b/.changelog/unreleased/improvements/3494-expand-correct-test-vectors.md new file mode 100644 index 0000000000..181aed4cc8 --- /dev/null +++ b/.changelog/unreleased/improvements/3494-expand-correct-test-vectors.md @@ -0,0 +1,2 @@ +- Expanded the scope of test vector generation and updated outdated components + of the test vector code. ([\#3494](https://github.com/anoma/namada/pull/3494)) \ No newline at end of file diff --git a/crates/governance/src/storage/proposal.rs b/crates/governance/src/storage/proposal.rs index 3ee6113088..3cdf7c65e7 100644 --- a/crates/governance/src/storage/proposal.rs +++ b/crates/governance/src/storage/proposal.rs @@ -1,7 +1,7 @@ use std::collections::{BTreeMap, BTreeSet}; use std::fmt::Display; -use borsh::{BorshDeserialize, BorshSerialize}; +use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; use itertools::Itertools; use namada_core::address::Address; use namada_core::hash::Hash; @@ -33,6 +33,7 @@ pub enum ProposalError { Debug, Clone, PartialEq, + BorshSchema, BorshSerialize, BorshDeserialize, BorshDeserializer, @@ -69,6 +70,7 @@ impl InitProposalData { Debug, Clone, PartialEq, + BorshSchema, BorshSerialize, BorshDeserialize, BorshDeserializer, @@ -191,6 +193,7 @@ impl StoragePgfFunding { Debug, Clone, PartialEq, + BorshSchema, BorshSerialize, BorshDeserialize, BorshDeserializer, @@ -221,6 +224,7 @@ pub enum ProposalType { Eq, PartialOrd, Ord, + BorshSchema, BorshSerialize, BorshDeserialize, Serialize, @@ -250,6 +254,7 @@ where Debug, Clone, PartialEq, + BorshSchema, BorshSerialize, BorshDeserialize, BorshDeserializer, @@ -303,6 +308,7 @@ impl Display for PGFTarget { Debug, Clone, PartialEq, + BorshSchema, BorshSerialize, BorshDeserialize, BorshDeserializer, @@ -413,6 +419,7 @@ impl borsh::BorshSchema for PGFIbcTarget { Debug, Clone, PartialEq, + BorshSchema, BorshSerialize, BorshDeserialize, BorshDeserializer, diff --git a/crates/governance/src/storage/vote.rs b/crates/governance/src/storage/vote.rs index 01327a3960..ef0129011f 100644 --- a/crates/governance/src/storage/vote.rs +++ b/crates/governance/src/storage/vote.rs @@ -1,6 +1,6 @@ use std::fmt::Display; -use borsh::{BorshDeserialize, BorshSerialize}; +use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; use namada_macros::BorshDeserializer; #[cfg(feature = "migrations")] use namada_migrations::*; @@ -10,6 +10,7 @@ use serde::{Deserialize, Serialize}; Debug, Clone, PartialEq, + BorshSchema, BorshSerialize, BorshDeserialize, BorshDeserializer, diff --git a/crates/ibc/src/lib.rs b/crates/ibc/src/lib.rs index 9037d70f22..72a9101792 100644 --- a/crates/ibc/src/lib.rs +++ b/crates/ibc/src/lib.rs @@ -443,6 +443,12 @@ pub fn received_ibc_token( pub mod testing { use std::str::FromStr; + use ibc::apps::nft_transfer::types::msgs::transfer::MsgTransfer as MsgNftTransfer; + use ibc::apps::nft_transfer::types::packet::PacketData as NftPacketData; + use ibc::apps::nft_transfer::types::{ + ClassData, ClassId, ClassUri, Memo as NftMemo, PrefixedClassId, + TokenData, TokenId, TokenIds, TokenUri, + }; use ibc::apps::transfer::types::msgs::transfer::MsgTransfer; use ibc::apps::transfer::types::packet::PacketData; use ibc::apps::transfer::types::{ @@ -456,7 +462,7 @@ pub mod testing { use ibc::primitives::proto::Any; use ibc::primitives::{Timestamp, ToProto}; use proptest::prelude::{Just, Strategy}; - use proptest::{collection, prop_compose, prop_oneof}; + use proptest::{collection, option, prop_compose, prop_oneof}; prop_compose! { /// Generate an arbitrary port ID @@ -505,6 +511,13 @@ pub mod testing { } } + prop_compose! { + /// Generate an arbitrary IBC NFT memo + pub fn arb_ibc_nft_memo()(memo in "[a-zA-Z0-9_]*") -> NftMemo { + memo.into() + } + } + prop_compose! { /// Generate an arbitrary IBC memo pub fn arb_ibc_signer()(signer in "[a-zA-Z0-9_]*") -> Signer { @@ -605,10 +618,116 @@ pub mod testing { } } + prop_compose! { + /// Generate an arbitrary IBC token ID + pub fn arb_ibc_token_id()(token_id in "[a-zA-Z0-9_]+") -> TokenId { + TokenId::from_str(&token_id).expect("generated invalid IBC token ID") + } + } + + prop_compose! { + /// Generate an arbitrary IBC token ID vector + pub fn arb_ibc_token_ids()(token_ids in collection::vec(arb_ibc_token_id(), 1..10)) -> TokenIds { + TokenIds(token_ids) + } + } + + prop_compose! { + /// Generate arbitrary IBC class data + pub fn arb_ibc_class_data()(class_data in "[a-zA-Z0-9_]*") -> ClassData { + ClassData::from_str(&class_data).expect("generated invalid IBC class data") + } + } + + prop_compose! { + /// Generate an arbitrary IBC class ID + pub fn arb_ibc_class_id()(token_id in "[a-zA-Z0-9_]+") -> ClassId { + ClassId::from_str(&token_id).expect("generated invalid IBC class ID") + } + } + + prop_compose! { + /// Generate an arbitrary IBC prefixed class ID + pub fn arb_ibc_prefixed_class_id()( + trace_path in arb_ibc_trace_path(), + base_class_id in arb_ibc_class_id(), + ) -> PrefixedClassId { + PrefixedClassId { + trace_path, + base_class_id, + } + } + } + + prop_compose! { + /// Generate arbitrary IBC token data + pub fn arb_ibc_token_data()( + token_data in "[a-zA-Z0-9_]*", + ) -> TokenData { + TokenData::from_str(&token_data).expect("generated invalid IBC token data") + } + } + + // An arbitrary URI for the tests. Generating random URIs would not increase + // test coverage since they are encoded as length-prefixed strings. + const ARBITRARY_URI: &str = "https://namada.net/#ibc-interoperability"; + + prop_compose! { + /// Generate arbitrary NFT packet data + pub fn arb_ibc_nft_packet_data()( + token_ids in arb_ibc_token_ids(), + token_uri in Just(TokenUri::from_str(ARBITRARY_URI).unwrap()), + )( + sender in arb_ibc_signer(), + receiver in arb_ibc_signer(), + memo in option::of(arb_ibc_nft_memo()), + class_data in option::of(arb_ibc_class_data()), + class_id in arb_ibc_prefixed_class_id(), + class_uri in option::of(Just(ClassUri::from_str(ARBITRARY_URI).unwrap())), + token_uris in option::of(collection::vec(Just(token_uri), token_ids.0.len())), + token_data in option::of(collection::vec(arb_ibc_token_data(), token_ids.0.len())), + token_ids in Just(token_ids), + ) -> NftPacketData { + NftPacketData { + token_ids, + sender, + receiver, + memo, + class_data, + class_id, + class_uri, + token_uris, + token_data, + } + } + } + + prop_compose! { + /// Generate an arbitrary IBC NFT transfer message + pub fn arb_ibc_msg_nft_transfer()( + port_id_on_a in arb_ibc_port_id(), + chan_id_on_a in arb_ibc_channel_id(), + packet_data in arb_ibc_nft_packet_data(), + timeout_height_on_b in arb_ibc_timeout_data(), + timeout_timestamp_on_b in arb_ibc_timestamp(), + ) -> MsgNftTransfer { + MsgNftTransfer { + port_id_on_a, + chan_id_on_a, + packet_data, + timeout_height_on_b, + timeout_timestamp_on_b, + } + } + } + prop_compose! { /// Generate an arbitrary IBC any object - pub fn arb_ibc_any()(msg_transfer in arb_ibc_msg_transfer()) -> Any { - msg_transfer.to_any() + pub fn arb_ibc_any()(any in prop_oneof![ + arb_ibc_msg_transfer().prop_map(|x| x.to_any()), + arb_ibc_msg_nft_transfer().prop_map(|x| x.to_any()), + ]) -> Any { + any } } } diff --git a/crates/ibc/src/msg.rs b/crates/ibc/src/msg.rs index d3a32330f1..38705213c7 100644 --- a/crates/ibc/src/msg.rs +++ b/crates/ibc/src/msg.rs @@ -1,4 +1,7 @@ -use borsh::{BorshDeserialize, BorshSerialize}; +use std::collections::BTreeMap; + +use borsh::schema::{Declaration, Definition, Fields}; +use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; use data_encoding::HEXUPPER; use ibc::apps::nft_transfer::types::msgs::transfer::MsgTransfer as IbcMsgNftTransfer; use ibc::apps::nft_transfer::types::packet::PacketData as NftPacketData; @@ -59,6 +62,22 @@ impl BorshDeserialize for MsgTransfer { } } +impl BorshSchema for MsgTransfer { + fn add_definitions_recursively( + definitions: &mut BTreeMap, + ) { + <(Vec, Option)>::add_definitions_recursively(definitions); + let fields = Fields::UnnamedFields(vec![ + <(Vec, Option)>::declaration(), + ]); + definitions.insert(Self::declaration(), Definition::Struct { fields }); + } + + fn declaration() -> Declaration { + "MsgTransfer".into() + } +} + /// IBC NFT transfer message with `Transfer` #[derive(Debug, Clone)] pub struct MsgNftTransfer { @@ -92,6 +111,22 @@ impl BorshDeserialize for MsgNftTransfer { } } +impl BorshSchema for MsgNftTransfer { + fn add_definitions_recursively( + definitions: &mut BTreeMap, + ) { + <(Vec, Option)>::add_definitions_recursively(definitions); + let fields = Fields::UnnamedFields(vec![ + <(Vec, Option)>::declaration(), + ]); + definitions.insert(Self::declaration(), Definition::Struct { fields }); + } + + fn declaration() -> Declaration { + "MsgNftTransfer".into() + } +} + /// Shielding data in IBC packet memo #[derive(Debug, Clone, BorshDeserialize, BorshSerialize)] pub struct IbcShieldingData { diff --git a/crates/sdk/src/lib.rs b/crates/sdk/src/lib.rs index 63bd4eb85a..8ca26fd906 100644 --- a/crates/sdk/src/lib.rs +++ b/crates/sdk/src/lib.rs @@ -838,24 +838,25 @@ where /// Tests and strategies for transactions #[cfg(any(test, feature = "testing"))] pub mod testing { + use ::borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; use borsh_ext::BorshSerializeExt; use governance::ProposalType; - use ibc::primitives::proto::Any; use masp_primitives::transaction::components::sapling::builder::StoredBuildParams; use namada_account::{InitAccount, UpdateAccount}; use namada_core::address::testing::{ arb_established_address, arb_non_internal_address, }; - use namada_core::address::MASP; + use namada_core::collections::HashMap; use namada_core::eth_bridge_pool::PendingTransfer; use namada_core::hash::testing::arb_hash; use namada_core::key::testing::arb_common_keypair; - use namada_core::masp::addr_taddr; + use namada_core::masp::AssetData; use namada_governance::storage::proposal::testing::{ arb_init_proposal, arb_vote_proposal, }; use namada_governance::{InitProposalData, VoteProposalData}; - use namada_ibc::testing::arb_ibc_any; + use namada_ibc::testing::{arb_ibc_msg_nft_transfer, arb_ibc_msg_transfer}; + use namada_ibc::{MsgNftTransfer, MsgTransfer}; use namada_token::testing::arb_denominated_amount; use namada_token::Transfer; use namada_tx::data::pgf::UpdateStewardCommission; @@ -866,17 +867,15 @@ pub mod testing { use namada_tx::data::{Fee, TxType, WrapperTx}; use proptest::prelude::{Just, Strategy}; use proptest::{arbitrary, collection, option, prop_compose, prop_oneof}; - use prost::Message; - use token::testing::arb_vectorized_transparent_transfer; + use token::testing::arb_transparent_transfer; use super::*; use crate::account::tests::{arb_init_account, arb_update_account}; use crate::chain::ChainId; use crate::eth_bridge_pool::testing::arb_pending_transfer; use crate::key::testing::arb_common_pk; - use crate::masp::testing::{ - arb_deshielding_transfer, arb_shielded_transfer, arb_shielding_transfer, - }; + use crate::masp::testing::arb_shielded_transfer; + use crate::masp::ShieldedTransfer; use crate::time::{DateTime, DateTimeUtc, TimeZone, Utc}; use crate::tx::data::pgf::tests::arb_update_steward_commission; use crate::tx::data::pos::tests::{ @@ -889,7 +888,8 @@ pub mod testing { TxCommitments, }; - #[derive(Debug, Clone)] + #[derive(Debug, Clone, BorshDeserialize, BorshSchema, BorshSerialize)] + #[borsh(crate = "::borsh")] #[allow(clippy::large_enum_variant)] #[allow(missing_docs)] /// To facilitate propagating debugging information @@ -909,14 +909,14 @@ pub mod testing { UpdateAccount(UpdateAccount), VoteProposal(VoteProposalData), Withdraw(Withdraw), - TransparentTransfer(Transfer), - MaspTransfer(Transfer, (StoredBuildParams, String)), + Transfer(Transfer, Option<(StoredBuildParams, String)>), Bond(Bond), Redelegation(Redelegation), UpdateStewardCommission(UpdateStewardCommission), ResignSteward(Address), PendingTransfer(PendingTransfer), - IbcAny(Any), + IbcMsgTransfer(MsgTransfer, Option<(StoredBuildParams, String)>), + IbcMsgNftTransfer(MsgNftTransfer, Option<(StoredBuildParams, String)>), Custom, } @@ -1064,117 +1064,52 @@ pub mod testing { } } - prop_compose! { - /// Generate an arbitrary transfer transaction - pub fn arb_transparent_transfer_tx()( - mut header in arb_header(), - wrapper in arb_wrapper_tx(), - transfer in arb_vectorized_transparent_transfer(10), - code_hash in arb_hash(), - ) -> (Tx, TxData) { - header.tx_type = TxType::Wrapper(Box::new(wrapper)); - let mut tx = Tx { header, sections: vec![] }; - tx.add_data(transfer.clone()); - tx.add_code_from_hash(code_hash, Some(TX_TRANSFER_WASM.to_owned())); - (tx, TxData::TransparentTransfer(transfer)) - } - } - // Maximum number of notes to include in a transaction const MAX_ASSETS: usize = 2; - // Type of MASP transaction - #[derive(Debug, Clone)] - enum MaspTxType { - // Shielded transaction - Shielded, - // Shielding transaction - Shielding, - // Unshielding transaction - Unshielding, + prop_compose! { + /// Generate an arbitrary transfer + pub fn arb_transfer()( + arb in prop_oneof![ + arb_transparent_transfer(..5).prop_map(|xfer| (xfer, None)), + arb_shielded_transfer(0..MAX_ASSETS) + .prop_map(|(w, x, y, z)| (w, Some((x, y, z)))) + ], + ) -> (Transfer, Option<(ShieldedTransfer, HashMap, StoredBuildParams)>) { + arb + } } prop_compose! { /// Generate an arbitrary masp transfer transaction - pub fn arb_masp_transfer_tx()(transfers in arb_vectorized_transparent_transfer(5))( + pub fn arb_transfer_tx()( mut header in arb_header(), wrapper in arb_wrapper_tx(), code_hash in arb_hash(), - (masp_tx_type, (shielded_transfer, asset_types, build_params)) in prop_oneof![ - (Just(MaspTxType::Shielded), arb_shielded_transfer(0..MAX_ASSETS)), - (Just(MaspTxType::Shielding), arb_shielding_transfer(addr_taddr(transfers.sources.keys().next().unwrap().owner.clone()), 1)), - (Just(MaspTxType::Unshielding), arb_deshielding_transfer(addr_taddr(transfers.targets.keys().next().unwrap().owner.clone()), 1)), - ], - transfers in Just(transfers), + (transfer, aux) in arb_transfer(), ) -> (Tx, TxData) { header.tx_type = TxType::Wrapper(Box::new(wrapper)); let mut tx = Tx { header, sections: vec![] }; - let shielded_section_hash = tx.add_masp_tx_section(shielded_transfer.masp_tx).1; - let build_param_bytes = - data_encoding::HEXLOWER.encode(&build_params.serialize_to_vec()); - let tx_data = match masp_tx_type { - MaspTxType::Shielded => { - tx.add_code_from_hash(code_hash, Some(TX_TRANSFER_WASM.to_owned())); - let mut data = Transfer::masp(shielded_section_hash); - if let Some((account, amount)) = transfers.targets.first_key_value() { - data = data.transfer(MASP, account.owner.to_owned(), account.token.to_owned(), *amount).unwrap(); - } - tx.add_data(data.clone()); - TxData::MaspTransfer(data, (build_params, build_param_bytes)) - }, - MaspTxType::Shielding => { - // Set the transparent amount and token - let (decoded, value) = asset_types.iter().next().unwrap(); - let token = decoded.token.clone(); - let amount = DenominatedAmount::new( - token::Amount::from_masp_denominated(*value, decoded.position), - decoded.denom, - ); - tx.add_code_from_hash(code_hash, Some(TX_TRANSFER_WASM.to_owned())); - let data = transfers.sources.into_iter().try_fold( - Transfer::masp(shielded_section_hash), - |acc, transfer| acc.transfer( - transfer.0.owner, - MASP, - token.clone(), - amount, - ), - ).unwrap(); - tx.add_data(data.clone()); - TxData::MaspTransfer(data, (build_params, build_param_bytes)) - }, - MaspTxType::Unshielding => { - // Set the transparent amount and token - let (decoded, value) = asset_types.iter().next().unwrap(); - let token = decoded.token.clone(); - let amount = DenominatedAmount::new( - token::Amount::from_masp_denominated(*value, decoded.position), - decoded.denom, - ); - tx.add_code_from_hash(code_hash, Some(TX_TRANSFER_WASM.to_owned())); - let data = transfers.targets.into_iter().try_fold( - Transfer::masp(shielded_section_hash), - |acc, transfer| acc.transfer( - MASP, - transfer.0.owner, - token.clone(), - amount, - ) - ).unwrap(); - tx.add_data(data.clone()); - TxData::MaspTransfer(data, (build_params, build_param_bytes)) - }, - }; - tx.add_masp_builder(MaspBuilder { - asset_types: asset_types.into_keys().collect(), - // Store how the Info objects map to Descriptors/Outputs - metadata: shielded_transfer.metadata, - // Store the data that was used to construct the Transaction - builder: shielded_transfer.builder, - // Link the Builder to the Transaction by hash code - target: shielded_section_hash, - }); - (tx, tx_data) + tx.add_code_from_hash(code_hash, Some(TX_TRANSFER_WASM.to_owned())); + tx.add_data(transfer.clone()); + if let Some((shielded_transfer, asset_types, build_params)) = aux { + let shielded_section_hash = + tx.add_masp_tx_section(shielded_transfer.masp_tx).1; + tx.add_masp_builder(MaspBuilder { + asset_types: asset_types.into_keys().collect(), + // Store how the Info objects map to Descriptors/Outputs + metadata: shielded_transfer.metadata, + // Store the data that was used to construct the Transaction + builder: shielded_transfer.builder, + // Link the Builder to the Transaction by hash code + target: shielded_section_hash, + }); + let build_param_bytes = + data_encoding::HEXLOWER.encode(&build_params.serialize_to_vec()); + (tx, TxData::Transfer(transfer, Some((build_params, build_param_bytes)))) + } else { + (tx, TxData::Transfer(transfer, None)) + } } } @@ -1532,29 +1467,104 @@ pub mod testing { } } + prop_compose! { + /// Generate an arbitrary IBC transfer message + pub fn arb_msg_transfer()( + message in arb_ibc_msg_transfer(), + transfer_aux in option::of(arb_transfer()), + ) -> (MsgTransfer, Option<(ShieldedTransfer, HashMap, StoredBuildParams)>) { + if let Some((transfer, aux)) = transfer_aux { + (MsgTransfer { message, transfer: Some(transfer) }, aux) + } else { + (MsgTransfer { message, transfer: None }, None) + } + } + } + prop_compose! { /// Generate an arbitrary IBC any transaction - pub fn arb_ibc_any_tx()( + pub fn arb_ibc_msg_transfer_tx()( mut header in arb_header(), wrapper in arb_wrapper_tx(), - ibc_any in arb_ibc_any(), + (msg_transfer, aux) in arb_msg_transfer(), code_hash in arb_hash(), ) -> (Tx, TxData) { header.tx_type = TxType::Wrapper(Box::new(wrapper)); let mut tx = Tx { header, sections: vec![] }; - let mut tx_data = vec![]; - ibc_any.encode(&mut tx_data).expect("unable to encode IBC data"); - tx.add_serialized_data(tx_data); + tx.add_serialized_data(msg_transfer.serialize_to_vec()); tx.add_code_from_hash(code_hash, Some(TX_IBC_WASM.to_owned())); - (tx, TxData::IbcAny(ibc_any)) + if let Some((shielded_transfer, asset_types, build_params)) = aux { + let shielded_section_hash = + tx.add_masp_tx_section(shielded_transfer.masp_tx).1; + tx.add_masp_builder(MaspBuilder { + asset_types: asset_types.into_keys().collect(), + // Store how the Info objects map to Descriptors/Outputs + metadata: shielded_transfer.metadata, + // Store the data that was used to construct the Transaction + builder: shielded_transfer.builder, + // Link the Builder to the Transaction by hash code + target: shielded_section_hash, + }); + let build_param_bytes = + data_encoding::HEXLOWER.encode(&build_params.serialize_to_vec()); + (tx, TxData::IbcMsgTransfer(msg_transfer, Some((build_params, build_param_bytes)))) + } else { + (tx, TxData::IbcMsgTransfer(msg_transfer, None)) + } + } + } + + prop_compose! { + /// Generate an arbitrary IBC NFT transfer message + pub fn arb_msg_nft_transfer()( + message in arb_ibc_msg_nft_transfer(), + transfer_aux in option::of(arb_transfer()), + ) -> (MsgNftTransfer, Option<(ShieldedTransfer, HashMap, StoredBuildParams)>) { + if let Some((transfer, aux)) = transfer_aux { + (MsgNftTransfer { message, transfer: Some(transfer) }, aux) + } else { + (MsgNftTransfer { message, transfer: None }, None) + } + } + } + + prop_compose! { + /// Generate an arbitrary IBC any transaction + pub fn arb_ibc_msg_nft_transfer_tx()( + mut header in arb_header(), + wrapper in arb_wrapper_tx(), + (msg_transfer, aux) in arb_msg_nft_transfer(), + code_hash in arb_hash(), + ) -> (Tx, TxData) { + header.tx_type = TxType::Wrapper(Box::new(wrapper)); + let mut tx = Tx { header, sections: vec![] }; + tx.add_serialized_data(msg_transfer.serialize_to_vec()); + tx.add_code_from_hash(code_hash, Some(TX_IBC_WASM.to_owned())); + if let Some((shielded_transfer, asset_types, build_params)) = aux { + let shielded_section_hash = + tx.add_masp_tx_section(shielded_transfer.masp_tx).1; + tx.add_masp_builder(MaspBuilder { + asset_types: asset_types.into_keys().collect(), + // Store how the Info objects map to Descriptors/Outputs + metadata: shielded_transfer.metadata, + // Store the data that was used to construct the Transaction + builder: shielded_transfer.builder, + // Link the Builder to the Transaction by hash code + target: shielded_section_hash, + }); + let build_param_bytes = + data_encoding::HEXLOWER.encode(&build_params.serialize_to_vec()); + (tx, TxData::IbcMsgNftTransfer(msg_transfer, Some((build_params, build_param_bytes)))) + } else { + (tx, TxData::IbcMsgNftTransfer(msg_transfer, None)) + } } } /// Generate an arbitrary tx pub fn arb_tx() -> impl Strategy { prop_oneof![ - arb_transparent_transfer_tx(), - arb_masp_transfer_tx(), + arb_transfer_tx(), arb_bond_tx(), arb_unbond_tx(), arb_init_account_tx(), @@ -1575,7 +1585,8 @@ pub mod testing { arb_update_steward_commission_tx(), arb_resign_steward_tx(), arb_pending_transfer_tx(), - arb_ibc_any_tx(), + arb_ibc_msg_transfer_tx(), + arb_ibc_msg_nft_transfer_tx(), ] } diff --git a/crates/sdk/src/masp.rs b/crates/sdk/src/masp.rs index bcf28db17b..583d953e12 100644 --- a/crates/sdk/src/masp.rs +++ b/crates/sdk/src/masp.rs @@ -2528,6 +2528,8 @@ pub mod testing { use masp_proofs::bellman::groth16::Proof; use masp_proofs::bls12_381; use masp_proofs::bls12_381::{Bls12, G1Affine, G2Affine}; + use namada_core::address::testing::arb_non_internal_address; + use namada_token::{DenominatedAmount, Transfer}; use proptest::prelude::*; use proptest::sample::SizeRange; use proptest::test_runner::TestRng; @@ -2535,6 +2537,7 @@ pub mod testing { use super::*; use crate::address::testing::arb_address; + use crate::address::MASP; use crate::masp_primitives::consensus::BranchId; use crate::masp_primitives::constants::VALUE_COMMITMENT_RANDOMNESS_GENERATOR; use crate::masp_primitives::merkle_tree::FrozenCommitmentTree; @@ -3024,11 +3027,26 @@ pub mod testing { .iter() .map(|(asset, values)| arb_output_descriptions(asset.clone(), values.clone())) .collect::>(), + input_data in collection::vec((any::(), arb_non_internal_address()), assets.len() * MAX_SPLITS), + output_data in collection::vec((any::(), arb_non_internal_address()), assets.len() * MAX_SPLITS), assets in Just(assets), ) -> ( + Transfer, Builder::, HashMap, ) { + // Enable assets to be more easily decoded + let mut asset_decoder = BTreeMap::new(); + for asset_data in assets.keys() { + let asset_type = encode_asset_type( + asset_data.token.clone(), + asset_data.denom, + asset_data.position, + asset_data.epoch, + ).unwrap(); + asset_decoder.insert(asset_type, asset_data); + } + let mut transfer = Transfer::default(); let mut builder = Builder::::new( NETWORK, // NOTE: this is going to add 20 more blocks to the actual @@ -3043,13 +3061,52 @@ pub mod testing { } let tree = FrozenCommitmentTree::new(&leaves); // Then use the notes knowing that they all have the same anchor - for (idx, (esk, div, note, _node)) in spend_descriptions.iter().flatten().enumerate() { - builder.add_sapling_spend(*esk, *div, *note, tree.path(idx)).unwrap(); + for ((is_shielded, address), (idx, (esk, div, note, _node))) in + input_data.into_iter().zip(spend_descriptions.iter().flatten().enumerate()) + { + // Compute the equivalent transparent movement + let asset_data = asset_decoder[¬e.asset_type]; + let amount = DenominatedAmount::new( + token::Amount::from_masp_denominated(note.value, asset_data.position), + asset_data.denom, + ); + // Use either a transparent input or a shielded input + if is_shielded { + builder.add_sapling_spend(*esk, *div, *note, tree.path(idx)).unwrap(); + transfer = transfer.debit(MASP, asset_data.token.clone(), amount).unwrap(); + } else { + let txout = TxOut { + address: TAddrData::Addr(address.clone()).taddress(), + asset_type: note.asset_type, + value: note.value, + }; + builder.add_transparent_input(txout).unwrap(); + transfer = transfer.debit(address, asset_data.token.clone(), amount).unwrap(); + } } - for (ovk, payment_addr, asset_type, value, memo) in output_descriptions.into_iter().flatten() { - builder.add_sapling_output(ovk, payment_addr, asset_type, value, memo).unwrap(); + for ((is_shielded, address), (ovk, payment_addr, asset_type, value, memo)) in + output_data.into_iter().zip(output_descriptions.into_iter().flatten()) + { + // Compute the equivalent transparent movement + let asset_data = asset_decoder[&asset_type]; + let amount = DenominatedAmount::new( + token::Amount::from_masp_denominated(value, asset_data.position), + asset_data.denom, + ); + // Use either a transparent output or a shielded output + if is_shielded { + builder.add_sapling_output(ovk, payment_addr, asset_type, value, memo).unwrap(); + transfer = transfer.credit(MASP, asset_data.token.clone(), amount).unwrap(); + } else { + builder.add_transparent_output( + &TAddrData::Addr(address.clone()).taddress(), + asset_type, + value, + ).unwrap(); + transfer = transfer.credit(address, asset_data.token.clone(), amount).unwrap(); + } } - (builder, assets.into_iter().map(|(k, v)| (k, v.iter().sum())).collect()) + (transfer, builder, assets.into_iter().map(|(k, v)| (k, v.iter().sum())).collect()) } } @@ -3077,172 +3134,17 @@ pub mod testing { } } - prop_compose! { - /// Generate an arbitrary shielding MASP transaction builder - pub fn arb_shielding_builder( - source: TransparentAddress, - asset_range: impl Into, - )( - assets in collection::hash_map( - arb_pre_asset_type(), - collection::vec(..MAX_MONEY, ..MAX_SPLITS), - asset_range, - ), - )( - expiration_height in arb_height(BranchId::MASP, &Network), - txins in assets - .iter() - .map(|(asset, values)| arb_txouts(asset.clone(), values.clone(), source)) - .collect::>(), - output_descriptions in assets - .iter() - .map(|(asset, values)| arb_output_descriptions(asset.clone(), values.clone())) - .collect::>(), - assets in Just(assets), - ) -> ( - Builder::, - HashMap, - ) { - let mut builder = Builder::::new( - NETWORK, - // NOTE: this is going to add 20 more blocks to the actual - // expiration but there's no other exposed function that we could - // use from the masp crate to specify the expiration better - expiration_height.unwrap(), - ); - for txin in txins.into_iter().flatten() { - builder.add_transparent_input(txin).unwrap(); - } - for (ovk, payment_addr, asset_type, value, memo) in output_descriptions.into_iter().flatten() { - builder.add_sapling_output(ovk, payment_addr, asset_type, value, memo).unwrap(); - } - (builder, assets.into_iter().map(|(k, v)| (k, v.iter().sum())).collect()) - } - } - - prop_compose! { - /// Generate an arbitrary deshielding MASP transaction builder - pub fn arb_deshielding_builder( - target: TransparentAddress, - asset_range: impl Into, - )( - assets in collection::hash_map( - arb_pre_asset_type(), - collection::vec(..MAX_MONEY, ..MAX_SPLITS), - asset_range, - ), - )( - expiration_height in arb_height(BranchId::MASP, &Network), - spend_descriptions in assets - .iter() - .map(|(asset, values)| arb_spend_descriptions(asset.clone(), values.clone())) - .collect::>(), - txouts in assets - .iter() - .map(|(asset, values)| arb_txouts(asset.clone(), values.clone(), target)) - .collect::>(), - assets in Just(assets), - ) -> ( - Builder::, - HashMap, - ) { - let mut builder = Builder::::new( - NETWORK, - // NOTE: this is going to add 20 more blocks to the actual - // expiration but there's no other exposed function that we could - // use from the masp crate to specify the expiration better - expiration_height.unwrap(), - ); - let mut leaves = Vec::new(); - // First construct a Merkle tree containing all notes to be used - for (_esk, _div, _note, node) in spend_descriptions.iter().flatten() { - leaves.push(*node); - } - let tree = FrozenCommitmentTree::new(&leaves); - // Then use the notes knowing that they all have the same anchor - for (idx, (esk, div, note, _node)) in spend_descriptions.into_iter().flatten().enumerate() { - builder.add_sapling_spend(esk, div, note, tree.path(idx)).unwrap(); - } - for txout in txouts.into_iter().flatten() { - builder.add_transparent_output(&txout.address, txout.asset_type, txout.value).unwrap(); - } - (builder, assets.into_iter().map(|(k, v)| (k, v.iter().sum())).collect()) - } - } - prop_compose! { /// Generate an arbitrary MASP shielded transfer pub fn arb_shielded_transfer( asset_range: impl Into, )(asset_range in Just(asset_range.into()))( - (builder, asset_types) in arb_shielded_builder(asset_range), - epoch in arb_masp_epoch(), - prover_rng in arb_rng().prop_map(TestCsprng), - mut rng in arb_rng().prop_map(TestCsprng), - bparams_rng in arb_rng().prop_map(TestCsprng), - ) -> (ShieldedTransfer, HashMap, StoredBuildParams) { - let mut rng_build_params = RngBuildParams::new(bparams_rng); - let (masp_tx, metadata) = builder.clone().build( - &MockTxProver(Mutex::new(prover_rng)), - &FeeRule::non_standard(U64Sum::zero()), - &mut rng, - &mut rng_build_params, - ).unwrap(); - (ShieldedTransfer { - builder: builder.map_builder(WalletMap), - metadata, - masp_tx, - epoch, - }, asset_types, rng_build_params.to_stored().unwrap()) - } - } - - prop_compose! { - /// Generate an arbitrary MASP shielded transfer - pub fn arb_shielding_transfer( - source: TransparentAddress, - asset_range: impl Into, - )(asset_range in Just(asset_range.into()))( - (builder, asset_types) in arb_shielding_builder( - source, - asset_range, - ), - epoch in arb_masp_epoch(), - prover_rng in arb_rng().prop_map(TestCsprng), - mut rng in arb_rng().prop_map(TestCsprng), - bparams_rng in arb_rng().prop_map(TestCsprng), - ) -> (ShieldedTransfer, HashMap, StoredBuildParams) { - let mut rng_build_params = RngBuildParams::new(bparams_rng); - let (masp_tx, metadata) = builder.clone().build( - &MockTxProver(Mutex::new(prover_rng)), - &FeeRule::non_standard(U64Sum::zero()), - &mut rng, - &mut rng_build_params, - ).unwrap(); - (ShieldedTransfer { - builder: builder.map_builder(WalletMap), - metadata, - masp_tx, - epoch, - }, asset_types, rng_build_params.to_stored().unwrap()) - } - } - - prop_compose! { - /// Generate an arbitrary MASP shielded transfer - pub fn arb_deshielding_transfer( - target: TransparentAddress, - asset_range: impl Into, - )(asset_range in Just(asset_range.into()))( - (builder, asset_types) in arb_deshielding_builder( - target, - asset_range, - ), + (mut transfer, builder, asset_types) in arb_shielded_builder(asset_range), epoch in arb_masp_epoch(), prover_rng in arb_rng().prop_map(TestCsprng), mut rng in arb_rng().prop_map(TestCsprng), bparams_rng in arb_rng().prop_map(TestCsprng), - ) -> (ShieldedTransfer, HashMap, StoredBuildParams) { + ) -> (Transfer, ShieldedTransfer, HashMap, StoredBuildParams) { let mut rng_build_params = RngBuildParams::new(bparams_rng); let (masp_tx, metadata) = builder.clone().build( &MockTxProver(Mutex::new(prover_rng)), @@ -3250,7 +3152,8 @@ pub mod testing { &mut rng, &mut rng_build_params, ).unwrap(); - (ShieldedTransfer { + transfer.shielded_section_hash = Some(masp_tx.txid().into()); + (transfer, ShieldedTransfer { builder: builder.map_builder(WalletMap), metadata, masp_tx, diff --git a/crates/sdk/src/signing.rs b/crates/sdk/src/signing.rs index 66f5d78047..3e5e3a3c6a 100644 --- a/crates/sdk/src/signing.rs +++ b/crates/sdk/src/signing.rs @@ -15,13 +15,14 @@ use namada_core::address::{Address, ImplicitAddress, InternalAddress, MASP}; use namada_core::arith::checked; use namada_core::collections::{HashMap, HashSet}; use namada_core::key::*; -use namada_core::masp::{AssetData, ExtendedViewingKey, PaymentAddress}; +use namada_core::masp::{AssetData, ExtendedViewingKey, PaymentAddress, TxId}; use namada_core::sign::SignatureIndex; use namada_core::token::{Amount, DenominatedAmount}; use namada_governance::storage::proposal::{ InitProposalData, ProposalType, VoteProposalData, }; use namada_governance::storage::vote::ProposalVote; +use namada_ibc::{MsgNftTransfer, MsgTransfer}; use namada_parameters::storage as parameter_storage; use namada_token as token; use namada_token::storage_key::balance_key; @@ -29,7 +30,6 @@ use namada_tx::data::pgf::UpdateStewardCommission; use namada_tx::data::pos::BecomeValidator; use namada_tx::data::{pos, Fee}; use namada_tx::{MaspBuilder, Section, Tx}; -use prost::Message; use rand::rngs::OsRng; use serde::{Deserialize, Serialize}; use tokio::sync::RwLock; @@ -38,8 +38,6 @@ use crate::args::SdkTypes; use crate::error::{EncodingError, Error, TxSubmitError}; use crate::eth_bridge_pool::PendingTransfer; use crate::governance::storage::proposal::{AddRemove, PGFAction, PGFTarget}; -use crate::ibc::apps::transfer::types::msgs::transfer::MsgTransfer; -use crate::ibc::primitives::proto::Any; use crate::io::*; use crate::rpc::validate_amount; use crate::token::Account; @@ -945,6 +943,30 @@ fn proposal_type_to_ledger_vector( } } +// Find the MASP Builder that was used to construct the given Transaction. +// Additionally record how to decode AssetTypes using information from the +// builder. +fn find_masp_builder<'a>( + tx: &'a Tx, + shielded_section_hash: Option, + asset_types: &mut HashMap, +) -> Result, std::io::Error> { + for section in &tx.sections { + match section { + Section::MaspBuilder(builder) + if Some(builder.target) == shielded_section_hash => + { + for decoded in &builder.asset_types { + asset_types.insert(decoded.encode()?, decoded.clone()); + } + return Ok(Some(builder)); + } + _ => {} + } + } + Ok(None) +} + /// Converts the given transaction to the form that is displayed on the Ledger /// device pub async fn to_ledger_vector( @@ -1265,30 +1287,17 @@ pub async fn to_ledger_vector( .map_err(|err| { Error::from(EncodingError::Conversion(err.to_string())) })?; - // To facilitate lookups of MASP AssetTypes - let mut asset_types = HashMap::new(); - let builder = tx.sections.iter().find_map(|x| match x { - Section::MaspBuilder(builder) - if Some(builder.target) - == transfer.shielded_section_hash => - { - for decoded in &builder.asset_types { - match decoded.encode() { - Err(_) => None, - Ok(asset) => { - asset_types.insert(asset, decoded.clone()); - Some(builder) - } - }?; - } - Some(builder) - } - _ => None, - }); - tv.name = "Transfer_0".to_string(); - tv.output.push("Type : Transfer".to_string()); + + // To facilitate lookups of MASP AssetTypes + let mut asset_types = HashMap::new(); + let builder = find_masp_builder( + tx, + transfer.shielded_section_hash, + &mut asset_types, + ) + .map_err(|_| Error::Other("Invalid Data".to_string()))?; make_ledger_token_transfer_endpoints( &tokens, &mut tv.output, @@ -1306,71 +1315,272 @@ pub async fn to_ledger_vector( ) .await?; } else if code_sec.tag == Some(TX_IBC_WASM.to_string()) { - let any_msg = Any::decode( - tx.data(cmt) - .ok_or_else(|| Error::Other("Invalid Data".to_string()))? - .as_ref(), - ) - .map_err(|x| { - Error::from(EncodingError::Conversion(x.to_string())) - })?; - - tv.name = "IBC_0".to_string(); - tv.output.push("Type : IBC".to_string()); - - match MsgTransfer::try_from(any_msg.clone()) { - Ok(transfer) => { - let transfer_token = format!( - "{} {}", - transfer.packet_data.token.amount, - transfer.packet_data.token.denom - ); - tv.output.extend(vec![ - format!("Source port : {}", transfer.port_id_on_a), - format!("Source channel : {}", transfer.chan_id_on_a), - format!("Token : {}", transfer_token), - format!("Sender : {}", transfer.packet_data.sender), - format!("Receiver : {}", transfer.packet_data.receiver), - format!( - "Timeout height : {}", - transfer.timeout_height_on_b - ), - format!( - "Timeout timestamp : {}", - transfer - .timeout_timestamp_on_b - .into_tm_time() - .map_or("(none)".to_string(), |time| time - .to_rfc3339()) - ), - ]); - tv.output_expert.extend(vec![ - format!("Source port : {}", transfer.port_id_on_a), - format!("Source channel : {}", transfer.chan_id_on_a), - format!("Token : {}", transfer_token), - format!("Sender : {}", transfer.packet_data.sender), - format!("Receiver : {}", transfer.packet_data.receiver), - format!( - "Timeout height : {}", - transfer.timeout_height_on_b - ), - format!( - "Timeout timestamp : {}", - transfer - .timeout_timestamp_on_b - .into_tm_time() - .map_or("(none)".to_string(), |time| time - .to_rfc3339()) - ), - ]); + let data = tx + .data(cmt) + .ok_or_else(|| Error::Other("Invalid Data".to_string()))?; + + if let Ok(transfer) = MsgTransfer::try_from_slice(data.as_ref()) { + tv.name = "IBC_Transfer_0".to_string(); + tv.output.push("Type : IBC Transfer".to_string()); + let transfer_token = format!( + "{} {}", + transfer.message.packet_data.token.amount, + transfer.message.packet_data.token.denom + ); + tv.output.extend(vec![ + format!("Source port : {}", transfer.message.port_id_on_a), + format!( + "Source channel : {}", + transfer.message.chan_id_on_a + ), + format!("Token : {}", transfer_token), + format!("Sender : {}", transfer.message.packet_data.sender), + format!( + "Receiver : {}", + transfer.message.packet_data.receiver + ), + format!("Memo : {}", transfer.message.packet_data.memo), + format!( + "Timeout height : {}", + transfer.message.timeout_height_on_b + ), + format!( + "Timeout timestamp : {}", + transfer + .message + .timeout_timestamp_on_b + .into_tm_time() + .map_or("no timestamp".to_string(), |time| time + .to_rfc3339()) + ), + ]); + tv.output_expert.extend(vec![ + format!("Source port : {}", transfer.message.port_id_on_a), + format!( + "Source channel : {}", + transfer.message.chan_id_on_a + ), + format!("Token : {}", transfer_token), + format!("Sender : {}", transfer.message.packet_data.sender), + format!( + "Receiver : {}", + transfer.message.packet_data.receiver + ), + format!("Memo : {}", transfer.message.packet_data.memo), + format!( + "Timeout height : {}", + transfer.message.timeout_height_on_b + ), + format!( + "Timeout timestamp : {}", + transfer + .message + .timeout_timestamp_on_b + .into_tm_time() + .map_or("no timestamp".to_string(), |time| time + .to_rfc3339()) + ), + ]); + if let Some(transfer) = transfer.transfer { + // To facilitate lookups of MASP AssetTypes + let mut asset_types = HashMap::new(); + let builder = find_masp_builder( + tx, + transfer.shielded_section_hash, + &mut asset_types, + ) + .map_err(|_| Error::Other("Invalid Data".to_string()))?; + make_ledger_token_transfer_endpoints( + &tokens, + &mut tv.output, + &transfer, + builder, + &asset_types, + ) + .await?; + make_ledger_token_transfer_endpoints( + &tokens, + &mut tv.output_expert, + &transfer, + builder, + &asset_types, + ) + .await?; + } + } else if let Ok(transfer) = + MsgNftTransfer::try_from_slice(data.as_ref()) + { + tv.name = "IBC_NFT_Transfer_0".to_string(); + tv.output.push("Type : IBC NFT Transfer".to_string()); + tv.output.extend(vec![ + format!("Source port : {}", transfer.message.port_id_on_a), + format!( + "Source channel : {}", + transfer.message.chan_id_on_a + ), + format!( + "Class ID: {}", + transfer.message.packet_data.class_id + ), + ]); + if let Some(class_uri) = &transfer.message.packet_data.class_uri + { + tv.output.push(format!("Class URI: {}", class_uri)); + } + if let Some(class_data) = + &transfer.message.packet_data.class_data + { + tv.output.push(format!("Class data: {}", class_data)); + } + for (idx, token_id) in + transfer.message.packet_data.token_ids.0.iter().enumerate() + { + tv.output.push(format!("Token ID: {}", token_id)); + if let Some(token_uris) = + &transfer.message.packet_data.token_uris + { + tv.output.push(format!( + "Token URI: {}", + token_uris.get(idx).ok_or_else(|| Error::Other( + "Invalid Data".to_string() + ))?, + )); + } + if let Some(token_data) = + &transfer.message.packet_data.token_data + { + tv.output.push(format!( + "Token data: {}", + token_data.get(idx).ok_or_else(|| Error::Other( + "Invalid Data".to_string() + ))?, + )); + } + } + tv.output.extend(vec![ + format!("Sender : {}", transfer.message.packet_data.sender), + format!( + "Receiver : {}", + transfer.message.packet_data.receiver + ), + ]); + if let Some(memo) = &transfer.message.packet_data.memo { + tv.output.push(format!("Memo: {}", memo)); + } + tv.output.extend(vec![ + format!( + "Timeout height : {}", + transfer.message.timeout_height_on_b + ), + format!( + "Timeout timestamp : {}", + transfer + .message + .timeout_timestamp_on_b + .into_tm_time() + .map_or("no timestamp".to_string(), |time| time + .to_rfc3339()) + ), + ]); + tv.output_expert.extend(vec![ + format!("Source port : {}", transfer.message.port_id_on_a), + format!( + "Source channel : {}", + transfer.message.chan_id_on_a + ), + format!( + "Class ID: {}", + transfer.message.packet_data.class_id + ), + ]); + if let Some(class_uri) = &transfer.message.packet_data.class_uri + { + tv.output_expert.push(format!("Class URI: {}", class_uri)); } - _ => { - for line in format!("{:#?}", any_msg).split('\n') { - let stripped = line.trim_start(); - tv.output.push(format!("Part : {}", stripped)); - tv.output_expert.push(format!("Part : {}", stripped)); + if let Some(class_data) = + &transfer.message.packet_data.class_data + { + tv.output_expert + .push(format!("Class data: {}", class_data)); + } + for (idx, token_id) in + transfer.message.packet_data.token_ids.0.iter().enumerate() + { + tv.output_expert.push(format!("Token ID: {}", token_id)); + if let Some(token_uris) = + &transfer.message.packet_data.token_uris + { + tv.output_expert.push(format!( + "Token URI: {}", + token_uris.get(idx).ok_or_else(|| Error::Other( + "Invalid Data".to_string() + ))?, + )); + } + if let Some(token_data) = + &transfer.message.packet_data.token_data + { + tv.output_expert.push(format!( + "Token data: {}", + token_data.get(idx).ok_or_else(|| Error::Other( + "Invalid Data".to_string() + ))?, + )); } } + tv.output_expert.extend(vec![ + format!("Sender : {}", transfer.message.packet_data.sender), + format!( + "Receiver : {}", + transfer.message.packet_data.receiver + ), + ]); + if let Some(memo) = &transfer.message.packet_data.memo { + tv.output_expert.push(format!("Memo: {}", memo)); + } + tv.output_expert.extend(vec![ + format!( + "Timeout height : {}", + transfer.message.timeout_height_on_b + ), + format!( + "Timeout timestamp : {}", + transfer + .message + .timeout_timestamp_on_b + .into_tm_time() + .map_or("no timestamp".to_string(), |time| time + .to_rfc3339()) + ), + ]); + if let Some(transfer) = transfer.transfer { + // To facilitate lookups of MASP AssetTypes + let mut asset_types = HashMap::new(); + let builder = find_masp_builder( + tx, + transfer.shielded_section_hash, + &mut asset_types, + ) + .map_err(|_| Error::Other("Invalid Data".to_string()))?; + make_ledger_token_transfer_endpoints( + &tokens, + &mut tv.output, + &transfer, + builder, + &asset_types, + ) + .await?; + make_ledger_token_transfer_endpoints( + &tokens, + &mut tv.output_expert, + &transfer, + builder, + &asset_types, + ) + .await?; + } + } else { + return Result::Err(Error::Other("Invalid Data".to_string())); } } else if code_sec.tag == Some(TX_BOND_WASM.to_string()) { let bond = pos::Bond::try_from_slice( diff --git a/crates/token/src/lib.rs b/crates/token/src/lib.rs index 6ad9b323bb..8a27109e41 100644 --- a/crates/token/src/lib.rs +++ b/crates/token/src/lib.rs @@ -221,12 +221,13 @@ pub mod testing { pub use namada_core::token::*; pub use namada_trans_token::testing::*; use proptest::prelude::*; + use proptest::sample::SizeRange; use super::Transfer; prop_compose! { /// Generate a transparent transfer - fn arb_transparent_transfer()( + fn arb_single_transparent_transfer()( source in arb_non_internal_address(), target in arb_non_internal_address(), token in arb_established_address().prop_map(Address::Established), @@ -242,17 +243,20 @@ pub mod testing { } /// Generate a vectorized transparent transfer - pub fn arb_vectorized_transparent_transfer( - number_of_txs: usize, + pub fn arb_transparent_transfer( + number_of_txs: impl Into, ) -> impl Strategy { - proptest::collection::vec(arb_transparent_transfer(), 0..number_of_txs) - .prop_filter_map("Transfers must not overflow", |data| { - data.into_iter().try_fold( - Transfer::default(), - |acc, (source, target, token, amount)| { - acc.transfer(source, target, token, amount) - }, - ) - }) + proptest::collection::vec( + arb_single_transparent_transfer(), + number_of_txs, + ) + .prop_filter_map("Transfers must not overflow", |data| { + data.into_iter().try_fold( + Transfer::default(), + |acc, (source, target, token, amount)| { + acc.transfer(source, target, token, amount) + }, + ) + }) } } diff --git a/crates/tx/src/data/pgf.rs b/crates/tx/src/data/pgf.rs index 72de35f42f..8b2c9f3944 100644 --- a/crates/tx/src/data/pgf.rs +++ b/crates/tx/src/data/pgf.rs @@ -1,5 +1,5 @@ use namada_core::address::Address; -use namada_core::borsh::{BorshDeserialize, BorshSerialize}; +use namada_core::borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; use namada_core::collections::HashMap; use namada_core::dec::Dec; use namada_macros::BorshDeserializer; @@ -20,6 +20,7 @@ pub enum PgfError { Debug, Clone, PartialEq, + BorshSchema, BorshSerialize, BorshDeserialize, BorshDeserializer, diff --git a/examples/tx_schema.rs b/examples/tx_schema.rs index 1fcf24e162..e76cec4a08 100644 --- a/examples/tx_schema.rs +++ b/examples/tx_schema.rs @@ -3,6 +3,7 @@ use std::error::Error; use masp_primitives::transaction::components::sapling::builder::StoredBuildParams; use namada_sdk::borsh::BorshSchema; +use namada_sdk::testing::TxData; use namada_sdk::tx::Tx; fn main() -> Result<(), Box> { @@ -13,6 +14,7 @@ fn main() -> Result<(), Box> { } let mut definitions = BTreeMap::new(); Tx::add_definitions_recursively(&mut definitions); + TxData::add_definitions_recursively(&mut definitions); StoredBuildParams::add_definitions_recursively(&mut definitions); std::fs::write(&args[1], format!("{:#?}", definitions)) .expect("unable to save schema"); diff --git a/wasm/checksums.json b/wasm/checksums.json index d9882a84df..f9af0e6bf0 100644 --- a/wasm/checksums.json +++ b/wasm/checksums.json @@ -23,4 +23,4 @@ "tx_withdraw.wasm": "tx_withdraw.358d5c6556ae07e0175c230c340d23f21ce0c9cde599b52b4d32b569253cbb5b.wasm", "vp_implicit.wasm": "vp_implicit.8bdb0fb0cace962ac68b9afd0030dcbdbfc3d140437cffdb8e7bf52172689348.wasm", "vp_user.wasm": "vp_user.1398e393bd42c0a2e63d1bc5cccd9e2bedaf4d70711927e60b8a408254477e38.wasm" -} \ No newline at end of file +}