Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
51317ac
Removed the normed MASP amounts due to them causing unit errors.
murisi Oct 27, 2024
598e420
Increase usage of Amount relative to DenominatedAmount.
murisi Oct 28, 2024
8584591
Fixed the logic for computing change in MASP transactions.
murisi Oct 28, 2024
a5de4c1
Test unshielding the rewards and the principal.
murisi Oct 28, 2024
d4781f3
Combine fee payment with construction of transaction inputs and outputs.
murisi Oct 28, 2024
609f477
Tweak integration test balance values
sug0 Nov 2, 2024
e35c0df
Run make fmt
sug0 Oct 29, 2024
01643f4
Appease clippy
sug0 Oct 29, 2024
ebcd92f
Add new fns to update the masp token map
sug0 Oct 29, 2024
c72a124
Test enabling masp rewards after shielding some token
sug0 Oct 29, 2024
1234db0
Add method to iterate over words in amount
sug0 Oct 29, 2024
bd15ac0
Convert from usize to masp digit pos
sug0 Oct 29, 2024
9109ab5
Account for masp digits in masp change
sug0 Oct 29, 2024
0195f2b
Touch up comments in apply conversions
sug0 Oct 30, 2024
92d2303
Bail on null conversion threshold
sug0 Oct 30, 2024
4d766ca
Test masp values spanning multiple words
sug0 Oct 30, 2024
404e88c
Fix change conversion from i128
sug0 Oct 30, 2024
d3a516c
Switch from I256 to i128 to compute change
sug0 Oct 30, 2024
9adf0c8
Adjust reward amounts in int test
sug0 Oct 31, 2024
0318d63
Invert negative check on `is_amount_required`
sug0 Oct 31, 2024
88fb503
Express converted delta as a fold
sug0 Oct 31, 2024
6aa6cf5
Refactor for loop in `is_amount_required`
sug0 Oct 31, 2024
4fad7e3
Comment each relevant argument in `is_amount_required`
sug0 Oct 31, 2024
8da315e
Fix up docstrings
sug0 Oct 31, 2024
66579c0
Add balance checks to dynamic assets masp test
sug0 Oct 31, 2024
a8f58b8
Improve denom query err msg
sug0 Oct 31, 2024
e8995ed
Test initializing the token map with null rewards
sug0 Nov 2, 2024
df29273
Make `enable_rewards_after_shielding` more robust
sug0 Nov 2, 2024
9171617
Test rewards in `values_spanning_multiple_masp_digits`
sug0 Nov 2, 2024
6cd9e94
Changelog for #3959
sug0 Oct 31, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changelog/unreleased/bug-fixes/3959-fix-masp-change.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
- Fix a variety of MASP rewards related client bugs. Moreover, add
regression tests that cover the expected behavior. The client logic
that made MASP fee payments possible was also dramatically simplified,
making this code easier to maintain in the foreseeable future.
([\#3959](https://github.com/anoma/namada/pull/3959))
48 changes: 15 additions & 33 deletions crates/apps_lib/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4661,16 +4661,13 @@ pub mod args {
});
}

let gas_spending_keys = self
.gas_spending_keys
.iter()
.map(|key| chain_ctx.get_cached(key))
.collect();
let gas_spending_key =
self.gas_spending_key.map(|key| chain_ctx.get_cached(&key));

Ok(TxShieldedTransfer::<SdkTypes> {
tx,
data,
gas_spending_keys,
gas_spending_key,
disposable_signing_key: self.disposable_signing_key,
tx_code_path: self.tx_code_path.to_path_buf(),
})
Expand All @@ -4691,16 +4688,13 @@ pub mod args {
token,
amount,
}];
let mut gas_spending_keys = vec![];
if let Some(key) = GAS_SPENDING_KEY.parse(matches) {
gas_spending_keys.push(key);
}
let gas_spending_key = GAS_SPENDING_KEY.parse(matches);
let disposable_gas_payer = DISPOSABLE_SIGNING_KEY.parse(matches);

