From 81c9d75467394fe91dbba46cf22ebd120dca432a Mon Sep 17 00:00:00 2001 From: satan Date: Mon, 4 Nov 2024 12:41:59 +0100 Subject: [PATCH 1/9] Added estimations function and tests --- Cargo.lock | 1 + crates/core/src/masp.rs | 14 + crates/shielded_token/Cargo.toml | 2 + .../src/masp/shielded_wallet.rs | 631 ++++++++++++++++++ crates/shielded_token/src/masp/test_utils.rs | 269 +++++++- wasm/Cargo.lock | 1 + wasm_for_tests/Cargo.lock | 480 ++++++++++++- 7 files changed, 1392 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4e44acc96f..512058e838 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5401,6 +5401,7 @@ dependencies = [ "sha2 0.9.9", "smooth-operator", "tempfile", + "tendermint-rpc", "test-log", "thiserror", "tokio", diff --git a/crates/core/src/masp.rs b/crates/core/src/masp.rs index 4d727b3913..ea6d915e4d 100644 --- a/crates/core/src/masp.rs +++ b/crates/core/src/masp.rs @@ -144,6 +144,11 @@ impl MaspEpoch { Some(Self(self.0.checked_sub(1)?)) } + /// Change to the next masp epoch. + pub fn next(&self) -> Option { + Some(Self(self.0.checked_add(1)?)) + } + /// Initialize a new masp epoch from the provided one #[cfg(any(test, feature = "testing"))] pub const fn new(epoch: u64) -> Self { @@ -201,6 +206,15 @@ impl AssetData { } } + /// Update the MaspEpoch to the next one + pub fn redate_to_next_epoch(&mut self) { + if let Some(ep) = self.epoch.as_mut() { + if let Some(next) = ep.next() { + *ep = next; + } + } + } + /// Remove the epoch associated with this pre-asset type pub fn undate(&mut self) { self.epoch = None; diff --git a/crates/shielded_token/Cargo.toml b/crates/shielded_token/Cargo.toml index 126c543462..460fdb0961 100644 --- a/crates/shielded_token/Cargo.toml +++ b/crates/shielded_token/Cargo.toml @@ -22,6 +22,7 @@ multicore = ["dep:rayon"] testing = [ "multicore", "namada_core/testing", + "namada_tx/testing", "masp_primitives/test-dependencies", "proptest", "std" @@ -70,6 +71,7 @@ serde_json.workspace = true sha2.workspace = true smooth-operator.workspace = true tempfile.workspace = true +tendermint-rpc.workspace = true thiserror.workspace = true tracing.workspace = true typed-builder.workspace = true diff --git a/crates/shielded_token/src/masp/shielded_wallet.rs b/crates/shielded_token/src/masp/shielded_wallet.rs index 76181b7d23..23f8950762 100644 --- a/crates/shielded_token/src/masp/shielded_wallet.rs +++ b/crates/shielded_token/src/masp/shielded_wallet.rs @@ -698,6 +698,95 @@ pub trait ShieldedApi: } } + /// We estimate the total rewards accumulated by the assets owned by + /// the provided viewing key. This is done by assuming the same rewards + /// rate on each asset as in the latest masp epoch. + #[allow(async_fn_in_trait)] + async fn estimate_next_epoch_rewards( + &mut self, + context: &impl NamadaIo, + vk: &ViewingKey, + ) -> Result { + let native_token = Self::query_native_token(context.client()).await?; + let current_epoch = Self::query_masp_epoch(context.client()).await?; + let target_epoch = current_epoch + .next() + .ok_or_else(|| eyre!("The final MASP epoch is already afoot."))?; + // get the raw balance of the notes associated with this key + if let Some(balance) = self.compute_shielded_balance(vk).await? { + // convert amount and get used conversions + let mut conversions = self + .compute_exchanged_amount( + context.client(), + context.io(), + balance.clone(), + target_epoch, + Conversions::new(), + ) + .await? + .2; + + // re-date the all the latest conversions up one epoch + let mut estimated_conversions = Conversions::new(); + for (asset_type, (conv, wit, _)) in &conversions { + let mut asset = match self + .decode_asset_type(context.client(), *asset_type) + .await + { + Some( + data @ AssetData { + epoch: Some(ep), .. + }, + ) if ep.next() == Some(current_epoch) => data, + _ => continue, + }; + asset.redate_to_next_epoch(); + let decoded_conv = self + .decode_sum(context.client(), conv.clone().into()) + .await; + let mut est_conv = I128Sum::zero(); + for ((_, asset_data), val) in decoded_conv.components() { + let mut new_asset = asset_data.clone(); + if new_asset.epoch != Some(MaspEpoch::zero()) { + new_asset.redate_to_next_epoch(); + } + est_conv += ValueSum::from_pair(new_asset.encode()?, *val) + } + estimated_conversions.insert( + asset.encode().unwrap(), + (AllowedConversion::from(est_conv), wit.clone(), 0), + ); + } + conversions.extend(estimated_conversions); + // use the estimations to convert the amount + let exchanged_amount = self + .compute_exchanged_amount( + context.client(), + context.io(), + balance.clone(), + target_epoch, + conversions, + ) + .await? + .0; + + let rewards = exchanged_amount - balance; + // sum up the rewards. + Ok(self + .decode_sum(context.client(), rewards) + .await + .components() + .filter(|((_, data), _)| { + // this should always be true, but we check it anyway + data.token == native_token + }) + .map(|(_, val)| *val) + .sum::()) + } else { + Ok(0) + } + } + /// Collect enough unspent notes in this context to exceed the given amount /// of the specified asset type. Return the total value accumulated plus /// notes and the corresponding diversifiers/merkle paths that were used to @@ -1624,3 +1713,545 @@ impl> ShieldedApi for T { } + +#[cfg(test)] +mod test_shielded_wallet { + use namada_core::address::InternalAddress; + use namada_core::borsh::BorshSerializeExt; + use namada_core::masp::AssetData; + use namada_core::token::MaspDigitPos; + use namada_io::NamadaIo; + use proptest::proptest; + use tempfile::tempdir; + + use super::*; + use crate::masp::fs::FsShieldedUtils; + use crate::masp::test_utils::{ + arbitrary_pa, arbitrary_vk, create_note, MockNamadaIo, TestingContext, + }; + + #[tokio::test] + async fn test_compute_shielded_balance() { + let (_client_channel, context) = MockNamadaIo::new(); + 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 vk = arbitrary_vk(); + let pa = arbitrary_pa(); + let mut asset_data = AssetData { + token: native_token.clone(), + denom: 0.into(), + position: MaspDigitPos::Zero, + epoch: None, + }; + // check that if no notes are found, None is returned + let balance = wallet + .compute_shielded_balance(&vk) + .await + .expect("Test failed"); + assert!(balance.is_none()); + + // check that the correct balance if found for a single note + wallet.add_note(create_note(asset_data.clone(), 10, pa), vk); + let balance = wallet + .compute_shielded_balance(&vk) + .await + .expect("Test failed") + .expect("Test failed"); + let expected = + I128Sum::from_nonnegative(asset_data.encode().unwrap(), 10) + .expect("Test failed"); + assert_eq!(balance, expected); + + // check that multiple notes of the same asset are added together + let new_note = create_note(asset_data.clone(), 11, pa); + wallet.add_note(new_note, vk); + + let balance = wallet + .compute_shielded_balance(&vk) + .await + .expect("Test failed") + .expect("Test failed"); + let expected = + I128Sum::from_nonnegative(asset_data.encode().unwrap(), 21) + .expect("Test failed"); + assert_eq!(balance, expected); + + // check that spending a note works correctly + wallet.spend_note(&new_note); + + let balance = wallet + .compute_shielded_balance(&vk) + .await + .expect("Test failed") + .expect("Test failed"); + let expected = + I128Sum::from_nonnegative(asset_data.encode().unwrap(), 10) + .expect("Test failed"); + + assert_eq!(balance, expected); + + // check that the balance does not add together non-fungible asset types + asset_data.epoch = Some(MaspEpoch::new(1)); + wallet.add_note(create_note(asset_data.clone(), 7, pa), vk); + let balance = wallet + .compute_shielded_balance(&vk) + .await + .expect("Test failed") + .expect("Test failed"); + let expected = expected + + I128Sum::from_nonnegative(asset_data.encode().unwrap(), 7) + .expect("Test failed"); + + assert_eq!(balance, expected); + assert_eq!(balance.components().count(), 2); + + // check that a missing index causes an error + wallet.note_map.clear(); + assert!(wallet.compute_shielded_balance(&vk).await.is_err()) + } + + #[tokio::test] + async fn test_estimate_rewards_no_conversions() { + let (channel, context) = MockNamadaIo::new(); + // the response to the current masp epoch query + channel + .send(MaspEpoch::new(1).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 vk = arbitrary_vk(); + let pa = arbitrary_pa(); + let asset_data = AssetData { + token: native_token.clone(), + denom: 0.into(), + position: MaspDigitPos::Zero, + epoch: Some(MaspEpoch::new(1)), + }; + wallet.add_asset_type(asset_data.clone()); + wallet.add_note(create_note(asset_data.clone(), 10, pa), vk); + let rewards_est = wallet + .estimate_next_epoch_rewards(&context, &vk) + .await + .expect("Test failed"); + assert_eq!(rewards_est, 0); + } + + proptest! { + /// In this test, we have a single incentivized token + /// shielded at MaspEpoch(1) owned by the shielded wallet. + /// The amount of owned token is the parameter `principal`. + /// + /// We add a conversion from MaspEpoch(1) to MaspEpoch(2) + /// which issues `reward_rate` nam tokens for each of our + /// incentivized token. + /// + /// We test that estimating the rewards for MaspEpoch(3) + /// applies the same conversions as the last epoch, yielding + /// a total reward estimation of 2 * principal * reward_rate. + /// + /// Furthermore, we own `rewardless` amount of a token that + /// is not incentivized and thus should not contribute to + /// rewards. + #[test] + fn test_estimate_rewards_with_conversions( + // fairly arbitrary upper bounds, but they are large + // and guaranteed that 2 * reward_rate * principal + // does not exceed 64 bits + principal in 1u64 .. 100_000, + reward_rate in 1i128 .. 1_000, + rewardless in 1u64 .. 100_000, + ) { + // #[tokio::test] doesn't work with the proptest! macro + tokio::runtime::Runtime::new().unwrap().block_on(async { + + let (channel, mut context) = MockNamadaIo::new(); + // the response to the current masp epoch query + channel.send(MaspEpoch::new(2).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"); + + // we use a random addresses as our token + let incentivized_token = Address::Internal(InternalAddress::Pgf); + let unincentivized = Address::Internal(InternalAddress::ReplayProtection); + + // add asset type decodings + wallet.add_asset_type(AssetData { + token: native_token.clone(), + denom: 0.into(), + position: MaspDigitPos::Zero, + epoch: Some(MaspEpoch::new(0)), + }); + wallet.add_asset_type(AssetData { + token: unincentivized.clone(), + denom: 0.into(), + position: MaspDigitPos::Zero, + epoch: None, + }); + + for epoch in 0..4 { + wallet.add_asset_type(AssetData { + token: incentivized_token.clone(), + denom: 0.into(), + position: MaspDigitPos::Zero, + epoch: Some(MaspEpoch::new(epoch)), + }); + } + + // add conversions for the incentivized tokens + let mut conv = I128Sum::from_pair( + AssetData { + token: incentivized_token.clone(), + denom: 0.into(), + position: MaspDigitPos::Zero, + epoch: Some(MaspEpoch::new(1)), + }.encode().unwrap(), + -1, + ); + conv += I128Sum::from_pair( + AssetData { + token: incentivized_token.clone(), + denom: 0.into(), + position: MaspDigitPos::Zero, + epoch: Some(MaspEpoch::new(2)), + }.encode().unwrap(), + 1, + ); + conv += I128Sum::from_pair( + AssetData { + token: native_token.clone(), + denom: 0.into(), + position: MaspDigitPos::Zero, + epoch: Some(MaspEpoch::new(0)), + }.encode().unwrap(), + reward_rate, + ); + context.add_conversions( + AssetData { + token: incentivized_token.clone(), + denom: 0.into(), + position: MaspDigitPos::Zero, + epoch: Some(MaspEpoch::new(1)), + }, + ( + incentivized_token.clone(), + 0.into(), + MaspDigitPos::Zero, + MaspEpoch::new(1), + conv, + MerklePath::from_path(vec![], 0), + ) + ); + + let vk = arbitrary_vk(); + let pa = arbitrary_pa(); + let asset_data = AssetData { + token: incentivized_token.clone(), + denom: 0.into(), + position: MaspDigitPos::Zero, + epoch: Some(MaspEpoch::new(1)), + }; + + wallet.add_note( + create_note(asset_data.clone(), principal, pa), + vk, + ); + + // add an unincentivized token which should not contribute + // to the rewards + let asset_data = AssetData { + token: unincentivized.clone(), + denom: 0.into(), + position: MaspDigitPos::Zero, + epoch: None, + }; + + wallet.add_note( + create_note(asset_data.clone(), rewardless, pa), + vk, + ); + let rewards_est = wallet.estimate_next_epoch_rewards(&context, &vk).await.expect("Test failed"); + assert_eq!(rewards_est, 2 * reward_rate * i128::from(principal)); + }); + } + + /// A more complicated test that checks asset estimations when multiple + /// different incentivized assets are present and multiple conversions need + /// to be applied to the same note. + #[test] + fn test_ests_with_mult_incentivized_assets( + principal1 in 1u64..10_000, + principal2 in 1u64..10_000, + tok1_reward_rate in 1i128..1000, + tok2_reward_rate in 1i128..1000, + ) { + + // #[tokio::test] doesn't work with the proptest! macro + tokio::runtime::Runtime::new().unwrap().block_on(async { + let (channel, mut context) = MockNamadaIo::new(); + // the response to the current masp epoch query + channel + .send(MaspEpoch::new(3).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"); + + // we use a random addresses as our tokens + let tok1 = Address::Internal(InternalAddress::Pgf); + let tok2 = Address::Internal(InternalAddress::ReplayProtection); + + // add asset type decodings + wallet.add_asset_type(AssetData { + token: native_token.clone(), + denom: 0.into(), + position: MaspDigitPos::Zero, + epoch: Some(MaspEpoch::new(0)), + }); + + for tok in [&tok1, &tok2] { + for epoch in 0..5 { + wallet.add_asset_type(AssetData { + token: tok.clone(), + denom: 0.into(), + position: MaspDigitPos::Zero, + epoch: Some(MaspEpoch::new(epoch)), + }); + } + } + // add conversions from epoch 1 -> 2 for tok1 + let mut conv = I128Sum::from_pair( + AssetData { + token: tok1.clone(), + denom: 0.into(), + position: MaspDigitPos::Zero, + epoch: Some(MaspEpoch::new(1)), + } + .encode() + .unwrap(), + -1, + ); + conv += I128Sum::from_pair( + AssetData { + token: tok1.clone(), + denom: 0.into(), + position: MaspDigitPos::Zero, + epoch: Some(MaspEpoch::new(2)), + } + .encode() + .unwrap(), + 1, + ); + conv += I128Sum::from_pair( + AssetData { + token: native_token.clone(), + denom: 0.into(), + position: MaspDigitPos::Zero, + epoch: Some(MaspEpoch::new(0)), + } + .encode() + .unwrap(), + tok1_reward_rate, + ); + context.add_conversions( + AssetData { + token: tok1.clone(), + denom: 0.into(), + position: MaspDigitPos::Zero, + epoch: Some(MaspEpoch::new(1)), + }, + ( + tok1.clone(), + 0.into(), + MaspDigitPos::Zero, + MaspEpoch::new(1), + conv, + MerklePath::from_path(vec![], 0), + ), + ); + + // add conversions from epoch 2 -> 3 for tok1 + let mut conv = I128Sum::from_pair( + AssetData { + token: tok1.clone(), + denom: 0.into(), + position: MaspDigitPos::Zero, + epoch: Some(MaspEpoch::new(2)), + } + .encode() + .unwrap(), + -1, + ); + conv += I128Sum::from_pair( + AssetData { + token: tok1.clone(), + denom: 0.into(), + position: MaspDigitPos::Zero, + epoch: Some(MaspEpoch::new(3)), + } + .encode() + .unwrap(), + 1, + ); + conv += I128Sum::from_pair( + AssetData { + token: native_token.clone(), + denom: 0.into(), + position: MaspDigitPos::Zero, + epoch: Some(MaspEpoch::new(0)), + } + .encode() + .unwrap(), + 1, + ); + context.add_conversions( + AssetData { + token: tok1.clone(), + denom: 0.into(), + position: MaspDigitPos::Zero, + epoch: Some(MaspEpoch::new(2)), + }, + ( + tok1.clone(), + 0.into(), + MaspDigitPos::Zero, + MaspEpoch::new(2), + conv, + MerklePath::from_path(vec![], 0), + ), + ); + // add conversions from epoch 2 -> 3 for tok2 + let mut conv = I128Sum::from_pair( + AssetData { + token: tok2.clone(), + denom: 0.into(), + position: MaspDigitPos::Zero, + epoch: Some(MaspEpoch::new(2)), + } + .encode() + .unwrap(), + -1, + ); + conv += I128Sum::from_pair( + AssetData { + token: tok2.clone(), + denom: 0.into(), + position: MaspDigitPos::Zero, + epoch: Some(MaspEpoch::new(3)), + } + .encode() + .unwrap(), + 1, + ); + conv += I128Sum::from_pair( + AssetData { + token: native_token.clone(), + denom: 0.into(), + position: MaspDigitPos::Zero, + epoch: Some(MaspEpoch::new(0)), + } + .encode() + .unwrap(), + tok2_reward_rate, + ); + context.add_conversions( + AssetData { + token: tok2.clone(), + denom: 0.into(), + position: MaspDigitPos::Zero, + epoch: Some(MaspEpoch::new(2)), + }, + ( + tok2.clone(), + 0.into(), + MaspDigitPos::Zero, + MaspEpoch::new(2), + conv, + MerklePath::from_path(vec![], 0), + ), + ); + + // create note with tok1 + let vk = arbitrary_vk(); + let pa = arbitrary_pa(); + let asset_data = AssetData { + token: tok1.clone(), + denom: 0.into(), + position: MaspDigitPos::Zero, + epoch: Some(MaspEpoch::new(1)), + }; + + wallet.add_note( + create_note(asset_data.clone(), principal1, pa), + vk, + ); + + // create note with tok2 + let asset_data = AssetData { + token: tok2.clone(), + denom: 0.into(), + position: MaspDigitPos::Zero, + epoch: Some(MaspEpoch::new(2)), + }; + + wallet.add_note( + create_note(asset_data.clone(), principal2, pa), + vk, + ); + + let rewards_est = wallet + .estimate_next_epoch_rewards(&context, &vk) + .await + .expect("Test failed"); + let principal1 = i128::from(principal1); + let principal2 = i128::from(principal2); + // reward from epoch 1->2 + reward from epoch 2->3 + reward from + // epoch 2->3 + let expected_tok1_rewards = + principal1 * tok1_reward_rate + principal1 + principal1; + // reward from epoch 2->3 + reward from epoch 2->3 + let expected_tok2_rewards = + principal2 * tok2_reward_rate + principal2 * tok2_reward_rate; + assert_eq!( + rewards_est, + expected_tok1_rewards + expected_tok2_rewards + ); + }); + } + } +} diff --git a/crates/shielded_token/src/masp/test_utils.rs b/crates/shielded_token/src/masp/test_utils.rs index a98f68ca46..3ab948623f 100644 --- a/crates/shielded_token/src/masp/test_utils.rs +++ b/crates/shielded_token/src/masp/test_utils.rs @@ -1,25 +1,47 @@ use core::str::FromStr; use std::collections::BTreeMap; +use std::sync::Arc; use borsh::BorshDeserialize; -use masp_primitives::merkle_tree::{CommitmentTree, IncrementalWitness}; -use masp_primitives::sapling::{Node, ViewingKey}; +use eyre::eyre; +use masp_primitives::asset_type::AssetType; +use masp_primitives::merkle_tree::{ + CommitmentTree, IncrementalWitness, MerklePath, +}; +use masp_primitives::sapling::{Node, Note, Rseed, ViewingKey}; +use masp_primitives::transaction::components::I128Sum; use masp_primitives::transaction::Transaction; use masp_primitives::zip32::ExtendedFullViewingKey; +use namada_core::address::Address; +use namada_core::borsh::BorshSerializeExt; use namada_core::chain::BlockHeight; use namada_core::collections::HashMap; -use namada_core::masp::ExtendedViewingKey; +use namada_core::masp::{ + AssetData, ExtendedViewingKey, MaspEpoch, PaymentAddress, +}; +use namada_core::time::DurationSecs; +use namada_core::token::{Denomination, MaspDigitPos}; +use namada_io::client::EncodedResponseQuery; +use namada_io::{Client, MaybeSend, MaybeSync, NamadaIo, NullIo}; use namada_tx::IndexedTx; use namada_wallet::DatedKeypair; use thiserror::Error; +use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender}; +use tokio::sync::Mutex; +use crate::masp::shielded_wallet::ShieldedQueries; use crate::masp::utils::{ IndexedNoteEntry, MaspClient, MaspClientCapabilities, }; +use crate::masp::ShieldedUtils; +use crate::ShieldedWallet; /// A viewing key derived from A_SPENDING_KEY pub const AA_VIEWING_KEY: &str = "zvknam1qqqqqqqqqqqqqq9v0sls5r5de7njx8ehu49pqgmqr9ygelg87l5x8y4s9r0pjlvu6x74w9gjpw856zcu826qesdre628y6tjc26uhgj6d9zqur9l5u3p99d9ggc74ald6s8y3sdtka74qmheyqvdrasqpwyv2fsmxlz57lj4grm2pthzj3sflxc0jx0edrakx3vdcngrfjmru8ywkguru8mxss2uuqxdlglaz6undx5h8w7g70t2es850g48xzdkqay5qs0yw06rtxcpjdve6"; +// A payment address derived from A_SPENDING_KEY +pub const AA_PAYMENT_ADDRESS: &str = "znam1ky620tz7z658cralqt693qpvk42wvth468zp38nqvq2apmex5rfut3dfqm2asrsqv0tc7saqje7"; + pub fn dated_arbitrary_vk() -> DatedKeypair { arbitrary_vk().into() } @@ -32,6 +54,10 @@ pub fn arbitrary_vk() -> ViewingKey { .vk } +pub fn arbitrary_pa() -> PaymentAddress { + FromStr::from_str(AA_PAYMENT_ADDRESS).expect("Test failed") +} + /// A serialized transaction that will work for testing. /// Would love to do this in a less opaque fashion, but /// making these things is a misery not worth my time. @@ -414,3 +440,240 @@ impl MaspClient for TestingMaspClient { unimplemented!("Witness map fetching is not implemented by this client") } } + +/// A shielded context for testing +#[derive(Debug)] +pub struct TestingContext { + wallet: ShieldedWallet, +} + +impl TestingContext { + pub fn new(wallet: ShieldedWallet) -> Self { + Self { wallet } + } + + pub fn add_asset_type(&mut self, asset_data: AssetData) { + self.asset_types + .insert(asset_data.encode().unwrap(), asset_data); + } + + /// Add a note to a given viewing key + pub fn add_note(&mut self, note: Note, vk: ViewingKey) { + let next_note_idx = self + .wallet + .note_map + .keys() + .max() + .map(|ix| ix + 1) + .unwrap_or_default(); + self.wallet.note_map.insert(next_note_idx, note); + let avail_notes = self.wallet.pos_map.entry(vk).or_default(); + avail_notes.insert(next_note_idx); + } + + pub fn spend_note(&mut self, note: &Note) { + let idx = self + .wallet + .note_map + .iter() + .find(|(_, v)| *v == note) + .map(|(idx, _)| idx) + .expect("Could find the note to spend in the note map"); + self.wallet.spents.insert(*idx); + } +} + +impl std::ops::Deref + for TestingContext +{ + type Target = ShieldedWallet; + + fn deref(&self) -> &Self::Target { + &self.wallet + } +} + +impl std::ops::DerefMut + for TestingContext +{ + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.wallet + } +} + +impl ShieldedQueries + for TestingContext +{ + async fn query_native_token( + _: &C, + ) -> Result { + Ok(Address::Established([0u8; 20].into())) + } + + async fn query_denom( + client: &C, + token: &Address, + ) -> Option { + Some( + if *token + == Self::query_native_token(client).await.expect("Infallible") + { + Denomination(6) + } else { + Denomination(0) + }, + ) + } + + async fn query_conversion( + client: &C, + asset_type: AssetType, + ) -> Option { + let resp = client + .request(asset_type.to_string(), None, None, false) + .await + .ok()?; + BorshDeserialize::try_from_slice(&resp.data).unwrap() + } + + async fn query_block( + _: &C, + ) -> Result, eyre::Error> { + unimplemented!() + } + + async fn query_max_block_time_estimate( + _: &C, + ) -> Result { + unimplemented!() + } + + async fn query_masp_epoch( + client: &C, + ) -> Result { + let resp = client + .request("".to_string(), None, None, false) + .await + .map_err(|e| eyre!("{}", e))?; + BorshDeserialize::try_from_slice(&resp.data).map_err(|e| eyre!("{}", e)) + } +} + +pub type ConversionResp = ( + Address, + Denomination, + MaspDigitPos, + MaspEpoch, + I128Sum, + MerklePath, +); + +/// A mock client for making "queries" on behalf +/// of a `TestingContext` +pub struct MockClient { + channel: Arc>>>, + pub conversions: HashMap, +} + +impl MockClient { + pub fn new() -> (UnboundedSender>, Self) { + let (send, recv) = tokio::sync::mpsc::unbounded_channel(); + ( + send, + Self { + channel: Arc::new(Mutex::new(recv)), + conversions: Default::default(), + }, + ) + } +} + +#[cfg_attr(feature = "async-send", async_trait::async_trait)] +#[cfg_attr(not(feature = "async-send"), async_trait::async_trait(?Send))] +impl Client for MockClient { + type Error = eyre::Error; + + async fn request( + &self, + req: String, + _: Option>, + _: Option, + _: bool, + ) -> Result { + let resp = if let Ok(asset_type) = AssetType::from_str(&req) { + self.conversions.get(&asset_type).serialize_to_vec() + } else { + let mut locked = self.channel.lock().await; + locked + .recv() + .await + .ok_or_else(|| eyre!("Client did not respond"))? + }; + Ok(EncodedResponseQuery { + data: resp, + info: "".to_string(), + proof: None, + height: Default::default(), + }) + } + + async fn perform( + &self, + _: R, + ) -> Result + where + R: tendermint_rpc::request::SimpleRequest, + { + unimplemented!() + } +} + +pub struct MockNamadaIo { + client: MockClient, + io: NullIo, +} + +impl MockNamadaIo { + pub fn new() -> (UnboundedSender>, Self) { + let (send, client) = MockClient::new(); + (send, Self { client, io: NullIo }) + } + + pub fn add_conversions( + &mut self, + asset_data: AssetData, + conv: ConversionResp, + ) { + self.client + .conversions + .insert(asset_data.encode().unwrap(), conv); + } +} + +impl NamadaIo for MockNamadaIo { + type Client = MockClient; + type Io = NullIo; + + fn client(&self) -> &Self::Client { + &self.client + } + + fn io(&self) -> &Self::Io { + &self.io + } +} + +pub fn create_note( + asset_data: AssetData, + value: u64, + pa: PaymentAddress, +) -> Note { + let payment_addr: masp_primitives::sapling::PaymentAddress = pa.into(); + Note { + value, + g_d: payment_addr.g_d().unwrap(), + pk_d: *payment_addr.pk_d(), + asset_type: asset_data.encode().unwrap(), + rseed: Rseed::AfterZip212([0; 32]), + } +} diff --git a/wasm/Cargo.lock b/wasm/Cargo.lock index 6d2ba7e400..37ed4b06f4 100644 --- a/wasm/Cargo.lock +++ b/wasm/Cargo.lock @@ -3991,6 +3991,7 @@ dependencies = [ "sha2 0.9.9", "smooth-operator", "tempfile", + "tendermint-rpc", "thiserror", "tracing", "typed-builder", diff --git a/wasm_for_tests/Cargo.lock b/wasm_for_tests/Cargo.lock index ed683156ed..21d9985b3b 100644 --- a/wasm_for_tests/Cargo.lock +++ b/wasm_for_tests/Cargo.lock @@ -890,6 +890,15 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + [[package]] name = "fpe" version = "0.6.1" @@ -1017,8 +1026,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] [[package]] @@ -1594,6 +1605,145 @@ dependencies = [ "sha3", ] +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.65", +] + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + [[package]] name = "impl-codec" version = "0.6.0" @@ -1854,6 +2004,12 @@ version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" +[[package]] +name = "litemap" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" + [[package]] name = "log" version = "0.4.20" @@ -2187,6 +2343,7 @@ dependencies = [ "sha2 0.9.9", "smooth-operator", "tempfile", + "tendermint-rpc", "thiserror", "tracing", "typed-builder", @@ -2651,6 +2808,39 @@ dependencies = [ "password-hash", ] +[[package]] +name = "peg" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "295283b02df346d1ef66052a757869b2876ac29a6bb0ac3f5f7cd44aebe40e8f" +dependencies = [ + "peg-macros", + "peg-runtime", +] + +[[package]] +name = "peg-macros" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdad6a1d9cf116a059582ce415d5f5566aabcd4008646779dab7fdc2a9a9d426" +dependencies = [ + "peg-runtime", + "proc-macro2", + "quote", +] + +[[package]] +name = "peg-runtime" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3aeb8f54c078314c2065ee649a7241f46b9d8e418e1a9581ba0546657d7aa3a" + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + [[package]] name = "pest" version = "2.7.5" @@ -2672,6 +2862,26 @@ dependencies = [ "indexmap 2.2.6", ] +[[package]] +name = "pin-project" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.65", +] + [[package]] name = "pin-project-lite" version = "0.2.13" @@ -3074,7 +3284,7 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" dependencies = [ - "semver", + "semver 0.11.0", ] [[package]] @@ -3102,6 +3312,15 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "scale-info" version = "2.11.3" @@ -3174,6 +3393,12 @@ dependencies = [ "semver-parser", ] +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + [[package]] name = "semver-parser" version = "0.10.2" @@ -3254,6 +3479,15 @@ dependencies = [ "syn 2.0.65", ] +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", +] + [[package]] name = "serdect" version = "0.2.0" @@ -3323,6 +3557,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + [[package]] name = "smooth-operator" version = "0.7.0" @@ -3363,6 +3603,12 @@ dependencies = [ "der", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "static_assertions" version = "1.1.0" @@ -3459,6 +3705,17 @@ dependencies = [ "syn 2.0.65", ] +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.65", +] + [[package]] name = "tap" version = "1.0.1" @@ -3509,6 +3766,20 @@ dependencies = [ "zeroize", ] +[[package]] +name = "tendermint-config" +version = "0.38.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9de111ea653b2adaef627ac2452b463c77aa615c256eaaddf279ec5a1cf9775f" +dependencies = [ + "flex-error", + "serde", + "serde_json", + "tendermint", + "toml", + "url", +] + [[package]] name = "tendermint-light-client-verifier" version = "0.38.1" @@ -3538,6 +3809,35 @@ dependencies = [ "time", ] +[[package]] +name = "tendermint-rpc" +version = "0.38.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02f96a2b8a0d3d0b59e4024b1a6bdc1589efc6af4709d08a480a20cc4ba90f63" +dependencies = [ + "async-trait", + "bytes", + "flex-error", + "getrandom", + "peg", + "pin-project", + "rand", + "semver 1.0.23", + "serde", + "serde_bytes", + "serde_json", + "subtle", + "subtle-encoding", + "tendermint", + "tendermint-config", + "tendermint-proto", + "thiserror", + "time", + "url", + "uuid", + "walkdir", +] + [[package]] name = "thiserror" version = "1.0.50" @@ -3597,6 +3897,16 @@ dependencies = [ "crunchy", ] +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "tinyvec" version = "1.6.0" @@ -3612,11 +3922,26 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +[[package]] +name = "toml" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac2caab0bf757388c6c0ae23b3293fdb463fee59434529014f85e3263b995c28" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.22.16", +] + [[package]] name = "toml_datetime" version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" +dependencies = [ + "serde", +] [[package]] name = "toml_edit" @@ -3626,7 +3951,7 @@ checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" dependencies = [ "indexmap 2.2.6", "toml_datetime", - "winnow", + "winnow 0.5.25", ] [[package]] @@ -3637,7 +3962,20 @@ checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" dependencies = [ "indexmap 2.2.6", "toml_datetime", - "winnow", + "winnow 0.5.25", +] + +[[package]] +name = "toml_edit" +version = "0.22.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "278f3d518e152219c994ce877758516bca5e118eaed6996192a774fb9fbf0788" +dependencies = [ + "indexmap 2.2.6", + "serde", + "serde_spanned", + "toml_datetime", + "winnow 0.6.20", ] [[package]] @@ -3902,6 +4240,35 @@ dependencies = [ "subtle", ] +[[package]] +name = "url" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d157f1b96d14500ffdc1f10ba712e780825526c03d9a49b4d0324b0d9113ada" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "uuid" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" + [[package]] name = "version_check" version = "0.9.4" @@ -3973,6 +4340,16 @@ dependencies = [ "rlsf", ] +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -4061,6 +4438,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -4217,6 +4603,27 @@ dependencies = [ "memchr", ] +[[package]] +name = "winnow" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +dependencies = [ + "memchr", +] + +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + [[package]] name = "wyz" version = "0.5.1" @@ -4237,6 +4644,30 @@ dependencies = [ "serde", ] +[[package]] +name = "yoke" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.65", + "synstructure", +] + [[package]] name = "zcash_encoding" version = "0.2.0" @@ -4246,6 +4677,27 @@ dependencies = [ "nonempty", ] +[[package]] +name = "zerofrom" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.65", + "synstructure", +] + [[package]] name = "zeroize" version = "1.7.0" @@ -4265,3 +4717,25 @@ dependencies = [ "quote", "syn 2.0.65", ] + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.65", +] From a45701ae6487a2e467dece397416c99505698027 Mon Sep 17 00:00:00 2001 From: satan Date: Mon, 4 Nov 2024 16:43:30 +0100 Subject: [PATCH 2/9] Added cli command for rewards estimations and added it to an integration test --- crates/apps_lib/src/cli.rs | 65 ++++++++++++++++++++++++++++ crates/apps_lib/src/cli/client.rs | 12 +++++ crates/apps_lib/src/client/rpc.rs | 24 ++++++++++ crates/sdk/src/args.rs | 10 +++++ crates/tests/src/integration/masp.rs | 40 +++++++++++++++++ 5 files changed, 151 insertions(+) diff --git a/crates/apps_lib/src/cli.rs b/crates/apps_lib/src/cli.rs index 4c10e9f50b..2185205e4e 100644 --- a/crates/apps_lib/src/cli.rs +++ b/crates/apps_lib/src/cli.rs @@ -273,6 +273,7 @@ pub mod cmds { .subcommand(QueryMaspRewardTokens::def().display_order(5)) .subcommand(QueryBlock::def().display_order(5)) .subcommand(QueryBalance::def().display_order(5)) + .subcommand(QueryRewardsEstimate::def().display_order(5)) .subcommand(QueryBonds::def().display_order(5)) .subcommand(QueryBondedStake::def().display_order(5)) .subcommand(QuerySlashes::def().display_order(5)) @@ -356,6 +357,8 @@ pub mod cmds { Self::parse_with_ctx(matches, QueryMaspRewardTokens); let query_block = Self::parse_with_ctx(matches, QueryBlock); let query_balance = Self::parse_with_ctx(matches, QueryBalance); + let query_rewards_estimate = + Self::parse_with_ctx(matches, QueryRewardsEstimate); let query_bonds = Self::parse_with_ctx(matches, QueryBonds); let query_bonded_stake = Self::parse_with_ctx(matches, QueryBondedStake); @@ -427,6 +430,7 @@ pub mod cmds { .or(query_masp_reward_tokens) .or(query_block) .or(query_balance) + .or(query_rewards_estimate) .or(query_bonds) .or(query_bonded_stake) .or(query_slashes) @@ -523,6 +527,7 @@ pub mod cmds { QueryMaspRewardTokens(QueryMaspRewardTokens), QueryBlock(QueryBlock), QueryBalance(QueryBalance), + QueryRewardsEstimate(QueryRewardsEstimate), QueryBonds(QueryBonds), QueryBondedStake(QueryBondedStake), QueryCommissionRate(QueryCommissionRate), @@ -1875,6 +1880,31 @@ pub mod cmds { } } + #[derive(Clone, Debug)] + pub struct QueryRewardsEstimate( + pub args::QueryRewardsEstimate, + ); + + impl SubCmd for QueryRewardsEstimate { + const CMD: &'static str = "estimate-rewards"; + + fn parse(matches: &ArgMatches) -> Option { + matches.subcommand_matches(Self::CMD).map(|matches| { + QueryRewardsEstimate(args::QueryRewardsEstimate::parse(matches)) + }) + } + + fn def() -> App { + App::new(Self::CMD) + .about(wrap!( + "Estimate the amount of MASP rewards accumulated by the \ + next MASP epoch. Please run shielded-sync first for best \ + results." + )) + .add_args::>() + } + } + #[derive(Clone, Debug)] pub struct QueryBonds(pub args::QueryBonds); @@ -6318,6 +6348,41 @@ pub mod args { } } + impl CliToSdk> + for QueryRewardsEstimate + { + type Error = std::convert::Infallible; + + fn to_sdk( + self, + ctx: &mut Context, + ) -> Result, Self::Error> { + let query = self.query.to_sdk(ctx)?; + let chain_ctx = ctx.borrow_mut_chain_or_exit(); + + Ok(QueryRewardsEstimate:: { + query, + owner: chain_ctx.get_cached(&self.owner), + }) + } + } + + impl Args for QueryRewardsEstimate { + fn parse(matches: &ArgMatches) -> Self { + let query = Query::parse(matches); + let owner = VIEWING_KEY.parse(matches); + Self { query, owner } + } + + fn def(app: App) -> App { + app.add_args::>().arg( + VIEWING_KEY + .def() + .help(wrap!("The viewing key whose rewards to estimate.")), + ) + } + } + impl CliToSdk> for QueryBonds { type Error = std::convert::Infallible; diff --git a/crates/apps_lib/src/cli/client.rs b/crates/apps_lib/src/cli/client.rs index d755c7d7f4..6542a55b63 100644 --- a/crates/apps_lib/src/cli/client.rs +++ b/crates/apps_lib/src/cli/client.rs @@ -551,6 +551,18 @@ impl CliApi { let namada = ctx.to_sdk(client, io); rpc::query_balance(&namada, args).await; } + Sub::QueryRewardsEstimate(QueryRewardsEstimate(args)) => { + let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let ledger_address = + chain_ctx.get(&args.query.ledger_address); + let client = client.unwrap_or_else(|| { + C::from_tendermint_address(&ledger_address) + }); + client.wait_until_node_is_synced(&io).await?; + let args = args.to_sdk(&mut ctx)?; + let namada = ctx.to_sdk(client, io); + rpc::query_rewards_estimate(&namada, args).await; + } Sub::QueryBonds(QueryBonds(args)) => { let chain_ctx = ctx.borrow_mut_chain_or_exit(); let ledger_address = diff --git a/crates/apps_lib/src/client/rpc.rs b/crates/apps_lib/src/client/rpc.rs index 7814c2db31..1f78e27376 100644 --- a/crates/apps_lib/src/client/rpc.rs +++ b/crates/apps_lib/src/client/rpc.rs @@ -11,6 +11,7 @@ use masp_primitives::sapling::Node; use masp_primitives::transaction::components::I128Sum; use masp_primitives::zip32::ExtendedFullViewingKey; use namada_core::masp::{BalanceOwner, MaspEpoch}; +use namada_core::token::Amount; use namada_sdk::address::{Address, InternalAddress, MASP}; use namada_sdk::chain::{BlockHeight, Epoch}; use namada_sdk::collections::{HashMap, HashSet}; @@ -359,6 +360,29 @@ pub async fn query_proposal_by_id( namada_sdk::rpc::query_proposal_by_id(client, proposal_id).await } +/// Estimate MASP rewards for next MASP epoch +pub async fn query_rewards_estimate( + context: &impl Namada, + args: args::QueryRewardsEstimate, +) { + let mut shielded = context.shielded_mut().await; + let _ = shielded.load().await; + // Save the update state so that future fetches can be short-circuited + let _ = shielded.save().await; + let rewards_estimate = shielded + .estimate_next_epoch_rewards(context, &args.owner.as_viewing_key()) + .await + .unwrap() + .unsigned_abs(); + let rewards_estimate = + DenominatedAmount::new(Amount::from_u128(rewards_estimate), 6.into()); + display_line!( + context.io(), + "Estimated nam rewards for the next MASP epoch: {}", + rewards_estimate + ); +} + /// Query token shielded balance(s) async fn query_shielded_balance( context: &impl Namada, diff --git a/crates/sdk/src/args.rs b/crates/sdk/src/args.rs index 5933a663f3..706e26477e 100644 --- a/crates/sdk/src/args.rs +++ b/crates/sdk/src/args.rs @@ -1600,6 +1600,16 @@ pub struct QueryBalance { pub height: Option, } +/// Get an estimate for the MASP rewards accumulated by the next +/// MASP epoch. +#[derive(Clone, Debug)] +pub struct QueryRewardsEstimate { + /// Common query args + pub query: Query, + /// Viewing key + pub owner: C::ViewingKey, +} + /// Query historical transfer(s) #[derive(Clone, Debug)] pub struct QueryTransfers { diff --git a/crates/tests/src/integration/masp.rs b/crates/tests/src/integration/masp.rs index 569cc96ffc..366af3a4ce 100644 --- a/crates/tests/src/integration/masp.rs +++ b/crates/tests/src/integration/masp.rs @@ -1348,6 +1348,25 @@ fn masp_incentives() -> Result<()> { assert!(captured.result.is_ok()); assert!(captured.contains("nam: 0")); + // Assert the rewards estimate is also zero + let captured = CapturedOutput::of(|| { + run( + &node, + Bin::Client, + vec![ + "estimate-rewards", + "--key", + AA_VIEWING_KEY, + "--node", + validator_one_rpc, + ], + ) + }); + assert!(captured.result.is_ok()); + assert!( + captured.contains("Estimated nam rewards for the next MASP epoch: 0") + ); + // Wait till epoch boundary node.next_masp_epoch(); @@ -1397,6 +1416,27 @@ fn masp_incentives() -> Result<()> { assert!(captured.result.is_ok()); assert!(captured.contains("nam: 0.063")); + // Assert the rewards estimate is a number higher than the actual rewards + let captured = CapturedOutput::of(|| { + run( + &node, + Bin::Client, + vec![ + "estimate-rewards", + "--key", + AA_VIEWING_KEY, + "--node", + validator_one_rpc, + ], + ) + }); + assert!(captured.result.is_ok()); + // note that 0.126 = 2 * 0.063 which is expected + assert!( + captured + .contains("Estimated nam rewards for the next MASP epoch: 0.126") + ); + // Assert NAM balance at MASP pool is exclusively the // rewards from the shielded BTC let captured = CapturedOutput::of(|| { From 7d1d8e5bf552b5a8da25a4d920103952d34ae622 Mon Sep 17 00:00:00 2001 From: satan Date: Mon, 4 Nov 2024 16:46:28 +0100 Subject: [PATCH 3/9] changelog --- .changelog/unreleased/features/3974-masp-rewards-estimation.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/unreleased/features/3974-masp-rewards-estimation.md diff --git a/.changelog/unreleased/features/3974-masp-rewards-estimation.md b/.changelog/unreleased/features/3974-masp-rewards-estimation.md new file mode 100644 index 0000000000..f40c90dd7e --- /dev/null +++ b/.changelog/unreleased/features/3974-masp-rewards-estimation.md @@ -0,0 +1,3 @@ +- Adds a cli command to estimate the amount of MASP rewards that will be accumulated by the next epoch. + This is done by applying the latest set of conversions for each asset again. + ([\#3974](https://github.com/anoma/namada/pull/3974)) \ No newline at end of file From 9b886ec720b0620f6184a3f7a973a7aeea33fa82 Mon Sep 17 00:00:00 2001 From: satan Date: Tue, 5 Nov 2024 10:14:08 +0100 Subject: [PATCH 4/9] Revert wasm lock files --- wasm/Cargo.lock | 1 - wasm_for_tests/Cargo.lock | 480 +------------------------------------- 2 files changed, 3 insertions(+), 478 deletions(-) diff --git a/wasm/Cargo.lock b/wasm/Cargo.lock index 37ed4b06f4..6d2ba7e400 100644 --- a/wasm/Cargo.lock +++ b/wasm/Cargo.lock @@ -3991,7 +3991,6 @@ dependencies = [ "sha2 0.9.9", "smooth-operator", "tempfile", - "tendermint-rpc", "thiserror", "tracing", "typed-builder", diff --git a/wasm_for_tests/Cargo.lock b/wasm_for_tests/Cargo.lock index 21d9985b3b..ed683156ed 100644 --- a/wasm_for_tests/Cargo.lock +++ b/wasm_for_tests/Cargo.lock @@ -890,15 +890,6 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -[[package]] -name = "form_urlencoded" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" -dependencies = [ - "percent-encoding", -] - [[package]] name = "fpe" version = "0.6.1" @@ -1026,10 +1017,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", - "js-sys", "libc", "wasi", - "wasm-bindgen", ] [[package]] @@ -1605,145 +1594,6 @@ dependencies = [ "sha3", ] -[[package]] -name = "icu_collections" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locid" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_locid_transform" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_locid_transform_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_locid_transform_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" - -[[package]] -name = "icu_normalizer" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "utf16_iter", - "utf8_iter", - "write16", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" - -[[package]] -name = "icu_properties" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_locid_transform", - "icu_properties_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" - -[[package]] -name = "icu_provider" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_provider_macros", - "stable_deref_trait", - "tinystr", - "writeable", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_provider_macros" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.65", -] - -[[package]] -name = "idna" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" -dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", -] - -[[package]] -name = "idna_adapter" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" -dependencies = [ - "icu_normalizer", - "icu_properties", -] - [[package]] name = "impl-codec" version = "0.6.0" @@ -2004,12 +1854,6 @@ version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" -[[package]] -name = "litemap" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" - [[package]] name = "log" version = "0.4.20" @@ -2343,7 +2187,6 @@ dependencies = [ "sha2 0.9.9", "smooth-operator", "tempfile", - "tendermint-rpc", "thiserror", "tracing", "typed-builder", @@ -2808,39 +2651,6 @@ dependencies = [ "password-hash", ] -[[package]] -name = "peg" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "295283b02df346d1ef66052a757869b2876ac29a6bb0ac3f5f7cd44aebe40e8f" -dependencies = [ - "peg-macros", - "peg-runtime", -] - -[[package]] -name = "peg-macros" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdad6a1d9cf116a059582ce415d5f5566aabcd4008646779dab7fdc2a9a9d426" -dependencies = [ - "peg-runtime", - "proc-macro2", - "quote", -] - -[[package]] -name = "peg-runtime" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3aeb8f54c078314c2065ee649a7241f46b9d8e418e1a9581ba0546657d7aa3a" - -[[package]] -name = "percent-encoding" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" - [[package]] name = "pest" version = "2.7.5" @@ -2862,26 +2672,6 @@ dependencies = [ "indexmap 2.2.6", ] -[[package]] -name = "pin-project" -version = "1.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.65", -] - [[package]] name = "pin-project-lite" version = "0.2.13" @@ -3284,7 +3074,7 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" dependencies = [ - "semver 0.11.0", + "semver", ] [[package]] @@ -3312,15 +3102,6 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - [[package]] name = "scale-info" version = "2.11.3" @@ -3393,12 +3174,6 @@ dependencies = [ "semver-parser", ] -[[package]] -name = "semver" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" - [[package]] name = "semver-parser" version = "0.10.2" @@ -3479,15 +3254,6 @@ dependencies = [ "syn 2.0.65", ] -[[package]] -name = "serde_spanned" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" -dependencies = [ - "serde", -] - [[package]] name = "serdect" version = "0.2.0" @@ -3557,12 +3323,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "smallvec" -version = "1.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" - [[package]] name = "smooth-operator" version = "0.7.0" @@ -3603,12 +3363,6 @@ dependencies = [ "der", ] -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - [[package]] name = "static_assertions" version = "1.1.0" @@ -3705,17 +3459,6 @@ dependencies = [ "syn 2.0.65", ] -[[package]] -name = "synstructure" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.65", -] - [[package]] name = "tap" version = "1.0.1" @@ -3766,20 +3509,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "tendermint-config" -version = "0.38.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9de111ea653b2adaef627ac2452b463c77aa615c256eaaddf279ec5a1cf9775f" -dependencies = [ - "flex-error", - "serde", - "serde_json", - "tendermint", - "toml", - "url", -] - [[package]] name = "tendermint-light-client-verifier" version = "0.38.1" @@ -3809,35 +3538,6 @@ dependencies = [ "time", ] -[[package]] -name = "tendermint-rpc" -version = "0.38.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02f96a2b8a0d3d0b59e4024b1a6bdc1589efc6af4709d08a480a20cc4ba90f63" -dependencies = [ - "async-trait", - "bytes", - "flex-error", - "getrandom", - "peg", - "pin-project", - "rand", - "semver 1.0.23", - "serde", - "serde_bytes", - "serde_json", - "subtle", - "subtle-encoding", - "tendermint", - "tendermint-config", - "tendermint-proto", - "thiserror", - "time", - "url", - "uuid", - "walkdir", -] - [[package]] name = "thiserror" version = "1.0.50" @@ -3897,16 +3597,6 @@ dependencies = [ "crunchy", ] -[[package]] -name = "tinystr" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" -dependencies = [ - "displaydoc", - "zerovec", -] - [[package]] name = "tinyvec" version = "1.6.0" @@ -3922,26 +3612,11 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" -[[package]] -name = "toml" -version = "0.8.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac2caab0bf757388c6c0ae23b3293fdb463fee59434529014f85e3263b995c28" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit 0.22.16", -] - [[package]] name = "toml_datetime" version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" -dependencies = [ - "serde", -] [[package]] name = "toml_edit" @@ -3951,7 +3626,7 @@ checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" dependencies = [ "indexmap 2.2.6", "toml_datetime", - "winnow 0.5.25", + "winnow", ] [[package]] @@ -3962,20 +3637,7 @@ checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" dependencies = [ "indexmap 2.2.6", "toml_datetime", - "winnow 0.5.25", -] - -[[package]] -name = "toml_edit" -version = "0.22.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "278f3d518e152219c994ce877758516bca5e118eaed6996192a774fb9fbf0788" -dependencies = [ - "indexmap 2.2.6", - "serde", - "serde_spanned", - "toml_datetime", - "winnow 0.6.20", + "winnow", ] [[package]] @@ -4240,35 +3902,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "url" -version = "2.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d157f1b96d14500ffdc1f10ba712e780825526c03d9a49b4d0324b0d9113ada" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", -] - -[[package]] -name = "utf16_iter" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" - -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - -[[package]] -name = "uuid" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" - [[package]] name = "version_check" version = "0.9.4" @@ -4340,16 +3973,6 @@ dependencies = [ "rlsf", ] -[[package]] -name = "walkdir" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -4438,15 +4061,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -[[package]] -name = "winapi-util" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" -dependencies = [ - "windows-sys 0.52.0", -] - [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -4603,27 +4217,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "winnow" -version = "0.6.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" -dependencies = [ - "memchr", -] - -[[package]] -name = "write16" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" - -[[package]] -name = "writeable" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" - [[package]] name = "wyz" version = "0.5.1" @@ -4644,30 +4237,6 @@ dependencies = [ "serde", ] -[[package]] -name = "yoke" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" -dependencies = [ - "serde", - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] - -[[package]] -name = "yoke-derive" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.65", - "synstructure", -] - [[package]] name = "zcash_encoding" version = "0.2.0" @@ -4677,27 +4246,6 @@ dependencies = [ "nonempty", ] -[[package]] -name = "zerofrom" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.65", - "synstructure", -] - [[package]] name = "zeroize" version = "1.7.0" @@ -4717,25 +4265,3 @@ dependencies = [ "quote", "syn 2.0.65", ] - -[[package]] -name = "zerovec" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.65", -] From be79dcd9cef81a654a8c3817bf97966fa04fa43d Mon Sep 17 00:00:00 2001 From: satan Date: Tue, 5 Nov 2024 10:29:34 +0100 Subject: [PATCH 5/9] Fixed dev-dep issue --- crates/shielded_token/Cargo.toml | 2 +- crates/shielded_token/src/masp/test_utils.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/shielded_token/Cargo.toml b/crates/shielded_token/Cargo.toml index 460fdb0961..4a3d2ca58d 100644 --- a/crates/shielded_token/Cargo.toml +++ b/crates/shielded_token/Cargo.toml @@ -71,7 +71,6 @@ serde_json.workspace = true sha2.workspace = true smooth-operator.workspace = true tempfile.workspace = true -tendermint-rpc.workspace = true thiserror.workspace = true tracing.workspace = true typed-builder.workspace = true @@ -94,5 +93,6 @@ masp_proofs = { workspace = true, features = ["download-params"] } proptest.workspace = true rand_core.workspace = true rayon.workspace = true +tendermint-rpc.workspace = true test-log.workspace = true tokio.workspace = true diff --git a/crates/shielded_token/src/masp/test_utils.rs b/crates/shielded_token/src/masp/test_utils.rs index 3ab948623f..f19187a530 100644 --- a/crates/shielded_token/src/masp/test_utils.rs +++ b/crates/shielded_token/src/masp/test_utils.rs @@ -588,6 +588,7 @@ impl MockClient { } } +#[cfg(test)] #[cfg_attr(feature = "async-send", async_trait::async_trait)] #[cfg_attr(not(feature = "async-send"), async_trait::async_trait(?Send))] impl Client for MockClient { From 4c756bd3b88def9e39fc2663c5d78e7fee08c0d7 Mon Sep 17 00:00:00 2001 From: satan Date: Tue, 5 Nov 2024 10:48:17 +0100 Subject: [PATCH 6/9] Rebasing on main --- crates/shielded_token/src/masp/shielded_wallet.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/shielded_token/src/masp/shielded_wallet.rs b/crates/shielded_token/src/masp/shielded_wallet.rs index 23f8950762..7d370d933f 100644 --- a/crates/shielded_token/src/masp/shielded_wallet.rs +++ b/crates/shielded_token/src/masp/shielded_wallet.rs @@ -724,7 +724,7 @@ pub trait ShieldedApi: Conversions::new(), ) .await? - .2; + .1; // re-date the all the latest conversions up one epoch let mut estimated_conversions = Conversions::new(); @@ -743,7 +743,7 @@ pub trait ShieldedApi: asset.redate_to_next_epoch(); let decoded_conv = self .decode_sum(context.client(), conv.clone().into()) - .await; + .await.0; let mut est_conv = I128Sum::zero(); for ((_, asset_data), val) in decoded_conv.components() { let mut new_asset = asset_data.clone(); @@ -775,6 +775,7 @@ pub trait ShieldedApi: Ok(self .decode_sum(context.client(), rewards) .await + .0 .components() .filter(|((_, data), _)| { // this should always be true, but we check it anyway From 1c393fa54243bb6d0edc02df9161b92e0fc44c9c Mon Sep 17 00:00:00 2001 From: satan Date: Tue, 5 Nov 2024 15:02:27 +0100 Subject: [PATCH 7/9] Fmt --- crates/shielded_token/src/masp/shielded_wallet.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/shielded_token/src/masp/shielded_wallet.rs b/crates/shielded_token/src/masp/shielded_wallet.rs index 7d370d933f..eca2917964 100644 --- a/crates/shielded_token/src/masp/shielded_wallet.rs +++ b/crates/shielded_token/src/masp/shielded_wallet.rs @@ -743,7 +743,8 @@ pub trait ShieldedApi: asset.redate_to_next_epoch(); let decoded_conv = self .decode_sum(context.client(), conv.clone().into()) - .await.0; + .await + .0; let mut est_conv = I128Sum::zero(); for ((_, asset_data), val) in decoded_conv.components() { let mut new_asset = asset_data.clone(); From 1d77d6d3c67770571d29e8eb150de00873ad4fbd Mon Sep 17 00:00:00 2001 From: satan Date: Mon, 11 Nov 2024 17:26:40 +0100 Subject: [PATCH 8/9] Removed unnecessary shielded wallet save --- crates/apps_lib/src/client/rpc.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/apps_lib/src/client/rpc.rs b/crates/apps_lib/src/client/rpc.rs index 1f78e27376..f1daf9d04a 100644 --- a/crates/apps_lib/src/client/rpc.rs +++ b/crates/apps_lib/src/client/rpc.rs @@ -367,8 +367,6 @@ pub async fn query_rewards_estimate( ) { let mut shielded = context.shielded_mut().await; let _ = shielded.load().await; - // Save the update state so that future fetches can be short-circuited - let _ = shielded.save().await; let rewards_estimate = shielded .estimate_next_epoch_rewards(context, &args.owner.as_viewing_key()) .await From 16ddaf3340c8d64572117405d09adee9f4fda4c5 Mon Sep 17 00:00:00 2001 From: brentstone Date: Wed, 13 Nov 2024 22:27:15 -0800 Subject: [PATCH 9/9] cleanup --- crates/apps_lib/src/client/rpc.rs | 12 ++++++++---- crates/tests/src/integration/masp.rs | 11 ++++++----- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/crates/apps_lib/src/client/rpc.rs b/crates/apps_lib/src/client/rpc.rs index f1daf9d04a..f44fc4023e 100644 --- a/crates/apps_lib/src/client/rpc.rs +++ b/crates/apps_lib/src/client/rpc.rs @@ -47,7 +47,9 @@ use namada_sdk::rpc::{ }; use namada_sdk::storage::BlockResults; use namada_sdk::tendermint_rpc::endpoint::status; -use namada_sdk::token::{DenominatedAmount, MaspDigitPos}; +use namada_sdk::token::{ + DenominatedAmount, MaspDigitPos, NATIVE_MAX_DECIMAL_PLACES, +}; use namada_sdk::tx::display_batch_resp; use namada_sdk::wallet::AddressVpType; use namada_sdk::{error, state as storage, token, Namada}; @@ -372,11 +374,13 @@ pub async fn query_rewards_estimate( .await .unwrap() .unsigned_abs(); - let rewards_estimate = - DenominatedAmount::new(Amount::from_u128(rewards_estimate), 6.into()); + let rewards_estimate = DenominatedAmount::new( + Amount::from_u128(rewards_estimate), + NATIVE_MAX_DECIMAL_PLACES.into(), + ); display_line!( context.io(), - "Estimated nam rewards for the next MASP epoch: {}", + "Estimated native token rewards for the next MASP epoch: {}", rewards_estimate ); } diff --git a/crates/tests/src/integration/masp.rs b/crates/tests/src/integration/masp.rs index 366af3a4ce..a8ea6b906a 100644 --- a/crates/tests/src/integration/masp.rs +++ b/crates/tests/src/integration/masp.rs @@ -1364,7 +1364,9 @@ fn masp_incentives() -> Result<()> { }); assert!(captured.result.is_ok()); assert!( - captured.contains("Estimated nam rewards for the next MASP epoch: 0") + captured.contains( + "Estimated native token rewards for the next MASP epoch: 0" + ) ); // Wait till epoch boundary @@ -1432,10 +1434,9 @@ fn masp_incentives() -> Result<()> { }); assert!(captured.result.is_ok()); // note that 0.126 = 2 * 0.063 which is expected - assert!( - captured - .contains("Estimated nam rewards for the next MASP epoch: 0.126") - ); + assert!(captured.contains( + "Estimated native token rewards for the next MASP epoch: 0.126" + )); // Assert NAM balance at MASP pool is exclusively the // rewards from the shielded BTC