diff --git a/.changelog/unreleased/testing/4193-test-inverse-conversions.md b/.changelog/unreleased/testing/4193-test-inverse-conversions.md new file mode 100644 index 0000000000..87f31b3b29 --- /dev/null +++ b/.changelog/unreleased/testing/4193-test-inverse-conversions.md @@ -0,0 +1,2 @@ +- Added tests for MASP inverse conversions. + ([\#4193](https://github.com/anoma/namada/pull/4193)) \ No newline at end of file diff --git a/crates/shielded_token/src/masp/shielded_wallet.rs b/crates/shielded_token/src/masp/shielded_wallet.rs index de8620b91b..65ce440d85 100644 --- a/crates/shielded_token/src/masp/shielded_wallet.rs +++ b/crates/shielded_token/src/masp/shielded_wallet.rs @@ -1989,6 +1989,131 @@ mod test_shielded_wallet { assert!(wallet.compute_shielded_balance(&vk).await.is_err()) } + // Test that `compute_exchanged_amount` can perform inverse conversions to + // match the desired epoch + #[tokio::test] + async fn test_inverse_conversions() { + let (channel, mut context) = MockNamadaIo::new(); + // the response to the current masp epoch query + channel + .send(MaspEpoch::new(5).serialize_to_vec()) + .expect("Test failed"); + let temp_dir = tempdir().unwrap(); + let mut wallet = TestingContext::new(FsShieldedUtils::new( + temp_dir.path().to_path_buf(), + )); + let native_token = + TestingContext::::query_native_token( + context.client(), + ) + .await + .expect("Test failed"); + let native_token_denom = + TestingContext::::query_denom( + context.client(), + &native_token, + ) + .await + .expect("Test failed"); + + for epoch in 0..=5 { + wallet.add_asset_type(AssetData { + token: native_token.clone(), + denom: native_token_denom, + position: MaspDigitPos::Zero, + epoch: Some(MaspEpoch::new(epoch)), + }); + } + + for epoch in 0..5 { + let mut conv = I128Sum::from_pair( + AssetData { + token: native_token.clone(), + denom: native_token_denom, + position: MaspDigitPos::Zero, + epoch: Some(MaspEpoch::new(epoch)), + } + .encode() + .unwrap(), + -1, + ); + conv += I128Sum::from_pair( + AssetData { + token: native_token.clone(), + denom: native_token_denom, + position: MaspDigitPos::Zero, + epoch: Some(MaspEpoch::new(epoch + 1)), + } + .encode() + .unwrap(), + 2, + ); + context.add_conversions( + AssetData { + token: native_token.clone(), + denom: native_token_denom, + position: MaspDigitPos::Zero, + epoch: Some(MaspEpoch::new(epoch)), + }, + ( + native_token.clone(), + native_token_denom, + MaspDigitPos::Zero, + MaspEpoch::new(epoch), + conv, + MerklePath::from_path(vec![], 0), + ), + ); + } + + let vk = arbitrary_vk(); + let pa = arbitrary_pa(); + // Shield at epoch 5 + let asset_data = AssetData { + token: native_token.clone(), + denom: native_token_denom, + position: MaspDigitPos::Zero, + epoch: Some(MaspEpoch::new(5)), + }; + wallet.add_asset_type(asset_data.clone()); + wallet.add_note(create_note(asset_data.clone(), 10, pa), vk); + let balance = wallet + .compute_shielded_balance(&vk) + .await + .unwrap() + .unwrap_or_else(ValueSum::zero); + // TODO: would be nice to check even earlier epochs but for that we need + // to construct conversions properly, just like the protocol does, i.e. + // with conversions for non-consecutive epochs + + // Query the balance with conversions at epoch 4 + let amount = wallet + .compute_exchanged_amount( + context.client(), + context.io(), + balance, + MaspEpoch::new(4), + Conversions::new(), + ) + .await + .unwrap() + .0; + assert_eq!( + amount, + I128Sum::from_pair( + AssetData { + token: native_token.clone(), + denom: native_token_denom, + position: MaspDigitPos::Zero, + epoch: Some(MaspEpoch::new(4)), + } + .encode() + .unwrap(), + 5 + ) + ); + } + #[tokio::test] // Test that the estimated rewards are 0 when no conversions are available async fn test_estimate_rewards_no_conversions() { @@ -2103,7 +2228,7 @@ mod test_shielded_wallet { let pa = arbitrary_pa(); let asset_data = AssetData { token: native_token.clone(), - denom: 0.into(), + denom: native_token_denom, position: MaspDigitPos::Zero, epoch: Some(MaspEpoch::new(2)), }; diff --git a/crates/tests/src/integration/masp.rs b/crates/tests/src/integration/masp.rs index f8f79d8e47..4e22e063b5 100644 --- a/crates/tests/src/integration/masp.rs +++ b/crates/tests/src/integration/masp.rs @@ -1688,6 +1688,28 @@ fn masp_incentives() -> Result<()> { assert!(captured.result.is_ok()); assert!(captured.contains("nam: 0.18887")); + // Assert the rewards estimate are 0 since we haven't shielded any more + // tokens + let captured = CapturedOutput::of(|| { + run( + &node, + Bin::Client, + vec![ + "estimate-shielding-rewards", + "--key", + AA_VIEWING_KEY, + "--node", + validator_one_rpc, + ], + ) + }); + assert!(captured.result.is_ok()); + assert!( + captured.contains( + "Estimated native token rewards for the next MASP epoch: 0" + ) + ); + // Assert NAM balance at MASP pool is exclusively the // rewards from the shielded BTC let captured = CapturedOutput::of(|| {