From 95c5f6f097ae16e80f0b5f2194da1e196a08e0d9 Mon Sep 17 00:00:00 2001 From: Murisi Tarusenga Date: Tue, 5 Dec 2023 14:57:47 +0200 Subject: [PATCH] Protocol now ensures that a DenominatedAmounts denom is correct. Added checked arithmetic operations to DenominatedAmount to catch mistakes. --- apps/src/lib/cli.rs | 22 +++-- apps/src/lib/config/genesis.rs | 24 ++--- apps/src/lib/config/genesis/chain.rs | 2 +- apps/src/lib/config/genesis/templates.rs | 6 +- apps/src/lib/config/genesis/transactions.rs | 59 ++++++++--- .../lib/node/ledger/shell/finalize_block.rs | 6 +- apps/src/lib/node/ledger/shell/init_chain.rs | 8 +- apps/src/lib/node/ledger/shell/mod.rs | 12 +-- .../lib/node/ledger/shell/prepare_proposal.rs | 16 +-- .../lib/node/ledger/shell/process_proposal.rs | 8 +- core/src/ledger/ibc/context/token_transfer.rs | 2 +- core/src/types/token.rs | 93 ++++++++++++++---- core/src/types/transaction/wrapper.rs | 10 +- ethereum_bridge/src/parameters.rs | 11 ++- sdk/src/eth_bridge/bridge_pool.rs | 14 +-- sdk/src/lib.rs | 13 +-- sdk/src/masp.rs | 12 +-- sdk/src/queries/router.rs | 32 +++--- sdk/src/rpc.rs | 6 +- sdk/src/signing.rs | 10 +- sdk/src/tx.rs | 10 +- shared/src/ledger/native_vp/ibc/context.rs | 15 +-- shared/src/ledger/native_vp/masp.rs | 9 +- shared/src/ledger/protocol/mod.rs | 10 +- shared/src/vm/host_env.rs | 17 ++-- tests/src/e2e/eth_bridge_tests.rs | 41 +++----- tests/src/e2e/ledger_tests.rs | 10 +- tests/src/e2e/setup.rs | 8 +- tx_prelude/src/ibc.rs | 4 +- tx_prelude/src/token.rs | 7 +- wasm/checksums.json | 50 +++++----- wasm/wasm_source/src/vp_implicit.rs | 24 ++--- wasm/wasm_source/src/vp_testnet_faucet.rs | 18 ++-- wasm/wasm_source/src/vp_user.rs | 24 ++--- wasm/wasm_source/src/vp_validator.rs | 24 ++--- wasm_for_tests/tx_memory_limit.wasm | Bin 421361 -> 421407 bytes wasm_for_tests/tx_mint_tokens.wasm | Bin 594890 -> 594890 bytes wasm_for_tests/tx_proposal_code.wasm | Bin 418426 -> 418454 bytes wasm_for_tests/tx_read_storage_key.wasm | Bin 427447 -> 427487 bytes wasm_for_tests/tx_write.wasm | Bin 431956 -> 431996 bytes wasm_for_tests/tx_write_storage_key.wasm | Bin 465107 -> 465107 bytes wasm_for_tests/vp_always_false.wasm | Bin 391355 -> 391352 bytes wasm_for_tests/vp_always_true.wasm | Bin 391497 -> 391494 bytes wasm_for_tests/vp_eval.wasm | Bin 467705 -> 467745 bytes wasm_for_tests/vp_memory_limit.wasm | Bin 445498 -> 445538 bytes wasm_for_tests/vp_read_storage_key.wasm | Bin 451219 -> 451259 bytes 46 files changed, 362 insertions(+), 275 deletions(-) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index 3bb7f5c7adf..9a222243794 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -2872,9 +2872,11 @@ pub mod args { pub const BRIDGE_POOL_GAS_AMOUNT: ArgDefault = arg_default( "pool-gas-amount", - DefaultFn(|| token::DenominatedAmount { - amount: token::Amount::zero(), - denom: NATIVE_MAX_DECIMAL_PLACES.into(), + DefaultFn(|| { + token::DenominatedAmount::new( + token::Amount::zero(), + NATIVE_MAX_DECIMAL_PLACES.into(), + ) }), ); pub const BRIDGE_POOL_GAS_PAYER: ArgOpt = @@ -2944,9 +2946,11 @@ pub mod args { pub const FEE_PAYER: Arg = arg("fee-payer"); pub const FEE_AMOUNT: ArgDefault = arg_default( "fee-amount", - DefaultFn(|| token::DenominatedAmount { - amount: token::Amount::default(), - denom: NATIVE_MAX_DECIMAL_PLACES.into(), + DefaultFn(|| { + token::DenominatedAmount::new( + token::Amount::default(), + NATIVE_MAX_DECIMAL_PLACES.into(), + ) }), ); pub const GENESIS_PATH: Arg = arg("genesis-path"); @@ -4261,7 +4265,7 @@ pub mod args { println!("Could not parse bond amount: {:?}", e); safe_exit(1); }) - .amount; + .amount(); let source = SOURCE_OPT.parse(matches); let tx_code_path = PathBuf::from(TX_BOND_WASM); Self { @@ -4311,7 +4315,7 @@ pub mod args { println!("Could not parse bond amount: {:?}", e); safe_exit(1); }) - .amount; + .amount(); let source = SOURCE_OPT.parse(matches); let tx_code_path = PathBuf::from(TX_UNBOND_WASM); Self { @@ -4437,7 +4441,7 @@ pub mod args { println!("Could not parse bond amount: {:?}", e); safe_exit(1); }) - .amount; + .amount(); let tx_code_path = PathBuf::from(TX_REDELEGATE_WASM); Self { tx, diff --git a/apps/src/lib/config/genesis.rs b/apps/src/lib/config/genesis.rs index b1fac85e811..3d146dd61ca 100644 --- a/apps/src/lib/config/genesis.rs +++ b/apps/src/lib/config/genesis.rs @@ -411,10 +411,10 @@ pub fn make_dev_genesis( StringEncoded { raw: account_keypair.ref_to(), }, - token::DenominatedAmount { - amount: token::Amount::native_whole(200_000), - denom: NATIVE_MAX_DECIMAL_PLACES.into(), - }, + token::DenominatedAmount::new( + token::Amount::native_whole(200_000), + NATIVE_MAX_DECIMAL_PLACES.into(), + ), ); } // transfer funds from implicit key to validator @@ -425,10 +425,10 @@ pub fn make_dev_genesis( raw: account_keypair.ref_to(), }, target: alias.clone(), - amount: token::DenominatedAmount { - amount: token::Amount::native_whole(200_000), - denom: NATIVE_MAX_DECIMAL_PLACES.into(), - }, + amount: token::DenominatedAmount::new( + token::Amount::native_whole(200_000), + NATIVE_MAX_DECIMAL_PLACES.into(), + ), }) } // self bond @@ -436,10 +436,10 @@ pub fn make_dev_genesis( bonds.push(transactions::BondTx { source: transactions::AliasOrPk::Alias(alias.clone()), validator: alias, - amount: token::DenominatedAmount { - amount: token::Amount::native_whole(100_000), - denom: NATIVE_MAX_DECIMAL_PLACES.into(), - }, + amount: token::DenominatedAmount::new( + token::Amount::native_whole(100_000), + NATIVE_MAX_DECIMAL_PLACES.into(), + ), }) } } diff --git a/apps/src/lib/config/genesis/chain.rs b/apps/src/lib/config/genesis/chain.rs index 502e8c27f53..dbf7ba3b498 100644 --- a/apps/src/lib/config/genesis/chain.rs +++ b/apps/src/lib/config/genesis/chain.rs @@ -313,7 +313,7 @@ impl Finalized { .map(|(token, amt)| { ( self.tokens.token.get(token).cloned().unwrap().address, - amt.amount, + amt.amount(), ) }) .collect(), diff --git a/apps/src/lib/config/genesis/templates.rs b/apps/src/lib/config/genesis/templates.rs index 50afeb9ac88..0668130c299 100644 --- a/apps/src/lib/config/genesis/templates.rs +++ b/apps/src/lib/config/genesis/templates.rs @@ -491,7 +491,7 @@ pub struct EthBridgeParams { impl TokenBalances { pub fn get(&self, pk: common::PublicKey) -> Option { let pk = StringEncoded { raw: pk }; - self.0.get(&pk).map(|amt| amt.amount) + self.0.get(&pk).map(|amt| amt.amount()) } } #[derive( @@ -906,7 +906,7 @@ pub fn validate_balances( let sum = next.0.values().try_fold( token::Amount::default(), |acc, amount| { - let res = acc.checked_add(amount.amount); + let res = acc.checked_add(amount.amount()); if res.as_ref().is_none() { is_valid = false; eprintln!( @@ -997,7 +997,7 @@ mod tests { .0 .get(&StringEncoded { raw: pk }) .unwrap() - .amount + .amount() ); } } diff --git a/apps/src/lib/config/genesis/transactions.rs b/apps/src/lib/config/genesis/transactions.rs index d457ef87f5f..54b0cf8d806 100644 --- a/apps/src/lib/config/genesis/transactions.rs +++ b/apps/src/lib/config/genesis/transactions.rs @@ -167,7 +167,7 @@ pub fn init_validator( validator_wallet, )]); - let transfer = if transfer_from_source_amount.amount.is_zero() { + let transfer = if transfer_from_source_amount.amount().is_zero() { None } else { let unsigned_transfer_tx = TransferTx { @@ -181,7 +181,7 @@ pub fn init_validator( Some(vec![transfer_tx]) }; - let bond = if self_bond_amount.amount.is_zero() { + let bond = if self_bond_amount.amount().is_zero() { None } else { let unsigned_bond_tx = BondTx { @@ -513,7 +513,7 @@ impl Transactions { BTreeMap::new(); for tx in txs { let entry = stakes.entry(&tx.validator).or_default(); - *entry += tx.amount.amount; + *entry += tx.amount.amount(); } stakes.into_iter().any(|(_validator, stake)| { @@ -1129,8 +1129,19 @@ fn validate_bond( balances.pks.0.remove(source); } } + } else if let Some(new_balance) = + balance.checked_sub(*amount) + { + *balance = new_balance; } else { - balance.amount -= amount.amount; + eprintln!( + "Invalid bond tx. Amount {} should have the \ + denomination {:?}. Got {:?}.", + amount, + balance.denom(), + amount.denom(), + ); + is_valid = false; } } } @@ -1370,7 +1381,7 @@ pub fn validate_transfer( match balances.get_mut(token) { Some(balances) => match balances.pks.0.get_mut(source) { Some(balance) => { - if balance.amount < amount.amount { + if *balance < *amount { eprintln!( "Invalid transfer tx. Source {source} doesn't have \ enough balance of token \"{token}\" to transfer {}. \ @@ -1380,21 +1391,47 @@ pub fn validate_transfer( is_valid = false; } else { // Deduct the amount from source - if amount.amount == balance.amount { + if amount == balance { balances.pks.0.remove(source); + } else if let Some(new_balance) = + balance.checked_sub(*amount) + { + *balance = new_balance; } else { - balance.amount -= amount.amount; + eprintln!( + "Invalid bond tx. Amount {} should have the \ + denomination {:?}. Got {:?}.", + amount, + balance.denom(), + amount.denom(), + ); + is_valid = false; } // Add the amount to target let target_balance = balances .aliases .entry(target.clone()) - .or_insert_with(|| DenominatedAmount { - amount: token::Amount::zero(), - denom: amount.denom, + .or_insert_with(|| { + DenominatedAmount::new( + token::Amount::zero(), + amount.denom(), + ) }); - target_balance.amount += amount.amount; + if let Some(new_balance) = + target_balance.checked_add(*amount) + { + *target_balance = new_balance; + } else { + eprintln!( + "Invalid bond tx. Amount {} should have the \ + denomination {:?}. Got {:?}.", + amount, + target_balance.denom(), + amount.denom(), + ); + is_valid = false; + } } } None => { diff --git a/apps/src/lib/node/ledger/shell/finalize_block.rs b/apps/src/lib/node/ledger/shell/finalize_block.rs index 30a3838010c..ae0928d360e 100644 --- a/apps/src/lib/node/ledger/shell/finalize_block.rs +++ b/apps/src/lib/node/ledger/shell/finalize_block.rs @@ -3451,7 +3451,7 @@ mod test_finalize_block { let fee_amount = wrapper.header().wrapper().unwrap().get_tx_fee().unwrap(); let fee_amount = fee_amount - .apply_precision( + .to_amount( &wrapper.header().wrapper().unwrap().fee.token, &shell.wl_storage, ) @@ -3493,7 +3493,7 @@ mod test_finalize_block { .unwrap(); assert_eq!( new_proposer_balance, - proposer_balance.checked_add(fee_amount.amount).unwrap() + proposer_balance.checked_add(fee_amount).unwrap() ); let new_signer_balance = storage_api::token::read_balance( @@ -3504,7 +3504,7 @@ mod test_finalize_block { .unwrap(); assert_eq!( new_signer_balance, - signer_balance.checked_sub(fee_amount.amount).unwrap() + signer_balance.checked_sub(fee_amount).unwrap() ) } diff --git a/apps/src/lib/node/ledger/shell/init_chain.rs b/apps/src/lib/node/ledger/shell/init_chain.rs index aad1fcc3629..4e8c832bd23 100644 --- a/apps/src/lib/node/ledger/shell/init_chain.rs +++ b/apps/src/lib/node/ledger/shell/init_chain.rs @@ -326,10 +326,10 @@ where &mut self.wl_storage, token_address, &owner, - balance.amount, + balance.amount(), ) .expect("Couldn't credit initial balance"); - total_token_balance += balance.amount; + total_token_balance += balance.amount(); } // Write the total amount of tokens for the ratio self.wl_storage @@ -530,7 +530,7 @@ where token, &source, &target, - amount.amount, + amount.amount(), ) { tracing::warn!( "Genesis token transfer tx failed with: {err}. \ @@ -592,7 +592,7 @@ where &mut self.wl_storage, Some(&source), validator, - amount.amount, + amount.amount(), current_epoch, Some(0), ) { diff --git a/apps/src/lib/node/ledger/shell/mod.rs b/apps/src/lib/node/ledger/shell/mod.rs index 03c7130d6cb..92d536170c9 100644 --- a/apps/src/lib/node/ledger/shell/mod.rs +++ b/apps/src/lib/node/ledger/shell/mod.rs @@ -1403,10 +1403,10 @@ where match wrapper .fee .amount_per_gas_unit - .apply_precision(&wrapper.fee.token, &self.wl_storage) + .to_amount(&wrapper.fee.token, &self.wl_storage) { Ok(amount_per_gas_unit) - if amount_per_gas_unit.amount < minimum_gas_price => + if amount_per_gas_unit < minimum_gas_price => { // The fees do not match the minimum required return Err(Error::TxApply(protocol::Error::FeeError( @@ -2872,10 +2872,10 @@ mod shell_tests { let mut wrapper = Tx::from_type(TxType::Wrapper(Box::new(WrapperTx::new( Fee { - amount_per_gas_unit: DenominatedAmount { - amount: 100.into(), - denom: apfel_denom, - }, + amount_per_gas_unit: DenominatedAmount::new( + 100.into(), + apfel_denom, + ), token: address::apfel(), }, crate::wallet::defaults::albert_keypair().ref_to(), diff --git a/apps/src/lib/node/ledger/shell/prepare_proposal.rs b/apps/src/lib/node/ledger/shell/prepare_proposal.rs index 1543e61e45e..210464af8bf 100644 --- a/apps/src/lib/node/ledger/shell/prepare_proposal.rs +++ b/apps/src/lib/node/ledger/shell/prepare_proposal.rs @@ -1146,10 +1146,10 @@ mod test_prepare_proposal { let wrapper = WrapperTx::new( Fee { - amount_per_gas_unit: DenominatedAmount { - amount: 100.into(), - denom: btc_denom, - }, + amount_per_gas_unit: DenominatedAmount::new( + 100.into(), + btc_denom, + ), token: address::btc(), }, crate::wallet::defaults::albert_keypair().ref_to(), @@ -1194,10 +1194,10 @@ mod test_prepare_proposal { let wrapper = WrapperTx::new( Fee { - amount_per_gas_unit: DenominatedAmount { - amount: 100.into(), - denom: apfel_denom, - }, + amount_per_gas_unit: DenominatedAmount::new( + 100.into(), + apfel_denom, + ), token: address::apfel(), }, crate::wallet::defaults::albert_keypair().ref_to(), diff --git a/apps/src/lib/node/ledger/shell/process_proposal.rs b/apps/src/lib/node/ledger/shell/process_proposal.rs index 357bba2a7bd..928369566ea 100644 --- a/apps/src/lib/node/ledger/shell/process_proposal.rs +++ b/apps/src/lib/node/ledger/shell/process_proposal.rs @@ -1856,10 +1856,10 @@ mod test_process_proposal { let mut wrapper = Tx::from_type(TxType::Wrapper(Box::new(WrapperTx::new( Fee { - amount_per_gas_unit: DenominatedAmount { - amount: 100.into(), - denom: apfel_denom, - }, + amount_per_gas_unit: DenominatedAmount::new( + 100.into(), + apfel_denom, + ), token: address::apfel(), }, crate::wallet::defaults::albert_keypair().ref_to(), diff --git a/core/src/ledger/ibc/context/token_transfer.rs b/core/src/ledger/ibc/context/token_transfer.rs index 4c41b8c956a..ee0fb9f70fa 100644 --- a/core/src/ledger/ibc/context/token_transfer.rs +++ b/core/src/ledger/ibc/context/token_transfer.rs @@ -64,7 +64,7 @@ where .into(), ) })?; - let amount = token::DenominatedAmount { amount, denom }; + let amount = token::DenominatedAmount::new(amount, denom); Ok((token, amount)) } diff --git a/core/src/types/token.rs b/core/src/types/token.rs index f9559a851ff..6364bc8b038 100644 --- a/core/src/types/token.rs +++ b/core/src/types/token.rs @@ -169,7 +169,7 @@ impl Amount { .map(|result| Self { raw: result }) } - /// Checked division. Returns `None` on overflow. + /// Checked multiplication. Returns `None` on overflow. pub fn checked_mul(&self, amount: Amount) -> Option { self.raw .checked_mul(amount.raw) @@ -181,9 +181,7 @@ impl Amount { string: impl AsRef, denom: impl Into, ) -> Result { - DenominatedAmount::from_str(string.as_ref())? - .increase_precision(denom.into().into()) - .map(Into::into) + DenominatedAmount::from_str(string.as_ref())?.scale(denom) } /// Attempt to convert an unsigned integer to an `Amount` with the @@ -320,12 +318,17 @@ impl From for u8 { )] pub struct DenominatedAmount { /// The mantissa - pub amount: Amount, + amount: Amount, /// The number of decimal places in base ten. - pub denom: Denomination, + denom: Denomination, } impl DenominatedAmount { + /// Make a new denominated amount + pub fn new(amount: Amount, denom: Denomination) -> Self { + Self { amount, denom } + } + /// Return a denominated native token amount. pub const fn native(amount: Amount) -> Self { Self { @@ -402,12 +405,14 @@ impl DenominatedAmount { .ok_or(AmountParseError::PrecisionOverflow) } - /// Attempt to apply the precision of the given token to this amount. - pub fn apply_precision( + /// Convert this denominated amount into a plain amount by increasing its + /// precision to the given token's denomination and then taking the + /// significand. + pub fn to_amount( self, token: &Address, storage: &impl StorageRead, - ) -> storage_api::Result { + ) -> storage_api::Result { let denom = read_denom(storage, token)?.ok_or_else(|| { storage_api::Error::SimpleMessage( "No denomination found in storage for the given token", @@ -415,6 +420,64 @@ impl DenominatedAmount { })?; self.increase_precision(denom) .map_err(storage_api::Error::new) + .map(|x| x.amount) + } + + /// Multiply this number by 10^denom and return the computed integer if + /// possible. Otherwise error out. + pub fn scale( + self, + denom: impl Into, + ) -> Result { + self.increase_precision(Denomination(denom.into())) + .map(|x| x.amount) + } + + /// Checked multiplication. Returns `None` on overflow. + pub fn checked_mul(&self, rhs: DenominatedAmount) -> Option { + let amount = self.amount.checked_mul(rhs.amount)?; + let denom = self.denom.0.checked_add(rhs.denom.0)?.into(); + Some(Self { amount, denom }) + } + + /// Checked subtraction. Returns `None` on overflow. + pub fn checked_sub(&self, mut rhs: DenominatedAmount) -> Option { + let mut lhs = *self; + if lhs.denom < rhs.denom { + lhs = lhs.increase_precision(rhs.denom).ok()?; + } else { + rhs = rhs.increase_precision(lhs.denom).ok()?; + } + let amount = lhs.amount.checked_sub(rhs.amount)?; + Some(Self { + amount, + denom: lhs.denom, + }) + } + + /// Checked addition. Returns `None` on overflow. + pub fn checked_add(&self, mut rhs: DenominatedAmount) -> Option { + let mut lhs = *self; + if lhs.denom < rhs.denom { + lhs = lhs.increase_precision(rhs.denom).ok()?; + } else { + rhs = rhs.increase_precision(lhs.denom).ok()?; + } + let amount = lhs.amount.checked_add(rhs.amount)?; + Some(Self { + amount, + denom: lhs.denom, + }) + } + + /// Returns the significand of this number + pub fn amount(&self) -> Amount { + self.amount + } + + /// Returns the denomination of this number + pub fn denom(&self) -> Denomination { + self.denom } } @@ -569,15 +632,9 @@ impl<'de> serde::Deserialize<'de> for DenominatedAmount { } } -impl<'a> From<&'a DenominatedAmount> for &'a Amount { - fn from(denom: &'a DenominatedAmount) -> Self { - &denom.amount - } -} - -impl From for Amount { - fn from(denom: DenominatedAmount) -> Self { - denom.amount +impl From for DenominatedAmount { + fn from(amount: Amount) -> Self { + DenominatedAmount::new(amount, 0.into()) } } diff --git a/core/src/types/transaction/wrapper.rs b/core/src/types/transaction/wrapper.rs index ee0a17e6074..fbac749ef64 100644 --- a/core/src/types/transaction/wrapper.rs +++ b/core/src/types/transaction/wrapper.rs @@ -315,12 +315,10 @@ pub mod wrapper_tx { /// Get the [`Amount`] of fees to be paid by the given wrapper. Returns /// an error if the amount overflows pub fn get_tx_fee(&self) -> Result { - let mut amount_per_gas_unit = self.fee.amount_per_gas_unit; - amount_per_gas_unit.amount = amount_per_gas_unit - .amount - .checked_mul(self.gas_limit.into()) - .ok_or(WrapperTxErr::OverflowingFee)?; - Ok(amount_per_gas_unit) + self.fee + .amount_per_gas_unit + .checked_mul(Amount::from(self.gas_limit).into()) + .ok_or(WrapperTxErr::OverflowingFee) } } diff --git a/ethereum_bridge/src/parameters.rs b/ethereum_bridge/src/parameters.rs index 62d1f9a7a1c..34db6246b73 100644 --- a/ethereum_bridge/src/parameters.rs +++ b/ethereum_bridge/src/parameters.rs @@ -208,11 +208,12 @@ impl EthereumBridgeParams { .unwrap(); for Erc20WhitelistEntry { token_address: addr, - token_cap: DenominatedAmount { amount: cap, denom }, + token_cap, } in erc20_whitelist { - if addr == native_erc20 - && denom != &NATIVE_MAX_DECIMAL_PLACES.into() + let cap = token_cap.amount(); + let denom = token_cap.denom(); + if addr == native_erc20 && denom != NATIVE_MAX_DECIMAL_PLACES.into() { panic!( "Error writing Ethereum bridge config: The native token \ @@ -232,14 +233,14 @@ impl EthereumBridgeParams { suffix: whitelist::KeyType::Cap, } .into(); - wl_storage.write_bytes(&key, encode(cap)).unwrap(); + wl_storage.write_bytes(&key, encode(&cap)).unwrap(); let key = whitelist::Key { asset: *addr, suffix: whitelist::KeyType::Denomination, } .into(); - wl_storage.write_bytes(&key, encode(denom)).unwrap(); + wl_storage.write_bytes(&key, encode(&denom)).unwrap(); } // Initialize the storage for the Ethereum Bridge VP. vp::init_storage(wl_storage); diff --git a/sdk/src/eth_bridge/bridge_pool.rs b/sdk/src/eth_bridge/bridge_pool.rs index e682521987f..611a62a61b4 100644 --- a/sdk/src/eth_bridge/bridge_pool.rs +++ b/sdk/src/eth_bridge/bridge_pool.rs @@ -19,7 +19,7 @@ use namada_core::types::eth_bridge_pool::{ use namada_core::types::ethereum_events::EthAddress; use namada_core::types::keccak::KeccakHash; use namada_core::types::storage::Epoch; -use namada_core::types::token::{balance_key, Amount, DenominatedAmount}; +use namada_core::types::token::{balance_key, Amount}; use namada_core::types::voting_power::FractionalVotingPower; use owo_colors::OwoColorize; use serde::Serialize; @@ -144,12 +144,8 @@ async fn validate_bridge_pool_tx( }); // validate amounts - let ( - tok_denominated @ DenominatedAmount { amount, .. }, - fee_denominated @ DenominatedAmount { - amount: fee_amount, .. - }, - ) = futures::try_join!(validate_token_amount, validate_fee_amount)?; + let (tok_denominated, fee_denominated) = + futures::try_join!(validate_token_amount, validate_fee_amount)?; // build pending Bridge pool transfer let fee_payer = fee_payer.unwrap_or_else(|| sender.clone()); @@ -158,7 +154,7 @@ async fn validate_bridge_pool_tx( asset, recipient, sender, - amount, + amount: tok_denominated.amount(), kind: if nut { TransferToEthereumKind::Nut } else { @@ -167,7 +163,7 @@ async fn validate_bridge_pool_tx( }, gas_fee: GasFee { token: fee_token, - amount: fee_amount, + amount: fee_denominated.amount(), payer: fee_payer, }, }; diff --git a/sdk/src/lib.rs b/sdk/src/lib.rs index f5c0ae37fa3..3aa38ab3910 100644 --- a/sdk/src/lib.rs +++ b/sdk/src/lib.rs @@ -38,7 +38,6 @@ use namada_core::types::ethereum_events::EthAddress; use namada_core::types::key::*; use namada_core::types::masp::{TransferSource, TransferTarget}; use namada_core::types::token; -use namada_core::types::token::NATIVE_MAX_DECIMAL_PLACES; use namada_core::types::transaction::GasLimit; use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard}; @@ -50,7 +49,7 @@ use crate::rpc::{ denominate_amount, format_denominated_amount, query_native_token, }; use crate::signing::SigningTxData; -use crate::token::DenominatedAmount; +use crate::token::{DenominatedAmount, NATIVE_MAX_DECIMAL_PLACES}; use crate::tx::{ ProcessTxResponse, TX_BOND_WASM, TX_BRIDGE_POOL_WASM, TX_CHANGE_COMMISSION_WASM, TX_CHANGE_CONSENSUS_KEY_WASM, @@ -448,10 +447,12 @@ pub trait Namada<'a>: Sized { recipient, asset, amount, - fee_amount: InputAmount::Unvalidated(token::DenominatedAmount { - amount: token::Amount::default(), - denom: NATIVE_MAX_DECIMAL_PLACES.into(), - }), + fee_amount: InputAmount::Unvalidated( + token::DenominatedAmount::new( + token::Amount::default(), + NATIVE_MAX_DECIMAL_PLACES.into(), + ), + ), fee_payer: None, fee_token: self.native_token(), nut: false, diff --git a/sdk/src/masp.rs b/sdk/src/masp.rs index 68459688e0b..c2782c57065 100644 --- a/sdk/src/masp.rs +++ b/sdk/src/masp.rs @@ -911,7 +911,7 @@ impl ShieldedContext { tx.source.clone(), MaspChange { asset: token_addr, - change: -tx.amount.amount.change(), + change: -tx.amount.amount().change(), }, ); self.last_txidx += 1; @@ -1565,7 +1565,7 @@ impl ShieldedContext { // Convert transaction amount into MASP types let (asset_types, masp_amount) = - convert_amount(epoch, token, amount.amount)?; + convert_amount(epoch, token, amount.amount())?; // If there are shielded inputs if let Some(sk) = spending_key { @@ -1620,7 +1620,7 @@ impl ShieldedContext { builder .add_transparent_input(TxOut { asset_type: *asset_type, - value: denom.denominate(&amount), + value: denom.denominate(&amount.amount()), address: script, }) .map_err(builder::Error::TransparentBuild)?; @@ -1638,7 +1638,7 @@ impl ShieldedContext { ovk_opt, pa.into(), *asset_type, - denom.denominate(&amount), + denom.denominate(&amount.amount()), memo.clone(), ) .map_err(builder::Error::SaplingBuild)?; @@ -1659,7 +1659,7 @@ impl ShieldedContext { )); for (denom, asset_type) in MaspDenom::iter().zip(asset_types.iter()) { - let vout = denom.denominate(&amount); + let vout = denom.denominate(&amount.amount()); if vout != 0 { builder .add_transparent_output( @@ -1931,7 +1931,7 @@ impl ShieldedContext { transfer.source.clone(), MaspChange { asset: transfer.token.clone(), - change: -transfer.amount.amount.change(), + change: -transfer.amount.amount().change(), }, )]); diff --git a/sdk/src/queries/router.rs b/sdk/src/queries/router.rs index b6320977386..c83d2bb4495 100644 --- a/sdk/src/queries/router.rs +++ b/sdk/src/queries/router.rs @@ -1047,25 +1047,25 @@ mod test { let result = TEST_RPC.b1(&client).await.unwrap(); assert_eq!(result, "b1"); - let balance = token::DenominatedAmount { - amount: token::Amount::native_whole(123_000_000), - denom: NATIVE_MAX_DECIMAL_PLACES.into(), - }; + let balance = token::DenominatedAmount::new( + token::Amount::native_whole(123_000_000), + NATIVE_MAX_DECIMAL_PLACES.into(), + ); let result = TEST_RPC.b2i(&client, &balance).await.unwrap(); assert_eq!(result, format!("b2i/{balance}")); - let a1 = token::DenominatedAmount { - amount: token::Amount::native_whole(345), - denom: NATIVE_MAX_DECIMAL_PLACES.into(), - }; - let a2 = token::DenominatedAmount { - amount: token::Amount::native_whole(123_000), - denom: NATIVE_MAX_DECIMAL_PLACES.into(), - }; - let a3 = token::DenominatedAmount { - amount: token::Amount::native_whole(1_000_999), - denom: NATIVE_MAX_DECIMAL_PLACES.into(), - }; + let a1 = token::DenominatedAmount::new( + token::Amount::native_whole(345), + NATIVE_MAX_DECIMAL_PLACES.into(), + ); + let a2 = token::DenominatedAmount::new( + token::Amount::native_whole(123_000), + NATIVE_MAX_DECIMAL_PLACES.into(), + ); + let a3 = token::DenominatedAmount::new( + token::Amount::native_whole(1_000_999), + NATIVE_MAX_DECIMAL_PLACES.into(), + ); let result = TEST_RPC.b3(&client, &a1, &a2, &a3).await.unwrap(); assert_eq!(result, format!("b3/{a1}/{a2}/{a3}")); diff --git a/sdk/src/rpc.rs b/sdk/src/rpc.rs index 0d0d69a79ba..c8ec42b26c7 100644 --- a/sdk/src/rpc.rs +++ b/sdk/src/rpc.rs @@ -1004,7 +1004,7 @@ pub async fn validate_amount<'a, N: Namada<'a>>( "No denomination found for token: {token}, but --force \ was passed. Defaulting to the provided denomination." ); - Ok(input_amount.denom) + Ok(input_amount.denom()) } else { display_line!( context.io(), @@ -1017,7 +1017,7 @@ pub async fn validate_amount<'a, N: Namada<'a>>( } } }?; - if denom < input_amount.denom && !force { + if denom < input_amount.denom() && !force { display_line!( context.io(), "The input amount contained a higher precision than allowed by \ @@ -1123,7 +1123,7 @@ pub async fn denominate_amount( ); 0.into() }); - DenominatedAmount { amount, denom } + DenominatedAmount::new(amount, denom) } /// Look up the denomination of a token in order to format it diff --git a/sdk/src/signing.rs b/sdk/src/signing.rs index a462c00a00b..f6f2bb28e0c 100644 --- a/sdk/src/signing.rs +++ b/sdk/src/signing.rs @@ -533,7 +533,7 @@ pub async fn wrap_tx<'a, N: Namada<'a>>( } }; - let total_fee = fee_amount.amount * u64::from(args.gas_limit); + let total_fee = fee_amount.amount() * u64::from(args.gas_limit); let (unshield, unshielding_epoch) = match total_fee .checked_sub(updated_balance) @@ -544,15 +544,15 @@ pub async fn wrap_tx<'a, N: Namada<'a>>( let target = namada_core::types::masp::TransferTarget::Address( fee_payer_address.clone(), ); - let fee_amount = DenominatedAmount { + let fee_amount = DenominatedAmount::new( // NOTE: must unshield the total fee amount, not the // diff, because the ledger evaluates the transaction in // reverse (wrapper first, inner second) and cannot know // ahead of time if the inner will modify the balance of // the gas payer - amount: total_fee, - denom: 0.into(), - }; + total_fee, + 0.into(), + ); match ShieldedContext::::gen_shielded_transfer( context, diff --git a/sdk/src/tx.rs b/sdk/src/tx.rs index 93786158905..8dcd7e066b7 100644 --- a/sdk/src/tx.rs +++ b/sdk/src/tx.rs @@ -1951,7 +1951,7 @@ pub async fn build_ibc_transfer<'a>( validate_amount(context, args.amount, &args.token, args.tx.force) .await .expect("expected to validate amount"); - if validated_amount.canonical().denom.0 != 0 { + if validated_amount.canonical().denom().0 != 0 { return Err(Error::Other(format!( "The amount for the IBC transfer should be an integer: {}", validated_amount @@ -1964,7 +1964,7 @@ pub async fn build_ibc_transfer<'a>( let post_balance = check_balance_too_low_err( &args.token, &source, - validated_amount.amount, + validated_amount.amount(), balance_key, args.tx.force, context, @@ -2212,7 +2212,7 @@ pub async fn build_transfer<'a, N: Namada<'a>>( let post_balance = check_balance_too_low_err( &args.token, &source, - validated_amount.amount, + validated_amount.amount(), balance_key, args.tx.force, context, @@ -2234,7 +2234,7 @@ pub async fn build_transfer<'a, N: Namada<'a>>( // TODO Refactor me, we shouldn't rely on any specific token here. (token::Amount::zero(), args.native_token.clone()) } else { - (validated_amount.amount, args.token.clone()) + (validated_amount.amount(), args.token.clone()) }; // Determine whether to pin this transaction to a storage key let key = match &args.target { @@ -2258,7 +2258,7 @@ pub async fn build_transfer<'a, N: Namada<'a>>( Err(Build(builder::Error::InsufficientFunds(_))) => { Err(TxError::NegativeBalanceAfterTransfer( Box::new(source.clone()), - validated_amount.amount.to_string_native(), + validated_amount.amount().to_string_native(), Box::new(token.clone()), )) } diff --git a/shared/src/ledger/native_vp/ibc/context.rs b/shared/src/ledger/native_vp/ibc/context.rs index ffe2faa2f5c..86732dbaf31 100644 --- a/shared/src/ledger/native_vp/ibc/context.rs +++ b/shared/src/ledger/native_vp/ibc/context.rs @@ -202,14 +202,15 @@ where token: &Address, amount: DenominatedAmount, ) -> Result<()> { + let amount = amount.to_amount(token, self)?; let src_key = token::balance_key(token, src); let dest_key = token::balance_key(token, dest); let src_bal: Option = self.ctx.read(&src_key)?; let mut src_bal = src_bal.expect("The source has no balance"); - src_bal.spend(&amount.amount); + src_bal.spend(&amount); let mut dest_bal: Amount = self.ctx.read(&dest_key)?.unwrap_or_default(); - dest_bal.receive(&amount.amount); + dest_bal.receive(&amount); self.write(&src_key, src_bal.serialize_to_vec())?; self.write(&dest_key, dest_bal.serialize_to_vec()) @@ -253,15 +254,16 @@ where token: &Address, amount: DenominatedAmount, ) -> Result<()> { + let amount = amount.to_amount(token, self)?; let target_key = token::balance_key(token, target); let mut target_bal: Amount = self.ctx.read(&target_key)?.unwrap_or_default(); - target_bal.receive(&amount.amount); + target_bal.receive(&amount); let minted_key = token::minted_balance_key(token); let mut minted_bal: Amount = self.ctx.read(&minted_key)?.unwrap_or_default(); - minted_bal.receive(&amount.amount); + minted_bal.receive(&amount); self.write(&target_key, target_bal.serialize_to_vec())?; self.write(&minted_key, minted_bal.serialize_to_vec())?; @@ -279,15 +281,16 @@ where token: &Address, amount: DenominatedAmount, ) -> Result<()> { + let amount = amount.to_amount(token, self)?; let target_key = token::balance_key(token, target); let mut target_bal: Amount = self.ctx.read(&target_key)?.unwrap_or_default(); - target_bal.spend(&amount.amount); + target_bal.spend(&amount); let minted_key = token::minted_balance_key(token); let mut minted_bal: Amount = self.ctx.read(&minted_key)?.unwrap_or_default(); - minted_bal.spend(&amount.amount); + minted_bal.spend(&amount); self.write(&target_key, target_bal.serialize_to_vec())?; self.write(&minted_key, minted_bal.serialize_to_vec()) diff --git a/shared/src/ledger/native_vp/masp.rs b/shared/src/ledger/native_vp/masp.rs index 54048f67593..2abba82c226 100644 --- a/shared/src/ledger/native_vp/masp.rs +++ b/shared/src/ledger/native_vp/masp.rs @@ -124,6 +124,9 @@ where ) -> Result { let epoch = self.ctx.get_block_epoch()?; let (transfer, shielded_tx) = self.ctx.get_shielded_action(tx_data)?; + let transfer_amount = transfer + .amount + .to_amount(&transfer.token, &self.ctx.pre())?; 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(); @@ -137,7 +140,7 @@ where let (_transp_asset, transp_amt) = convert_amount( epoch, &transfer.token, - transfer.amount.into(), + transfer_amount, denom, ); @@ -212,7 +215,7 @@ where } if !valid_transfer_amount( out.value, - denom.denominate(&transfer.amount.amount), + denom.denominate(&transfer_amount), ) { return Ok(false); } @@ -220,7 +223,7 @@ where let (_transp_asset, transp_amt) = convert_amount( epoch, &transfer.token, - transfer.amount.amount, + transfer_amount, denom, ); diff --git a/shared/src/ledger/protocol/mod.rs b/shared/src/ledger/protocol/mod.rs index 98ab30a6afe..89813becf7d 100644 --- a/shared/src/ledger/protocol/mod.rs +++ b/shared/src/ledger/protocol/mod.rs @@ -406,15 +406,15 @@ where match wrapper.get_tx_fee() { Ok(fees) => { let fees = fees - .apply_precision(&wrapper.fee.token, wl_storage) + .to_amount(&wrapper.fee.token, wl_storage) .map_err(|e| Error::FeeError(e.to_string()))?; - if balance.checked_sub(fees.amount).is_some() { + if balance.checked_sub(fees).is_some() { token_transfer( wl_storage, &wrapper.fee.token, &wrapper.fee_payer(), block_proposer, - fees.amount, + fees, ) .map_err(|e| Error::FeeError(e.to_string())) } else { @@ -534,9 +534,9 @@ where .map_err(|e| Error::FeeError(e.to_string()))?; let fees = fees - .apply_precision(&wrapper.fee.token, wl_storage) + .to_amount(&wrapper.fee.token, wl_storage) .map_err(|e| Error::FeeError(e.to_string()))?; - if balance.checked_sub(fees.amount).is_some() { + if balance.checked_sub(fees).is_some() { Ok(()) } else { Err(Error::FeeError( diff --git a/shared/src/vm/host_env.rs b/shared/src/vm/host_env.rs index 2a527d2ff50..38a68ee6c63 100644 --- a/shared/src/vm/host_env.rs +++ b/shared/src/vm/host_env.rs @@ -2498,7 +2498,8 @@ where ) -> Result<(), storage_api::Error> { use namada_core::types::token; - if amount.amount != token::Amount::default() && src != dest { + let amount = amount.to_amount(token, self)?; + if amount != token::Amount::default() && src != dest { let src_key = token::balance_key(token, src); let dest_key = token::balance_key(token, dest); let src_bal = self.read::(&src_key)?; @@ -2506,10 +2507,10 @@ where self.log_string(format!("src {} has no balance", src_key)); unreachable!() }); - src_bal.spend(&amount.amount); + src_bal.spend(&amount); let mut dest_bal = self.read::(&dest_key)?.unwrap_or_default(); - dest_bal.receive(&amount.amount); + dest_bal.receive(&amount); self.write(&src_key, src_bal)?; self.write(&dest_key, dest_bal)?; } @@ -2559,15 +2560,16 @@ where ) -> Result<(), storage_api::Error> { use namada_core::types::token; + let amount = amount.to_amount(token, self)?; let target_key = token::balance_key(token, target); let mut target_bal = self.read::(&target_key)?.unwrap_or_default(); - target_bal.receive(&amount.amount); + target_bal.receive(&amount); let minted_key = token::minted_balance_key(token); let mut minted_bal = self.read::(&minted_key)?.unwrap_or_default(); - minted_bal.receive(&amount.amount); + minted_bal.receive(&amount); self.write(&target_key, target_bal)?; self.write(&minted_key, minted_bal)?; @@ -2587,16 +2589,17 @@ where ) -> Result<(), storage_api::Error> { use namada_core::types::token; + let amount = amount.to_amount(token, self)?; let target_key = token::balance_key(token, target); let mut target_bal = self.read::(&target_key)?.unwrap_or_default(); - target_bal.spend(&amount.amount); + target_bal.spend(&amount); // burn the minted amount let minted_key = token::minted_balance_key(token); let mut minted_bal = self.read::(&minted_key)?.unwrap_or_default(); - minted_bal.spend(&amount.amount); + minted_bal.spend(&amount); self.write(&target_key, target_bal)?; self.write(&minted_key, minted_bal) diff --git a/tests/src/e2e/eth_bridge_tests.rs b/tests/src/e2e/eth_bridge_tests.rs index 9f6c14fb1f6..2c0491b4ffa 100644 --- a/tests/src/e2e/eth_bridge_tests.rs +++ b/tests/src/e2e/eth_bridge_tests.rs @@ -137,11 +137,8 @@ async fn test_roundtrip_eth_transfer() -> Result<()> { assert_eq!(dai_supply, Some(transfer_amount)); // let's transfer them back to Ethereum - let amount = token::DenominatedAmount { - amount: transfer_amount, - denom: 0u8.into(), - } - .to_string(); + let amount = + token::DenominatedAmount::new(transfer_amount, 0u8.into()).to_string(); let dai_addr = DAI_ERC20_ETH_ADDRESS.to_string(); let tx_args = vec![ "add-erc20-transfer", @@ -762,10 +759,7 @@ async fn test_wdai_transfer_implicit_unauthorized() -> Result<()> { &albert_addr.to_string(), &bertha_addr.to_string(), &bertha_addr.to_string(), - &token::DenominatedAmount { - amount: token::Amount::from(10_000), - denom: 0u8.into(), - }, + &token::DenominatedAmount::new(token::Amount::from(10_000), 0u8.into()), )?; cmd.exp_string("Transaction is valid.")?; cmd.exp_string("Transaction is invalid.")?; @@ -832,10 +826,7 @@ async fn test_wdai_transfer_established_unauthorized() -> Result<()> { &albert_established_addr.to_string(), &bertha_addr.to_string(), &bertha_addr.to_string(), - &token::DenominatedAmount { - amount: token::Amount::from(10_000), - denom: 0u8.into(), - }, + &token::DenominatedAmount::new(token::Amount::from(10_000), 0u8.into()), )?; cmd.exp_string("Transaction is valid.")?; cmd.exp_string("Transaction is invalid.")?; @@ -882,10 +873,8 @@ async fn test_wdai_transfer_implicit_to_implicit() -> Result<()> { // attempt a transfer from Albert to Bertha that should succeed, as it's // signed with Albert's key let bertha_addr = find_address(&test, BERTHA)?; - let second_transfer_amount = &token::DenominatedAmount { - amount: token::Amount::from(10_000), - denom: 0u8.into(), - }; + let second_transfer_amount = + &token::DenominatedAmount::new(token::Amount::from(10_000), 0u8.into()); let mut cmd = attempt_wrapped_erc20_transfer( &test, &Who::Validator(0), @@ -960,10 +949,8 @@ async fn test_wdai_transfer_implicit_to_established() -> Result<()> { // attempt a transfer from Albert to Bertha that should succeed, as it's // signed with Albert's key - let second_transfer_amount = &token::DenominatedAmount { - amount: token::Amount::from(10_000), - denom: 0u8.into(), - }; + let second_transfer_amount = + &token::DenominatedAmount::new(token::Amount::from(10_000), 0u8.into()); let mut cmd = attempt_wrapped_erc20_transfer( &test, &Who::Validator(0), @@ -1041,10 +1028,8 @@ async fn test_wdai_transfer_established_to_implicit() -> Result<()> { // attempt a transfer from Albert to Bertha that should succeed, as it's // signed with Albert's key - let second_transfer_amount = &token::DenominatedAmount { - amount: token::Amount::from(10_000), - denom: 0u8.into(), - }; + let second_transfer_amount = + &token::DenominatedAmount::new(token::Amount::from(10_000), 0u8.into()); let mut cmd = attempt_wrapped_erc20_transfer( &test, &Who::Validator(0), @@ -1131,10 +1116,8 @@ async fn test_wdai_transfer_established_to_established() -> Result<()> { // attempt a transfer from Albert to Bertha that should succeed, as it's // signed with Albert's key - let second_transfer_amount = &token::DenominatedAmount { - amount: token::Amount::from(10_000), - denom: 0u8.into(), - }; + let second_transfer_amount = + &token::DenominatedAmount::new(token::Amount::from(10_000), 0u8.into()); let mut cmd = attempt_wrapped_erc20_transfer( &test, &Who::Validator(0), diff --git a/tests/src/e2e/ledger_tests.rs b/tests/src/e2e/ledger_tests.rs index 9f584e2b246..fd638c4d888 100644 --- a/tests/src/e2e/ledger_tests.rs +++ b/tests/src/e2e/ledger_tests.rs @@ -435,10 +435,10 @@ fn ledger_txs_and_queries() -> Result<()> { source: find_address(&test, BERTHA).unwrap(), target: find_address(&test, ALBERT).unwrap(), token: find_address(&test, NAM).unwrap(), - amount: token::DenominatedAmount { - amount: token::Amount::native_whole(10), - denom: token::NATIVE_MAX_DECIMAL_PLACES.into(), - }, + amount: token::DenominatedAmount::new( + token::Amount::native_whole(10), + token::NATIVE_MAX_DECIMAL_PLACES.into(), + ), key: None, shielded: None, } @@ -1488,7 +1488,7 @@ fn pos_init_validator() -> Result<()> { NATIVE_MAX_DECIMAL_PLACES.into(), ) .unwrap() - .amount + .amount() }, ) }) diff --git a/tests/src/e2e/setup.rs b/tests/src/e2e/setup.rs index 35f52169050..1f98ccf4440 100644 --- a/tests/src/e2e/setup.rs +++ b/tests/src/e2e/setup.rs @@ -170,14 +170,14 @@ where .0 .insert( StringEncoded::new(sk.ref_to()), - token::DenominatedAmount { - amount: token::Amount::from_uint( + token::DenominatedAmount::new( + token::Amount::from_uint( 3000000, NATIVE_MAX_DECIMAL_PLACES, ) .unwrap(), - denom: NATIVE_MAX_DECIMAL_PLACES.into(), - }, + NATIVE_MAX_DECIMAL_PLACES.into(), + ), ); // invoke `init-genesis-validator` signed by balance key to generate // validator pre-genesis wallet signed genesis txs diff --git a/tx_prelude/src/ibc.rs b/tx_prelude/src/ibc.rs index dfd0430e5b8..1415557a23e 100644 --- a/tx_prelude/src/ibc.rs +++ b/tx_prelude/src/ibc.rs @@ -66,7 +66,7 @@ impl IbcStorageContext for Ctx { &Address::Internal(InternalAddress::Ibc), target, token, - amount.amount, + amount.to_amount(token, self)?, ) } @@ -76,7 +76,7 @@ impl IbcStorageContext for Ctx { token: &Address, amount: DenominatedAmount, ) -> Result<(), Error> { - burn(self, target, token, amount.amount) + burn(self, target, token, amount.to_amount(token, self)?) } fn log_string(&self, message: String) { diff --git a/tx_prelude/src/token.rs b/tx_prelude/src/token.rs index 009cccb36d7..6f0d40f46a1 100644 --- a/tx_prelude/src/token.rs +++ b/tx_prelude/src/token.rs @@ -15,7 +15,8 @@ pub fn transfer( token: &Address, amount: DenominatedAmount, ) -> TxResult { - if amount.amount != Amount::default() && src != dest { + let amount = amount.to_amount(token, ctx)?; + if amount != Amount::default() && src != dest { let src_key = token::balance_key(token, src); let dest_key = token::balance_key(token, dest); let src_bal: Option = ctx.read(&src_key)?; @@ -23,9 +24,9 @@ pub fn transfer( log_string(format!("src {} has no balance", src_key)); unreachable!() }); - src_bal.spend(&amount.amount); + src_bal.spend(&amount); let mut dest_bal: Amount = ctx.read(&dest_key)?.unwrap_or_default(); - dest_bal.receive(&amount.amount); + dest_bal.receive(&amount); ctx.write(&src_key, src_bal)?; ctx.write(&dest_key, dest_bal)?; } diff --git a/wasm/checksums.json b/wasm/checksums.json index abda2d7f514..e0ae82c4863 100644 --- a/wasm/checksums.json +++ b/wasm/checksums.json @@ -1,27 +1,27 @@ { - "tx_bond.wasm": "tx_bond.24a1a93ab606bfb3ea280fcb6938eed4735ce98da25fbcfbac034bf466ceea3c.wasm", - "tx_bridge_pool.wasm": "tx_bridge_pool.986638b4c38d6ea6c9cd87a985ace2ebcf2f5e1bd5ec19478a8a134f9543d8f9.wasm", - "tx_change_consensus_key.wasm": "tx_change_consensus_key.6c723f526276e2f3444c85a226daba7ec4fee55514a1817710159fc1206f3178.wasm", - "tx_change_validator_commission.wasm": "tx_change_validator_commission.7bafc20d7aaa7acfcdecd09f059b151cf685c309dba8ae1ebf16b21759d0f111.wasm", - "tx_change_validator_metadata.wasm": "tx_change_validator_metadata.06775afbe8c702d52802d0db5c41c0391cf11bacee90214c4ce0f095379f2f0b.wasm", - "tx_claim_rewards.wasm": "tx_claim_rewards.352f86e06f979ff2b36dcad550ff57e3689fb975b6fd4573c5cacd26f2797987.wasm", - "tx_deactivate_validator.wasm": "tx_deactivate_validator.31b43c64a8e1a900002aa35e13a0c3d541ac329a585f6ca3df70870a760c7323.wasm", - "tx_ibc.wasm": "tx_ibc.0aa4d16c8f3a5c4b21dfc3092443b05705225f04bc3a1df8be8177ac73183fef.wasm", - "tx_init_account.wasm": "tx_init_account.eab0b875c424453c26c6420b972657434b23c6024e378ec436573f36c20f3d6b.wasm", - "tx_init_proposal.wasm": "tx_init_proposal.0e130b5904f51dea4d02fc911fb9bf95fb7af42084498dfa436771f41bad0f55.wasm", - "tx_init_validator.wasm": "tx_init_validator.99d21a71edf5f0ca52b775ff2542d5e1b4f28c13ddb8ad875379e0bee9ac7174.wasm", - "tx_reactivate_validator.wasm": "tx_reactivate_validator.b4c855c7b6ca1116cfb7ebb791df963820c1b180c1a95eab7d3c6faec7e734fe.wasm", - "tx_redelegate.wasm": "tx_redelegate.489a6f8618e4d82bc76eb8984b1af0f8aeaf445f8c5b2449d8de71a7c5afbac4.wasm", - "tx_resign_steward.wasm": "tx_resign_steward.b809936ec9fae98eee1c079fc83d2fcc1e085eb5a110057a086261f9731bd3bc.wasm", - "tx_reveal_pk.wasm": "tx_reveal_pk.da41d83cf22cd03cab83e3f5c0a6a0b054f63a0ed7a0a61ea8be8c5b2c61c2b4.wasm", - "tx_transfer.wasm": "tx_transfer.44cb4d4741d2e581747c9dd1c5c024bd182be7be2b600b275026c4b6af42fc8c.wasm", - "tx_unbond.wasm": "tx_unbond.180f5dd3b64105fc92b2f5949f77b097516ee50772a2d42dc80826cec2ea5e49.wasm", - "tx_unjail_validator.wasm": "tx_unjail_validator.2cea8c20197392eb3b09b19a8e7278ec70c16d85b11e4c20052ed5bf60951004.wasm", - "tx_update_account.wasm": "tx_update_account.54a2eac05115223a7a769830121def96d1feb4959eee37db4586fb717bd3c146.wasm", - "tx_update_steward_commission.wasm": "tx_update_steward_commission.4f5fd4ac948736be8ba4a6c84112a6bee2a3ad3369028a566e573f32059b0f22.wasm", - "tx_vote_proposal.wasm": "tx_vote_proposal.357ae21d13a6e2a845c6276fca6f42620bef23311ae6abd9ce25c0256cb7b4dd.wasm", - "tx_withdraw.wasm": "tx_withdraw.e7b3b93d8470858b02cd449e330ac6de84929c53e11dff1c4f7030d74a204bfe.wasm", - "vp_implicit.wasm": "vp_implicit.e843bdfe9b211a138cca599c8d3bc6426b38587d10cf81da113c8b9c65544b45.wasm", - "vp_user.wasm": "vp_user.a6c222ecc1c237144ac01a9145369c0fd7176a053985addffc005f8fa00e67fa.wasm", - "vp_validator.wasm": "vp_validator.755de40fa771cd675ec219794c849e8bd4e3b3ec244e34b82925b88ca25b5ab8.wasm" + "tx_bond.wasm": "tx_bond.4373d0c6fd6db013519be26d6c967518a525afd95ccf6ab661b83b3136a3bde7.wasm", + "tx_bridge_pool.wasm": "tx_bridge_pool.3c43c365b1589665697ea821388ad60de29deee0a9e53d82b39314982175d69b.wasm", + "tx_change_consensus_key.wasm": "tx_change_consensus_key.5aaab8f9b38d76e883087c8d7b44ae35e7e366c0fd69571c97cba81ca330a314.wasm", + "tx_change_validator_commission.wasm": "tx_change_validator_commission.f46d30072abedb9215e0a6960e27c43e38d9024c8ce2278dd430c93f5efb69bf.wasm", + "tx_change_validator_metadata.wasm": "tx_change_validator_metadata.44acdf1b6f2a4c6baa683c296ba7ab4d210a394b3716ff44981b0c931ef0046c.wasm", + "tx_claim_rewards.wasm": "tx_claim_rewards.42df44fbd540898e7887f7de81f1d60988945132ff379671bb37ab94049d9b8f.wasm", + "tx_deactivate_validator.wasm": "tx_deactivate_validator.b60b92858676dc6a8bea275054e0c9248650e4124b796b267843b9dd2d53ed20.wasm", + "tx_ibc.wasm": "tx_ibc.0e7068406299fae90c58ed71fbbff4c56c7eedda3bfce15eb8624f87e40e5339.wasm", + "tx_init_account.wasm": "tx_init_account.57d1f2f429c452860242c3683f979b948032c2f646e76659c9f0fda9f5938222.wasm", + "tx_init_proposal.wasm": "tx_init_proposal.8b02e1b601b1ef9d33ab9ba3931db2a7c6f821a280653e00f67f4a92560fc51e.wasm", + "tx_init_validator.wasm": "tx_init_validator.78fabdb251f3e54617c0a1819677f602a37b0e7c9bb7ebbb3f811e8acdff1f7d.wasm", + "tx_reactivate_validator.wasm": "tx_reactivate_validator.a1b502803bb5ac5d5b1d0d46baee9cbebddad354977aec876c11f79728377c6b.wasm", + "tx_redelegate.wasm": "tx_redelegate.fe7388cc7d385b15b402bf38c7e73fef6a8a1baf58de1bab893eedf947fda89d.wasm", + "tx_resign_steward.wasm": "tx_resign_steward.edce1bf663399964ced3f56f3b753f4c8ded12850359e0754b6a28a90d3d8e69.wasm", + "tx_reveal_pk.wasm": "tx_reveal_pk.1c34064d4df5c98f75b527429141ec1b9b3ce648862fcc88bf4d27c20684234e.wasm", + "tx_transfer.wasm": "tx_transfer.48000f5df6a3ea39b9ce9de73ce8a901185de635b09fac0dda6780ba25c99112.wasm", + "tx_unbond.wasm": "tx_unbond.5099496c79f0b06581f8d56772f7aaee041175d88d7e338c7599324786a88ecb.wasm", + "tx_unjail_validator.wasm": "tx_unjail_validator.52beb890e48494dd338e283f8acaa5594b223689422cedd92953a05d9d9d7c31.wasm", + "tx_update_account.wasm": "tx_update_account.709e50e66e854e55ebce338ed9b000ac99b1a5f2ff01abd151139ae444b1ba53.wasm", + "tx_update_steward_commission.wasm": "tx_update_steward_commission.199041932781aed4328ca769b3f7c3531c0a9052494c37a8eab8362cc1131753.wasm", + "tx_vote_proposal.wasm": "tx_vote_proposal.92cf747e8308b20d9b0ab43a646f180849467bf33a172904284533fbbca4a2cf.wasm", + "tx_withdraw.wasm": "tx_withdraw.03442bcf4cf61162fbc073438419e18ce8babf9123793826194fc9aeaeb59e3e.wasm", + "vp_implicit.wasm": "vp_implicit.e3812f7e6f4966e369adbe66488dffcf6d1cb1afa390edaadf91c7cb44af52aa.wasm", + "vp_user.wasm": "vp_user.33496832f27dcb133427db0fdaee6a0395c88baa9cdf9db14907e107853836a1.wasm", + "vp_validator.wasm": "vp_validator.9e4c36bec5f96570462a336bd805b994c2a48d70bb0e720fd9a936fd3e8f362c.wasm" } \ No newline at end of file diff --git a/wasm/wasm_source/src/vp_implicit.rs b/wasm/wasm_source/src/vp_implicit.rs index 95d1401c545..8f46ceb9b1c 100644 --- a/wasm/wasm_source/src/vp_implicit.rs +++ b/wasm/wasm_source/src/vp_implicit.rs @@ -349,10 +349,10 @@ mod tests { // able to transfer from it tx_env.credit_tokens(&source, &token, amount); - let amount = token::DenominatedAmount { + let amount = token::DenominatedAmount::new( amount, - denom: token::NATIVE_MAX_DECIMAL_PLACES.into(), - }; + token::NATIVE_MAX_DECIMAL_PLACES.into(), + ); // Initialize VP environment from a transaction vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |address| { // Apply transfer in a transaction @@ -584,10 +584,10 @@ mod tests { ) .unwrap(); - let amount = token::DenominatedAmount { + let amount = token::DenominatedAmount::new( amount, - denom: token::NATIVE_MAX_DECIMAL_PLACES.into(), - }; + token::NATIVE_MAX_DECIMAL_PLACES.into(), + ); // Initialize VP environment from a transaction vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |address| { @@ -645,10 +645,10 @@ mod tests { ) .unwrap(); - let amount = token::DenominatedAmount { + let amount = token::DenominatedAmount::new( amount, - denom: token::NATIVE_MAX_DECIMAL_PLACES.into(), - }; + token::NATIVE_MAX_DECIMAL_PLACES.into(), + ); // Initialize VP environment from a transaction vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |address| { // Apply transfer in a transaction @@ -716,10 +716,10 @@ mod tests { ) .unwrap(); - let amount = token::DenominatedAmount { + let amount = token::DenominatedAmount::new( amount, - denom: token::NATIVE_MAX_DECIMAL_PLACES.into(), - }; + token::NATIVE_MAX_DECIMAL_PLACES.into(), + ); // Initialize VP environment from a transaction vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |address| { diff --git a/wasm/wasm_source/src/vp_testnet_faucet.rs b/wasm/wasm_source/src/vp_testnet_faucet.rs index d8144d441c2..b5c8b3456e9 100644 --- a/wasm/wasm_source/src/vp_testnet_faucet.rs +++ b/wasm/wasm_source/src/vp_testnet_faucet.rs @@ -163,10 +163,10 @@ mod tests { // able to transfer from it tx_env.credit_tokens(&source, &token, amount); - let amount = token::DenominatedAmount { + let amount = token::DenominatedAmount::new( amount, - denom: token::NATIVE_MAX_DECIMAL_PLACES.into(), - }; + token::NATIVE_MAX_DECIMAL_PLACES.into(), + ); // Initialize VP environment from a transaction vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |address| { @@ -325,10 +325,10 @@ mod tests { // be able to transfer from it tx_env.credit_tokens(&vp_owner, &token, amount); tx_env.commit_genesis(); - let amount = token::DenominatedAmount { + let amount = token::DenominatedAmount::new( amount, - denom: token::NATIVE_MAX_DECIMAL_PLACES.into() - }; + token::NATIVE_MAX_DECIMAL_PLACES.into() + ); // Initialize VP environment from a transaction vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |address| { @@ -380,10 +380,10 @@ mod tests { let solution = challenge.solve(); let solution_bytes = solution.serialize_to_vec(); - let amount = token::DenominatedAmount { + let amount = token::DenominatedAmount::new( amount, - denom: token::NATIVE_MAX_DECIMAL_PLACES.into(), - }; + token::NATIVE_MAX_DECIMAL_PLACES.into(), + ); // Initialize VP environment from a transaction vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |address| { diff --git a/wasm/wasm_source/src/vp_user.rs b/wasm/wasm_source/src/vp_user.rs index 28bfcbdc9f4..33928f55837 100644 --- a/wasm/wasm_source/src/vp_user.rs +++ b/wasm/wasm_source/src/vp_user.rs @@ -244,10 +244,10 @@ mod tests { ) .unwrap(); - let amount = token::DenominatedAmount { + let amount = token::DenominatedAmount::new( amount, - denom: token::NATIVE_MAX_DECIMAL_PLACES.into(), - }; + token::NATIVE_MAX_DECIMAL_PLACES.into(), + ); // Initialize VP environment from a transaction vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |address| { // Apply transfer in a transaction @@ -299,10 +299,10 @@ mod tests { // be able to transfer from it tx_env.credit_tokens(&vp_owner, &token, amount); - let amount = token::DenominatedAmount { + let amount = token::DenominatedAmount::new( amount, - denom: token::NATIVE_MAX_DECIMAL_PLACES.into(), - }; + token::NATIVE_MAX_DECIMAL_PLACES.into(), + ); // Initialize VP environment from a transaction vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |address| { // Apply transfer in a transaction @@ -357,10 +357,10 @@ mod tests { ) .unwrap(); - let amount = token::DenominatedAmount { + let amount = token::DenominatedAmount::new( amount, - denom: token::NATIVE_MAX_DECIMAL_PLACES.into(), - }; + token::NATIVE_MAX_DECIMAL_PLACES.into(), + ); // Initialize VP environment from a transaction vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |address| { @@ -592,10 +592,10 @@ mod tests { // be able to transfer from it tx_env.credit_tokens(&source, &token, amount); - let amount = token::DenominatedAmount { + let amount = token::DenominatedAmount::new( amount, - denom: token::NATIVE_MAX_DECIMAL_PLACES.into(), - }; + token::NATIVE_MAX_DECIMAL_PLACES.into(), + ); // Initialize VP environment from a transaction vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |address| { diff --git a/wasm/wasm_source/src/vp_validator.rs b/wasm/wasm_source/src/vp_validator.rs index 2c104c13d8f..e199600f600 100644 --- a/wasm/wasm_source/src/vp_validator.rs +++ b/wasm/wasm_source/src/vp_validator.rs @@ -321,10 +321,10 @@ mod tests { ) .unwrap(); - let amount = token::DenominatedAmount { + let amount = token::DenominatedAmount::new( amount, - denom: token::NATIVE_MAX_DECIMAL_PLACES.into(), - }; + token::NATIVE_MAX_DECIMAL_PLACES.into(), + ); // Initialize VP environment from a transaction vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |address| { // Apply transfer in a transaction @@ -368,10 +368,10 @@ mod tests { // Credit the tokens to the VP owner before running the transaction to // be able to transfer from it tx_env.credit_tokens(&vp_owner, &token, amount); - let amount = token::DenominatedAmount { + let amount = token::DenominatedAmount::new( amount, - denom: token::NATIVE_MAX_DECIMAL_PLACES.into(), - }; + token::NATIVE_MAX_DECIMAL_PLACES.into(), + ); // write the denomination of NAM into storage storage_api::token::write_denom( &mut tx_env.wl_storage, @@ -434,10 +434,10 @@ mod tests { ) .unwrap(); - let amount = token::DenominatedAmount { + let amount = token::DenominatedAmount::new( amount, - denom: token::NATIVE_MAX_DECIMAL_PLACES.into(), - }; + token::NATIVE_MAX_DECIMAL_PLACES.into(), + ); // Initialize VP environment from a transaction vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |address| { @@ -683,10 +683,10 @@ mod tests { // Credit the tokens to the VP owner before running the transaction to // be able to transfer from it tx_env.credit_tokens(&source, &token, amount); - let amount = token::DenominatedAmount { + let amount = token::DenominatedAmount::new( amount, - denom: token::NATIVE_MAX_DECIMAL_PLACES.into(), - }; + token::NATIVE_MAX_DECIMAL_PLACES.into(), + ); // Initialize VP environment from a transaction vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |address| { diff --git a/wasm_for_tests/tx_memory_limit.wasm b/wasm_for_tests/tx_memory_limit.wasm index 624f796b52ab40097f01f3f220d3604992f2652e..8f5d647c9e7eeb2cfa7327c7bdbec76ada01092d 100755 GIT binary patch delta 6449 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 delta 95 zcmX@LT; o7V)x3DX>jWNS||V*+AkAZ7t#Rv-q6v2RZ^<4Cy+0H5F(2LJ#7 diff --git a/wasm_for_tests/tx_proposal_code.wasm b/wasm_for_tests/tx_proposal_code.wasm index b8ec32f2409395fc82865879c4f957e0adc4d540..9cc64ccd8f9b88588a59621bb6da93b2b2ebb860 100755 GIT binary patch delta 1861 zcmcIlO^6&t6t1f2s;-{uURvFd9T&B_@vx&r27f(GR%tqFW zFb9?FJ?J1r(1QmNe@G5MNy)4~?@WM;Fa_-p9H96Y3rY_&BR?m;* z59*Pq@#h`!@khH;!>4~yFFdH`s~;&ZcL|vrN_w;{`?>a-)ZuB8y$L0Sax{^Jw0eGB zWO_*N%htf~RoskhXmrqk&4hE*%)&Tv6hXue2vm`2ig9w|lf$X$$U;PpLI~?fLxf|K z1SjLjKxjt+MBu0a(Qu?76fBo2-vUQDeke0INxW_g@vsm+AzL5~Ta3>spVMMSh*{hR zKHMWx;*>6Is+NxMry2gbsqPv?6f6Y;A88-Vs5!6q1@k_jIXq9>tlFD77W0{QUVB$~ zLx0FVHCFbNel^J=*RyIKdi{K6d_=nEa!ZXOk0|V~jO~|k`Ogr8e~Bo8v)TStC~p<7b>5?Nke(=uVo5oQe!K}3ch3rqG9GfRTKBdaRc>L+P`8dw-fX3&N1W(b+Bp;?a2p$GPVi2dJr-_u{t?9JeIfX%&`spcJ= z=?)inc>9*R@5zdf@CF}Y%|}QzAFNQV+l1 zQV&3+S5^C&2)!jKE@qU-btjldMVUTKrdUWq2$`DPbP|jf1_u+j0RxmA^MpR}XqUz6 zw7{>p%X6+jr^iuK>;OG{Mu;(9$|9U%6xl}@FgpIlx}b;X&9hjxkCg*gXAzCF09b?_m^|tv*qvA_*dAh5 zO#yf`t0sM1c}IoHyizCv4p#=Z2RI$V9Gsi(e_pO4=Cr18WN`Q9kVg4gZcyM+)z{T+fl ajIS%`1palo)At`+YX|uBXsvBNn)?g0wOK6y delta 1812 zcmcIlO^6&t6t1f7s_x$EUOKojSr@gs<6*}T!yX)$u!nibBpb3x7EdbV5_0MhW!5k! zVGgdus|aZY4|?$6NlAn`7y_dPB-x1K$@qgHCLtP=EG|ec>-V}lA^39%NYneaUe~Ml zec!A1=H$8Q+n-N=^ox??S1+s4WhLDwTUIZQMS4}*N#TT(w0iNxwzG1y?X0@`n7X+( zl0U1bBk#ut;_?mc&6G!fQ^yxbWP$3`4=E>i37LnK^k`G|bL}*#!!sm%lS&C`>E_y4 zWQHa)O>q{Kv{ODfbRmC_qcAYq_f#xJGGS>V({XGmg0Yrt7Q~UI5G$AfCbXns0!scFp$XBtB2p3Nij3P>3A2Y2_c;Nw424F5gH-w&T8?V=ve;}ijH z-IoZ9oDs8H)jO|Oryd>xBj*scdF zko?q#9}K_qUUnh2O%L*bWPb+k>+gJeO4VLfIc}d`-H^4zuN*&IdHq@w7gmnb=f3`` z2}%1iz`{sy7p1lLLdc>9TEw|I^)UJ$a{o8)NBb*TbT7EAP;nvCbw5Kh)8*opbGOvv z&((TFcl2PStVcwR9_(N}B5L$t2kQ}0qX#=`J#hDXXvSKW^5a|TfHWu9)f3W`yVq6g z7-k9@HZEqB$W7PpprEWC8dEGL0gOyduDc29j!TV(yMhg;uirkW<{Vzz-q7?6*YJ?#s(0g ziw|ZYRxxVqLi88Cd44SDNqYAJ2I@0azcnnvyDR_}X@@2cy9s6#rV56KXshi29=6q# zi|f;?n+#ql96qN*#W2S9GaZa65-v#Jui9V=Ai!`tcLQ(~4thhFhZv~~iah8cbb{nQ zgvJQvd4Ta%AdCq!FAJ7YH(_dZE?iwsnV=~!+XW97OpacTqxYi_4QH(QY)tY3vz3z~ zAC9#+Gq(_=s=*XfoK-14G@3iJ8?%W~brS)Z4PQB~xdcR0L*Ttty;xpT(uR5@#Y9Ul z$V|Er`#^Q{(uR8eOSv{W|C#sf5&70=N9MmBW&%H9V`0D9IVXhhoA(gxVZ6(DPh4vG Mc=hN~%bc123kYUXumAu6 diff --git a/wasm_for_tests/tx_read_storage_key.wasm b/wasm_for_tests/tx_read_storage_key.wasm index e85b8aa6e5a2596bdf417be63ea7f067b44cabc0..53b10af81ed9091199174088e807a3d2780cdc20 100755 GIT binary patch delta 4062 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 diff --git a/wasm_for_tests/tx_write.wasm b/wasm_for_tests/tx_write.wasm index 3a4dfe82b5eebfbdddf1e155afa59a087143edaa..23c39cc39fc2e6f4ac331b7d0e0461ef1c6e690a 100755 GIT binary patch delta 4138 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 Lx1Z}~W9JUB5gC^4 KM`T#P-v$85tt9CH delta 117 zcmdn-QGEAD@rEsoJtbUA9v)y&WO3wEWS-7w%q+2eT?wNr8{>}cU#b{W85wu9N7XO_ zG1K;_8fL`;#`D{2-Y`dMF`n6eO^oF%t0IHr4xoPSx1t6N3=Aw#>;j}+f!Ga*+YidH LY(FT&`u#QlbRs0= diff --git a/wasm_for_tests/vp_always_true.wasm b/wasm_for_tests/vp_always_true.wasm index 821283ddcafed3335303aaef42be111f1d723e0b..03321c33c5e5cc4784b5b6735b69b65be9bd3dc6 100755 GIT binary patch delta 114 zcmX^4N&MI+@rEsoQ%kt`6qz0OJUqal$TEFl38UEdttE`^Y>Z2`|E*$7Wn^5^o?OES z#7x_hYnUYq7>{kQeZw57#rSahbupH+ta1hn3=Aw#>;j}+f!K|~@dD65?q==wqcSYp KkIJyN+yMYDB_(12 delta 117 zcmX^1N&Ms|@rEsoQ%ks(JUqal$l}PS$UJ>v38UEdttE`^Y>Yd$|E*$7Wn|pZo?OES z#7x_hYnUYq7*B4meZw57#dv1>bupH+tcnbdJAnGRXKNcUFfgz{u?vuP1!6ZKZa*Z$ Lvi*<@Ys(z~*IFfA diff --git a/wasm_for_tests/vp_eval.wasm b/wasm_for_tests/vp_eval.wasm index 44e575b73c5f5bc6aa67a62975ac537cb6855587..ee95a921b35ee91e865fbb924c5b3f9e9ba8878e 100755 GIT binary patch delta 4028 zcmd^CU2Ggz6~5=r?9Qxb*Pb-0+88&vGY-aE8Z`vEF*c#T4QUHXWROMu{xakry0a`d16 zFe4(@e6!nKQ$O%iDrTLhwyZY#&-DK6zFwgx9rMRu&?n8i-Ll~34y&9QIzf+;XO5qs zU8_Cig!^T;lFCtTr6f1zJ5Aq7>ZSYZpE^nRQgu!M9hJj!SY_N{CG~=+;5m<{(2G>* zmm+q;!=&T}F&4Ypy|0j04t6>^`?aXU!I6GB8fWc3Je1Qf{n+2F*X`g<%GWAfWx>d| zTcd1{z_`~}S!{QeZ^CIPlQTP~srTm5D63pZiJXAnu5v1{>gj1R1MSLvTdttowgN5F z&Nsuev^L9{M!uPtp)Wc)T>80biq-?U{#D;Q|HQzf&x0Pg3jIpMyC(c|JmAIoQL;{5jT?3SFLbo!sXx@2+ zzEfaNW*Wj-^q|Y)%xk}*5AX7!(eBtUh#)`~{NCywb);%ZotU(cm{eG5Qce|MQbA8R zdl1GBJwuhqX`19TZ~Ve(+H$AW-fe21D(dQn?oA-o{OoDEeMgc5i4Ip19lDn4(4o4Z zLzkZS_e6OW>TAo~IwhNKH6Nd)+o^0GnWgt_U+zr5>A8kNac2`sRDyq3rBEYilL%T# znY(AHmEaWwgi>KIuh7+$o_#rT)9kNrZ;0*0mUadKg)Z|Hie5GLSQ8zXv5bffQ7k zlH8lT#vs8a93m0B;SrmlDvvWRzHnfmUl?Z*6zxVX>Vdh#9GIg!vX%qN=9hES>y)Fs zdG-yujeN83DBWTnJxVv3FCV4OTqoog9TDZsJ+IPM@WOXrC3O?Tit&v>5y=TDhDscBJ~vPjFJPhELGio=%rmdi zE!7&hB8N!M3lh*QzXribhEs_lf{yKxz5te|rl~gE6B#(Y(fxn=# z^xVNySfPe^%NVPIEHB9^EU;Yo05@VEiFgGk!bk+RHze8V>6%jksX+O%8Tk!u8EG+! z<^AGv)^7q0no5AcvU4J|LodiBLLd20g(70Xwt`3-Syp-NjE@drClCic1+5^lq53*) za!_~X$FI`|tDfx=8F*o$*)0wc`jZ}D8rbY`3??nGu?0L~f&K>IV7qGkW7#hD0NvVx zxETh3!WP6Wv$F@7w)!wZF&*KAddDXt$5@6X<%&xYJq&^^Y1O`vAS%)0Y*a}QgXS1zoF zhsH1~;5CjOg4e@lzx131zK@GWzwNq*08ZB8~m*7!fB2n?tgtdFH;#;(d@)Jg_zt*mZl z@7q+m14g2v^XxplLw<+P5xWP*#z<=zYsJ6?X6;2<^M7abxwk1^%f!h82-C665$kBo z7+m-@v+5Fc$2A!vA;)cMp={wv_8*6ror+N8%#KSmq?czK{OUqPb6>}gpD90CN-CIVf6;MqWwz#P5VX=U%wJavEmUz-RbPKE|Q zhPwB;!+Ed)oKbXc!0aE*Q*U);`CN$XM%?it5^sZO-eK$YhVD2VJ52EWQzI#J{Oqi( z7mB(9G%Bgq$#SqutPdOwEqbeH5&``&)A1eJ1+HCRP_F0euh6i1% z2frhCC!MH=ACeCdo!I}_CW$|%{@5n>_`H1gV7(Y*CW~~g{_~Gyj%Cf>p-i8z7x7Cx SIM%cO&`_p+Xej&OU;YUzw;!$m delta 3943 zcmdT{U2I%O6`nKq?(V(5yY`JjaFe>}-0Kk8{YjlPSqFPl?HDI52vAk=BY^~=QUR@l z6eKG3iCu)-Bx6L4$Prgidq@6z$R+`=K(x=it>=$}Lnm_%9K5I6dq`=J_Q5m!CBt1%=`SnS9 zc%`SDaF1N0q;iy7%FCf_&J4Ut{mxe9nAx+my84}0=}szd#hfRLD&-cH)Wr$>c_k(E z2UHr6B6h+fq~zVA7`xiNKL@E;=jikgqAmv;2Ba6K?K=D^hhO~(eft-9k@B?)S6DFe z?b0Y6Bru=J6Rz_7QtYUd?lb3SX(XK*bVQUf-#A76tGp~Mt>(mO8c81c?*(A3Zdr>w&@MGoTaZPN>$S-(Fu4~Ka)YJhN)=#Tpew0e zP{GslPAN(;%X}>$iGq{WaS%}l-z$~pz^B6|_a+Yq(%Ry0OEm=owsfkkDH7G;^ zg$&gJWn0iK49%I~3_YG@`Ayj@dBkOD=J#jl-f#NQXCn4DRP?TIIZJ$zed~cv6{Or%^OO(x~X>yXlQ-)HM-;T)(flx z?GMLr%hC_;)1p~-)xzop^?sEQi$ATVbX|_1u=oEuWy#UX6i#y=F$v8xM1XO zXmJ1{Zk2JX51pR5XWcbXVHOe8F6L5vJZ&9n4yd3ox#r#Q<`v4_04_z+~CPKR*v)v|+-m)b(g zJ93%MQc^61K3>X>T)MLmICw`jo$^g4@5}8SUZdaC;h-NNq0ge2; z^Bk>KsNAh|k91?(5eL--Wgt=a=jUjXM4~)@j_xdbwm={Sa}YLmet>XHp^hT_s=3^2|T$wEoMu;;;#YC}pK6v`YPVqIaWva0;=<#r`pqs#A|S zI7=JMj<;z%l^K?k@+SvAy>eLUAubXe33_j%)4E%rEmfH#F*h|1{4a?Pj-8_7n1J=~ zyiH#Txt^eIj0388d08~Y3$*Ii;)KpB7q)}hBKiV6#!)`-c))!B0&VeHLVk6DzHQJd zMdg|-jWrx#x;=cQJv`nXB9N|e#6VsB+`iLdP&Rh%2Qal5cn-kPMF1ah6kGW@#Ul1M zCVQON^BH#!la?afv*n6EAri{Mq&#`C!1se&%x0Jf1W-lB@UwC2F$h3* zXWo%`8FQF1k6xzHIq@O{%tw3CKQ7ZfWo~!y5hH8|VN(YJ9|#f%=PHZfSRlwC zO#~Mtc5>7w7lhB3W0aL>$l1v=e4%V)BZ=SCXfriOz43owIqOZY(0RJifmqhY##Y}u^l5ZA`qtoxV&cF!+kC;d_`=2 z2rb|LxbB(1JuPqf1Z1~r9oR(=qbt??JLGebPW@)4r?oKS_+Sox*Jqv`O|6$g?3o@-Ri{VO-}=kH0Y@bS-~a#s diff --git a/wasm_for_tests/vp_memory_limit.wasm b/wasm_for_tests/vp_memory_limit.wasm index cdebb21ec9613e09265ce3bb0497431cac83bf4d..31c584ff3b37984bf1f04a2821238e7227981ce9 100755 GIT binary patch delta 4188 zcmds4U2I%O6`p(M?(W?``$jI%{Lj5f({-FQZho#8J4roG;yNOgUzTDh60R{(*fatp z5aofcp{<%$1-Vk&2|_~q5R_JGg+pDo1SFjDl1GptQBfD%(nLiKQXYyFA*%45xp&uV zI~EZ_LgHcXo%_wqne+3Vb7n7`S^wUd^)vq@=bhI+rf3@&bDN<6o)rfiv9p=p)w8a@|T-`y#PVJHaI!1#us8a5rBE2B0c)o?F&^MfFi9{&< zSx3=MF&?_wy}wvc5-X)ne>2EQY$?%Ln6~TiP_jf`=$+J?AK*pG)5_0VF!1csAdOFk zajvJ*Snev%ymAi>%dAS9$9gIF#8{A4F60C<&u>@BD(vdnMW)=Y-V?NndPj=RGW|R= zzn3_Mc{}Xt>L=~fU z(-v6?(q_A+jS+Q=an_P`#PSbBHOzGj5ko#h*8vq;(47q3FhAN&PZrsWsg~TLrhb>j znG<_x&x8kMPKI7lNrMiVXy_WBqDuo>_C{_v{UXwu0h)pA6Bg(wJm%8u1z0*ANt9{hF zYxSf_#59v8$(eN#opLJXbVN^QI+qi(k`ObZLZ|s*A8oGPK+I)?3?==MB_x8wS?gMi zr%r&v_?pxrNFnlY2}fnpCYA0}n-F)Q)H@qGN?D z9GdKiVcv19;DJ0k(~q~4jg`^W9``V;H z8YDE@B`m7ZE+K2S_0u*k^8GYXTUq4o338>H@d0nNACSGJiWydsRuk~cb^%`PmJ+zk zYv;Bk*wSf(({5vG(e(BRa0y--m>NeWM(J3@eB=4iNE~X9d$J8{!9gq_ueCIfrOEz5 z#3$Z=C=vqz^-IKr76TE-ocEO@@lypE3K;r|@&(XUXI8Nx)F zT^wE$Efh(pMZ94C>KsNdu>`!WBp->3&-p*|3=&g6|J+54kb=BqoK*q;Bbvbg!-c!K zk9#2E>;kSJC6Tv%C1z(v*JTwo$u^M>_tSyVWsM`rD7n1!N=`{M4Uyzr-j?@RXihKC z5}{xHPlY04`6ESLHj{@_grcP5WlW)%6M~{Av%LzA5YjqBt zw935~!njsv@KAVop?gyQ#^EbgM!CE?Z((k3n09g3V^V-&iJ*-6#vpC8!u=-Z>$kjW z;Qu#-UmK)NvJz$tt`d);EaA1^ys7E1o9&~sCO1TC2NH=*kUIq~w<>!sQxzzey1=L*P{{qsx1Fb&*_X>N;YAGa|LnYAj@R1HN05#&hV zkBcoLplnh-Z52Wd0fHx2T#G;}Ab2(r#7~U3#Sj0VE;t3kKLihwT@E-K3|g*mKyj%D zI!A8L1D_e8E#V4bEm_Icrf#Mo4{h$>dGs8sD4LvPKTON&R-v5EJi3zoqXG(xLbIWY zY$U#dP>OzmOfRARg@QA?#E}(Ab>LPyj?Fa`hyO)~g}ZTW(DlZ-qx9l~&RKJ?PIx`_ zLY?rFjTh?f#ygzZ#%r^#lXhNiyney`Sx7%v(>VT)TgjN;zVA*uFPq90_s5?+cCRnK dEQC0Z-%+72ftyFm>?^X0?hcRjIQY$7gq2$4ZmWJX$zcaHx zlAW~*?Nc9i@0~OEo_p?h&iT%n8w`?+fPQQ@%|cNib#q{wEAv)s`M`cUn6$6w zcfRS{t>y<^X1eIO`F$6qW$L_2nLl^Y)0Sf%J4B~i9cBA3(EW;( ztz9D+)c`w8N^bD3MwHBigaNbe@;;e z{kc`_AQ9UBdzPYZQ3$1$lNlU>b+%6aC}^^=p@W<-8Lh*gviZ@S)_cFsi=8Qh9T)aYhi*os`GV{51)E}{NJrP% zh_92`dY!b&;5r#yvQNXIO?t&D26pv2cI`~>VH)m@%$GIuE0#)KHNGaOnEu?ubnwy0 z>yZJU#Rjx&HNaCXFrYNxr} zQoH(vR&-cQj%Ht0bVQ7gs+VVHvE~e3&UhX|m>MkcnwTh4Lr5di7MK$Q0!-))07bf^ z=>HaXt#IH3Shb-dmlY-NQlxq!E#=KxkVcKnaf}|KhrsJk4biEYyLml2#lHKL=#Yz> z4*?l9I)tfMQv)Txt1FqA$fO!;Nw5}cdo2^0WMd_W^M+>HGxvB&_jt!N-Qezcqf#T{ zB_`^h5DLs_b}k<||7`T6=7#x6$ssXIBOKQqp#Ys=atKjj`8%P31Es_oN>mJm|1J>I z=cveH4x=|JQ}gOyscmfi=8DMC952ThZS!;F;c@*szd`=#H(tAh8B&dx;4n<8X_EKPQya`U8u597jo92~7@* znXfTQbYGFf_zgE?j`?YU9%9vgRpg^E6$Af&pxV>4$Ige2sn+i&z#G;19&A}Wq)j`)J&NQTFr8Ksj2E(|CDV-S@b4ojguRuP~h z)Bi|4##w>I+EWrJ0I34%J%%xq1q@Rr^9c3B85Nz@IDOB&K2E{RDiud74qvHHFV?3^ z^(peXSAMrM}^0>$<)+;Y}*(HAD+(pBQ&Uv|F z;ob>4!SQ-}g4+DC5CMr>L~n67bA623{(o-s<^+XLFe!02FdN|kA4kCOCqRsk9~TrS z6zKnD!AmiFB8^HEBCf_F&!X$(1mPWVgfU<#vtyEuepv*`S_FMjk07MYmq*a6lhnJP zTNZqi#LN}Z1X3RokOebSBv~~dP13GS?wO+dx4&Lolxx@HTJ#@Y@98Ogur!9R(mj;S zGVGYy9T{ZBwMC{@WsjG)2FmOXWWur=Oj)E}HKz5{6e+770JQys{|Uzhafke|b+@ zN$dUc#^$ay^Y#@us4fnjsWU;JU+HunJ zFVn)MO&`CsoSN)Q8b}h>$A3@ngmUuYhOFO_W^tijxSbc;^B2)%lNQ)Jo}&_RRF-zh zi}Zp)_uAWDrsYOpUPS@bDj5=m>QDoZ1aBY2WWAx*EvNJHd8G_mY?v|mOnzG;{InHT zgI=NQDWo2Kg)S`!Sw?I=Ki=Y&VTn|dkBa80yjN+Eajs>k$6BbbJ@Hj4rrH*U&B?ry zS6Ts4X>qxgrSRO0CzBnND&j0*#G1(BC$s8=km8)&sAYiCkipsU90Ny+_~b;;p~J&6 ziny6Dr^|s8HIZdXD|U>7jGGQ=JQ#7&kZW2&kPFLHb5_vV6jo=vPJ{YPjt4CPE^#BG zS78|`7tbED(Tjjl5z zhdYy75l}{CU0?=u2+6HRDjv{YbpjEPTWNZ1KD=MVt6V3;BT!2_*_f_wPg6cssrob} zMptWnwYE6BgQXHV!x(c>F^y^mDw;(VYE*~%G);HqYNrM~A!-&prP|-6{(=u<)#{}; zY4qtP)SI3#b6G?iyu~#*C!-^mu6x@l2ufK2xxwhZhoc;t)@{Uk6s2j@7kDcnV$V3?iQuoQ8Dtah4!y8bOHHl1p&N<`Jv z^%OU%;!(AGJ)Iyu9|{SFnRsNp<;2xQaI-K3n~b zGOeyC6?+zjXWHA@Xm1{qa`z2P%Az@%Nif6YG#?jm1BXBQIdrSx=MYxcw$O>XKey1O zwMX`6M}l#rny!GwegWZcR57GgLr!Ff`>X_?u zWl6ROFP0d~7CMO9q6E_&x`>~IM4*^fTqj2(c4Z?^C0Oh!I7}|)DUH^t^HTR~8_Qx* zfReccZU0W=tx0ph>TrrCInqhCe4=+ylyY&yn|Qn08t2edYweKuN#2YXydp2PtxPuJ z$`MPjh%+7Bd=1G)BNZ#vI#;REbyP8_yWcn+KT}CY&kkznQbf0BnOcijx!K{ABe(Ly zn7VDbdyIS=Q#GQM`bI)=!<$*^!dB{oCU$u%l^C!`{jQaY%R&-83XO%!I;<}70>a#p zWxDK34ejB22QjJ}F_+ZAR_Z?#swYCAMK|$i^*D} zoAuE5>9S#P^O>3Gckt|Md-v;(%&VIBJ`J9Z+L>!W4cH(Xa0?&CGl!djSm;MA17fic zp{2B-#PpihrS_1w*{C61^0;;4(!9ho{eeHz`A!1YZ=}8@(-O!~nBwfiz=UyDF0S9Y zU&aDaG)}JX;$Z%NYGACVfol0iN?feFnZ8iyuIU8d@fJq6u2(oM=FTNE!SH1uDx;V| zpd4m2@&xj3nv+Mkj3QaI|8(5&s(lz%r+q->v7V`=>OP=8{gHVRwRF^68tGht@O|R8 zG^cr>As{dD)E&#gKrS$NdKg5ARr?}@Pm?7S<+yyCh+-5SJZ$@HfW0Y_z}l3*9y*zv&B%zKZV8>?V6o zK9BdxU-0?i_yzV-i*L&1d$-&7jNpq&zl<5hA0ukDH=n|5^g3=dFAh{J+m&W=sN9N0 zGq!lH5s?e`0$C*6Gf262C*aYt$%8kxIs0AqQ)Rq?_j8APz5cS)O-Qd8?x$m!t7HWq zXy01SpP~NWOxCK&=h~o~&#i6vQ^2vWQA>!+)mR+0Ktd^5Z?~VpH}ZDox}0?ZLkHwqyGNl8t*ZBAf9dhs!PYg7bJ%m|1YsL|z>I!M$)n ztOO3qkr9~%@?=ytWOls2J~sX%W9=^W_3q;r|<|RjqrFC#cQd^&pQCZL<$O#BU^C_oaQ)BYfbu$ySj4 zp*{ame(NZ+O=m}6Bx=E0epb-*Vrf@z;OF!i(cYP6xHIUl1N|K*cL2`VpugkfRzJL1 zeefxN@_U}K7j5S=1MovZ8$UH?z(GGl2sq$}_o$kEyyE)`*sza(6hX+BaAU+_7Dakk z6zO46L~d3&g~nTcf#2bbeX!7Y-tb%EF!oszk$cp-{>EXJd>6*a6O0LysnNdUdgEBS zU_-ZSBGO997C)>j!w>7q@WZ+?{BV=~&l`;6P6=*6qRA#(G#T%g^Sb@&ub&tVw9vj` zn_&m&H8tWh<4l*PP2KRBaS_e6-}%f~%xQtTWv4MCb6oA*X{;#oGRwv%A8Rg==2(_tk=ZKF{(D8FcC($m||9tVZuIn}D zi%|hT4@bGfdy1~>DfX-%iW_ylZ=WKTN3k|uAzE{2u9|tBsHJwb^E$B`IrPzNG5!L2 zOT|}-uHLcHF?K0qY$Y*vA=cPvd*o_S5X^nJB*v};qBzLdiRzlY;y7h}Au81ge-a`4 zxwWDq!PgYnZEd2+RJC7<`+G6w9a#~`92uhiwo{a-#k)ns7;)`&s^30w|FI2__)9Q; z0l)K@T!{G?x$ii&e4nVKWvX+ZxQb@0)P8Xf3AJy(I3r)27GqOD+`t(NV-@P`4pBZN z4fJq;H8ZcCeY17ttl2f!&YC*InswE*8nzdE1rF&`9bz;Xeb^yNhkK0rhZuv3%!3;Q zW5@d7EkgC`#DDO|b%F!}J#-6Lh&Q?G(QTr{q_nJjbKGAfOKI7lp+$YGzsEtWiE)Y7iG~blI2~`vumY!U=l6 zOs)7zT$S&k-v(-^gC5r!E2mjMQ=MI65M84N zd@bhF6KcuV;>Q)$T3-XBo z*WrgdDO;;n92B?oopX8&-viJY>v>p9AjYvt{pgS=rB*fXkSGpxVRx{)|Bx7>{(eY2 YmD^kqV*^3`u3qnYB=GVh!LR@EA056y-v9sr delta 6626 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