From 632ec06ae91bde89776448dd84fb20446d752b78 Mon Sep 17 00:00:00 2001 From: Murisi Tarusenga Date: Mon, 20 May 2024 19:21:39 +0200 Subject: [PATCH] Moved SignedAmount into core and removed use of unchecked arithmetic. --- crates/core/src/arith.rs | 4 +- crates/core/src/uint.rs | 237 +++++++++++++++++- .../ethereum_bridge/bridge_pool_vp.rs | 203 +++++++-------- crates/namada/src/ledger/native_vp/masp.rs | 13 +- crates/namada/src/ledger/native_vp/mod.rs | 212 +--------------- 5 files changed, 349 insertions(+), 320 deletions(-) diff --git a/crates/core/src/arith.rs b/crates/core/src/arith.rs index 4812fff775..96d7642392 100644 --- a/crates/core/src/arith.rs +++ b/crates/core/src/arith.rs @@ -3,5 +3,7 @@ pub use masp_primitives::num_traits::ops::checked::{ CheckedAdd, CheckedDiv, CheckedMul, CheckedNeg, CheckedRem, CheckedSub, }; -pub use masp_primitives::num_traits::ops::overflowing::OverflowingAdd; +pub use masp_primitives::num_traits::ops::overflowing::{ + OverflowingAdd, OverflowingSub, +}; pub use smooth_operator::{checked, Error}; diff --git a/crates/core/src/uint.rs b/crates/core/src/uint.rs index 08958ec692..07067f9032 100644 --- a/crates/core/src/uint.rs +++ b/crates/core/src/uint.rs @@ -6,6 +6,7 @@ use std::cmp::Ordering; use std::fmt; +use std::ops::Not; use std::str::FromStr; use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; @@ -17,7 +18,10 @@ use num_integer::Integer; use uint::construct_uint; use super::dec::{Dec, POS_DECIMAL_PRECISION}; -use crate::arith::{self, checked, CheckedAdd}; +use crate::arith::{ + self, checked, CheckedAdd, CheckedNeg, CheckedSub, OverflowingAdd, + OverflowingSub, +}; use crate::token; use crate::token::{AmountParseError, MaspDigitPos}; @@ -880,6 +884,237 @@ impl TryFrom for i128 { } } +construct_uint! { + #[derive( + BorshSerialize, + BorshDeserialize, + BorshSchema, + )] + + struct SignedAmountInt(5); +} + +/// A positive or negative amount +#[derive( + Copy, + Clone, + Eq, + PartialEq, + BorshSerialize, + BorshDeserialize, + BorshSchema, + Default, +)] +pub struct I320(SignedAmountInt); + +impl OverflowingAdd for I320 +where + T: Into, +{ + type Output = Self; + + fn overflowing_add(self, other: T) -> (Self, bool) { + let (res, overflow) = self.0.overflowing_add(other.into().0); + (Self(res), overflow) + } +} + +impl<'a, T> OverflowingAdd for &'a I320 +where + T: Into, +{ + type Output = I320; + + fn overflowing_add(self, other: T) -> (I320, bool) { + let (res, overflow) = self.0.overflowing_add(other.into().0); + (I320(res), overflow) + } +} + +impl OverflowingSub for I320 +where + T: Into, +{ + type Output = Self; + + fn overflowing_sub(self, other: T) -> (Self, bool) { + let (res, overflow) = self.0.overflowing_sub(other.into().0); + (I320(res), overflow) + } +} + +impl<'a, T> OverflowingSub for &'a I320 +where + T: Into, +{ + type Output = I320; + + fn overflowing_sub(self, other: T) -> (I320, bool) { + let (res, overflow) = self.0.overflowing_sub(other.into().0); + (I320(res), overflow) + } +} + +impl From for I320 { + fn from(lo: Uint) -> Self { + let mut arr = [0u64; Self::N_WORDS]; + arr[..4].copy_from_slice(&lo.0); + Self(SignedAmountInt(arr)) + } +} + +impl From for I320 { + fn from(lo: token::Amount) -> Self { + let mut arr = [0u64; Self::N_WORDS]; + arr[..4].copy_from_slice(&lo.raw_amount().0); + Self(SignedAmountInt(arr)) + } +} + +impl TryInto for I320 { + type Error = std::io::Error; + + fn try_into(self) -> Result { + if self.0.0[Self::N_WORDS - 1] == 0 { + Ok(token::Amount::from_uint( + Uint([self.0.0[0], self.0.0[1], self.0.0[2], self.0.0[3]]), + 0, + ) + .unwrap()) + } else { + Err(std::io::Error::new( + std::io::ErrorKind::InvalidInput, + "Integer overflow when casting to Amount", + )) + } + } +} + +impl I320 { + const N_WORDS: usize = 5; + + /// Gives the one value of an SignedAmount + pub fn one() -> Self { + Self(SignedAmountInt::one()) + } + + /// Check if the amount is negative (less than zero) + pub fn is_negative(&self) -> bool { + self.0.bit(Self::N_WORDS * SignedAmountInt::WORD_BITS - 1) + } + + /// Check if the amount is positive (greater than zero) + pub fn is_positive(&self) -> bool { + !self.is_negative() && !self.0.is_zero() + } + + /// Get the absolute value + fn abs(&self) -> Self { + if self.is_negative() { + self.overflowing_neg() + } else { + *self + } + } + + /// Get the absolute value + pub fn checked_abs(&self) -> Option { + if self.is_negative() { + self.checked_neg() + } else { + Some(*self) + } + } + + /// Compute the negation of a number. + pub fn overflowing_neg(self) -> Self { + (!self).overflowing_add(Self::one()).0 + } + + /// Get a string representation of `self` as a + /// native token amount. + pub fn to_string_native(self) -> String { + let mut res = self.abs().0.to_string(); + if self.is_negative() { + res.insert(0, '-'); + } + res + } + + /// Given a u128 and [`MaspDigitPos`], construct the corresponding + /// amount. + pub fn from_masp_denominated( + val: i128, + denom: MaspDigitPos, + ) -> Result>::Error> { + let abs = val.unsigned_abs(); + #[allow(clippy::cast_possible_truncation)] + let lo = abs as u64; + let hi = (abs >> 64) as u64; + let lo_pos = denom as usize; + #[allow(clippy::arithmetic_side_effects)] + let hi_pos = lo_pos + 1; + let mut raw = [0u64; Self::N_WORDS]; + raw[lo_pos] = lo; + raw[hi_pos] = hi; + i64::try_from(raw[Self::N_WORDS - 1]).map(|_| { + let res = Self(SignedAmountInt(raw)); + if val.is_negative() { + res.checked_neg().unwrap() + } else { + res + } + }) + } +} + +impl Not for I320 { + type Output = Self; + + fn not(self) -> Self { + Self(!self.0) + } +} + +impl CheckedNeg for I320 { + type Output = I320; + + fn checked_neg(self) -> Option { + let neg = self.overflowing_neg(); + (neg != self).then_some(neg) + } +} + +impl CheckedAdd for I320 { + type Output = I320; + + fn checked_add(self, rhs: Self) -> Option { + let res = self.overflowing_add(rhs).0; + ((self.is_negative() != rhs.is_negative()) + || (self.is_negative() == res.is_negative())) + .then_some(res) + } +} + +impl CheckedSub for I320 { + type Output = I320; + + fn checked_sub(self, rhs: Self) -> Option { + let res = self.overflowing_add(rhs.overflowing_neg()).0; + ((self.is_negative() == rhs.is_negative()) + || (res.is_negative() == self.is_negative())) + .then_some(res) + } +} + +impl PartialOrd for I320 { + fn partial_cmp(&self, other: &Self) -> Option { + #[allow(clippy::arithmetic_side_effects)] + (!self.is_negative(), self.0 << 1) + .partial_cmp(&(!other.is_negative(), other.0 << 1)) + } +} + #[cfg(any(test, feature = "testing"))] /// Testing helpers pub mod testing { diff --git a/crates/namada/src/ledger/native_vp/ethereum_bridge/bridge_pool_vp.rs b/crates/namada/src/ledger/native_vp/ethereum_bridge/bridge_pool_vp.rs index 9f4aaa5a5b..dd483797b1 100644 --- a/crates/namada/src/ledger/native_vp/ethereum_bridge/bridge_pool_vp.rs +++ b/crates/namada/src/ledger/native_vp/ethereum_bridge/bridge_pool_vp.rs @@ -17,7 +17,7 @@ use std::fmt::Debug; use std::marker::PhantomData; use borsh::BorshDeserialize; -use namada_core::arith::{checked, CheckedAdd}; +use namada_core::arith::{checked, CheckedAdd, CheckedNeg, CheckedSub}; use namada_core::booleans::BoolResultUnitExt; use namada_core::eth_bridge_pool::erc20_token_address; use namada_core::hints; @@ -34,12 +34,11 @@ use namada_tx::Tx; use crate::address::{Address, InternalAddress}; use crate::eth_bridge_pool::{PendingTransfer, TransferToEthereumKind}; use crate::ethereum_events::EthAddress; -use crate::ledger::native_vp::{ - self, Ctx, NativeVp, SignedAmount, StorageReader, -}; +use crate::ledger::native_vp::{self, Ctx, NativeVp, StorageReader}; use crate::storage::Key; use crate::token::storage_key::balance_key; use crate::token::Amount; +use crate::uint::I320; use crate::vm::WasmCacheAccess; #[derive(thiserror::Error, Debug)] @@ -53,14 +52,14 @@ struct AmountDelta { /// The base [`Amount`], before applying the delta. base: Amount, /// The delta to be applied to the base amount. - delta: SignedAmount, + delta: I320, } impl AmountDelta { /// Resolve the updated amount by applying the delta value. #[inline] - fn resolve(self) -> Result { - checked!(self.delta + SignedAmount::from(self.base)) + fn resolve(self) -> Result { + checked!(self.delta + I320::from(self.base)) .map_err(|e| Error(e.into())) } } @@ -108,7 +107,12 @@ where }; Ok(Some(AmountDelta { base: before, - delta: SignedAmount::from(after) - SignedAmount::from(before), + delta: checked!(I320::from(after) - I320::from(before)).map_err( + |error| { + tracing::warn!(?error, %account_key, "reading pre value"); + Error(error.into()) + }, + )?, })) } @@ -146,10 +150,11 @@ where ( Some(AmountDelta { delta: debit, .. }), Some(escrow_balance @ AmountDelta { delta: credit, .. }), - ) if !debit.is_positive() && !credit.is_negative() => Ok((debit - == -SignedAmount::from(expected_debit) - && credit == SignedAmount::from(expected_credit)) - .then_some(escrow_balance)), + ) if !debit.is_positive() && !credit.is_negative() => { + Ok((Some(debit) == I320::from(expected_debit).checked_neg() + && credit == I320::from(expected_credit)) + .then_some(escrow_balance)) + } // user did not debit from their account (Some(AmountDelta { delta, .. }), _) if !delta.is_negative() => { tracing::debug!( @@ -277,7 +282,7 @@ where .map_err(Error)? .unwrap_or_default() }; - if escrowed_balance > SignedAmount::from(wnam_cap) { + if escrowed_balance > I320::from(wnam_cap) { tracing::debug!( ?transfer, escrowed_nam = %escrowed_balance.to_string_native(), @@ -795,35 +800,35 @@ mod test_bridge_pool_vp { update_balances( write_log, Balance::new(TransferToEthereumKind::Erc20, bertha_address()), - SignedAmount::from(BERTHA_WEALTH), - SignedAmount::from(BERTHA_TOKENS), + I320::from(BERTHA_WEALTH), + I320::from(BERTHA_TOKENS), ); update_balances( write_log, Balance::new(TransferToEthereumKind::Nut, daewon_address()), - SignedAmount::from(DAEWONS_GAS), - SignedAmount::from(DAES_NUTS), + I320::from(DAEWONS_GAS), + I320::from(DAES_NUTS), ); // set up the initial balances of the bridge pool update_balances( write_log, Balance::new(TransferToEthereumKind::Erc20, BRIDGE_POOL_ADDRESS), - SignedAmount::from(ESCROWED_AMOUNT), - SignedAmount::from(ESCROWED_TOKENS), + I320::from(ESCROWED_AMOUNT), + I320::from(ESCROWED_TOKENS), ); update_balances( write_log, Balance::new(TransferToEthereumKind::Nut, BRIDGE_POOL_ADDRESS), - SignedAmount::from(ESCROWED_AMOUNT), - SignedAmount::from(ESCROWED_NUTS), + I320::from(ESCROWED_AMOUNT), + I320::from(ESCROWED_NUTS), ); // set up the initial balances of the ethereum bridge account update_balances( write_log, Balance::new(TransferToEthereumKind::Erc20, BRIDGE_ADDRESS), - SignedAmount::from(ESCROWED_AMOUNT), + I320::from(ESCROWED_AMOUNT), // we only care about escrowing NAM - SignedAmount::from(0), + I320::from(0), ); write_log.commit_tx(); } @@ -833,8 +838,8 @@ mod test_bridge_pool_vp { fn update_balances( write_log: &mut WriteLog, balance: Balance, - gas_delta: SignedAmount, - token_delta: SignedAmount, + gas_delta: I320, + token_delta: I320, ) -> BTreeSet { // wnam is drawn from the same account if balance.asset == wnam() @@ -843,9 +848,7 @@ mod test_bridge_pool_vp { // update the balance of nam let original_balance = std::cmp::max(balance.token, balance.gas); let updated_balance: Amount = - (SignedAmount::from(original_balance) - + gas_delta - + token_delta) + (I320::from(original_balance) + gas_delta + token_delta) .try_into() .unwrap(); @@ -882,13 +885,11 @@ mod test_bridge_pool_vp { let account_key = balance_key(&nam(), &balance.owner); // update the balance of nam - let new_gas_balance: Amount = (SignedAmount::from(balance.gas) - + gas_delta) - .try_into() - .unwrap(); + let new_gas_balance: Amount = + (I320::from(balance.gas) + gas_delta).try_into().unwrap(); // update the balance of tokens - let new_token_balance: Amount = (SignedAmount::from(balance.token) + let new_token_balance: Amount = (I320::from(balance.token) + token_delta) .try_into() .unwrap(); @@ -957,10 +958,10 @@ mod test_bridge_pool_vp { /// Helper function that tests various ways gas can be escrowed, /// either correctly or incorrectly, is handled appropriately fn assert_bridge_pool( - payer_gas_delta: SignedAmount, - gas_escrow_delta: SignedAmount, - payer_delta: SignedAmount, - escrow_delta: SignedAmount, + payer_gas_delta: I320, + gas_escrow_delta: I320, + payer_delta: I320, + escrow_delta: I320, insert_transfer: F, expect: Expect, ) where @@ -1047,10 +1048,10 @@ mod test_bridge_pool_vp { #[test] fn test_happy_flow() { assert_bridge_pool( - -SignedAmount::from(GAS_FEE), - SignedAmount::from(GAS_FEE), - -SignedAmount::from(TOKENS), - SignedAmount::from(TOKENS), + -I320::from(GAS_FEE), + I320::from(GAS_FEE), + -I320::from(TOKENS), + I320::from(TOKENS), |transfer, log| { log.write( &get_pending_key(transfer), @@ -1068,10 +1069,10 @@ mod test_bridge_pool_vp { #[test] fn test_incorrect_gas_withdrawn() { assert_bridge_pool( - -SignedAmount::from(10), - SignedAmount::from(GAS_FEE), - -SignedAmount::from(TOKENS), - SignedAmount::from(TOKENS), + -I320::from(10), + I320::from(GAS_FEE), + -I320::from(TOKENS), + I320::from(TOKENS), |transfer, log| { log.write( &get_pending_key(transfer), @@ -1089,10 +1090,10 @@ mod test_bridge_pool_vp { #[test] fn test_payer_balance_must_decrease() { assert_bridge_pool( - SignedAmount::from(GAS_FEE), - SignedAmount::from(GAS_FEE), - -SignedAmount::from(TOKENS), - SignedAmount::from(TOKENS), + I320::from(GAS_FEE), + I320::from(GAS_FEE), + -I320::from(TOKENS), + I320::from(TOKENS), |transfer, log| { log.write( &get_pending_key(transfer), @@ -1110,10 +1111,10 @@ mod test_bridge_pool_vp { #[test] fn test_incorrect_gas_deposited() { assert_bridge_pool( - -SignedAmount::from(GAS_FEE), - SignedAmount::from(10), - -SignedAmount::from(TOKENS), - SignedAmount::from(TOKENS), + -I320::from(GAS_FEE), + I320::from(10), + -I320::from(TOKENS), + I320::from(TOKENS), |transfer, log| { log.write( &get_pending_key(transfer), @@ -1132,10 +1133,10 @@ mod test_bridge_pool_vp { #[test] fn test_incorrect_token_deltas() { assert_bridge_pool( - -SignedAmount::from(GAS_FEE), - SignedAmount::from(GAS_FEE), - -SignedAmount::from(TOKENS), - SignedAmount::from(10), + -I320::from(GAS_FEE), + I320::from(GAS_FEE), + -I320::from(TOKENS), + I320::from(10), |transfer, log| { log.write( &get_pending_key(transfer), @@ -1153,10 +1154,10 @@ mod test_bridge_pool_vp { #[test] fn test_incorrect_tokens_escrowed() { assert_bridge_pool( - -SignedAmount::from(GAS_FEE), - SignedAmount::from(GAS_FEE), - -SignedAmount::from(10), - SignedAmount::from(10), + -I320::from(GAS_FEE), + I320::from(GAS_FEE), + -I320::from(10), + I320::from(10), |transfer, log| { log.write( &get_pending_key(transfer), @@ -1174,10 +1175,10 @@ mod test_bridge_pool_vp { #[test] fn test_escrowed_gas_must_increase() { assert_bridge_pool( - -SignedAmount::from(GAS_FEE), - -SignedAmount::from(GAS_FEE), - -SignedAmount::from(TOKENS), - SignedAmount::from(TOKENS), + -I320::from(GAS_FEE), + -I320::from(GAS_FEE), + -I320::from(TOKENS), + I320::from(TOKENS), |transfer, log| { log.write( &get_pending_key(transfer), @@ -1195,10 +1196,10 @@ mod test_bridge_pool_vp { #[test] fn test_escrowed_tokens_must_increase() { assert_bridge_pool( - -SignedAmount::from(GAS_FEE), - SignedAmount::from(GAS_FEE), - SignedAmount::from(TOKENS), - -SignedAmount::from(TOKENS), + -I320::from(GAS_FEE), + I320::from(GAS_FEE), + I320::from(TOKENS), + -I320::from(TOKENS), |transfer, log| { log.write( &get_pending_key(transfer), @@ -1216,10 +1217,10 @@ mod test_bridge_pool_vp { #[test] fn test_not_adding_transfer_rejected() { assert_bridge_pool( - -SignedAmount::from(GAS_FEE), - SignedAmount::from(GAS_FEE), - -SignedAmount::from(TOKENS), - SignedAmount::from(TOKENS), + -I320::from(GAS_FEE), + I320::from(GAS_FEE), + -I320::from(TOKENS), + I320::from(TOKENS), |transfer, _| BTreeSet::from([get_pending_key(transfer)]), Expect::Rejected, ); @@ -1230,10 +1231,10 @@ mod test_bridge_pool_vp { #[test] fn test_add_wrong_transfer() { assert_bridge_pool( - -SignedAmount::from(GAS_FEE), - SignedAmount::from(GAS_FEE), - -SignedAmount::from(TOKENS), - SignedAmount::from(TOKENS), + -I320::from(GAS_FEE), + I320::from(GAS_FEE), + -I320::from(TOKENS), + I320::from(TOKENS), |transfer, log| { let t = PendingTransfer { transfer: TransferToEthereum { @@ -1262,10 +1263,10 @@ mod test_bridge_pool_vp { #[test] fn test_add_wrong_key() { assert_bridge_pool( - -SignedAmount::from(GAS_FEE), - SignedAmount::from(GAS_FEE), - -SignedAmount::from(TOKENS), - SignedAmount::from(TOKENS), + -I320::from(GAS_FEE), + I320::from(GAS_FEE), + -I320::from(TOKENS), + I320::from(TOKENS), |transfer, log| { let t = PendingTransfer { transfer: TransferToEthereum { @@ -1294,10 +1295,10 @@ mod test_bridge_pool_vp { #[test] fn test_signed_merkle_root_changes_rejected() { assert_bridge_pool( - -SignedAmount::from(GAS_FEE), - SignedAmount::from(GAS_FEE), - -SignedAmount::from(TOKENS), - SignedAmount::from(TOKENS), + -I320::from(GAS_FEE), + I320::from(GAS_FEE), + -I320::from(TOKENS), + I320::from(TOKENS), |transfer, log| { log.write( &get_pending_key(transfer), @@ -1343,8 +1344,8 @@ mod test_bridge_pool_vp { gas: BERTHA_WEALTH.into(), token: BERTHA_TOKENS.into(), }, - -SignedAmount::from(GAS_FEE), - -SignedAmount::from(TOKENS), + -I320::from(GAS_FEE), + -I320::from(TOKENS), ); keys_changed.append(&mut new_keys_changed); @@ -1358,8 +1359,8 @@ mod test_bridge_pool_vp { gas: ESCROWED_AMOUNT.into(), token: ESCROWED_TOKENS.into(), }, - SignedAmount::from(GAS_FEE), - SignedAmount::from(TOKENS), + I320::from(GAS_FEE), + I320::from(TOKENS), ); keys_changed.append(&mut new_keys_changed); let verifiers = BTreeSet::default(); @@ -1722,8 +1723,8 @@ mod test_bridge_pool_vp { gas: DAEWONS_GAS.into(), token: DAES_NUTS.into(), }, - -SignedAmount::from(GAS_FEE), - -SignedAmount::from(TOKENS), + -I320::from(GAS_FEE), + -I320::from(TOKENS), ); keys_changed.append(&mut new_keys_changed); @@ -1737,8 +1738,8 @@ mod test_bridge_pool_vp { gas: ESCROWED_AMOUNT.into(), token: ESCROWED_NUTS.into(), }, - SignedAmount::from(GAS_FEE), - SignedAmount::from(TOKENS), + I320::from(GAS_FEE), + I320::from(TOKENS), ); keys_changed.append(&mut new_keys_changed); @@ -1785,10 +1786,10 @@ mod test_bridge_pool_vp { #[test] fn test_bridge_pool_vp_rejects_wnam_nut() { assert_bridge_pool( - -SignedAmount::from(GAS_FEE), - SignedAmount::from(GAS_FEE), - -SignedAmount::from(TOKENS), - SignedAmount::from(TOKENS), + -I320::from(GAS_FEE), + I320::from(GAS_FEE), + -I320::from(TOKENS), + I320::from(TOKENS), |transfer, log| { transfer.transfer.kind = TransferToEthereumKind::Nut; transfer.transfer.asset = wnam(); @@ -1807,10 +1808,10 @@ mod test_bridge_pool_vp { #[test] fn test_bridge_pool_vp_accepts_wnam_erc20() { assert_bridge_pool( - -SignedAmount::from(GAS_FEE), - SignedAmount::from(GAS_FEE), - -SignedAmount::from(TOKENS), - SignedAmount::from(TOKENS), + -I320::from(GAS_FEE), + I320::from(GAS_FEE), + -I320::from(TOKENS), + I320::from(TOKENS), |transfer, log| { transfer.transfer.kind = TransferToEthereumKind::Erc20; transfer.transfer.asset = wnam(); diff --git a/crates/namada/src/ledger/native_vp/masp.rs b/crates/namada/src/ledger/native_vp/masp.rs index e4b6ad3324..c4fc68e566 100644 --- a/crates/namada/src/ledger/native_vp/masp.rs +++ b/crates/namada/src/ledger/native_vp/masp.rs @@ -38,9 +38,10 @@ use token::storage_key::{ use token::Amount; use crate::ledger::native_vp; -use crate::ledger::native_vp::{Ctx, NativeVp, SignedAmount}; +use crate::ledger::native_vp::{Ctx, NativeVp}; use crate::token; use crate::token::MaspDigitPos; +use crate::uint::I320; use crate::vm::WasmCacheAccess; #[allow(missing_docs)] @@ -524,14 +525,14 @@ fn validate_transparent_bundle( // Apply the given Sapling value balance component to the accumulator fn apply_balance_component( - acc: &ValueSum, + acc: &ValueSum, val: i128, digit: MaspDigitPos, address: Address, -) -> Result> { +) -> Result> { // Put val into the correct digit position - let decoded_change = SignedAmount::from_masp_denominated(val, digit) - .map_err(|_| { + let decoded_change = + I320::from_masp_denominated(val, digit).map_err(|_| { Error::NativeVpError(native_vp::Error::SimpleMessage( "Overflow in MASP value balance", )) @@ -556,7 +557,7 @@ fn verify_sapling_balancing_value( tokens: &BTreeMap, conversion_state: &ConversionState, ) -> Result<()> { - let mut acc = ValueSum::::from_sum(post.clone()); + let mut acc = ValueSum::::from_sum(post.clone()); for (asset_type, val) in sapling_value_balance.components() { // Only assets with at most the target timestamp count match conversion_state.assets.get(asset_type) { diff --git a/crates/namada/src/ledger/native_vp/mod.rs b/crates/namada/src/ledger/native_vp/mod.rs index ed286772a4..37f55122e7 100644 --- a/crates/namada/src/ledger/native_vp/mod.rs +++ b/crates/namada/src/ledger/native_vp/mod.rs @@ -11,20 +11,15 @@ pub mod parameters; use std::cell::RefCell; use std::collections::BTreeSet; use std::fmt::Debug; -use std::ops::{Add, Neg, Not, Sub}; -use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; -use namada_core::arith::{CheckedAdd, CheckedNeg, CheckedSub, OverflowingAdd}; +use borsh::BorshDeserialize; use namada_core::storage; use namada_core::storage::Epochs; -use namada_core::uint::Uint; use namada_events::{Event, EventType}; use namada_gas::GasMetering; -use namada_token::{Amount, MaspDigitPos}; use namada_tx::Tx; pub use namada_vp_env::VpEnv; use state::StateRead; -use uint::construct_uint; use super::vp_host_fns; use crate::address::Address; @@ -647,208 +642,3 @@ pub(super) mod testing { } } } - -construct_uint! { - #[derive( - BorshSerialize, - BorshDeserialize, - BorshSchema, - )] - - struct SignedAmountInt(5); -} - -/// A positive or negative amount -#[derive( - Copy, - Clone, - Eq, - PartialEq, - BorshSerialize, - BorshDeserialize, - BorshSchema, - Default, -)] -pub struct SignedAmount(SignedAmountInt); - -impl OverflowingAdd for SignedAmount -where - T: Into, -{ - type Output = Self; - - fn overflowing_add(self, other: T) -> (Self, bool) { - let (res, overflow) = self.0.overflowing_add(other.into().0); - (Self(res), overflow) - } -} - -impl<'a, T> Add for &'a SignedAmount -where - T: Into, -{ - type Output = SignedAmount; - - fn add(self, other: T) -> SignedAmount { - SignedAmount(self.0.overflowing_add(other.into().0).0) - } -} - -impl Sub for SignedAmount -where - T: Into, -{ - type Output = Self; - - fn sub(self, other: T) -> Self { - Self(self.0.overflowing_sub(other.into().0).0) - } -} - -impl<'a, T> Sub for &'a SignedAmount -where - T: Into, -{ - type Output = SignedAmount; - - fn sub(self, other: T) -> SignedAmount { - SignedAmount(self.0.overflowing_sub(other.into().0).0) - } -} - -impl From for SignedAmount { - fn from(lo: Uint) -> Self { - let mut arr = [0u64; Self::N_WORDS]; - arr[..4].copy_from_slice(&lo.0); - Self(SignedAmountInt(arr)) - } -} - -impl From for SignedAmount { - fn from(lo: Amount) -> Self { - let mut arr = [0u64; Self::N_WORDS]; - arr[..4].copy_from_slice(&lo.raw_amount().0); - Self(SignedAmountInt(arr)) - } -} - -impl TryInto for SignedAmount { - type Error = std::io::Error; - - fn try_into(self) -> Result { - if self.0.0[Self::N_WORDS - 1] == 0 { - Ok(Amount::from_uint( - Uint([self.0.0[0], self.0.0[1], self.0.0[2], self.0.0[3]]), - 0, - ) - .unwrap()) - } else { - Err(std::io::Error::new( - std::io::ErrorKind::InvalidInput, - "Integer overflow when casting to Amount", - )) - } - } -} - -impl SignedAmount { - const N_WORDS: usize = 5; - - fn one() -> Self { - Self(SignedAmountInt::one()) - } - - fn is_negative(&self) -> bool { - self.0.bit(Self::N_WORDS * SignedAmountInt::WORD_BITS - 1) - } - - fn is_positive(&self) -> bool { - !self.is_negative() && !self.0.is_zero() - } - - fn abs(&self) -> Self { - if self.is_negative() { -*self } else { *self } - } - - fn to_string_native(self) -> String { - let mut res = self.abs().0.to_string(); - if self.is_negative() { - res.insert(0, '-'); - } - res - } - - /// Given a u128 and [`MaspDigitPos`], construct the corresponding - /// amount. - pub fn from_masp_denominated( - val: i128, - denom: MaspDigitPos, - ) -> Result>::Error> { - let abs = val.unsigned_abs(); - let lo = abs as u64; - let hi = (abs >> 64) as u64; - let lo_pos = denom as usize; - let hi_pos = lo_pos + 1; - let mut raw = [0u64; Self::N_WORDS]; - raw[lo_pos] = lo; - raw[hi_pos] = hi; - i64::try_from(raw[Self::N_WORDS - 1]).map(|_| { - let res = Self(SignedAmountInt(raw)); - if val.is_negative() { -res } else { res } - }) - } -} - -impl Not for SignedAmount { - type Output = Self; - - fn not(self) -> Self { - Self(!self.0) - } -} - -impl Neg for SignedAmount { - type Output = Self; - - fn neg(self) -> Self { - (!self).overflowing_add(Self::one()).0 - } -} - -impl CheckedNeg for SignedAmount { - type Output = SignedAmount; - - fn checked_neg(self) -> Option { - let neg = -self; - (neg != self).then_some(neg) - } -} - -impl CheckedAdd for SignedAmount { - type Output = SignedAmount; - - fn checked_add(self, rhs: Self) -> Option { - let res = self.overflowing_add(rhs).0; - ((self.is_negative() != rhs.is_negative()) - || (self.is_negative() == res.is_negative())) - .then_some(res) - } -} - -impl CheckedSub for SignedAmount { - type Output = SignedAmount; - - fn checked_sub(self, rhs: Self) -> Option { - let res = self.overflowing_add(-rhs).0; - ((self.is_negative() == rhs.is_negative()) - || (res.is_negative() == self.is_negative())) - .then_some(res) - } -} - -impl PartialOrd for SignedAmount { - fn partial_cmp(&self, other: &Self) -> Option { - (!self.is_negative(), self.0 << 1) - .partial_cmp(&(!other.is_negative(), other.0 << 1)) - } -}