Skip to content

Commit 676d5d3

Browse files
committed
Implemented reading conversion updates from memory.
1 parent 005845d commit 676d5d3

File tree

14 files changed

+668
-41
lines changed

14 files changed

+668
-41
lines changed

Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/core/src/chain.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,7 @@ impl Epoch {
357357
pub fn iter_bounds_inclusive(
358358
start: Self,
359359
end: Self,
360-
) -> impl Iterator<Item = Epoch> + Clone {
360+
) -> impl DoubleEndedIterator<Item = Epoch> + Clone {
361361
let start_ix = start.0;
362362
let end_ix = end.0;
363363
(start_ix..=end_ix).map(Epoch::from)

crates/core/src/masp.rs

+8
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,14 @@ impl MaspEpoch {
135135
))
136136
}
137137

138+
/// Iterate a range of epochs, inclusive of the start and end.
139+
pub fn iter_bounds_inclusive(
140+
start: Self,
141+
end: Self,
142+
) -> impl DoubleEndedIterator<Item = Self> + Clone {
143+
Epoch::iter_bounds_inclusive(start.0, end.0).map(Self)
144+
}
145+
138146
/// Returns a 0 masp epoch
139147
pub const fn zero() -> Self {
140148
Self(Epoch(0))

crates/node/src/shell/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -402,7 +402,7 @@ pub fn is_key_diff_storable(key: &namada_sdk::storage::Key) -> bool {
402402
&& *key != token::storage_key::masp_convert_anchor_key()
403403
&& *key != token::storage_key::masp_token_map_key()
404404
&& *key != token::storage_key::masp_assets_hash_key()
405-
&& !token::storage_key::is_masp_commitment_anchor_key(key)
405+
&& token::storage_key::is_masp_commitment_anchor_key(key).is_none()
406406
|| ibc::storage::is_ibc_counter_key(key)
407407
|| proof_of_stake::storage_key::is_delegation_targets_key(key))
408408
}

crates/node/src/shell/testing/node.rs

