diff --git a/apps/src/lib/node/ledger/shell/init_chain.rs b/apps/src/lib/node/ledger/shell/init_chain.rs index 18d7acd749d..710974dd884 100644 --- a/apps/src/lib/node/ledger/shell/init_chain.rs +++ b/apps/src/lib/node/ledger/shell/init_chain.rs @@ -18,7 +18,8 @@ use namada::types::key::*; use namada::types::storage::KeySeg; use namada::types::time::{DateTimeUtc, TimeZone, Utc}; use namada::types::token::{ - MASP_NOTE_COMMITMENT_ANCHOR_PREFIX, MASP_NOTE_COMMITMENT_TREE_KEY, + MASP_CONVERT_ANCHOR_KEY, MASP_NOTE_COMMITMENT_ANCHOR_PREFIX, + MASP_NOTE_COMMITMENT_TREE_KEY, }; use namada::vm::validate_untrusted_wasm; use namada_sdk::eth_bridge::EthBridgeStatus; @@ -200,6 +201,20 @@ where .write(&commitment_tree_anchor_key, ()) .unwrap(); + // Init masp convert anchor + let convert_anchor_key = Key::from(MASP.to_db_key()) + .push(&MASP_CONVERT_ANCHOR_KEY.to_owned()) + .expect("Cannot obtain a storage key"); + self.wl_storage.write( + &convert_anchor_key, + namada::core::types::hash::Hash( + bls12_381::Scalar::from( + self.wl_storage.storage.conversion_state.tree.root(), + ) + .to_bytes(), + ), + )?; + // Set the initial validator set response.validators = self .get_abci_validator_updates(true, |pk, power| { diff --git a/core/src/ledger/masp_conversions.rs b/core/src/ledger/masp_conversions.rs index c3d32ae1803..26e04abcef2 100644 --- a/core/src/ledger/masp_conversions.rs +++ b/core/src/ledger/masp_conversions.rs @@ -5,6 +5,7 @@ use std::collections::BTreeMap; use borsh::{BorshDeserialize, BorshSerialize}; use borsh_ext::BorshSerializeExt; use masp_primitives::asset_type::AssetType; +use masp_primitives::bls12_381; use masp_primitives::convert::AllowedConversion; use masp_primitives::merkle_tree::FrozenCommitmentTree; use masp_primitives::sapling::Node; @@ -211,7 +212,7 @@ where use rayon::prelude::ParallelSlice; use crate::types::address; - use crate::types::token::MASP_CONVERT_ANCHOR_PREFIX; + use crate::types::token::MASP_CONVERT_ANCHOR_KEY; // The derived conversions will be placed in MASP address space let masp_addr = MASP; @@ -455,18 +456,19 @@ where // obtained wl_storage.storage.conversion_state.tree = FrozenCommitmentTree::merge(&tree_parts); - // Publish the new anchor in storage + // Update the anchor in storage let anchor_key = Key::from(MASP.to_db_key()) - .push(&MASP_CONVERT_ANCHOR_PREFIX.to_owned()) - .expect("Cannot obtain a storage key") - .push(&crate::types::hash::Hash( - masp_primitives::bls12_381::Scalar::from( + .push(&MASP_CONVERT_ANCHOR_KEY.to_owned()) + .expect("Cannot obtain a storage key"); + wl_storage.write( + &anchor_key, + crate::types::hash::Hash( + bls12_381::Scalar::from( wl_storage.storage.conversion_state.tree.root(), ) .to_bytes(), - )) - .expect("Cannot obtain a storage key"); - wl_storage.write(&anchor_key, ())?; + ), + )?; // Add purely decoding entries to the assets map. These will be // overwritten before the creation of the next commitment tree diff --git a/core/src/types/token.rs b/core/src/types/token.rs index 8081e5ab7dd..e5d6c3706b9 100644 --- a/core/src/types/token.rs +++ b/core/src/types/token.rs @@ -907,7 +907,7 @@ pub const MASP_NOTE_COMMITMENT_TREE_KEY: &str = "commitment_tree"; /// Key segment prefix for the note commitment anchor pub const MASP_NOTE_COMMITMENT_ANCHOR_PREFIX: &str = "note_commitment_anchor"; /// Key segment prefix for the convert anchor -pub const MASP_CONVERT_ANCHOR_PREFIX: &str = "convert_anchor"; +pub const MASP_CONVERT_ANCHOR_KEY: &str = "convert_anchor"; /// Last calculated inflation value handed out pub const MASP_LAST_INFLATION_KEY: &str = "last_inflation"; /// The last locked ratio diff --git a/shared/src/ledger/native_vp/masp.rs b/shared/src/ledger/native_vp/masp.rs index 3b7c96c04ce..7bae49dde20 100644 --- a/shared/src/ledger/native_vp/masp.rs +++ b/shared/src/ledger/native_vp/masp.rs @@ -20,7 +20,7 @@ use namada_core::types::storage::{BlockHeight, Epoch, Key, KeySeg, TxIndex}; use namada_core::types::token::{ self, is_masp_allowed_key, is_masp_key, is_masp_nullifier_key, is_masp_tx_pin_key, is_masp_tx_prefix_key, Transfer, HEAD_TX_KEY, - MASP_CONVERT_ANCHOR_PREFIX, MASP_NOTE_COMMITMENT_ANCHOR_PREFIX, + MASP_CONVERT_ANCHOR_KEY, MASP_NOTE_COMMITMENT_ANCHOR_PREFIX, MASP_NOTE_COMMITMENT_TREE_KEY, MASP_NULLIFIERS_KEY_PREFIX, PIN_KEY_PREFIX, TX_KEY_PREFIX, }; @@ -256,24 +256,31 @@ where &self, transaction: &Transaction, ) -> Result { - for description in transaction - .sapling_bundle() - .map_or(&vec![], |bundle| &bundle.shielded_converts) - { - let anchor_key = Key::from(MASP.to_db_key()) - .push(&MASP_CONVERT_ANCHOR_PREFIX.to_owned()) - .expect("Cannot obtain a storage key") - .push(&namada_core::types::hash::Hash( - description.anchor.to_bytes(), - )) - .expect("Cannot obtain a storage key"); - - // Check if the provided anchor was published before - if !self.ctx.has_key_pre(&anchor_key)? { - tracing::debug!( - "Convert description refers to an invalid anchor" - ); - return Ok(false); + if let Some(bundle) = transaction.sapling_bundle() { + if !bundle.shielded_converts.is_empty() { + let anchor_key = Key::from(MASP.to_db_key()) + .push(&MASP_CONVERT_ANCHOR_KEY.to_owned()) + .expect("Cannot obtain a storage key"); + let expected_anchor = self + .ctx + .read_pre::(&anchor_key)? + .ok_or(Error::NativeVpError( + native_vp::Error::SimpleMessage("Cannot read storage"), + ))?; + + for description in &bundle.shielded_converts { + // Check if the provided anchor matches the current + // conversion tree's one + if namada_core::types::hash::Hash( + description.anchor.to_bytes(), + ) != expected_anchor + { + tracing::debug!( + "Convert description refers to an invalid anchor" + ); + return Ok(false); + } + } } }