diff --git a/.changelog/unreleased/SDK/2051-native-masp.md b/.changelog/unreleased/SDK/2051-native-masp.md new file mode 100644 index 0000000000..1187eb12fd --- /dev/null +++ b/.changelog/unreleased/SDK/2051-native-masp.md @@ -0,0 +1,3 @@ +- Masp as internal address. Updated `aux_signing_data` + to return no key and 0 threshold if owner is masp. + ([\#2051](https://github.com/anoma/namada/pull/2051)) \ No newline at end of file diff --git a/.changelog/unreleased/improvements/2051-native-masp.md b/.changelog/unreleased/improvements/2051-native-masp.md new file mode 100644 index 0000000000..46bc4db2be --- /dev/null +++ b/.changelog/unreleased/improvements/2051-native-masp.md @@ -0,0 +1,2 @@ +- Moved the masp vp to native. + ([\#2051](https://github.com/anoma/namada/pull/2051)) \ No newline at end of file diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index 2cfb817bf6..34e88f7fab 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -39,7 +39,7 @@ use namada::ledger::pos::PosParams; use namada::ledger::queries::RPC; use namada::ledger::storage::ConversionState; use namada::proof_of_stake::types::{ValidatorState, WeightedValidator}; -use namada::types::address::{masp, Address, InternalAddress}; +use namada::types::address::{Address, InternalAddress, MASP}; use namada::types::hash::Hash; use namada::types::ibc::{is_ibc_denom, IbcTokenHash}; use namada::types::io::Io; @@ -203,7 +203,7 @@ pub async fn query_transfers<'a>( ); // Display the transparent changes first for (account, MaspChange { ref asset, change }) in tfer_delta { - if account != masp() { + if account != MASP { display!(context.io(), " {}:", account); let token_alias = lookup_token_alias(context, asset, &account).await; @@ -230,7 +230,7 @@ pub async fn query_transfers<'a>( display!(context.io(), " {}:", fvk_map[&account]); for (token_addr, val) in masp_change { let token_alias = - lookup_token_alias(context, &token_addr, &masp()).await; + lookup_token_alias(context, &token_addr, &MASP).await; let sign = match val.cmp(&Change::zero()) { Ordering::Greater => "+", Ordering::Less => "-", @@ -528,7 +528,7 @@ pub async fn query_pinned_balance<'a>( .format_amount(token_addr, (*value).into()) .await; let token_alias = - lookup_token_alias(context, token_addr, &masp()).await; + lookup_token_alias(context, token_addr, &MASP).await; display_line!( context.io(), " {}: {}", @@ -821,7 +821,7 @@ pub async fn query_shielded_balance<'a>( // Here the user wants to know the balance for a specific token (Some(base_token), true) => { let tokens = - query_tokens(context, Some(&base_token), Some(&masp())).await; + query_tokens(context, Some(&base_token), Some(&MASP)).await; for (token_alias, token) in tokens { // Query the multi-asset balance at the given spending key let viewing_key = @@ -939,7 +939,7 @@ pub async fn query_shielded_balance<'a>( } for ((fvk, token), token_balance) in balance_map { // Only assets with the current timestamp count - let alias = lookup_token_alias(context, &token, &masp()).await; + let alias = lookup_token_alias(context, &token, &MASP).await; display_line!(context.io(), "Shielded Token {}:", alias); let formatted = context.format_amount(&token, token_balance.into()).await; @@ -1066,7 +1066,7 @@ pub async fn print_decoded_balance<'a>( display_line!( context.io(), "{} : {}", - lookup_token_alias(context, token_addr, &masp()).await, + lookup_token_alias(context, token_addr, &MASP).await, context.format_amount(token_addr, (*amount).into()).await, ); } @@ -2335,7 +2335,7 @@ pub async fn query_conversions<'a>( .wallet() .await .get_addresses_with_vp_type(AddressVpType::Token); - let masp_addr = masp(); + let masp_addr = MASP; let key_prefix: Key = masp_addr.to_db_key().into(); let state_key = key_prefix .push(&(token::CONVERSION_KEY_PREFIX.to_owned())) diff --git a/apps/src/lib/config/genesis/chain.rs b/apps/src/lib/config/genesis/chain.rs index 0f7b9cdd7e..bab430d4b1 100644 --- a/apps/src/lib/config/genesis/chain.rs +++ b/apps/src/lib/config/genesis/chain.rs @@ -5,7 +5,7 @@ use std::str::FromStr; use borsh::{BorshDeserialize, BorshSerialize}; use borsh_ext::BorshSerializeExt; use namada::ledger::parameters::EpochDuration; -use namada::types::address::{masp, Address, EstablishedAddressGen}; +use namada::types::address::{Address, EstablishedAddressGen, MASP}; use namada::types::chain::{ChainId, ChainIdPrefix}; use namada::types::dec::Dec; use namada::types::hash::Hash; @@ -410,7 +410,7 @@ impl Finalized { pub fn get_user_address(&self, alias: &Alias) -> Option
{ if alias.to_string() == *"masp" { - return Some(masp()); + return Some(MASP); } let established = self.transactions.established_account.as_ref()?; let validators = self.transactions.validator_account.as_ref()?; diff --git a/apps/src/lib/node/ledger/shell/init_chain.rs b/apps/src/lib/node/ledger/shell/init_chain.rs index e14b74da2d..f33f655a45 100644 --- a/apps/src/lib/node/ledger/shell/init_chain.rs +++ b/apps/src/lib/node/ledger/shell/init_chain.rs @@ -293,7 +293,7 @@ where .insert(alias, address.clone()); } } - let key_prefix: Key = address::masp().to_db_key().into(); + let key_prefix: Key = address::MASP.to_db_key().into(); // Save the current conversion state let state_key = key_prefix .push(&(token::CONVERSION_KEY_PREFIX.to_owned())) @@ -355,11 +355,6 @@ where genesis: &genesis::chain::Finalized, vp_cache: &mut HashMap>, ) { - let vp_code = self.lookup_vp("vp_masp", genesis, vp_cache); - let code_hash = CodeHash::sha256(&vp_code); - self.wl_storage - .write_bytes(&Key::validity_predicate(&address::masp()), code_hash) - .unwrap(); if let Some(txs) = genesis.transactions.established_account.as_ref() { for FinalizedEstablishedAccountTx { address, diff --git a/apps/src/lib/wallet/defaults.rs b/apps/src/lib/wallet/defaults.rs index e994203c88..71be346c70 100644 --- a/apps/src/lib/wallet/defaults.rs +++ b/apps/src/lib/wallet/defaults.rs @@ -73,6 +73,7 @@ mod dev { ("christel".into(), christel_address()), ("daewon".into(), daewon_address()), ("ester".into(), ester_address()), + ("masp".into(), namada::types::address::MASP), ] .into_iter() .collect(); diff --git a/benches/native_vps.rs b/benches/native_vps.rs index b3eff8041b..b72de2d29e 100644 --- a/benches/native_vps.rs +++ b/benches/native_vps.rs @@ -33,24 +33,29 @@ use namada::ledger::native_vp::ethereum_bridge::nut::NonUsableTokens; use namada::ledger::native_vp::ethereum_bridge::vp::EthBridge; use namada::ledger::native_vp::ibc::context::PseudoExecutionContext; use namada::ledger::native_vp::ibc::Ibc; +use namada::ledger::native_vp::masp::MaspVp; use namada::ledger::native_vp::multitoken::MultitokenVp; use namada::ledger::native_vp::parameters::ParametersVp; use namada::ledger::native_vp::{Ctx, NativeVp}; use namada::ledger::pgf::PgfVp; use namada::ledger::pos::PosVP; +use namada::namada_sdk::masp::verify_shielded_tx; +use namada::namada_sdk::masp_primitives::transaction::Transaction; use namada::proof_of_stake; use namada::proof_of_stake::KeySeg; use namada::proto::{Code, Section, Tx}; use namada::types::address::InternalAddress; use namada::types::eth_bridge_pool::{GasFee, PendingTransfer}; +use namada::types::masp::{TransferSource, TransferTarget}; use namada::types::storage::{Epoch, TxIndex}; use namada::types::transaction::governance::{ InitProposalData, VoteProposalData, }; use namada_apps::bench_utils::{ - generate_foreign_key_tx, BenchShell, TX_BRIDGE_POOL_WASM, TX_IBC_WASM, - TX_INIT_PROPOSAL_WASM, TX_RESIGN_STEWARD, TX_TRANSFER_WASM, - TX_UPDATE_STEWARD_COMMISSION, TX_VOTE_PROPOSAL_WASM, + generate_foreign_key_tx, BenchShell, BenchShieldedCtx, + ALBERT_PAYMENT_ADDRESS, ALBERT_SPENDING_KEY, BERTHA_PAYMENT_ADDRESS, + TX_BRIDGE_POOL_WASM, TX_IBC_WASM, TX_INIT_PROPOSAL_WASM, TX_RESIGN_STEWARD, + TX_TRANSFER_WASM, TX_UPDATE_STEWARD_COMMISSION, TX_VOTE_PROPOSAL_WASM, }; use namada_apps::wallet::defaults; @@ -464,6 +469,135 @@ fn vp_multitoken(c: &mut Criterion) { } } +// Generate and run masp transaction to be verified +fn setup_storage_for_masp_verification( + bench_name: &str, +) -> (BenchShieldedCtx, Tx) { + let amount = Amount::native_whole(500); + let mut shielded_ctx = BenchShieldedCtx::default(); + + let albert_spending_key = shielded_ctx + .wallet + .find_spending_key(ALBERT_SPENDING_KEY, None) + .unwrap() + .to_owned(); + let albert_payment_addr = shielded_ctx + .wallet + .find_payment_addr(ALBERT_PAYMENT_ADDRESS) + .unwrap() + .to_owned(); + let bertha_payment_addr = shielded_ctx + .wallet + .find_payment_addr(BERTHA_PAYMENT_ADDRESS) + .unwrap() + .to_owned(); + + // Shield some tokens for Albert + let shield_tx = shielded_ctx.generate_masp_tx( + amount, + TransferSource::Address(defaults::albert_address()), + TransferTarget::PaymentAddress(albert_payment_addr), + ); + shielded_ctx.shell.execute_tx(&shield_tx); + shielded_ctx.shell.wl_storage.commit_tx(); + shielded_ctx.shell.commit(); + + let signed_tx = match bench_name { + "shielding" => shielded_ctx.generate_masp_tx( + amount, + TransferSource::Address(defaults::albert_address()), + TransferTarget::PaymentAddress(albert_payment_addr), + ), + "unshielding" => shielded_ctx.generate_masp_tx( + amount, + TransferSource::ExtendedSpendingKey(albert_spending_key), + TransferTarget::Address(defaults::albert_address()), + ), + "shielded" => shielded_ctx.generate_masp_tx( + amount, + TransferSource::ExtendedSpendingKey(albert_spending_key), + TransferTarget::PaymentAddress(bertha_payment_addr), + ), + _ => panic!("Unexpected bench test"), + }; + shielded_ctx.shell.execute_tx(&signed_tx); + + (shielded_ctx, signed_tx) +} + +fn masp(c: &mut Criterion) { + let mut group = c.benchmark_group("vp_masp"); + + for bench_name in ["shielding", "unshielding", "shielded"] { + group.bench_function(bench_name, |b| { + let (shielded_ctx, signed_tx) = + setup_storage_for_masp_verification(bench_name); + let (verifiers, keys_changed) = shielded_ctx + .shell + .wl_storage + .write_log + .verifiers_and_changed_keys(&BTreeSet::default()); + + let masp = MaspVp { + ctx: Ctx::new( + &Address::Internal(InternalAddress::Masp), + &shielded_ctx.shell.wl_storage.storage, + &shielded_ctx.shell.wl_storage.write_log, + &signed_tx, + &TxIndex(0), + VpGasMeter::new_from_tx_meter( + &TxGasMeter::new_from_sub_limit(u64::MAX.into()), + ), + &keys_changed, + &verifiers, + shielded_ctx.shell.vp_wasm_cache.clone(), + ), + }; + + b.iter(|| { + assert!( + masp.validate_tx( + &signed_tx, + masp.ctx.keys_changed, + masp.ctx.verifiers, + ) + .unwrap() + ); + }) + }); + } + + group.finish(); +} + +fn masp_verify_shielded_tx(c: &mut Criterion) { + let mut group = c.benchmark_group("vp_masp_verify_shielded_tx"); + + for bench_name in ["shielding", "unshielding", "shielded"] { + group.bench_function(bench_name, |b| { + let (_, signed_tx) = + setup_storage_for_masp_verification(bench_name); + + let transaction = signed_tx + .sections + .into_iter() + .filter_map(|section| match section { + Section::MaspTx(transaction) => Some(transaction), + _ => None, + }) + .collect::>() + .first() + .unwrap() + .to_owned(); + b.iter(|| { + assert!(verify_shielded_tx(&transaction)); + }) + }); + } + + group.finish(); +} + fn pgf(c: &mut Criterion) { let mut group = c.benchmark_group("vp_pgf"); @@ -1146,6 +1280,8 @@ criterion_group!( governance, // slash_fund, ibc, + masp, + masp_verify_shielded_tx, vp_multitoken, pgf, eth_bridge_nut, diff --git a/benches/vps.rs b/benches/vps.rs index 9023e38bcd..16883d3547 100644 --- a/benches/vps.rs +++ b/benches/vps.rs @@ -14,24 +14,21 @@ use namada::ledger::gas::{TxGasMeter, VpGasMeter}; use namada::proto::{Code, Section}; use namada::types::hash::Hash; use namada::types::key::ed25519; -use namada::types::masp::{TransferSource, TransferTarget}; use namada::types::storage::{Key, TxIndex}; use namada::types::transaction::governance::VoteProposalData; use namada::types::transaction::pos::{Bond, CommissionChange}; use namada::vm::wasm::run; use namada_apps::bench_utils::{ - generate_foreign_key_tx, BenchShell, BenchShieldedCtx, - ALBERT_PAYMENT_ADDRESS, ALBERT_SPENDING_KEY, BERTHA_PAYMENT_ADDRESS, - TX_BOND_WASM, TX_CHANGE_VALIDATOR_COMMISSION_WASM, TX_REVEAL_PK_WASM, - TX_TRANSFER_WASM, TX_UNBOND_WASM, TX_UPDATE_ACCOUNT_WASM, - TX_VOTE_PROPOSAL_WASM, VP_VALIDATOR_WASM, + generate_foreign_key_tx, BenchShell, TX_BOND_WASM, + TX_CHANGE_VALIDATOR_COMMISSION_WASM, TX_REVEAL_PK_WASM, TX_TRANSFER_WASM, + TX_UNBOND_WASM, TX_UPDATE_ACCOUNT_WASM, TX_VOTE_PROPOSAL_WASM, + VP_VALIDATOR_WASM, }; use namada_apps::wallet::defaults; use sha2::Digest; const VP_USER_WASM: &str = "vp_user.wasm"; const VP_IMPLICIT_WASM: &str = "vp_implicit.wasm"; -const VP_MASP_WASM: &str = "vp_masp.wasm"; fn vp_user(c: &mut Criterion) { let mut group = c.benchmark_group("vp_user"); @@ -469,94 +466,5 @@ fn vp_validator(c: &mut Criterion) { group.finish(); } -fn vp_masp(c: &mut Criterion) { - let mut group = c.benchmark_group("vp_masp"); - - let amount = Amount::native_whole(500); - - for bench_name in ["shielding", "unshielding", "shielded"] { - group.bench_function(bench_name, |b| { - let mut shielded_ctx = BenchShieldedCtx::default(); - let vp_code_hash: Hash = shielded_ctx - .shell - .read_storage_key(&Key::wasm_hash(VP_MASP_WASM)) - .unwrap(); - - let albert_spending_key = shielded_ctx - .wallet - .find_spending_key(ALBERT_SPENDING_KEY, None) - .unwrap() - .to_owned(); - let albert_payment_addr = shielded_ctx - .wallet - .find_payment_addr(ALBERT_PAYMENT_ADDRESS) - .unwrap() - .to_owned(); - let bertha_payment_addr = shielded_ctx - .wallet - .find_payment_addr(BERTHA_PAYMENT_ADDRESS) - .unwrap() - .to_owned(); - - // Shield some tokens for Albert - let shield_tx = shielded_ctx.generate_masp_tx( - amount, - TransferSource::Address(defaults::albert_address()), - TransferTarget::PaymentAddress(albert_payment_addr), - ); - shielded_ctx.shell.execute_tx(&shield_tx); - shielded_ctx.shell.wl_storage.commit_tx(); - shielded_ctx.shell.commit(); - - let signed_tx = match bench_name { - "shielding" => shielded_ctx.generate_masp_tx( - amount, - TransferSource::Address(defaults::albert_address()), - TransferTarget::PaymentAddress(albert_payment_addr), - ), - "unshielding" => shielded_ctx.generate_masp_tx( - amount, - TransferSource::ExtendedSpendingKey(albert_spending_key), - TransferTarget::Address(defaults::albert_address()), - ), - "shielded" => shielded_ctx.generate_masp_tx( - amount, - TransferSource::ExtendedSpendingKey(albert_spending_key), - TransferTarget::PaymentAddress(bertha_payment_addr), - ), - _ => panic!("Unexpected bench test"), - }; - shielded_ctx.shell.execute_tx(&signed_tx); - let (verifiers, keys_changed) = shielded_ctx - .shell - .wl_storage - .write_log - .verifiers_and_changed_keys(&BTreeSet::default()); - - b.iter(|| { - assert!( - run::vp( - vp_code_hash, - &signed_tx, - &TxIndex(0), - &defaults::validator_address(), - &shielded_ctx.shell.wl_storage.storage, - &shielded_ctx.shell.wl_storage.write_log, - &mut VpGasMeter::new_from_tx_meter( - &TxGasMeter::new_from_sub_limit(u64::MAX.into()) - ), - &keys_changed, - &verifiers, - shielded_ctx.shell.vp_wasm_cache.clone(), - ) - .unwrap() - ); - }) - }); - } - - group.finish(); -} - -criterion_group!(whitelisted_vps, vp_user, vp_implicit, vp_validator, vp_masp,); +criterion_group!(whitelisted_vps, vp_user, vp_implicit, vp_validator,); criterion_main!(whitelisted_vps); diff --git a/core/src/ledger/gas.rs b/core/src/ledger/gas.rs index 0fefccd983..092c0ba3b2 100644 --- a/core/src/ledger/gas.rs +++ b/core/src/ledger/gas.rs @@ -53,6 +53,8 @@ pub const WASM_MEMORY_PAGE_GAS: u32 = pub const IBC_ACTION_VALIDATE_GAS: u64 = 7_511; /// The cost to execute an Ibc action pub const IBC_ACTION_EXECUTE_GAS: u64 = 47_452; +/// The cost to execute a masp tx verification +pub const MASP_VERIFY_SHIELDED_TX_GAS: u64 = 62_381_957; /// Gas module result for functions that may fail pub type Result = std::result::Result; diff --git a/core/src/ledger/ibc/mod.rs b/core/src/ledger/ibc/mod.rs index cf0962cdbd..2a4919100e 100644 --- a/core/src/ledger/ibc/mod.rs +++ b/core/src/ledger/ibc/mod.rs @@ -31,7 +31,7 @@ use crate::ibc::core::ics24_host::identifier::{ use crate::ibc::core::router::ModuleId; use crate::ibc::core::{execute, validate, MsgEnvelope, RouterError}; use crate::ibc_proto::google::protobuf::Any; -use crate::types::address::{masp, Address}; +use crate::types::address::{Address, MASP}; use crate::types::ibc::{ get_shielded_transfer, is_ibc_denom, EVENT_TYPE_DENOM_TRACE, EVENT_TYPE_PACKET, @@ -190,7 +190,7 @@ where .or_else(|_| { // Replace it with MASP address when the receiver is a // payment address - PaymentAddress::from_str(receiver).map(|_| masp()) + PaymentAddress::from_str(receiver).map(|_| MASP) }) .map_err(|_| { Error::Denom(format!( diff --git a/core/src/ledger/storage/masp_conversions.rs b/core/src/ledger/storage/masp_conversions.rs index 5cbbca570a..87f9095dcb 100644 --- a/core/src/ledger/storage/masp_conversions.rs +++ b/core/src/ledger/storage/masp_conversions.rs @@ -13,7 +13,7 @@ use crate::ledger::inflation::{RewardsController, ValsToUpdate}; use crate::ledger::parameters; use crate::ledger::storage_api::token::read_denom; use crate::ledger::storage_api::{StorageRead, StorageWrite}; -use crate::types::address::Address; +use crate::types::address::{Address, MASP}; use crate::types::dec::Dec; use crate::types::storage::Epoch; use crate::types::token::MaspDenom; @@ -58,7 +58,7 @@ where // a thousandth of the given asset. let precision = 10u128.pow(std::cmp::max(u32::from(denomination.0), 3) - 3); - let masp_addr = address::masp(); + let masp_addr = MASP; // Query the storage for information //// information about the amount of tokens on the chain @@ -213,7 +213,7 @@ where use crate::types::storage::{self, KeySeg}; // The derived conversions will be placed in MASP address space - let masp_addr = address::masp(); + let masp_addr = MASP; let key_prefix: storage::Key = masp_addr.to_db_key().into(); let tokens = address::tokens(); diff --git a/core/src/ledger/storage/mod.rs b/core/src/ledger/storage/mod.rs index f82eee1cfc..2479f9adfa 100644 --- a/core/src/ledger/storage/mod.rs +++ b/core/src/ledger/storage/mod.rs @@ -41,7 +41,7 @@ use crate::ledger::storage::merkle_tree::{ }; use crate::tendermint::merkle::proof::ProofOps; use crate::types::address::{ - masp, Address, EstablishedAddressGen, InternalAddress, + Address, EstablishedAddressGen, InternalAddress, MASP, }; use crate::types::chain::{ChainId, CHAIN_ID_LENGTH}; use crate::types::hash::{Error as HashError, Hash}; @@ -491,7 +491,7 @@ where .or_else(|_| self.rebuild_full_merkle_tree(height))?; if self.block.height.0 > 0 { // The derived conversions will be placed in MASP address space - let masp_addr = masp(); + let masp_addr = MASP; let key_prefix: Key = masp_addr.to_db_key().into(); // Load up the conversions currently being given as query // results diff --git a/core/src/ledger/vp_env.rs b/core/src/ledger/vp_env.rs index 1241adfed7..664e030786 100644 --- a/core/src/ledger/vp_env.rs +++ b/core/src/ledger/vp_env.rs @@ -109,7 +109,7 @@ where /// Get the shielded action including the transfer and the masp tx fn get_shielded_action( &self, - tx_data: Tx, + tx_data: &Tx, ) -> Result<(Transfer, Transaction), storage_api::Error> { let signed = tx_data; if let Ok(transfer) = @@ -143,9 +143,6 @@ where }) } - /// Verify a MASP transaction - fn verify_masp(&self, tx: Vec) -> Result; - /// Charge the provided gas for the current vp fn charge_gas(&self, used_gas: u64) -> Result<(), storage_api::Error>; diff --git a/core/src/types/address.rs b/core/src/types/address.rs index 8656ee0598..2543fc7f54 100644 --- a/core/src/types/address.rs +++ b/core/src/types/address.rs @@ -60,6 +60,8 @@ pub const POS_SLASH_POOL: Address = Address::Internal(InternalAddress::PosSlashPool); /// Internal Governance address pub const GOV: Address = Address::Internal(InternalAddress::Governance); +/// Internal MASP address +pub const MASP: Address = Address::Internal(InternalAddress::Masp); /// Error from decoding address from string pub type DecodeError = string_encoding::DecodeError; @@ -121,6 +123,7 @@ impl From> for Address { raw::Discriminant::IbcToken => Address::Internal( InternalAddress::IbcToken(IbcTokenHash(*raw_addr.data())), ), + raw::Discriminant::Masp => Address::Internal(InternalAddress::Masp), } } } @@ -210,6 +213,11 @@ impl<'addr> From<&'addr Address> for raw::Address<'addr, raw::Validated> { .validate() .expect("This raw address is valid") } + Address::Internal(InternalAddress::Masp) => { + raw::Address::from_discriminant(raw::Discriminant::Masp) + .validate() + .expect("This raw address is valid") + } } } } @@ -346,7 +354,7 @@ impl TryFrom for Address { Address::decode(signer.as_ref()).or( match crate::types::masp::PaymentAddress::from_str(signer.as_ref()) { - Ok(_) => Ok(masp()), + Ok(_) => Ok(MASP), Err(_) => Err(DecodeError::InvalidInnerEncoding(format!( "Invalid address for IBC transfer: {signer}" ))), @@ -485,6 +493,8 @@ pub enum InternalAddress { Multitoken, /// Pgf Pgf, + /// Masp + Masp, } impl Display for InternalAddress { @@ -505,6 +515,7 @@ impl Display for InternalAddress { Self::Nut(eth_addr) => format!("Non-usable token: {eth_addr}"), Self::Multitoken => "Multitoken".to_string(), Self::Pgf => "PublicGoodFundings".to_string(), + Self::Masp => "MASP".to_string(), } ) } @@ -519,6 +530,7 @@ impl InternalAddress { "ethbridge" => Some(InternalAddress::EthBridge), "bridgepool" => Some(InternalAddress::EthBridgePool), "governance" => Some(InternalAddress::Governance), + "masp" => Some(InternalAddress::Masp), _ => None, } } @@ -566,12 +578,6 @@ pub fn kartoffel() -> Address { .expect("The token address decoding shouldn't fail") } -/// Temporary helper for testing -pub fn masp() -> Address { - Address::decode("tnam1q9lm5pvkxhnw9wwwhu33vkvtylwfkn5kw53xwud8") - .expect("The token address decoding shouldn't fail") -} - /// Sentinel secret key to indicate a MASP source pub fn masp_tx_key() -> crate::types::key::common::SecretKey { use crate::types::key::common; @@ -809,6 +815,7 @@ pub mod testing { InternalAddress::Erc20(_) => {} InternalAddress::Nut(_) => {} InternalAddress::Pgf => {} + InternalAddress::Masp => {} InternalAddress::Multitoken => {} /* Add new addresses in the * `prop_oneof` below. */ }; @@ -825,6 +832,7 @@ pub mod testing { Just(arb_nut()), Just(InternalAddress::Multitoken), Just(InternalAddress::Pgf), + Just(InternalAddress::Masp), ] } diff --git a/core/src/types/address/raw.rs b/core/src/types/address/raw.rs index aade2af3a4..d975d219f9 100644 --- a/core/src/types/address/raw.rs +++ b/core/src/types/address/raw.rs @@ -63,6 +63,8 @@ pub enum Discriminant { Nut = 12, /// IBC token raw address. IbcToken = 13, + /// MASP raw address. + Masp = 14, } /// Raw address representation. diff --git a/core/src/types/masp.rs b/core/src/types/masp.rs index 685394f72c..89994539b2 100644 --- a/core/src/types/masp.rs +++ b/core/src/types/masp.rs @@ -8,7 +8,7 @@ use borsh_ext::BorshSerializeExt; use sha2::{Digest, Sha256}; use crate::impl_display_and_from_str_via_format; -use crate::types::address::{masp, Address, DecodeError, HASH_HEX_LEN}; +use crate::types::address::{Address, DecodeError, HASH_HEX_LEN, MASP}; use crate::types::string_encoding::{ self, MASP_EXT_FULL_VIEWING_KEY_HRP, MASP_EXT_SPENDING_KEY_HRP, MASP_PAYMENT_ADDRESS_HRP, @@ -303,7 +303,7 @@ impl TransferSource { Self::Address(x) => x.clone(), // An ExtendedSpendingKey for a source effectively means that // assets will be drawn from the MASP - Self::ExtendedSpendingKey(_) => masp(), + Self::ExtendedSpendingKey(_) => MASP, } } @@ -349,7 +349,7 @@ impl TransferTarget { Self::Address(x) => x.clone(), // An ExtendedSpendingKey for a source effectively means that // assets will be drawn from the MASP - Self::PaymentAddress(_) => masp(), + Self::PaymentAddress(_) => MASP, } } diff --git a/core/src/types/token.rs b/core/src/types/token.rs index 1b0e7a40d2..fbee830b47 100644 --- a/core/src/types/token.rs +++ b/core/src/types/token.rs @@ -18,7 +18,7 @@ use crate::ledger::storage as ledger_storage; use crate::ledger::storage_api::token::read_denom; use crate::ledger::storage_api::{self, StorageRead, StorageWrite}; use crate::types::address::{ - masp, Address, DecodeError as AddressError, InternalAddress, + Address, DecodeError as AddressError, InternalAddress, MASP, }; use crate::types::dec::Dec; use crate::types::hash::Hash; @@ -1123,7 +1123,7 @@ pub fn is_denom_key(token_addr: &Address, key: &Key) -> bool { pub fn is_masp_key(key: &Key) -> bool { matches!(&key.segments[..], [DbKeySeg::AddressSeg(addr), DbKeySeg::StringSeg(key)] - if *addr == masp() + if *addr == MASP && (key == HEAD_TX_KEY || key.starts_with(TX_KEY_PREFIX) || key.starts_with(PIN_KEY_PREFIX))) diff --git a/core/src/types/transaction/wrapper.rs b/core/src/types/transaction/wrapper.rs index 8c4f1fb4a9..f9b032b3f4 100644 --- a/core/src/types/transaction/wrapper.rs +++ b/core/src/types/transaction/wrapper.rs @@ -15,7 +15,7 @@ pub mod wrapper_tx { use thiserror::Error; use crate::proto::{Code, Data, Section, Tx}; - use crate::types::address::{masp, Address}; + use crate::types::address::{Address, MASP}; use crate::types::hash::Hash; use crate::types::key::*; use crate::types::storage::Epoch; @@ -310,7 +310,7 @@ pub mod wrapper_tx { ); let transfer = Transfer { - source: masp(), + source: MASP, target: self.fee_payer(), token: self.fee.token.clone(), amount: DenominatedAmount { diff --git a/genesis/localnet/validity-predicates.toml b/genesis/localnet/validity-predicates.toml index a0c2570121..907e7c522b 100644 --- a/genesis/localnet/validity-predicates.toml +++ b/genesis/localnet/validity-predicates.toml @@ -12,6 +12,3 @@ filename = "vp_user.wasm" [wasm.vp_validator] filename = "vp_validator.wasm" -# MASP VP -[wasm.vp_masp] -filename = "vp_masp.wasm" diff --git a/genesis/starter/validity-predicates.toml b/genesis/starter/validity-predicates.toml index 863ec7ec38..9c9e76fc83 100644 --- a/genesis/starter/validity-predicates.toml +++ b/genesis/starter/validity-predicates.toml @@ -16,7 +16,3 @@ filename = "vp_validator.wasm" [wasm.vp_token] filename = "vp_token.wasm" -# MASP VP -[wasm.vp_masp] -filename = "vp_masp.wasm" - diff --git a/sdk/src/masp.rs b/sdk/src/masp.rs index 2ead173e52..68459688e0 100644 --- a/sdk/src/masp.rs +++ b/sdk/src/masp.rs @@ -48,7 +48,7 @@ use masp_proofs::bellman::groth16::PreparedVerifyingKey; use masp_proofs::bls12_381::Bls12; use masp_proofs::prover::LocalTxProver; use masp_proofs::sapling::SaplingVerificationContext; -use namada_core::types::address::{masp, Address}; +use namada_core::types::address::{Address, MASP}; use namada_core::types::masp::{ BalanceOwner, ExtendedViewingKey, PaymentAddress, TransferSource, TransferTarget, @@ -740,7 +740,7 @@ impl ShieldedContext { Error, > { // The address of the MASP account - let masp_addr = masp(); + let masp_addr = MASP; // Construct the key where last transaction pointer is stored let head_tx_key = Key::from(masp_addr.to_db_key()) .push(&HEAD_TX_KEY.to_owned()) @@ -1340,7 +1340,7 @@ impl ShieldedContext { } } // The address of the MASP account - let masp_addr = masp(); + let masp_addr = MASP; // Construct the key for where the transaction ID would be stored let pin_key = Key::from(masp_addr.to_db_key()) .push(&(PIN_KEY_PREFIX.to_owned() + &owner.hash())) @@ -1919,8 +1919,8 @@ impl ShieldedContext { if let Some(transfer) = transfer { // Skip MASP addresses as they are already handled // by ShieldedContext - if transfer.source == masp() - || transfer.target == masp() + if transfer.source == MASP + || transfer.target == MASP { continue; } diff --git a/sdk/src/signing.rs b/sdk/src/signing.rs index 39120f8261..fb35662017 100644 --- a/sdk/src/signing.rs +++ b/sdk/src/signing.rs @@ -14,7 +14,7 @@ use namada_core::ledger::parameters::storage as parameter_storage; use namada_core::proto::SignatureIndex; use namada_core::types::account::AccountPublicKeysMap; use namada_core::types::address::{ - masp, masp_tx_key, Address, ImplicitAddress, + masp_tx_key, Address, ImplicitAddress, InternalAddress, MASP, }; use namada_core::types::key::*; use namada_core::types::masp::{ExtendedViewingKey, PaymentAddress}; @@ -184,7 +184,7 @@ pub async fn tx_signers<'a>( // Now actually fetch the signing key and apply it match signer { - Some(signer) if signer == masp() => Ok(vec![masp_tx_key().ref_to()]), + Some(signer) if signer == MASP => Ok(vec![masp_tx_key().ref_to()]), Some(signer) => Ok(vec![find_pk(context, &signer).await?]), None => other_err( @@ -348,9 +348,14 @@ pub async fn aux_signing_data<'a>( Some(AccountPublicKeysMap::from_iter(public_keys.clone())), 1u8, ), - Some(owner @ Address::Internal(_)) => { - return Err(Error::from(TxError::InvalidAccount(owner.encode()))); - } + Some(owner @ Address::Internal(internal)) => match internal { + InternalAddress::Masp => (None, 0u8), + _ => { + return Err(Error::from(TxError::InvalidAccount( + owner.encode(), + ))); + } + }, None => (None, 0u8), }; @@ -786,9 +791,9 @@ pub async fn make_ledger_masp_endpoints<'a>( builder: Option<&MaspBuilder>, assets: &HashMap, ) { - if transfer.source != masp() { + if transfer.source != MASP { output.push(format!("Sender : {}", transfer.source)); - if transfer.target == masp() { + if transfer.target == MASP { make_ledger_amount_addr( tokens, output, @@ -813,9 +818,9 @@ pub async fn make_ledger_masp_endpoints<'a>( .await; } } - if transfer.target != masp() { + if transfer.target != MASP { output.push(format!("Destination : {}", transfer.target)); - if transfer.source == masp() { + if transfer.source == MASP { make_ledger_amount_addr( tokens, output, @@ -840,7 +845,7 @@ pub async fn make_ledger_masp_endpoints<'a>( .await; } } - if transfer.source != masp() && transfer.target != masp() { + if transfer.source != MASP && transfer.target != MASP { make_ledger_amount_addr( tokens, output, diff --git a/sdk/src/tx.rs b/sdk/src/tx.rs index 05b61cdf0e..9c3ebed2d3 100644 --- a/sdk/src/tx.rs +++ b/sdk/src/tx.rs @@ -31,7 +31,7 @@ use namada_core::ledger::governance::storage::proposal::ProposalType; use namada_core::ledger::governance::storage::vote::StorageProposalVote; use namada_core::ledger::ibc::storage::channel_key; use namada_core::ledger::pgf::cli::steward::Commission; -use namada_core::types::address::{masp, Address, InternalAddress}; +use namada_core::types::address::{Address, InternalAddress, MASP}; use namada_core::types::dec::Dec; use namada_core::types::hash::Hash; use namada_core::types::ibc::IbcShieldedTransfer; @@ -2214,7 +2214,7 @@ pub async fn build_transfer<'a, N: Namada<'a>>( token: args.token.clone(), }); - let masp_addr = masp(); + let masp_addr = MASP; // For MASP sources, use a special sentinel key recognized by VPs as default // signer. Also, if the transaction is shielded, redact the amount and token @@ -2558,7 +2558,7 @@ pub async fn gen_ibc_shielded_transfer<'a, N: Namada<'a>>( let transfer = token::Transfer { source: source.clone(), - target: masp(), + target: MASP, token: token.clone(), amount: validated_amount, key, diff --git a/shared/src/ledger/native_vp/ibc/context.rs b/shared/src/ledger/native_vp/ibc/context.rs index defdd1a177..ffe2faa2f5 100644 --- a/shared/src/ledger/native_vp/ibc/context.rs +++ b/shared/src/ledger/native_vp/ibc/context.rs @@ -11,7 +11,7 @@ use crate::ledger::native_vp::CtxPreStorageRead; use crate::ledger::storage::write_log::StorageModification; use crate::ledger::storage::{self as ledger_storage, StorageHasher}; use crate::ledger::storage_api::{self, StorageRead, StorageWrite}; -use crate::types::address::{self, Address, InternalAddress}; +use crate::types::address::{Address, InternalAddress, MASP}; use crate::types::ibc::{IbcEvent, IbcShieldedTransfer}; use crate::types::storage::{ BlockHash, BlockHeight, Epoch, Header, Key, KeySeg, TxIndex, @@ -216,7 +216,7 @@ where } fn handle_masp_tx(&mut self, shielded: &IbcShieldedTransfer) -> Result<()> { - let masp_addr = address::masp(); + let masp_addr = MASP; let head_tx_key = Key::from(masp_addr.to_db_key()) .push(&HEAD_TX_KEY.to_owned()) .expect("Cannot obtain a storage key"); diff --git a/shared/src/ledger/native_vp/masp.rs b/shared/src/ledger/native_vp/masp.rs new file mode 100644 index 0000000000..54048f6759 --- /dev/null +++ b/shared/src/ledger/native_vp/masp.rs @@ -0,0 +1,300 @@ +//! MASP native VP + +use std::cmp::Ordering; +use std::collections::BTreeSet; + +use borsh_ext::BorshSerializeExt; +use masp_primitives::asset_type::AssetType; +use masp_primitives::transaction::components::I128Sum; +use namada_core::ledger::gas::MASP_VERIFY_SHIELDED_TX_GAS; +use namada_core::ledger::storage; +use namada_core::ledger::storage_api::OptionExt; +use namada_core::ledger::vp_env::VpEnv; +use namada_core::proto::Tx; +use namada_core::types::address::Address; +use namada_core::types::address::InternalAddress::Masp; +use namada_core::types::storage::{Epoch, Key}; +use namada_core::types::token; +use namada_sdk::masp::verify_shielded_tx; +use ripemd::Digest as RipemdDigest; +use sha2::Digest as Sha2Digest; +use thiserror::Error; + +use crate::ledger::native_vp; +use crate::ledger::native_vp::{Ctx, NativeVp}; +use crate::vm::WasmCacheAccess; + +#[allow(missing_docs)] +#[derive(Error, Debug)] +pub enum Error { + #[error("Native VP error: {0}")] + NativeVpError(native_vp::Error), +} + +/// MASP VP result +pub type Result = std::result::Result; + +/// MASP VP +pub struct MaspVp<'a, DB, H, CA> +where + DB: storage::DB + for<'iter> storage::DBIter<'iter>, + H: storage::StorageHasher, + CA: WasmCacheAccess, +{ + /// Context to interact with the host structures. + pub ctx: Ctx<'a, DB, H, CA>, +} + +/// Generates the current asset type given the current epoch and an +/// unique token address +fn asset_type_from_epoched_address( + epoch: Epoch, + token: &Address, + denom: token::MaspDenom, +) -> AssetType { + // Timestamp the chosen token with the current epoch + let token_bytes = (token, denom, epoch.0).serialize_to_vec(); + // Generate the unique asset identifier from the unique token address + AssetType::new(token_bytes.as_ref()).expect("unable to create asset type") +} + +/// Checks if the asset type matches the expected asset type, Adds a +/// debug log if the values do not match. +fn valid_asset_type( + asset_type: &AssetType, + asset_type_to_test: &AssetType, +) -> bool { + let res = + asset_type.get_identifier() == asset_type_to_test.get_identifier(); + if !res { + tracing::debug!( + "The asset type must be derived from the token address and \ + current epoch" + ); + } + res +} + +/// Checks if the reported transparent amount and the unshielded +/// values agree, if not adds to the debug log +fn valid_transfer_amount( + reporeted_transparent_value: u64, + unshielded_transfer_value: u64, +) -> bool { + let res = reporeted_transparent_value == unshielded_transfer_value; + if !res { + tracing::debug!( + "The unshielded amount {} disagrees with the calculated masp \ + transparented value {}", + unshielded_transfer_value, + reporeted_transparent_value + ); + } + res +} + +/// Convert Namada amount and token type to MASP equivalents +fn convert_amount( + epoch: Epoch, + token: &Address, + val: token::Amount, + denom: token::MaspDenom, +) -> (AssetType, I128Sum) { + let asset_type = asset_type_from_epoched_address(epoch, token, denom); + // Combine the value and unit into one amount + let amount = + I128Sum::from_nonnegative(asset_type, denom.denominate(&val) as i128) + .expect("invalid value or asset type for amount"); + (asset_type, amount) +} + +impl<'a, DB, H, CA> NativeVp for MaspVp<'a, DB, H, CA> +where + DB: 'static + storage::DB + for<'iter> storage::DBIter<'iter>, + H: 'static + storage::StorageHasher, + CA: 'static + WasmCacheAccess, +{ + type Error = Error; + + fn validate_tx( + &self, + tx_data: &Tx, + _keys_changed: &BTreeSet, + _verifiers: &BTreeSet
, + ) -> Result { + let epoch = self.ctx.get_block_epoch()?; + let (transfer, shielded_tx) = self.ctx.get_shielded_action(tx_data)?; + let mut transparent_tx_pool = I128Sum::zero(); + // The Sapling value balance adds to the transparent tx pool + transparent_tx_pool += shielded_tx.sapling_value_balance(); + + if transfer.source != Address::Internal(Masp) { + // Handle transparent input + // Note that the asset type is timestamped so shields + // where the shielded value has an incorrect timestamp + // are automatically rejected + for denom in token::MaspDenom::iter() { + let (_transp_asset, transp_amt) = convert_amount( + epoch, + &transfer.token, + transfer.amount.into(), + denom, + ); + + // Non-masp sources add to transparent tx pool + transparent_tx_pool += transp_amt; + } + } else { + // Handle shielded input + // The following boundary conditions must be satisfied + // 1. Zero transparent input + // 2. the transparent transaction value pool's amount must equal + // the containing wrapper transaction's fee + // amount Satisfies 1. + if let Some(transp_bundle) = shielded_tx.transparent_bundle() { + if !transp_bundle.vin.is_empty() { + tracing::debug!( + "Transparent input to a transaction from the masp \ + must be 0 but is {}", + transp_bundle.vin.len() + ); + return Ok(false); + } + } + } + + if transfer.target != Address::Internal(Masp) { + // Handle transparent output + // The following boundary conditions must be satisfied + // 1. One to 4 transparent outputs + // 2. Asset type must be properly derived + // 3. Value from the output must be the same as the containing + // transfer + // 4. Public key must be the hash of the target + + // Satisfies 1. + let transp_bundle = + shielded_tx.transparent_bundle().ok_or_err_msg( + "Expected transparent outputs in unshielding transaction", + )?; + + let out_length = transp_bundle.vout.len(); + if !(1..=4).contains(&out_length) { + tracing::debug!( + "Transparent output to a transaction to the masp must be \ + beteween 1 and 4 but is {}", + transp_bundle.vout.len() + ); + + return Ok(false); + } + let mut outs = transp_bundle.vout.iter(); + let mut valid_count = 0; + for denom in token::MaspDenom::iter() { + let out = match outs.next() { + Some(out) => out, + None => continue, + }; + + let expected_asset_type: AssetType = + asset_type_from_epoched_address( + epoch, + &transfer.token, + denom, + ); + + // Satisfies 2. and 3. + if !valid_asset_type(&expected_asset_type, &out.asset_type) { + // we don't know which masp denoms are necessary + // apriori. This is encoded via + // the asset types. + continue; + } + if !valid_transfer_amount( + out.value, + denom.denominate(&transfer.amount.amount), + ) { + return Ok(false); + } + + let (_transp_asset, transp_amt) = convert_amount( + epoch, + &transfer.token, + transfer.amount.amount, + denom, + ); + + // Non-masp destinations subtract from transparent tx pool + transparent_tx_pool -= transp_amt; + + // Satisfies 4. + let target_enc = transfer.target.serialize_to_vec(); + + let hash = ripemd::Ripemd160::digest(sha2::Sha256::digest( + &target_enc, + )); + + if <[u8; 20]>::from(hash) != out.address.0 { + tracing::debug!( + "the public key of the output account does not match \ + the transfer target" + ); + return Ok(false); + } + valid_count += 1; + } + // one or more of the denoms in the batch failed to verify + // the asset derivation. + if valid_count != out_length { + return Ok(false); + } + } else { + // Handle shielded output + // The following boundary conditions must be satisfied + // 1. Zero transparent output + + // Satisfies 1. + if let Some(transp_bundle) = shielded_tx.transparent_bundle() { + if !transp_bundle.vout.is_empty() { + tracing::debug!( + "Transparent output to a transaction from the masp \ + must be 0 but is {}", + transp_bundle.vout.len() + ); + return Ok(false); + } + } + } + + match transparent_tx_pool.partial_cmp(&I128Sum::zero()) { + None | Some(Ordering::Less) => { + tracing::debug!( + "Transparent transaction value pool must be nonnegative. \ + Violation may be caused by transaction being constructed \ + in previous epoch. Maybe try again." + ); + // Section 3.4: The remaining value in the transparent + // transaction value pool MUST be nonnegative. + return Ok(false); + } + Some(Ordering::Greater) => { + tracing::debug!( + "Transaction fees cannot be paid inside MASP transaction." + ); + return Ok(false); + } + _ => {} + } + // Verify the proofs and charge the gas for the expensive execution + self.ctx + .charge_gas(MASP_VERIFY_SHIELDED_TX_GAS) + .map_err(Error::NativeVpError)?; + Ok(verify_shielded_tx(&shielded_tx)) + } +} + +impl From for Error { + fn from(err: native_vp::Error) -> Self { + Self::NativeVpError(err) + } +} diff --git a/shared/src/ledger/native_vp/mod.rs b/shared/src/ledger/native_vp/mod.rs index a590a1e433..a18d5f5404 100644 --- a/shared/src/ledger/native_vp/mod.rs +++ b/shared/src/ledger/native_vp/mod.rs @@ -3,6 +3,7 @@ pub mod ethereum_bridge; pub mod ibc; +pub mod masp; pub mod multitoken; pub mod parameters; @@ -564,10 +565,6 @@ where } } - fn verify_masp(&self, _tx: Vec) -> Result { - unimplemented!("no masp native vp") - } - fn charge_gas(&self, used_gas: u64) -> Result<(), storage_api::Error> { self.gas_meter.borrow_mut().consume(used_gas).map_err(|_| { Error::SimpleMessage("Gas limit exceeded in native vp") diff --git a/shared/src/ledger/protocol/mod.rs b/shared/src/ledger/protocol/mod.rs index d142781127..64021faf6b 100644 --- a/shared/src/ledger/protocol/mod.rs +++ b/shared/src/ledger/protocol/mod.rs @@ -22,6 +22,7 @@ use crate::ledger::native_vp::ethereum_bridge::bridge_pool_vp::BridgePoolVp; use crate::ledger::native_vp::ethereum_bridge::nut::NonUsableTokens; use crate::ledger::native_vp::ethereum_bridge::vp::EthBridge; use crate::ledger::native_vp::ibc::Ibc; +use crate::ledger::native_vp::masp::MaspVp; use crate::ledger::native_vp::multitoken::MultitokenVp; use crate::ledger::native_vp::parameters::{self, ParametersVp}; use crate::ledger::native_vp::{self, NativeVp}; @@ -90,6 +91,8 @@ pub enum Error { BridgePoolNativeVpError(native_vp::ethereum_bridge::bridge_pool_vp::Error), #[error("Non usable tokens native VP error: {0}")] NutNativeVpError(native_vp::ethereum_bridge::nut::Error), + #[error("MASP native VP error: {0}")] + MaspNativeVpError(native_vp::masp::Error), #[error("Access to an internal address {0} is forbidden")] AccessForbidden(InternalAddress), } @@ -1005,6 +1008,16 @@ where ctx.sentinel.into_inner(), ) } + InternalAddress::Masp => { + let masp = MaspVp { ctx }; + let result = masp + .validate_tx(tx, &keys_changed, &verifiers) + .map_err(Error::MaspNativeVpError); + // Take the gas meter and the sentinel back out + // of the context + gas_meter = masp.ctx.gas_meter.into_inner(); + (result, masp.ctx.sentinel.into_inner()) + } }; accepted.map_err(|err| { diff --git a/shared/src/vm/host_env.rs b/shared/src/vm/host_env.rs index 2ea0037f9a..9dd19c1d74 100644 --- a/shared/src/vm/host_env.rs +++ b/shared/src/vm/host_env.rs @@ -10,7 +10,7 @@ use masp_primitives::transaction::Transaction; use namada_core::ledger::gas::{ GasMetering, TxGasMeter, MEMORY_ACCESS_GAS_PER_BYTE, }; -use namada_core::types::address::ESTABLISHED_ADDRESS_BYTES_LEN; +use namada_core::types::address::{ESTABLISHED_ADDRESS_BYTES_LEN, MASP}; use namada_core::types::internal::KeyVal; use namada_core::types::storage::TX_INDEX_LENGTH; use namada_core::types::transaction::TxSentinel; @@ -1945,40 +1945,6 @@ where } } -/// Verify a ShieldedTransaction. -pub fn vp_verify_masp( - env: &VpVmEnv, - tx_ptr: u64, - tx_len: u64, -) -> vp_host_fns::EnvResult -where - MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, - H: StorageHasher, - EVAL: VpEvaluator, - CA: WasmCacheAccess, -{ - let gas_meter = unsafe { env.ctx.gas_meter.get() }; - let sentinel = unsafe { env.ctx.sentinel.get() }; - let (tx_bytes, gas) = env - .memory - .read_bytes(tx_ptr, tx_len as _) - .map_err(|e| vp_host_fns::RuntimeError::MemoryError(Box::new(e)))?; - vp_host_fns::add_gas(gas_meter, gas, sentinel)?; - - let shielded: Transaction = - BorshDeserialize::try_from_slice(tx_bytes.as_slice()) - .map_err(vp_host_fns::RuntimeError::EncodingError)?; - - Ok( - // TODO: once the runtime gas meter is implemented we need to benchmark - // this funcion and charge the gas here. For the moment, the cost of - // this is included in the benchmark of the masp vp - HostEnvResult::from(namada_sdk::masp::verify_shielded_tx(&shielded)) - .to_i64(), - ) -} - /// Log a string from exposed to the wasm VM Tx environment. The message will be /// printed at the [`tracing::Level::INFO`]. This function is for development /// only. @@ -2431,7 +2397,7 @@ where &mut self, shielded: &IbcShieldedTransfer, ) -> Result<(), storage_api::Error> { - let masp_addr = address::masp(); + let masp_addr = MASP; let head_tx_key = Key::from(masp_addr.to_db_key()) .push(&HEAD_TX_KEY.to_owned()) .expect("Cannot obtain a storage key"); diff --git a/shared/src/vm/wasm/host_env.rs b/shared/src/vm/wasm/host_env.rs index a7dc51cc6f..fc5a5d5594 100644 --- a/shared/src/vm/wasm/host_env.rs +++ b/shared/src/vm/wasm/host_env.rs @@ -130,7 +130,6 @@ where "namada_vp_get_block_epoch" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_get_block_epoch), "namada_vp_get_ibc_events" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_get_ibc_events), "namada_vp_verify_tx_section_signature" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_verify_tx_section_signature), - "namada_vp_verify_masp" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_verify_masp), "namada_vp_eval" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_eval), "namada_vp_get_native_token" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_get_native_token), "namada_vp_log_string" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_log_string), diff --git a/test_fixtures/masp_proofs/0044C26D5E4F29FA81F3970709A98182D17943D69AB701C24FA90E1EFC1876F0.bin b/test_fixtures/masp_proofs/0044C26D5E4F29FA81F3970709A98182D17943D69AB701C24FA90E1EFC1876F0.bin new file mode 100644 index 0000000000..4eecf0358b Binary files /dev/null and b/test_fixtures/masp_proofs/0044C26D5E4F29FA81F3970709A98182D17943D69AB701C24FA90E1EFC1876F0.bin differ diff --git a/test_fixtures/masp_proofs/0198EA2E90E59AE189F8E4D2148EFC768FE777B9F1B84CA71D843A0062EB1509.bin b/test_fixtures/masp_proofs/0198EA2E90E59AE189F8E4D2148EFC768FE777B9F1B84CA71D843A0062EB1509.bin deleted file mode 100644 index 91158992f1..0000000000 Binary files a/test_fixtures/masp_proofs/0198EA2E90E59AE189F8E4D2148EFC768FE777B9F1B84CA71D843A0062EB1509.bin and /dev/null differ diff --git a/test_fixtures/masp_proofs/0754547040784C17718C4F689F4C5AEBFA800AD1ABEF2487C53BF97362CC90B0.bin b/test_fixtures/masp_proofs/0754547040784C17718C4F689F4C5AEBFA800AD1ABEF2487C53BF97362CC90B0.bin new file mode 100644 index 0000000000..3331bfe608 Binary files /dev/null and b/test_fixtures/masp_proofs/0754547040784C17718C4F689F4C5AEBFA800AD1ABEF2487C53BF97362CC90B0.bin differ diff --git a/test_fixtures/masp_proofs/0BB61ED029B895FEEB7681A39C6EDC5CC89E90D6FF92E31C8C872DCAB358239A.bin b/test_fixtures/masp_proofs/0BB61ED029B895FEEB7681A39C6EDC5CC89E90D6FF92E31C8C872DCAB358239A.bin deleted file mode 100644 index e5e6e93ec1..0000000000 Binary files a/test_fixtures/masp_proofs/0BB61ED029B895FEEB7681A39C6EDC5CC89E90D6FF92E31C8C872DCAB358239A.bin and /dev/null differ diff --git a/test_fixtures/masp_proofs/125E61BD706AB533F815E6E2869A7952F44DD3927485DEC3F649AEA68C3374C8.bin b/test_fixtures/masp_proofs/125E61BD706AB533F815E6E2869A7952F44DD3927485DEC3F649AEA68C3374C8.bin new file mode 100644 index 0000000000..dfd5a43f15 Binary files /dev/null and b/test_fixtures/masp_proofs/125E61BD706AB533F815E6E2869A7952F44DD3927485DEC3F649AEA68C3374C8.bin differ diff --git a/test_fixtures/masp_proofs/256ED96C075E42B8FEA99ABECA59DB09065F2CC91CBB86AD65A92E8B92B43743.bin b/test_fixtures/masp_proofs/256ED96C075E42B8FEA99ABECA59DB09065F2CC91CBB86AD65A92E8B92B43743.bin new file mode 100644 index 0000000000..8cb606ab8d Binary files /dev/null and b/test_fixtures/masp_proofs/256ED96C075E42B8FEA99ABECA59DB09065F2CC91CBB86AD65A92E8B92B43743.bin differ diff --git a/test_fixtures/masp_proofs/30F09813D596F8277911C99FF1C6B362D244EDD624244876F9B5F541EBA07FC5.bin b/test_fixtures/masp_proofs/30F09813D596F8277911C99FF1C6B362D244EDD624244876F9B5F541EBA07FC5.bin deleted file mode 100644 index 1329396ab9..0000000000 Binary files a/test_fixtures/masp_proofs/30F09813D596F8277911C99FF1C6B362D244EDD624244876F9B5F541EBA07FC5.bin and /dev/null differ diff --git a/test_fixtures/masp_proofs/315FBEBD16884E62FCFB437CEA4D13376048954D074973BAD19846CC5570D984.bin b/test_fixtures/masp_proofs/315FBEBD16884E62FCFB437CEA4D13376048954D074973BAD19846CC5570D984.bin deleted file mode 100644 index 3e42d029c4..0000000000 Binary files a/test_fixtures/masp_proofs/315FBEBD16884E62FCFB437CEA4D13376048954D074973BAD19846CC5570D984.bin and /dev/null differ diff --git a/test_fixtures/masp_proofs/50A4057E7DCFD57E26F23DFBF861DE6890ADDBC8CFB85C76A4E7885280D684F4.bin b/test_fixtures/masp_proofs/50A4057E7DCFD57E26F23DFBF861DE6890ADDBC8CFB85C76A4E7885280D684F4.bin new file mode 100644 index 0000000000..ed9a42045d Binary files /dev/null and b/test_fixtures/masp_proofs/50A4057E7DCFD57E26F23DFBF861DE6890ADDBC8CFB85C76A4E7885280D684F4.bin differ diff --git a/test_fixtures/masp_proofs/50C53F3CA843689FC9C1C3233686DF79F7E021A374BFCF3C6CA82DAC391C6533.bin b/test_fixtures/masp_proofs/50C53F3CA843689FC9C1C3233686DF79F7E021A374BFCF3C6CA82DAC391C6533.bin deleted file mode 100644 index 5d71d8783a..0000000000 Binary files a/test_fixtures/masp_proofs/50C53F3CA843689FC9C1C3233686DF79F7E021A374BFCF3C6CA82DAC391C6533.bin and /dev/null differ diff --git a/test_fixtures/masp_proofs/520C3ACC87D01E58CF1E907FA00A87507CCAA8EB7647CB2974EB9250922A0580.bin b/test_fixtures/masp_proofs/520C3ACC87D01E58CF1E907FA00A87507CCAA8EB7647CB2974EB9250922A0580.bin new file mode 100644 index 0000000000..0bc5155fb5 Binary files /dev/null and b/test_fixtures/masp_proofs/520C3ACC87D01E58CF1E907FA00A87507CCAA8EB7647CB2974EB9250922A0580.bin differ diff --git a/test_fixtures/masp_proofs/5219A58EE0E0BE74F6AFE1184C0CFF18DB68FAB6D994F680AAEA7EF99DF3C872.bin b/test_fixtures/masp_proofs/5219A58EE0E0BE74F6AFE1184C0CFF18DB68FAB6D994F680AAEA7EF99DF3C872.bin new file mode 100644 index 0000000000..b4f5db3059 Binary files /dev/null and b/test_fixtures/masp_proofs/5219A58EE0E0BE74F6AFE1184C0CFF18DB68FAB6D994F680AAEA7EF99DF3C872.bin differ diff --git a/test_fixtures/masp_proofs/4CCF96B9008D42BA633988264D3E116D3BC8B780C76EAEC072E1A71D709AE66F.bin b/test_fixtures/masp_proofs/59BBC98F00DE442A5EC9E1CE884333592C4A00DDC30896E174422F6863F71D32.bin similarity index 53% rename from test_fixtures/masp_proofs/4CCF96B9008D42BA633988264D3E116D3BC8B780C76EAEC072E1A71D709AE66F.bin rename to test_fixtures/masp_proofs/59BBC98F00DE442A5EC9E1CE884333592C4A00DDC30896E174422F6863F71D32.bin index 3887ce9a44..0dcda6bda3 100644 Binary files a/test_fixtures/masp_proofs/4CCF96B9008D42BA633988264D3E116D3BC8B780C76EAEC072E1A71D709AE66F.bin and b/test_fixtures/masp_proofs/59BBC98F00DE442A5EC9E1CE884333592C4A00DDC30896E174422F6863F71D32.bin differ diff --git a/test_fixtures/masp_proofs/5AF17E0AB182339D31853B3A1FC35062071533243874C260C81A6AEBE8480987.bin b/test_fixtures/masp_proofs/5AF17E0AB182339D31853B3A1FC35062071533243874C260C81A6AEBE8480987.bin new file mode 100644 index 0000000000..16c730b09c Binary files /dev/null and b/test_fixtures/masp_proofs/5AF17E0AB182339D31853B3A1FC35062071533243874C260C81A6AEBE8480987.bin differ diff --git a/test_fixtures/masp_proofs/7FEE0379CDF35F061B75B7B2145E4DB0A1861A41897246DE5EA7C6FD27651C8E.bin b/test_fixtures/masp_proofs/7FEE0379CDF35F061B75B7B2145E4DB0A1861A41897246DE5EA7C6FD27651C8E.bin new file mode 100644 index 0000000000..6516676ff6 Binary files /dev/null and b/test_fixtures/masp_proofs/7FEE0379CDF35F061B75B7B2145E4DB0A1861A41897246DE5EA7C6FD27651C8E.bin differ diff --git a/test_fixtures/masp_proofs/819FB7D977389C0AC073BE200FCE87F8358272338A32B5B411E93BD43D963AE0.bin b/test_fixtures/masp_proofs/819FB7D977389C0AC073BE200FCE87F8358272338A32B5B411E93BD43D963AE0.bin deleted file mode 100644 index f537b76d38..0000000000 Binary files a/test_fixtures/masp_proofs/819FB7D977389C0AC073BE200FCE87F8358272338A32B5B411E93BD43D963AE0.bin and /dev/null differ diff --git a/test_fixtures/masp_proofs/8686859C447B47946DC21922D83C2404E7EBDFF543A6EAA34544FFF644F08FA9.bin b/test_fixtures/masp_proofs/8686859C447B47946DC21922D83C2404E7EBDFF543A6EAA34544FFF644F08FA9.bin deleted file mode 100644 index a6d6153a3a..0000000000 Binary files a/test_fixtures/masp_proofs/8686859C447B47946DC21922D83C2404E7EBDFF543A6EAA34544FFF644F08FA9.bin and /dev/null differ diff --git a/test_fixtures/masp_proofs/931AE522191249FAFA4A38C9740412000E423E0085AB074BF8C493574AF3226B.bin b/test_fixtures/masp_proofs/931AE522191249FAFA4A38C9740412000E423E0085AB074BF8C493574AF3226B.bin deleted file mode 100644 index 4e66ffffab..0000000000 Binary files a/test_fixtures/masp_proofs/931AE522191249FAFA4A38C9740412000E423E0085AB074BF8C493574AF3226B.bin and /dev/null differ diff --git a/test_fixtures/masp_proofs/9EF9FF93C65A60615530272351A86DBF2921ACB1380E8F256AD0CE25151C869C.bin b/test_fixtures/masp_proofs/9EF9FF93C65A60615530272351A86DBF2921ACB1380E8F256AD0CE25151C869C.bin deleted file mode 100644 index b232255a68..0000000000 Binary files a/test_fixtures/masp_proofs/9EF9FF93C65A60615530272351A86DBF2921ACB1380E8F256AD0CE25151C869C.bin and /dev/null differ diff --git a/test_fixtures/masp_proofs/A28F93297BBBBD119DE0A2B2044027765771A646BC53E4AF92CA7A643EED36FC.bin b/test_fixtures/masp_proofs/A28F93297BBBBD119DE0A2B2044027765771A646BC53E4AF92CA7A643EED36FC.bin new file mode 100644 index 0000000000..8466cb8841 Binary files /dev/null and b/test_fixtures/masp_proofs/A28F93297BBBBD119DE0A2B2044027765771A646BC53E4AF92CA7A643EED36FC.bin differ diff --git a/test_fixtures/masp_proofs/C37373E2478B837D2468FBEE5CCA5D2C40DFF157E5FB70DDC84202C160D01CD3.bin b/test_fixtures/masp_proofs/C37373E2478B837D2468FBEE5CCA5D2C40DFF157E5FB70DDC84202C160D01CD3.bin deleted file mode 100644 index 944c1a805c..0000000000 Binary files a/test_fixtures/masp_proofs/C37373E2478B837D2468FBEE5CCA5D2C40DFF157E5FB70DDC84202C160D01CD3.bin and /dev/null differ diff --git a/test_fixtures/masp_proofs/DB978B3F08838665C1ECD7BEA4E600461287D8046692CDB4AA2CF94A1AC7F8F6.bin b/test_fixtures/masp_proofs/DB978B3F08838665C1ECD7BEA4E600461287D8046692CDB4AA2CF94A1AC7F8F6.bin deleted file mode 100644 index 8c5888b087..0000000000 Binary files a/test_fixtures/masp_proofs/DB978B3F08838665C1ECD7BEA4E600461287D8046692CDB4AA2CF94A1AC7F8F6.bin and /dev/null differ diff --git a/test_fixtures/masp_proofs/3EE70DC758071936C6AADB82E56C536AAC74604CB002CA3558559CACCC24B5EB.bin b/test_fixtures/masp_proofs/EBE3E35A8D4CBD2FC7A12736DB5399B77FF4B05B8C78BF1F016C6101367F0639.bin similarity index 55% rename from test_fixtures/masp_proofs/3EE70DC758071936C6AADB82E56C536AAC74604CB002CA3558559CACCC24B5EB.bin rename to test_fixtures/masp_proofs/EBE3E35A8D4CBD2FC7A12736DB5399B77FF4B05B8C78BF1F016C6101367F0639.bin index 92f501db51..6192916b9f 100644 Binary files a/test_fixtures/masp_proofs/3EE70DC758071936C6AADB82E56C536AAC74604CB002CA3558559CACCC24B5EB.bin and b/test_fixtures/masp_proofs/EBE3E35A8D4CBD2FC7A12736DB5399B77FF4B05B8C78BF1F016C6101367F0639.bin differ diff --git a/test_fixtures/masp_proofs/FBCAABF07FCA704A568838EAE2DA0C2C77847B19AE5C80C957F73BD81AD4C78D.bin b/test_fixtures/masp_proofs/FBCAABF07FCA704A568838EAE2DA0C2C77847B19AE5C80C957F73BD81AD4C78D.bin new file mode 100644 index 0000000000..691f353b5c Binary files /dev/null and b/test_fixtures/masp_proofs/FBCAABF07FCA704A568838EAE2DA0C2C77847B19AE5C80C957F73BD81AD4C78D.bin differ diff --git a/tests/src/e2e/setup.rs b/tests/src/e2e/setup.rs index cda8422001..644110bba6 100644 --- a/tests/src/e2e/setup.rs +++ b/tests/src/e2e/setup.rs @@ -1092,7 +1092,6 @@ pub mod constants { pub const DAEWON_KEY: &str = "Daewon-key"; pub const ESTER: &str = "Ester"; pub const MATCHMAKER_KEY: &str = "matchmaker-key"; - pub const MASP: &str = "tnam1q9lm5pvkxhnw9wwwhu33vkvtylwfkn5kw53xwud8"; // Shielded spending and viewing keys and payment addresses pub const A_SPENDING_KEY: &str = "zsknam1qqqqqqqqqqqqqq9v0sls5r5de7njx8ehu49pqgmqr9ygelg87l5x8y4s9r0pjlvu69au6gn3su5ewneas486hdccyayx32hxvt64p3d0hfuprpgcgv2q9gdx3jvxrn02f0nnp3jtdd6f5vwscfuyum083cvfv4jun75ak5sdgrm2pthzj3sflxc0jx0edrakx3vdcngrfjmru8ywkguru8mxss2uuqxdlglaz6undx5h8w7g70t2es850g48xzdkqay5qs0yw06rtxc9q0cqr"; @@ -1115,6 +1114,7 @@ pub mod constants { // Native VP aliases pub const GOVERNANCE_ADDRESS: &str = "governance"; + pub const MASP: &str = "masp"; // Fungible token addresses pub const NAM: &str = "NAM"; diff --git a/tx_prelude/src/token.rs b/tx_prelude/src/token.rs index 6067c82a46..009cccb36d 100644 --- a/tx_prelude/src/token.rs +++ b/tx_prelude/src/token.rs @@ -1,5 +1,5 @@ use masp_primitives::transaction::Transaction; -use namada_core::types::address::Address; +use namada_core::types::address::{Address, MASP}; use namada_core::types::storage::KeySeg; use namada_core::types::token; pub use namada_core::types::token::*; @@ -38,7 +38,7 @@ pub fn handle_masp_tx( transfer: &Transfer, shielded: &Transaction, ) -> TxResult { - let masp_addr = address::masp(); + let masp_addr = MASP; ctx.insert_verifier(&masp_addr)?; let head_tx_key = storage::Key::from(masp_addr.to_db_key()) .push(&HEAD_TX_KEY.to_owned()) diff --git a/vm_env/src/lib.rs b/vm_env/src/lib.rs index c3497c5c27..87331ee5ba 100644 --- a/vm_env/src/lib.rs +++ b/vm_env/src/lib.rs @@ -229,8 +229,6 @@ pub mod vp { input_data_len: u64, ) -> i64; - pub fn namada_vp_verify_masp(tx_ptr: u64, tx_len: u64) -> i64; - /// Charge the provided amount of gas for the current vp pub fn namada_vp_charge_gas(used_gas: u64); } diff --git a/vp_prelude/src/lib.rs b/vp_prelude/src/lib.rs index 3c2d4f7b88..abd034236a 100644 --- a/vp_prelude/src/lib.rs +++ b/vp_prelude/src/lib.rs @@ -352,12 +352,6 @@ impl<'view> VpEnv<'view> for Ctx { }) } - fn verify_masp(&self, tx: Vec) -> Result { - let valid = - unsafe { namada_vp_verify_masp(tx.as_ptr() as _, tx.len() as _) }; - Ok(HostEnvResult::is_success(valid)) - } - fn charge_gas(&self, used_gas: u64) -> Result<(), Error> { unsafe { namada_vp_charge_gas(used_gas) }; Ok(()) diff --git a/wasm/wasm_source/Cargo.toml b/wasm/wasm_source/Cargo.toml index 8daba98389..e3293b5523 100644 --- a/wasm/wasm_source/Cargo.toml +++ b/wasm/wasm_source/Cargo.toml @@ -35,7 +35,6 @@ tx_withdraw = ["namada_tx_prelude"] tx_update_steward_commission = ["namada_tx_prelude"] tx_resign_steward = ["namada_tx_prelude"] vp_implicit = ["namada_vp_prelude", "once_cell"] -vp_masp = ["namada_vp_prelude", "masp_primitives"] vp_token = ["namada_vp_prelude"] vp_user = ["namada_vp_prelude", "once_cell"] vp_validator = ["namada_vp_prelude", "once_cell"] diff --git a/wasm/wasm_source/Makefile b/wasm/wasm_source/Makefile index ca60559ccc..aa19173bd1 100644 --- a/wasm/wasm_source/Makefile +++ b/wasm/wasm_source/Makefile @@ -27,7 +27,6 @@ wasms += tx_withdraw wasms += tx_update_steward_commission wasms += tx_resign_steward wasms += vp_implicit -wasms += vp_masp wasms += vp_user wasms += vp_validator diff --git a/wasm/wasm_source/src/lib.rs b/wasm/wasm_source/src/lib.rs index c633505e67..15d365484a 100644 --- a/wasm/wasm_source/src/lib.rs +++ b/wasm/wasm_source/src/lib.rs @@ -43,8 +43,6 @@ pub mod tx_withdraw; #[cfg(feature = "vp_implicit")] pub mod vp_implicit; -#[cfg(feature = "vp_masp")] -pub mod vp_masp; #[cfg(feature = "vp_user")] pub mod vp_user; #[cfg(feature = "vp_validator")] diff --git a/wasm/wasm_source/src/vp_masp.rs b/wasm/wasm_source/src/vp_masp.rs deleted file mode 100644 index c04ba1b844..0000000000 --- a/wasm/wasm_source/src/vp_masp.rs +++ /dev/null @@ -1,252 +0,0 @@ -use std::cmp::Ordering; - -use masp_primitives::asset_type::AssetType; -use masp_primitives::transaction::components::I128Sum; -/// Multi-asset shielded pool VP. -use namada_vp_prelude::address::masp; -use namada_vp_prelude::borsh_ext::BorshSerializeExt; -use namada_vp_prelude::storage::Epoch; -use namada_vp_prelude::*; -use ripemd::{Digest, Ripemd160}; - -/// Generates the current asset type given the current epoch and an -/// unique token address -fn asset_type_from_epoched_address( - epoch: Epoch, - token: &Address, - denom: token::MaspDenom, -) -> AssetType { - // Timestamp the chosen token with the current epoch - let token_bytes = (token, denom, epoch.0).serialize_to_vec(); - // Generate the unique asset identifier from the unique token address - AssetType::new(token_bytes.as_ref()).expect("unable to create asset type") -} - -/// Checks if the asset type matches the expected asset type, Adds a -/// debug log if the values do not match. -fn valid_asset_type( - asset_type: &AssetType, - asset_type_to_test: &AssetType, -) -> bool { - let res = - asset_type.get_identifier() == asset_type_to_test.get_identifier(); - if !res { - debug_log!( - "The asset type must be derived from the token address and \ - current epoch" - ); - } - res -} - -/// Checks if the reported transparent amount and the unshielded -/// values agree, if not adds to the debug log -fn valid_transfer_amount( - reporeted_transparent_value: u64, - unshielded_transfer_value: u64, -) -> bool { - let res = reporeted_transparent_value == unshielded_transfer_value; - if !res { - debug_log!( - "The unshielded amount {} disagrees with the calculated masp \ - transparented value {}", - unshielded_transfer_value, - reporeted_transparent_value - ); - } - res -} - -/// Convert Namada amount and token type to MASP equivalents -fn convert_amount( - epoch: Epoch, - token: &Address, - val: token::Amount, - denom: token::MaspDenom, -) -> (AssetType, I128Sum) { - let asset_type = asset_type_from_epoched_address(epoch, token, denom); - // Combine the value and unit into one amount - let amount = - I128Sum::from_nonnegative(asset_type, denom.denominate(&val) as i128) - .expect("invalid value or asset type for amount"); - (asset_type, amount) -} - -#[validity_predicate(gas = 62210635)] -fn validate_tx( - ctx: &Ctx, - tx_data: Tx, - addr: Address, - keys_changed: BTreeSet, - verifiers: BTreeSet
, -) -> VpResult { - debug_log!( - "vp_masp called with {} bytes data, address {}, keys_changed {:?}, \ - verifiers {:?}", - tx_data.data().as_ref().map(|x| x.len()).unwrap_or(0), - addr, - keys_changed, - verifiers, - ); - - let (transfer, shielded_tx) = ctx.get_shielded_action(tx_data)?; - let mut transparent_tx_pool = I128Sum::zero(); - // The Sapling value balance adds to the transparent tx pool - transparent_tx_pool += shielded_tx.sapling_value_balance(); - - if transfer.source != masp() { - // Handle transparent input - // Note that the asset type is timestamped so shields - // where the shielded value has an incorrect timestamp - // are automatically rejected - for denom in token::MaspDenom::iter() { - let (_transp_asset, transp_amt) = convert_amount( - ctx.get_block_epoch().unwrap(), - &transfer.token, - transfer.amount.into(), - denom, - ); - - // Non-masp sources add to transparent tx pool - transparent_tx_pool += transp_amt; - } - } else { - // Handle shielded input - // The following boundary conditions must be satisfied - // 1. Zero transparent input - // 2. the transparent transaction value pool's amount must equal the - // containing wrapper transaction's fee amount - // Satisfies 1. - if let Some(transp_bundle) = shielded_tx.transparent_bundle() { - if !transp_bundle.vin.is_empty() { - debug_log!( - "Transparent input to a transaction from the masp must be \ - 0 but is {}", - transp_bundle.vin.len() - ); - return reject(); - } - } - } - - if transfer.target != masp() { - // Handle transparent output - // The following boundary conditions must be satisfied - // 1. One to 4 transparent outputs - // 2. Asset type must be properly derived - // 3. Value from the output must be the same as the containing - // transfer - // 4. Public key must be the hash of the target - - // Satisfies 1. - let transp_bundle = shielded_tx.transparent_bundle().ok_or_err_msg( - "Expected transparent outputs in unshielding transaction", - )?; - - let out_length = transp_bundle.vout.len(); - if !(1..=4).contains(&out_length) { - debug_log!( - "Transparent output to a transaction to the masp must be \ - beteween 1 and 4 but is {}", - transp_bundle.vout.len() - ); - - return reject(); - } - let mut outs = transp_bundle.vout.iter(); - let mut valid_count = 0; - for denom in token::MaspDenom::iter() { - let out = match outs.next() { - Some(out) => out, - None => continue, - }; - - let expected_asset_type: AssetType = - asset_type_from_epoched_address( - ctx.get_block_epoch().unwrap(), - &transfer.token, - denom, - ); - - // Satisfies 2. and 3. - if !valid_asset_type(&expected_asset_type, &out.asset_type) { - // we don't know which masp denoms are necessary apriori. - // This is encoded via the asset types. - continue; - } - if !valid_transfer_amount( - out.value, - denom.denominate(&transfer.amount.amount), - ) { - return reject(); - } - - let (_transp_asset, transp_amt) = convert_amount( - ctx.get_block_epoch().unwrap(), - &transfer.token, - transfer.amount.amount, - denom, - ); - - // Non-masp destinations subtract from transparent tx pool - transparent_tx_pool -= transp_amt; - - // Satisfies 4. - let target_enc = transfer.target.serialize_to_vec(); - - let hash = Ripemd160::digest(sha256(&target_enc).0.as_slice()); - - if <[u8; 20]>::from(hash) != out.address.0 { - debug_log!( - "the public key of the output account does not match the \ - transfer target" - ); - return reject(); - } - valid_count += 1; - } - // one or more of the denoms in the batch failed to verify - // the asset derivation. - if valid_count != out_length { - return reject(); - } - } else { - // Handle shielded output - // The following boundary conditions must be satisfied - // 1. Zero transparent output - - // Satisfies 1. - if let Some(transp_bundle) = shielded_tx.transparent_bundle() { - if !transp_bundle.vout.is_empty() { - debug_log!( - "Transparent output to a transaction from the masp must \ - be 0 but is {}", - transp_bundle.vout.len() - ); - return reject(); - } - } - } - - match transparent_tx_pool.partial_cmp(&I128Sum::zero()) { - None | Some(Ordering::Less) => { - debug_log!( - "Transparent transaction value pool must be nonnegative. \ - Violation may be caused by transaction being constructed in \ - previous epoch. Maybe try again." - ); - // Section 3.4: The remaining value in the transparent - // transaction value pool MUST be nonnegative. - return reject(); - } - Some(Ordering::Greater) => { - debug_log!( - "Transaction fees cannot be paid inside MASP transaction." - ); - return reject(); - } - _ => {} - } - // Do the expensive proof verification in the VM at the end. - ctx.verify_masp(shielded_tx.serialize_to_vec()) -} diff --git a/wasm/wasm_source/src/vp_user.rs b/wasm/wasm_source/src/vp_user.rs index 6e81bcdac1..7799a30ad8 100644 --- a/wasm/wasm_source/src/vp_user.rs +++ b/wasm/wasm_source/src/vp_user.rs @@ -8,7 +8,6 @@ //! //! Any other storage key changes are allowed only with a valid signature. -use namada_vp_prelude::address::masp; use namada_vp_prelude::storage::KeySeg; use namada_vp_prelude::*; use once_cell::unsync::Lazy; @@ -82,8 +81,7 @@ fn validate_tx( ctx.read_post(key)?.unwrap_or_default(); let change = post.change() - pre.change(); // debit has to signed, credit doesn't - let valid = - change.non_negative() || addr == masp() || *valid_sig; + let valid = change.non_negative() || *valid_sig; debug_log!( "token key: {}, change: {:?}, valid_sig: {}, valid \ modification: {}",