diff --git a/.changelog/unreleased/bug-fixes/2261-fix-sdk-0.28.0.md b/.changelog/unreleased/bug-fixes/2261-fix-sdk-0.28.0.md new file mode 100644 index 0000000000..1173c40189 --- /dev/null +++ b/.changelog/unreleased/bug-fixes/2261-fix-sdk-0.28.0.md @@ -0,0 +1,2 @@ +- Fix sdk compilation when using async-send feature flag. + ([\#2261](https://github.com/anoma/namada/pull/2261)) \ No newline at end of file diff --git a/.changelog/unreleased/bug-fixes/2263-fix-missing-changed-keys.md b/.changelog/unreleased/bug-fixes/2263-fix-missing-changed-keys.md new file mode 100644 index 0000000000..90e6e645be --- /dev/null +++ b/.changelog/unreleased/bug-fixes/2263-fix-missing-changed-keys.md @@ -0,0 +1,2 @@ +- Added back missing changed storage keys in transaction results. + ([\#2263](https://github.com/anoma/namada/pull/2263)) \ No newline at end of file diff --git a/.changelog/unreleased/bug-fixes/2264-fix-bp-tree-pruning.md b/.changelog/unreleased/bug-fixes/2264-fix-bp-tree-pruning.md new file mode 100644 index 0000000000..d309f1dbc2 --- /dev/null +++ b/.changelog/unreleased/bug-fixes/2264-fix-bp-tree-pruning.md @@ -0,0 +1,2 @@ +- Fix to skip pruning BridgePool Merkle trees when no signed nonce + ([\#2264](https://github.com/anoma/namada/issues/2264)) \ No newline at end of file diff --git a/.changelog/unreleased/bug-fixes/2270-masp-rewards-fixes.md b/.changelog/unreleased/bug-fixes/2270-masp-rewards-fixes.md new file mode 100644 index 0000000000..8230da5401 --- /dev/null +++ b/.changelog/unreleased/bug-fixes/2270-masp-rewards-fixes.md @@ -0,0 +1,2 @@ +- Initialize token total supply to zero at init chain. + ([\#2270](https://github.com/anoma/namada/pull/2270)) \ No newline at end of file diff --git a/.changelog/unreleased/improvements/2272-cache-masp-keys.md b/.changelog/unreleased/improvements/2272-cache-masp-keys.md new file mode 100644 index 0000000000..c456df797f --- /dev/null +++ b/.changelog/unreleased/improvements/2272-cache-masp-keys.md @@ -0,0 +1,2 @@ +- Preload and cache MASP verifying keys on ledger start-up. + ([\#2272](https://github.com/anoma/namada/pull/2272)) \ No newline at end of file diff --git a/.changelog/unreleased/improvements/2273-pre-gen-wallet.md b/.changelog/unreleased/improvements/2273-pre-gen-wallet.md new file mode 100644 index 0000000000..0b604f5643 --- /dev/null +++ b/.changelog/unreleased/improvements/2273-pre-gen-wallet.md @@ -0,0 +1,2 @@ +- Now join-network will try to look for non validator wallet in more places. + ([\#2273](https://github.com/anoma/namada/pull/2273)) \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index d5aa62a40a..94b614e382 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4440,6 +4440,7 @@ dependencies = [ "fd-lock", "futures", "itertools 0.10.5", + "lazy_static", "masp_primitives", "masp_proofs", "namada_core", diff --git a/Makefile b/Makefile index 339eb6c79e..6b7441b01a 100644 --- a/Makefile +++ b/Makefile @@ -84,7 +84,8 @@ check-mainnet: check-crates: $(foreach p,$(crates), echo "Checking $(p)" && cargo +$(nightly) check -Z unstable-options --tests -p $(p) && ) \ make -C $(wasms_for_tests) check && \ - cargo check --package namada --target wasm32-unknown-unknown --no-default-features --features "namada-sdk" + cargo check --package namada --target wasm32-unknown-unknown --no-default-features --features "namada-sdk" && \ + cargo check --package namada_sdk --all-features clippy-wasm = $(cargo) +$(nightly) clippy --manifest-path $(wasm)/Cargo.toml --all-targets -- -D warnings diff --git a/apps/src/lib/client/utils.rs b/apps/src/lib/client/utils.rs index 1d80f71f23..01e764c0a2 100644 --- a/apps/src/lib/client/utils.rs +++ b/apps/src/lib/client/utils.rs @@ -111,11 +111,12 @@ pub async fn join_network( // Pre-load the validator pre-genesis wallet and its keys to validate that // everything is in place before downloading the network archive - let validator_alias_and_pre_genesis_wallet = - validator_alias_and_dir.map(|(validator_alias, pre_genesis_dir)| { + let validator_alias_and_pre_genesis_wallet = validator_alias_and_dir + .as_ref() + .map(|(validator_alias, pre_genesis_dir)| { ( alias::Alias::from(validator_alias), - pre_genesis::load(&pre_genesis_dir).unwrap_or_else(|err| { + pre_genesis::load(pre_genesis_dir).unwrap_or_else(|err| { eprintln!( "Error loading validator pre-genesis wallet {err}", ); @@ -262,7 +263,15 @@ pub async fn join_network( // Try to load pre-genesis wallet, if any let pre_genesis_wallet_path = base_dir.join(PRE_GENESIS_DIR); - let pre_genesis_wallet = crate::wallet::load(&pre_genesis_wallet_path); + let pre_genesis_wallet = + if let Some(wallet) = crate::wallet::load(&pre_genesis_wallet_path) { + Some(wallet) + } else { + validator_alias_and_dir + .as_ref() + .and_then(|(_, path)| crate::wallet::load(path)) + }; + // Derive wallet from genesis let wallet = genesis.derive_wallet( &chain_dir, diff --git a/apps/src/lib/node/ledger/mod.rs b/apps/src/lib/node/ledger/mod.rs index 1d90bb2877..11bd75aba5 100644 --- a/apps/src/lib/node/ledger/mod.rs +++ b/apps/src/lib/node/ledger/mod.rs @@ -259,6 +259,10 @@ async fn run_aux(config: config::Ledger, wasm_dir: PathBuf) { } }; + tracing::info!("Loading MASP verifying keys."); + let _ = namada_sdk::masp::preload_verifying_keys(); + tracing::info!("Done loading MASP verifying keys."); + // Start ABCI server and broadcaster (the latter only if we are a validator // node) let (abci, broadcaster, shell_handler) = start_abci_broadcaster_shell( diff --git a/apps/src/lib/node/ledger/shell/finalize_block.rs b/apps/src/lib/node/ledger/shell/finalize_block.rs index 56c259ccbd..239b7b35da 100644 --- a/apps/src/lib/node/ledger/shell/finalize_block.rs +++ b/apps/src/lib/node/ledger/shell/finalize_block.rs @@ -431,7 +431,8 @@ where tx_event["hash"], result ); - changed_keys.append(&mut result.changed_keys); + changed_keys + .extend(result.changed_keys.iter().cloned()); stats.increment_successful_txs(); if let Some(wrapper) = embedding_wrapper { self.commit_inner_tx_hash(wrapper); diff --git a/apps/src/lib/node/ledger/storage/mod.rs b/apps/src/lib/node/ledger/storage/mod.rs index e17ff0db9a..4a02b2bcf9 100644 --- a/apps/src/lib/node/ledger/storage/mod.rs +++ b/apps/src/lib/node/ledger/storage/mod.rs @@ -591,11 +591,8 @@ mod tests { ); let new_epoch_start = BlockHeight(1); let signed_root_key = bridge_pool::get_signed_root_key(); + // the first nonce isn't written for a test skipping pruning let nonce = Uint::default(); - let root_proof = - BridgePoolRootProof::new((KeccakHash::default(), nonce)); - let bytes = types::encode(&root_proof); - storage.write(&signed_root_key, bytes).unwrap(); storage .begin_block(BlockHash::default(), new_epoch_start) @@ -622,11 +619,8 @@ mod tests { .write(&key, types::encode(&value)) .expect("write failed"); + // the second nonce isn't written for a test skipping pruning let nonce = nonce + 1; - let root_proof = - BridgePoolRootProof::new((KeccakHash::default(), nonce)); - let bytes = types::encode(&root_proof); - storage.write(&signed_root_key, bytes).unwrap(); storage.block.epoch = storage.block.epoch.next(); storage.block.pred_epochs.new_epoch(new_epoch_start); diff --git a/apps/src/lib/node/ledger/storage/rocksdb.rs b/apps/src/lib/node/ledger/storage/rocksdb.rs index a1a26b272f..7b2df4dc40 100644 --- a/apps/src/lib/node/ledger/storage/rocksdb.rs +++ b/apps/src/lib/node/ledger/storage/rocksdb.rs @@ -1453,17 +1453,21 @@ impl DB for RocksDB { &self, height: BlockHeight, last_height: BlockHeight, - ) -> Result { + ) -> Result> { let nonce_key = bridge_pool::get_signed_root_key(); let bytes = if height == BlockHeight(0) || height >= last_height { self.read_subspace_val(&nonce_key)? } else { self.read_subspace_val_with_height(&nonce_key, height, last_height)? }; - let bytes = bytes.expect("Signed root should exist"); - let bp_root_proof = BridgePoolRootProof::try_from_slice(&bytes) - .map_err(Error::BorshCodingError)?; - Ok(bp_root_proof.data.1) + match bytes { + Some(bytes) => { + let bp_root_proof = BridgePoolRootProof::try_from_slice(&bytes) + .map_err(Error::BorshCodingError)?; + Ok(Some(bp_root_proof.data.1)) + } + None => Ok(None), + } } fn write_replay_protection_entry( diff --git a/core/src/ledger/storage/mockdb.rs b/core/src/ledger/storage/mockdb.rs index a60c6ccd2e..94d949fa51 100644 --- a/core/src/ledger/storage/mockdb.rs +++ b/core/src/ledger/storage/mockdb.rs @@ -593,8 +593,8 @@ impl DB for MockDB { &self, _height: BlockHeight, _last_height: BlockHeight, - ) -> Result { - Ok(Uint::default()) + ) -> Result> { + Ok(None) } fn write_replay_protection_entry( diff --git a/core/src/ledger/storage/mod.rs b/core/src/ledger/storage/mod.rs index e40cbcc017..6a51b207a8 100644 --- a/core/src/ledger/storage/mod.rs +++ b/core/src/ledger/storage/mod.rs @@ -363,7 +363,7 @@ pub trait DB: std::fmt::Debug { &self, height: BlockHeight, last_height: BlockHeight, - ) -> Result; + ) -> Result>; /// Write a replay protection entry fn write_replay_protection_entry( @@ -1178,7 +1178,10 @@ where } // Prune the BridgePool subtree stores with invalid nonce - let mut epoch = self.get_oldest_epoch_with_valid_nonce()?; + let mut epoch = match self.get_oldest_epoch_with_valid_nonce()? { + Some(epoch) => epoch, + None => return Ok(()), + }; while oldest_epoch < epoch { epoch = epoch.prev(); self.db.prune_merkle_tree_store( @@ -1216,11 +1219,15 @@ where } /// Get oldest epoch which has the valid signed nonce of the bridge pool - pub fn get_oldest_epoch_with_valid_nonce(&self) -> Result { + fn get_oldest_epoch_with_valid_nonce(&self) -> Result> { let last_height = self.get_last_block_height(); - let current_nonce = self + let current_nonce = match self .db - .read_bridge_pool_signed_nonce(last_height, last_height)?; + .read_bridge_pool_signed_nonce(last_height, last_height)? + { + Some(nonce) => nonce, + None => return Ok(None), + }; let (mut epoch, _) = self.get_last_epoch(); // We don't need to check the older epochs because their Merkle tree // snapshots have been already removed @@ -1235,13 +1242,19 @@ where Some(h) => h, None => continue, }; - let nonce = - self.db.read_bridge_pool_signed_nonce(height, last_height)?; + let nonce = match self + .db + .read_bridge_pool_signed_nonce(height, last_height)? + { + Some(nonce) => nonce, + // skip pruning when the old epoch doesn't have the signed nonce + None => break, + }; if nonce < current_nonce { break; } } - Ok(epoch) + Ok(Some(epoch)) } /// Check it the given transaction's hash is already present in storage diff --git a/core/src/types/token.rs b/core/src/types/token.rs index 9682fb132a..6a730e9889 100644 --- a/core/src/types/token.rs +++ b/core/src/types/token.rs @@ -1052,6 +1052,9 @@ impl Parameters { wl_storage .write(&masp_kd_gain_key(address), kd_gain_nom) .expect("The nominal derivative gain must be initialized"); + wl_storage + .write(&minted_balance_key(address), Amount::zero()) + .expect("The total minted balance key must initialized"); } } diff --git a/sdk/Cargo.toml b/sdk/Cargo.toml index e0f19b33a0..3fd7219a9f 100644 --- a/sdk/Cargo.toml +++ b/sdk/Cargo.toml @@ -67,6 +67,7 @@ ethers.workspace = true fd-lock = { workspace = true, optional = true } futures.workspace = true itertools.workspace = true +lazy_static.workspace= true masp_primitives.workspace = true masp_proofs.workspace = true namada_core = {path = "../core", default-features = false, features = ["rand"]} diff --git a/sdk/src/lib.rs b/sdk/src/lib.rs index 3f8c607840..29aab3041a 100644 --- a/sdk/src/lib.rs +++ b/sdk/src/lib.rs @@ -542,11 +542,13 @@ pub trait Namada: Sized + MaybeSync + MaybeSend { tx: &mut Tx, args: &args::Tx, signing_data: SigningTxData, - with: impl Fn(Tx, common::PublicKey, HashSet, D) -> F, + with: impl Fn(Tx, common::PublicKey, HashSet, D) -> F + + MaybeSend + + MaybeSync, user_data: D, ) -> crate::error::Result<()> where - D: Clone, + D: Clone + MaybeSend + MaybeSync, F: MaybeSend + MaybeSync + std::future::Future>, diff --git a/sdk/src/masp.rs b/sdk/src/masp.rs index 79085893a7..68467e2673 100644 --- a/sdk/src/masp.rs +++ b/sdk/src/masp.rs @@ -11,6 +11,7 @@ use std::path::PathBuf; use borsh::{BorshDeserialize, BorshSerialize}; use borsh_ext::BorshSerializeExt; use itertools::Either; +use lazy_static::lazy_static; use masp_primitives::asset_type::AssetType; #[cfg(feature = "mainnet")] use masp_primitives::consensus::MainNetwork; @@ -137,38 +138,61 @@ pub enum TransferErr { General(#[from] Error), } -fn load_pvks() -> ( - PreparedVerifyingKey, - PreparedVerifyingKey, - PreparedVerifyingKey, -) { - let params_dir = get_params_dir(); - let [spend_path, convert_path, output_path] = - [SPEND_NAME, CONVERT_NAME, OUTPUT_NAME].map(|p| params_dir.join(p)); - - #[cfg(feature = "download-params")] - if !spend_path.exists() || !convert_path.exists() || !output_path.exists() { - let paths = masp_proofs::download_masp_parameters(None).expect( - "MASP parameters were not present, expected the download to \ - succeed", - ); - if paths.spend != spend_path - || paths.convert != convert_path - || paths.output != output_path +/// MASP verifying keys +pub struct PVKs { + /// spend verifying key + spend_vk: PreparedVerifyingKey, + /// convert verifying key + convert_vk: PreparedVerifyingKey, + /// output verifying key + output_vk: PreparedVerifyingKey, +} + +lazy_static! { + /// MASP verifying keys load from parameters + static ref VERIFIYING_KEYS: PVKs = { - panic!( - "unrecoverable: downloaded missing masp params, but to an \ - unfamiliar path" - ) + let params_dir = get_params_dir(); + let [spend_path, convert_path, output_path] = + [SPEND_NAME, CONVERT_NAME, OUTPUT_NAME].map(|p| params_dir.join(p)); + + #[cfg(feature = "download-params")] + if !spend_path.exists() || !convert_path.exists() || !output_path.exists() { + let paths = masp_proofs::download_masp_parameters(None).expect( + "MASP parameters were not present, expected the download to \ + succeed", + ); + if paths.spend != spend_path + || paths.convert != convert_path + || paths.output != output_path + { + panic!( + "unrecoverable: downloaded missing masp params, but to an \ + unfamiliar path" + ) + } } - } - // size and blake2b checked here - let params = masp_proofs::load_parameters( - spend_path.as_path(), - output_path.as_path(), - convert_path.as_path(), - ); - (params.spend_vk, params.convert_vk, params.output_vk) + // size and blake2b checked here + let params = masp_proofs::load_parameters( + spend_path.as_path(), + output_path.as_path(), + convert_path.as_path(), + ); + PVKs { + spend_vk: params.spend_vk, + convert_vk: params.convert_vk, + output_vk: params.output_vk + } + }; +} + +/// Make sure the MASP params are present and load verifying keys into memory +pub fn preload_verifying_keys() -> &'static PVKs { + &VERIFIYING_KEYS +} + +fn load_pvks() -> &'static PVKs { + &VERIFIYING_KEYS } /// check_spend wrapper @@ -300,20 +324,25 @@ pub fn verify_shielded_tx(transaction: &Transaction) -> bool { tracing::info!("sighash computed"); - let (spend_pvk, convert_pvk, output_pvk) = load_pvks(); + let PVKs { + spend_vk, + convert_vk, + output_vk, + } = load_pvks(); let mut ctx = SaplingVerificationContext::new(true); - let spends_valid = sapling_bundle.shielded_spends.iter().all(|spend| { - check_spend(spend, sighash.as_ref(), &mut ctx, &spend_pvk) - }); + let spends_valid = sapling_bundle + .shielded_spends + .iter() + .all(|spend| check_spend(spend, sighash.as_ref(), &mut ctx, spend_vk)); let converts_valid = sapling_bundle .shielded_converts .iter() - .all(|convert| check_convert(convert, &mut ctx, &convert_pvk)); + .all(|convert| check_convert(convert, &mut ctx, convert_vk)); let outputs_valid = sapling_bundle .shielded_outputs .iter() - .all(|output| check_output(output, &mut ctx, &output_pvk)); + .all(|output| check_output(output, &mut ctx, output_vk)); if !(spends_valid && outputs_valid && converts_valid) { return false; @@ -2021,8 +2050,8 @@ mod tests { .expect("expected a temp dir") .into_path(); let fake_params_paths = - [SPEND_NAME, CONVERT_NAME, OUTPUT_NAME].map(|p| tempdir.join(p)); - for path in fake_params_paths { + [SPEND_NAME, OUTPUT_NAME, CONVERT_NAME].map(|p| tempdir.join(p)); + for path in &fake_params_paths { let mut f = std::fs::File::create(path).expect("expected a temp file"); f.write_all(b"fake params") @@ -2033,7 +2062,11 @@ mod tests { std::env::set_var(super::ENV_VAR_MASP_PARAMS_DIR, tempdir.as_os_str()); // should panic here - super::load_pvks(); + masp_proofs::load_parameters( + &fake_params_paths[0], + &fake_params_paths[1], + &fake_params_paths[2], + ); } /// a more involved test, using dummy parameters with the right @@ -2085,11 +2118,11 @@ mod tests { // TODO: get masp to export these consts let fake_params_paths = [ (SPEND_NAME, 49848572u64), - (CONVERT_NAME, 22570940u64), (OUTPUT_NAME, 16398620u64), + (CONVERT_NAME, 22570940u64), ] .map(|(p, s)| (tempdir.join(p), s)); - for (path, size) in fake_params_paths { + for (path, size) in &fake_params_paths { let mut f = std::fs::File::create(path).expect("expected a temp file"); fake_params @@ -2099,14 +2132,19 @@ mod tests { // params should always be smaller than the large masp // circuit params. so this truncate extends the file, and // extra bytes at the end do not make it invalid. - f.set_len(size).expect("expected to truncate the temp file"); + f.set_len(*size) + .expect("expected to truncate the temp file"); f.sync_all() .expect("expected a writable temp file (on sync)"); } std::env::set_var(super::ENV_VAR_MASP_PARAMS_DIR, tempdir.as_os_str()); // should panic here - super::load_pvks(); + masp_proofs::load_parameters( + &fake_params_paths[0].0, + &fake_params_paths[1].0, + &fake_params_paths[2].0, + ); } } diff --git a/sdk/src/signing.rs b/sdk/src/signing.rs index d5e8132af8..7f854b59b0 100644 --- a/sdk/src/signing.rs +++ b/sdk/src/signing.rs @@ -58,7 +58,7 @@ use crate::tx::{ }; pub use crate::wallet::store::AddressVpType; use crate::wallet::{Wallet, WalletIo}; -use crate::{args, display_line, rpc, Namada}; +use crate::{args, display_line, rpc, MaybeSend, Namada}; #[cfg(feature = "std")] /// Env. var specifying where to store signing test vectors @@ -217,7 +217,7 @@ pub async fn sign_tx<'a, D, F, U>( user_data: D, ) -> Result<(), Error> where - D: Clone, + D: Clone + MaybeSend, U: WalletIo, F: std::future::Future>, { diff --git a/wasm/Cargo.lock b/wasm/Cargo.lock index 47f9f746fd..ed95945bfe 100644 --- a/wasm/Cargo.lock +++ b/wasm/Cargo.lock @@ -3476,6 +3476,7 @@ dependencies = [ "ethers", "futures", "itertools 0.10.5", + "lazy_static", "masp_primitives", "masp_proofs", "namada_core", diff --git a/wasm_for_tests/wasm_source/Cargo.lock b/wasm_for_tests/wasm_source/Cargo.lock index 4016982ed0..34e1113749 100644 --- a/wasm_for_tests/wasm_source/Cargo.lock +++ b/wasm_for_tests/wasm_source/Cargo.lock @@ -3476,6 +3476,7 @@ dependencies = [ "ethers", "futures", "itertools 0.10.5", + "lazy_static", "masp_primitives", "masp_proofs", "namada_core",