Self {
tx,
data,
gas_spending_keys,
gas_spending_key,
disposable_signing_key: disposable_gas_payer,
tx_code_path,
}
Expand Down Expand Up @@ -4830,16 +4824,13 @@ pub mod args {
amount: transfer_data.amount,
});
}
let gas_spending_keys = self
.gas_spending_keys
.iter()
.map(|key| chain_ctx.get_cached(key))
.collect();
let gas_spending_key =
self.gas_spending_key.map(|key| chain_ctx.get_cached(&key));

Ok(TxUnshieldingTransfer::<SdkTypes> {
tx,
data,
gas_spending_keys,
gas_spending_key,
disposable_signing_key: self.disposable_signing_key,
source: chain_ctx.get_cached(&self.source),
tx_code_path: self.tx_code_path.to_path_buf(),
Expand All @@ -4860,17 +4851,14 @@ pub mod args {
token,
amount,
}];
let mut gas_spending_keys = vec![];
if let Some(key) = GAS_SPENDING_KEY.parse(matches) {
gas_spending_keys.push(key);
}
let gas_spending_key = GAS_SPENDING_KEY.parse(matches);
let disposable_signing_key = DISPOSABLE_SIGNING_KEY.parse(matches);

Self {
tx,
source,
data,
gas_spending_keys,
gas_spending_key,
disposable_signing_key,
tx_code_path,
}
Expand Down Expand Up @@ -4919,11 +4907,8 @@ pub mod args {
) -> Result<TxIbcTransfer<SdkTypes>, Self::Error> {
let tx = self.tx.to_sdk(ctx)?;
let chain_ctx = ctx.borrow_mut_chain_or_exit();
let gas_spending_keys = self
.gas_spending_keys
.iter()
.map(|key| chain_ctx.get_cached(key))
.collect();
let gas_spending_key =
self.gas_spending_key.map(|key| chain_ctx.get_cached(&key));

Ok(TxIbcTransfer::<SdkTypes> {
tx,
Expand All @@ -4938,7 +4923,7 @@ pub mod args {
refund_target: chain_ctx.get_opt(&self.refund_target),
ibc_shielding_data: self.ibc_shielding_data,
ibc_memo: self.ibc_memo,
gas_spending_keys,
gas_spending_key,
disposable_signing_key: self.disposable_signing_key,
tx_code_path: self.tx_code_path.to_path_buf(),
})
Expand All @@ -4965,10 +4950,7 @@ pub mod args {
.expect("Failed to decode IBC shielding data")
});
let ibc_memo = IBC_MEMO.parse(matches);
let mut gas_spending_keys = vec![];
if let Some(key) = GAS_SPENDING_KEY.parse(matches) {
gas_spending_keys.push(key);
}
let gas_spending_key = GAS_SPENDING_KEY.parse(matches);
let disposable_signing_key = DISPOSABLE_SIGNING_KEY.parse(matches);
let tx_code_path = PathBuf::from(TX_IBC_WASM);
Self {
Expand All @@ -4984,7 +4966,7 @@ pub mod args {
refund_target,
ibc_shielding_data,
ibc_memo,
gas_spending_keys,
gas_spending_key,
disposable_signing_key,
tx_code_path,
}
Expand Down
12 changes: 4 additions & 8 deletions crates/core/src/masp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,11 +195,9 @@ impl AssetData {

/// Give this pre-asset type the given epoch if already has an epoch. Return
/// the replaced value.
pub fn redate(&mut self, to: MaspEpoch) -> Option<MaspEpoch> {
pub fn redate(&mut self, to: MaspEpoch) {
if self.epoch.is_some() {
self.epoch.replace(to)
} else {
None
self.epoch = Some(to);
}
}

Expand Down Expand Up @@ -1121,14 +1119,12 @@ mod test {
assert!(data.epoch.is_none());

let epoch_0 = MaspEpoch::new(3);
let old = data.redate(epoch_0);
assert!(old.is_none());
data.redate(epoch_0);
assert!(data.epoch.is_none());
data.epoch = Some(epoch_0);

let epoch_1 = MaspEpoch::new(5);
let old = data.redate(epoch_1);
assert_eq!(old, Some(epoch_0));
data.redate(epoch_1);
assert_eq!(data.epoch, Some(epoch_1));
}

Expand Down
30 changes: 21 additions & 9 deletions crates/core/src/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ pub const NATIVE_SCALE: u64 = 1_000_000;
pub type Change = I256;

impl Amount {
/// Iterate over all words in this [`Amount`].
pub fn iter_words(self) -> impl Iterator<Item = u64> {
self.raw.0.into_iter()
}

/// Convert a [`u64`] to an [`Amount`].
pub const fn from_u64(x: u64) -> Self {
Self {
Expand Down Expand Up @@ -905,23 +910,30 @@ pub enum MaspDigitPos {
Three,
}

impl From<u8> for MaspDigitPos {
fn from(denom: u8) -> Self {
impl TryFrom<u8> for MaspDigitPos {
type Error = &'static str;

fn try_from(denom: u8) -> Result<Self, Self::Error> {
match denom {
0 => Self::Zero,
1 => Self::One,
2 => Self::Two,
3 => Self::Three,
_ => panic!("Possible MASP denominations must be between 0 and 3"),
0 => Ok(Self::Zero),
1 => Ok(Self::One),
2 => Ok(Self::Two),
3 => Ok(Self::Three),
_ => Err("Possible MASP denominations must be between 0 and 3"),
}
}
}

impl MaspDigitPos {
/// Iterator over the possible denominations
pub fn iter() -> impl Iterator<Item = MaspDigitPos> {
// 0, 1, 2, 3
(0u8..4).map(Self::from)
[
MaspDigitPos::Zero,
MaspDigitPos::One,
MaspDigitPos::Two,
MaspDigitPos::Three,
]
.into_iter()
}

/// Get the corresponding u64 word from the input uint256.
Expand Down
64 changes: 40 additions & 24 deletions crates/core/src/uint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,8 @@ impl FromStr for I256 {
}

impl I256 {
const N_WORDS: usize = 4;

/// Compute the two's complement of a number.
pub fn negate(&self) -> Option<Self> {
let (uint, overflow) = self.0.negate();
Expand Down Expand Up @@ -609,38 +611,35 @@ impl I256 {
Self(self.0.canonical())
}

/// the maximum I256 value
/// The maximum value of an [`I256`].
pub fn maximum() -> Self {
Self(MAX_SIGNED_VALUE)
}

/// Attempt to convert a MASP-denominated integer to an I256
/// using the given denomination.
/// Given an i128 and [`MaspDigitPos`], construct the corresponding
/// amount.
pub fn from_masp_denominated(
value: impl Into<i128>,
val: i128,
denom: MaspDigitPos,
) -> Result<Self, AmountParseError> {
let value = value.into();
let is_negative = value < 0;
let value = value.unsigned_abs();
let mut result = [0u64; 4];
result[denom as usize] = u64::try_from(value)
.map_err(|_e| AmountParseError::PrecisionOverflow)?;
let result = Uint(result);
if result <= MAX_SIGNED_VALUE {
if is_negative {
let (inner, overflow) = result.negate();
if overflow {
Err(AmountParseError::InvalidRange)
} else {
Ok(Self(inner))
}
) -> Result<Self, <i64 as TryFrom<u64>>::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(Uint(raw));
if val.is_negative() {
res.checked_neg().unwrap()
} else {
Ok(Self(result).canonical())
res
}
} else {
Err(AmountParseError::InvalidRange)
}
})
}

/// Multiply by a decimal [`Dec`] with the result rounded up. Checks for
Expand Down Expand Up @@ -777,6 +776,23 @@ impl CheckedAdd for I256 {
}
}

// NOTE: This is here only because MASP requires it for `ValueSum` subtraction
impl CheckedSub for &I256 {
type Output = I256;

fn checked_sub(self, rhs: Self) -> Option<Self::Output> {
self.checked_sub(*rhs)
}
}

impl CheckedSub for I256 {
type Output = I256;

fn checked_sub(self, rhs: Self) -> Option<Self::Output> {
I256::checked_sub(&self, rhs)
}
}

// NOTE: This is here only because num_traits::CheckedAdd requires it
impl std::ops::Add for I256 {
type Output = Self;
Expand Down
13 changes: 5 additions & 8 deletions crates/sdk/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,7 @@ pub struct TxShieldedTransfer<C: NamadaTypes = SdkTypes> {
/// Transfer-specific data
pub data: Vec<TxShieldedTransferData<C>>,
/// Optional additional keys for gas payment
pub gas_spending_keys: Vec<C::SpendingKey>,
pub gas_spending_key: Option<C::SpendingKey>,
/// Generate an ephemeral signing key to be used only once to sign the
/// wrapper tx
pub disposable_signing_key: bool,
Expand Down Expand Up @@ -453,7 +453,7 @@ pub struct TxUnshieldingTransfer<C: NamadaTypes = SdkTypes> {
/// Transfer-specific data
pub data: Vec<TxUnshieldingTransferData<C>>,
/// Optional additional keys for gas payment
pub gas_spending_keys: Vec<C::SpendingKey>,
pub gas_spending_key: Option<C::SpendingKey>,
/// Generate an ephemeral signing key to be used only once to sign the
/// wrapper tx
pub disposable_signing_key: bool,
Expand Down Expand Up @@ -499,7 +499,7 @@ pub struct TxIbcTransfer<C: NamadaTypes = SdkTypes> {
/// Memo for IBC transfer packet
pub ibc_memo: Option<String>,
/// Optional additional keys for gas payment
pub gas_spending_keys: Vec<C::SpendingKey>,
pub gas_spending_key: Option<C::SpendingKey>,
/// Generate an ephemeral signing key to be used only once to sign the
/// wrapper tx
pub disposable_signing_key: bool,
Expand Down Expand Up @@ -591,12 +591,9 @@ impl<C: NamadaTypes> TxIbcTransfer<C> {
}

/// Gas spending keys
pub fn gas_spending_keys(
self,
gas_spending_keys: Vec<C::SpendingKey>,
) -> Self {
pub fn gas_spending_keys(self, gas_spending_key: C::SpendingKey) -> Self {
Self {
gas_spending_keys,
gas_spending_key: Some(gas_spending_key),
..self
}
}
Expand Down
10 changes: 5 additions & 5 deletions crates/sdk/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,12 +173,12 @@ pub trait Namada: NamadaIo {
fn new_shielded_transfer(
&self,
data: Vec<args::TxShieldedTransferData>,
gas_spending_keys: Vec<ExtendedSpendingKey>,
gas_spending_key: Option<ExtendedSpendingKey>,
disposable_signing_key: bool,
) -> args::TxShieldedTransfer {
args::TxShieldedTransfer {
data,
gas_spending_keys,
gas_spending_key,
tx_code_path: PathBuf::from(TX_TRANSFER_WASM),
disposable_signing_key,
tx: self.tx_builder(),
Expand Down Expand Up @@ -206,13 +206,13 @@ pub trait Namada: NamadaIo {
&self,
source: ExtendedSpendingKey,
data: Vec<args::TxUnshieldingTransferData>,
gas_spending_keys: Vec<ExtendedSpendingKey>,
gas_spending_key: Option<ExtendedSpendingKey>,
disposable_signing_key: bool,
) -> args::TxUnshieldingTransfer {
args::TxUnshieldingTransfer {
source,
data,
gas_spending_keys,
gas_spending_key,
disposable_signing_key,
tx_code_path: PathBuf::from(TX_TRANSFER_WASM),
tx: self.tx_builder(),
Expand Down Expand Up @@ -318,7 +318,7 @@ pub trait Namada: NamadaIo {
refund_target: None,
ibc_shielding_data: None,
ibc_memo: None,
gas_spending_keys: Default::default(),
gas_spending_key: Default::default(),
tx: self.tx_builder(),
tx_code_path: PathBuf::from(TX_IBC_WASM),
}
Expand Down
Loading