+11
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ use crate::ethereum_oracle::{
5050
control, last_processed_block, try_process_eth_events,
5151
};
5252
use crate::shell::testing::utils::TestDir;
53+
use crate::shell::token::MaspEpoch;
5354
use crate::shell::{EthereumOracleChannels, Shell};
5455
use crate::shims::abcipp_shim_types::shim::request::{
5556
FinalizeBlock, ProcessedTx,
@@ -407,6 +408,16 @@ impl MockNode {
407408
.0
408409
}
409410

411+
pub fn current_masp_epoch(&mut self) -> MaspEpoch {
412+
let masp_epoch_multiplier =
413+
namada_sdk::parameters::read_masp_epoch_multiplier_parameter(
414+
&self.shell.lock().unwrap().state,
415+
)
416+
.unwrap();
417+
let current_epoch = self.current_epoch();
418+
MaspEpoch::try_from_epoch(current_epoch, masp_epoch_multiplier).unwrap()
419+
}
420+
410421
pub fn next_masp_epoch(&mut self) -> Epoch {
411422
let masp_epoch_multiplier =
412423
namada_sdk::parameters::read_masp_epoch_multiplier_parameter(

crates/shielded_token/src/conversion.rs

+53-5
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,18 @@ use namada_core::masp::MaspEpoch;
2727
use namada_core::token::MaspDigitPos;
2828
use namada_core::token::{Amount, DenominatedAmount, Denomination};
2929
use namada_core::uint::Uint;
30+
use namada_state::iter_prefix_with_filter_map;
3031
use namada_systems::{parameters, trans_token};
3132

32-
#[cfg(any(feature = "multicore", test))]
33-
use crate::storage_key::{masp_assets_hash_key, masp_token_map_key};
3433
use crate::storage_key::{
35-
masp_kd_gain_key, masp_kp_gain_key, masp_last_inflation_key,
36-
masp_last_locked_amount_key, masp_locked_amount_target_key,
37-
masp_max_reward_rate_key, masp_reward_precision_key,
34+
is_masp_conversion_key, masp_conversion_key_prefix, masp_kd_gain_key,
35+
masp_kp_gain_key, masp_last_inflation_key, masp_last_locked_amount_key,
36+
masp_locked_amount_target_key, masp_max_reward_rate_key,
37+
masp_reward_precision_key,
3838
};
3939
#[cfg(any(feature = "multicore", test))]
40+
use crate::storage_key::{masp_assets_hash_key, masp_token_map_key};
41+
#[cfg(any(feature = "multicore", test))]
4042
use crate::{ConversionLeaf, Error, OptionExt, ResultExt};
4143
use crate::{Result, StorageRead, StorageWrite, WithConversionState};
4244

@@ -516,6 +518,51 @@ where
516518
Ok(denom)
517519
}
518520

521+
#[cfg(any(feature = "multicore", test))]
522+
/// Apply the conversion updates that are in storage to the in memory structure
523+
/// and delete them.
524+
fn apply_stored_conversion_updates<S>(storage: &mut S) -> Result<()>
525+
where
526+
S: StorageWrite + StorageRead + WithConversionState,
527+
{
528+
use masp_primitives::transaction::components::I128Sum;
529+
530+
let conversion_key_prefix = masp_conversion_key_prefix();
531+
let mut conversion_updates = BTreeMap::new();
532+
// Read conversion updates from storage and store them in a map
533+
for conv_result in iter_prefix_with_filter_map(
534+
storage,
535+
&conversion_key_prefix,
536+
is_masp_conversion_key,
537+
)? {
538+
match conv_result {
539+
Ok((asset_type, conv)) => {
540+
conversion_updates.insert(asset_type, conv);
541+
}
542+
Err(err) => {
543+
tracing::warn!("Encountered malformed conversion: {}", err);
544+
continue;
545+
}
546+
}
547+
}
548+
// Apply the conversion updates to the in memory structure
549+
let assets = &mut storage.conversion_state_mut().assets;
550+
for (asset_type, conv) in conversion_updates {
551+
let Some(leaf) = assets.get_mut(&asset_type) else {
552+
tracing::warn!(
553+
"Encountered non-existent asset type: {}",
554+
asset_type
555+
);
556+
continue;
557+
};
558+
// This operation will be expensive for large conversions
559+
leaf.conversion = From::<I128Sum>::from(conv);
560+
}
561+
// Delete the updates now that they have been applied
562+
storage.delete_prefix(&conversion_key_prefix)?;
563+
Ok(())
564+
}
565+
519566
#[cfg(any(feature = "multicore", test))]
520567
/// Update the MASP's allowed conversions
521568
pub fn update_allowed_conversions<S, Params, TransToken>(
@@ -542,6 +589,7 @@ where
542589

543590
use crate::mint_rewards;
544591

592+
apply_stored_conversion_updates(storage)?;
545593
let token_map_key = masp_token_map_key();
546594
let token_map: namada_core::masp::TokenMap =
547595
storage.read(&token_map_key)?.unwrap_or_default();

crates/shielded_token/src/storage_key.rs

+63-18
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22
33
use std::str::FromStr;
44

5+
use masp_primitives::asset_type::AssetType;
56
use masp_primitives::bls12_381::Scalar;
6-
use masp_primitives::sapling::Nullifier;
7+
use masp_primitives::sapling::{Node, Nullifier};
78
use namada_core::address::{self, Address};
89
use namada_core::hash::Hash;
910
use namada_core::storage::{self, DbKeySeg, KeySeg};
@@ -15,6 +16,8 @@ const BALANCE_STORAGE_KEY: &str = "balance";
1516
pub const MASP_NULLIFIERS_KEY: &str = "nullifiers";
1617
/// The key for the masp reward balance
1718
pub const MASP_UNDATED_BALANCE_KEY: &str = "undated_balance";
19+
/// Key segment prefix for the conversions
20+
pub const MASP_CONVERSIONS_KEY: &str = "conversions";
1821
/// Key segment prefix for the note commitment merkle tree
1922
pub const MASP_NOTE_COMMITMENT_TREE_KEY: &str = "commitment_tree";
2023
/// Key segment prefix for the note commitment anchor
@@ -141,21 +144,26 @@ pub fn is_masp_key(key: &storage::Key) -> bool {
141144
}
142145
}
143146

147+
/// Check if the given storage key is allowed to be touched by a governance
148+
/// proposal
149+
pub fn is_masp_governance_key(key: &storage::Key) -> bool {
150+
is_masp_token_map_key(key) || is_masp_conversion_key(key).is_some()
151+
}
152+
144153
/// Check if the given storage key is allowed to be touched by a masp transfer
145154
pub fn is_masp_transfer_key(key: &storage::Key) -> bool {
146-
match &key.segments[..] {
155+
is_masp_commitment_tree_key(key)
156+
|| is_masp_nullifier_key(key)
157+
|| is_masp_balance_key(key)
158+
|| is_masp_undated_balance_key(key).is_some()
159+
}
160+
161+
/// Check if the given storage key is a masp commitment tree key
162+
pub fn is_masp_commitment_tree_key(key: &storage::Key) -> bool {
163+
matches!(&key.segments[..],
147164
[DbKeySeg::AddressSeg(addr), DbKeySeg::StringSeg(key)]
148165
if *addr == address::MASP
149-
&& key == MASP_NOTE_COMMITMENT_TREE_KEY =>
150-
{
151-
true
152-
}
153-
_ => {
154-
is_masp_nullifier_key(key)
155-
|| is_masp_balance_key(key)
156-
|| is_masp_undated_balance_key(key).is_some()
157-
}
158-
}
166+
&& key == MASP_NOTE_COMMITMENT_TREE_KEY)
159167
}
160168

161169
/// Check if the given storage key is a masp nullifier key
@@ -168,12 +176,19 @@ pub fn is_masp_nullifier_key(key: &storage::Key) -> bool {
168176
}
169177

170178
/// Check if the given key is a masp commitment anchor
171-
pub fn is_masp_commitment_anchor_key(key: &storage::Key) -> bool {
172-
matches!(&key.segments[..],
173-
[DbKeySeg::AddressSeg(addr),
174-
DbKeySeg::StringSeg(prefix),
175-
..
176-
] if *addr == address::MASP && prefix == MASP_NOTE_COMMITMENT_ANCHOR_PREFIX)
179+
pub fn is_masp_commitment_anchor_key(key: &storage::Key) -> Option<Node> {
180+
match &key.segments[..] {
181+
[
182+
DbKeySeg::AddressSeg(addr),
183+
DbKeySeg::StringSeg(prefix),
184+
DbKeySeg::StringSeg(anchor),
185+
] if *addr == address::MASP
186+
&& prefix == MASP_NOTE_COMMITMENT_ANCHOR_PREFIX =>
187+
{
188+
Hash::from_str(anchor).map(|x| Node::new(x.0)).ok()
189+
}
190+
_ => None,
191+
}
177192
}
178193

179194
/// Check if the given storage key is a masp token map key
@@ -184,6 +199,20 @@ pub fn is_masp_token_map_key(key: &storage::Key) -> bool {
184199
] if *addr == address::MASP && prefix == MASP_TOKEN_MAP_KEY)
185200
}
186201

202+
/// Check if the given storage key is a masp nullifier key
203+
pub fn is_masp_conversion_key(key: &storage::Key) -> Option<AssetType> {
204+
match &key.segments[..] {
205+
[
206+
DbKeySeg::AddressSeg(address::MASP),
207+
DbKeySeg::StringSeg(prefix),
208+
DbKeySeg::StringSeg(asset_type),
209+
] if prefix == MASP_CONVERSIONS_KEY => {
210+
AssetType::from_str(asset_type).ok()
211+
}
212+
_ => None,
213+
}
214+
}
215+
187216
/// Get a key for a masp nullifier
188217
pub fn masp_nullifier_key(nullifier: &Nullifier) -> storage::Key {
189218
storage::Key::from(address::MASP.to_db_key())
@@ -193,6 +222,22 @@ pub fn masp_nullifier_key(nullifier: &Nullifier) -> storage::Key {
193222
.expect("Cannot obtain a storage key")
194223
}
195224

225+
/// Get a key for a masp conversion
226+
pub fn masp_conversion_key(asset_type: &AssetType) -> storage::Key {
227+
storage::Key::from(address::MASP.to_db_key())
228+
.push(&MASP_CONVERSIONS_KEY.to_owned())
229+
.expect("Cannot obtain a storage key")
230+
.push(&asset_type.to_string())
231+
.expect("Cannot obtain a storage key")
232+
}
233+
234+
/// Get the key prefix for masp conversions
235+
pub fn masp_conversion_key_prefix() -> storage::Key {
236+
storage::Key::from(address::MASP.to_db_key())
237+
.push(&MASP_CONVERSIONS_KEY.to_owned())
238+
.expect("Cannot obtain a storage key")
239+
}
240+
196241
/// Get the key for the masp commitment tree
197242
pub fn masp_commitment_tree_key() -> storage::Key {
198243
storage::Key::from(address::MASP.to_db_key())

crates/shielded_token/src/vp.rs

+8-8
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ use namada_tx::BatchedTxRef;
3030
use namada_vp_env::{Error, Result, VpEnv};
3131

3232
use crate::storage_key::{
33-
is_masp_key, is_masp_nullifier_key, is_masp_token_map_key,
33+
is_masp_governance_key, is_masp_key, is_masp_nullifier_key,
3434
is_masp_transfer_key, is_masp_undated_balance_key,
3535
masp_commitment_anchor_key, masp_commitment_tree_key,
3636
masp_convert_anchor_key, masp_nullifier_key, masp_undated_balance_key,
@@ -99,7 +99,7 @@ where
9999
let masp_keys_changed: Vec<&Key> =
100100
keys_changed.iter().filter(|key| is_masp_key(key)).collect();
101101
let non_allowed_changes = masp_keys_changed.iter().any(|key| {
102-
!is_masp_transfer_key(key) && !is_masp_token_map_key(key)
102+
!is_masp_transfer_key(key) && !is_masp_governance_key(key)
103103
});
104104

105105
// Check that the transaction didn't write unallowed masp keys
@@ -108,20 +108,20 @@ where
108108
"Found modifications to non-allowed masp keys",
109109
));
110110
}
111-
let masp_token_map_changed = masp_keys_changed
111+
let masp_governance_changes = masp_keys_changed
112112
.iter()
113-
.any(|key| is_masp_token_map_key(key));
113+
.any(|key| is_masp_governance_key(key));
114114
let masp_transfer_changes = masp_keys_changed
115115
.iter()
116116
.any(|key| is_masp_transfer_key(key));
117-
if masp_token_map_changed && masp_transfer_changes {
117+
if masp_governance_changes && masp_transfer_changes {
118118
Err(Error::new_const(
119119
"Cannot simultaneously do governance proposal and MASP \
120120
transfer",
121121
))
122-
} else if masp_token_map_changed {
123-
// The token map can only be changed by a successful governance
124-
// proposal
122+
} else if masp_governance_changes {
123+
// The token map or allowed conversions can only be changed by a
124+
// successful governance proposal
125125
Self::is_valid_parameter_change(ctx, tx_data)
126126
} else if masp_transfer_changes {
127127
// The MASP transfer keys can only be changed by a valid Transaction

crates/state/src/lib.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,9 @@ pub use namada_storage::conversion_state::{
5959
pub use namada_storage::types::{KVBytes, PatternIterator, PrefixIterator};
6060
pub use namada_storage::{
6161
collections, iter_prefix, iter_prefix_bytes, iter_prefix_with_filter,
62-
mockdb, tx_queue, BlockStateRead, BlockStateWrite, DBIter, DBWriteBatch,
63-
DbError, DbResult, Error, OptionExt, Result, ResultExt, StorageHasher,
64-
StorageRead, StorageWrite, DB,
62+
iter_prefix_with_filter_map, mockdb, tx_queue, BlockStateRead,
63+
BlockStateWrite, DBIter, DBWriteBatch, DbError, DbResult, Error, OptionExt,
64+
Result, ResultExt, StorageHasher, StorageRead, StorageWrite, DB,
6565
};
6666
use namada_systems::parameters;
6767
use thiserror::Error;

0 commit comments

Comments
 (0)