diff --git a/.changelog/unreleased/bug-fixes/913-fix-iter-prefix.md b/.changelog/unreleased/bug-fixes/913-fix-iter-prefix.md new file mode 100644 index 00000000000..a242d36c69b --- /dev/null +++ b/.changelog/unreleased/bug-fixes/913-fix-iter-prefix.md @@ -0,0 +1,2 @@ +- Fixed the prefix iterator method to respect modifications in the write log. + ([#913](https://github.com/anoma/namada/pull/913)) \ No newline at end of file diff --git a/apps/src/lib/node/ledger/mod.rs b/apps/src/lib/node/ledger/mod.rs index ca31310b740..06a137bd3a6 100644 --- a/apps/src/lib/node/ledger/mod.rs +++ b/apps/src/lib/node/ledger/mod.rs @@ -58,15 +58,16 @@ const ENV_VAR_RAYON_THREADS: &str = "NAMADA_RAYON_THREADS"; impl Shell { fn load_proposals(&mut self) { let proposals_key = gov_storage::get_commiting_proposals_prefix( - self.storage.last_epoch.0, + self.wl_storage.storage.last_epoch.0, ); - let (proposal_iter, _) = self.storage.iter_prefix(&proposals_key); + let (proposal_iter, _) = + self.wl_storage.storage.iter_prefix(&proposals_key); for (key, _, _) in proposal_iter { let key = Key::from_str(key.as_str()).expect("Key should be parsable"); if gov_storage::get_commit_proposal_epoch(&key).unwrap() - != self.storage.last_epoch.0 + != self.wl_storage.storage.last_epoch.0 { // NOTE: `iter_prefix` iterate over the matching prefix. In this // case a proposal with grace_epoch 110 will be diff --git a/apps/src/lib/node/ledger/shell/finalize_block.rs b/apps/src/lib/node/ledger/shell/finalize_block.rs index 8455522303e..2f2ed4d1e91 100644 --- a/apps/src/lib/node/ledger/shell/finalize_block.rs +++ b/apps/src/lib/node/ledger/shell/finalize_block.rs @@ -2,7 +2,6 @@ use namada::ledger::pos::types::into_tm_voting_power; use namada::ledger::protocol; -use namada::ledger::storage::write_log::StorageModification; use namada::ledger::storage_api::StorageRead; use namada::types::storage::{BlockHash, BlockResults, Header}; use namada::types::token::Amount; @@ -59,7 +58,7 @@ where let mut stats = InternalStats::default(); // Tracks the accepted transactions - self.storage.block.results = BlockResults::default(); + self.wl_storage.storage.block.results = BlockResults::default(); for (tx_index, processed_tx) in req.txs.iter().enumerate() { let tx = if let Ok(tx) = Tx::try_from(processed_tx.tx.as_ref()) { tx @@ -129,7 +128,7 @@ where // if the rejected tx was decrypted, remove it // from the queue of txs to be processed if let TxType::Decrypted(_) = &tx_type { - self.storage.tx_queue.pop(); + self.wl_storage.storage.tx_queue.pop(); } continue; } @@ -152,39 +151,16 @@ where let balance_key = token::balance_key(&wrapper.fee.token, &fee_payer); - let balance: Amount = - match self.write_log.read(&balance_key).0 { - Some(wal_mod) => { - // Read from WAL - if let StorageModification::Write { value } = - wal_mod - { - Amount::try_from_slice(value).unwrap() - } else { - Amount::default() - } - } - None => { - // Read from storage - let balance = StorageRead::read( - &self.storage, - &balance_key, - ); - // Storage read must not fail, but there might - // be no value, in which - // case default (0) is returned - balance - .expect( - "Storage read in the protocol must \ - not fail", - ) - .unwrap_or_default() - } - }; + let balance: token::Amount = self + .wl_storage + .read(&balance_key) + .expect("must be able to read") + .unwrap_or_default(); match balance.checked_sub(wrapper_fees) { Some(amount) => { - self.write_log + self.wl_storage + .storage .write( &balance_key, amount.try_to_vec().unwrap(), @@ -198,7 +174,8 @@ where let reject = true; if reject { // Burn remaining funds - self.write_log + self.wl_storage + .storage .write( &balance_key, Amount::from(0).try_to_vec().unwrap(), @@ -215,7 +192,7 @@ where } } - self.storage.tx_queue.push(WrapperTxInQueue { + self.wl_storage.storage.tx_queue.push(WrapperTxInQueue { tx: wrapper.clone(), #[cfg(not(feature = "mainnet"))] has_valid_pow, @@ -224,7 +201,7 @@ where } TxType::Decrypted(inner) => { // We remove the corresponding wrapper tx from the queue - self.storage.tx_queue.pop(); + self.wl_storage.storage.tx_queue.pop(); let mut event = Event::new_tx_event(&tx_type, height.0); match inner { @@ -271,8 +248,8 @@ where .expect("transaction index out of bounds"), ), &mut self.gas_meter, - &mut self.write_log, - &self.storage, + &mut self.wl_storage.write_log, + &self.wl_storage.storage, &mut self.vp_wasm_cache, &mut self.tx_wasm_cache, ) @@ -287,10 +264,14 @@ where result ); stats.increment_successful_txs(); - self.write_log.commit_tx(); + self.wl_storage.commit_tx(); if !tx_event.contains_key("code") { tx_event["code"] = ErrorCodes::Ok.into(); - self.storage.block.results.accept(tx_index); + self.wl_storage + .storage + .block + .results + .accept(tx_index); } if let Some(ibc_event) = &result.ibc_event { // Add the IBC event besides the tx_event @@ -320,7 +301,7 @@ where result.vps_result.rejected_vps ); stats.increment_rejected_txs(); - self.write_log.drop_tx(); + self.wl_storage.drop_tx(); tx_event["code"] = ErrorCodes::InvalidTx.into(); } tx_event["gas_used"] = result.gas_used.to_string(); @@ -333,7 +314,7 @@ where msg ); stats.increment_errored_txs(); - self.write_log.drop_tx(); + self.wl_storage.drop_tx(); tx_event["gas_used"] = self .gas_meter .get_current_transaction_gas() @@ -382,22 +363,25 @@ where hash: BlockHash, byzantine_validators: Vec, ) -> (BlockHeight, bool) { - let height = self.storage.last_height + 1; + let height = self.wl_storage.storage.last_height + 1; self.gas_meter.reset(); - self.storage + self.wl_storage + .storage .begin_block(hash, height) .expect("Beginning a block shouldn't fail"); let header_time = header.time; - self.storage + self.wl_storage + .storage .set_header(header) .expect("Setting a header shouldn't fail"); self.byzantine_validators = byzantine_validators; let new_epoch = self + .wl_storage .storage .update_epoch(height, header_time) .expect("Must be able to update epoch"); @@ -410,11 +394,11 @@ where /// changes to the validator sets and consensus parameters fn update_epoch(&self, response: &mut shim::response::FinalizeBlock) { // Apply validator set update - let (current_epoch, _gas) = self.storage.get_current_epoch(); - let pos_params = self.storage.read_pos_params(); + let (current_epoch, _gas) = self.wl_storage.storage.get_current_epoch(); + let pos_params = self.wl_storage.read_pos_params(); // TODO ABCI validator updates on block H affects the validator set // on block H+2, do we need to update a block earlier? - self.storage.validator_set_update( + self.wl_storage.validator_set_update( current_epoch, &pos_params, |update| { @@ -473,10 +457,11 @@ mod test_finalize_block { // Add unshielded balance for fee paymenty let balance_key = token::balance_key( - &shell.storage.native_token, + &shell.wl_storage.storage.native_token, &Address::from(&keypair.ref_to()), ); shell + .wl_storage .storage .write(&balance_key, Amount::whole(1000).try_to_vec().unwrap()) .unwrap(); @@ -490,7 +475,7 @@ mod test_finalize_block { let wrapper = WrapperTx::new( Fee { amount: MIN_FEE.into(), - token: shell.storage.native_token.clone(), + token: shell.wl_storage.storage.native_token.clone(), }, &keypair, Epoch(0), @@ -563,7 +548,7 @@ mod test_finalize_block { let wrapper = WrapperTx::new( Fee { amount: 0.into(), - token: shell.storage.native_token.clone(), + token: shell.wl_storage.storage.native_token.clone(), }, &keypair, Epoch(0), @@ -601,7 +586,7 @@ mod test_finalize_block { assert_eq!(code, &String::from(ErrorCodes::InvalidTx)); } // check that the corresponding wrapper tx was removed from the queue - assert!(shell.storage.tx_queue.is_empty()); + assert!(shell.wl_storage.storage.tx_queue.is_empty()); } /// Test that if a tx is undecryptable, it is applied @@ -621,7 +606,7 @@ mod test_finalize_block { let wrapper = WrapperTx { fee: Fee { amount: 0.into(), - token: shell.storage.native_token.clone(), + token: shell.wl_storage.storage.native_token.clone(), }, pk: keypair.ref_to(), epoch: Epoch(0), @@ -659,7 +644,7 @@ mod test_finalize_block { assert!(log.contains("Transaction could not be decrypted.")) } // check that the corresponding wrapper tx was removed from the queue - assert!(shell.storage.tx_queue.is_empty()); + assert!(shell.wl_storage.storage.tx_queue.is_empty()); } /// Test that the wrapper txs are queued in the order they @@ -674,10 +659,11 @@ mod test_finalize_block { // Add unshielded balance for fee paymenty let balance_key = token::balance_key( - &shell.storage.native_token, + &shell.wl_storage.storage.native_token, &Address::from(&keypair.ref_to()), ); shell + .wl_storage .storage .write(&balance_key, Amount::whole(1000).try_to_vec().unwrap()) .unwrap(); @@ -699,7 +685,7 @@ mod test_finalize_block { let wrapper_tx = WrapperTx::new( Fee { amount: MIN_FEE.into(), - token: shell.storage.native_token.clone(), + token: shell.wl_storage.storage.native_token.clone(), }, &keypair, Epoch(0), @@ -736,7 +722,7 @@ mod test_finalize_block { let wrapper_tx = WrapperTx::new( Fee { amount: MIN_FEE.into(), - token: shell.storage.native_token.clone(), + token: shell.wl_storage.storage.native_token.clone(), }, &keypair, Epoch(0), diff --git a/apps/src/lib/node/ledger/shell/governance.rs b/apps/src/lib/node/ledger/shell/governance.rs index 67c49c714d0..ce16a638db3 100644 --- a/apps/src/lib/node/ledger/shell/governance.rs +++ b/apps/src/lib/node/ledger/shell/governance.rs @@ -50,9 +50,10 @@ where ) })?; - let votes = get_proposal_votes(&shell.storage, proposal_end_epoch, id); + let votes = + get_proposal_votes(&shell.wl_storage, proposal_end_epoch, id); let is_accepted = votes.and_then(|votes| { - compute_tally(&shell.storage, proposal_end_epoch, votes) + compute_tally(&shell.wl_storage, proposal_end_epoch, votes) }); let transfer_address = match is_accepted { @@ -82,6 +83,7 @@ where let pending_execution_key = gov_storage::get_proposal_execution_key(id); shell + .wl_storage .storage .write(&pending_execution_key, "") .expect("Should be able to write to storage."); @@ -92,19 +94,20 @@ where * need it here. */ TxIndex::default(), &mut BlockGasMeter::default(), - &mut shell.write_log, - &shell.storage, + &mut shell.wl_storage.write_log, + &shell.wl_storage.storage, &mut shell.vp_wasm_cache, &mut shell.tx_wasm_cache, ); shell + .wl_storage .storage .delete(&pending_execution_key) .expect("Should be able to delete the storage."); match tx_result { Ok(tx_result) => { if tx_result.is_accepted() { - shell.write_log.commit_tx(); + shell.wl_storage.write_log.commit_tx(); let proposal_event: Event = ProposalEvent::new( EventType::Proposal.to_string(), @@ -119,7 +122,7 @@ where proposal_author } else { - shell.write_log.drop_tx(); + shell.wl_storage.write_log.drop_tx(); let proposal_event: Event = ProposalEvent::new( EventType::Proposal.to_string(), @@ -136,7 +139,7 @@ where } } Err(_e) => { - shell.write_log.drop_tx(); + shell.wl_storage.write_log.drop_tx(); let proposal_event: Event = ProposalEvent::new( EventType::Proposal.to_string(), TallyResult::Passed, @@ -201,9 +204,9 @@ where } }; - let native_token = shell.storage.native_token.clone(); + let native_token = shell.wl_storage.storage.native_token.clone(); // transfer proposal locked funds - shell.storage.transfer( + shell.wl_storage.transfer( &native_token, funds, &gov_address, diff --git a/apps/src/lib/node/ledger/shell/init_chain.rs b/apps/src/lib/node/ledger/shell/init_chain.rs index c58a8bd4ac0..a5f74f00af2 100644 --- a/apps/src/lib/node/ledger/shell/init_chain.rs +++ b/apps/src/lib/node/ledger/shell/init_chain.rs @@ -6,6 +6,7 @@ use std::hash::Hash; use namada::core::ledger::testnet_pow; use namada::ledger::parameters::Parameters; use namada::ledger::pos::into_tm_voting_power; +use namada::ledger::storage_api::StorageWrite; use namada::types::key::*; #[cfg(not(feature = "dev"))] use sha2::{Digest, Sha256}; @@ -29,7 +30,7 @@ where init: request::InitChain, ) -> Result { let mut response = response::InitChain::default(); - let (current_chain_id, _) = self.storage.get_chain_id(); + let (current_chain_id, _) = self.wl_storage.storage.get_chain_id(); if current_chain_id != init.chain_id { return Err(Error::ChainId(format!( "Current chain ID: {}, Tendermint chain ID: {}", @@ -37,11 +38,13 @@ where ))); } #[cfg(not(feature = "dev"))] - let genesis = genesis::genesis(&self.base_dir, &self.storage.chain_id); + let genesis = + genesis::genesis(&self.base_dir, &self.wl_storage.storage.chain_id); #[cfg(not(feature = "dev"))] { let genesis_bytes = genesis.try_to_vec().unwrap(); - let errors = self.storage.chain_id.validate(genesis_bytes); + let errors = + self.wl_storage.storage.chain_id.validate(genesis_bytes); use itertools::Itertools; assert!( errors.is_empty(), @@ -135,13 +138,16 @@ where #[cfg(not(feature = "mainnet"))] wrapper_tx_fees, }; - parameters.init_storage(&mut self.storage); + parameters.init_storage(&mut self.wl_storage.storage); // Initialize governance parameters - genesis.gov_params.init_storage(&mut self.storage); + genesis + .gov_params + .init_storage(&mut self.wl_storage.storage); // Depends on parameters being initialized - self.storage + self.wl_storage + .storage .init_genesis_epoch(initial_height, genesis_time, ¶meters) .expect("Initializing genesis epoch must not fail"); @@ -184,19 +190,19 @@ where ); } - self.storage - .write(&Key::validity_predicate(&address), vp_code) + self.wl_storage + .write_bytes(&Key::validity_predicate(&address), vp_code) .unwrap(); if let Some(pk) = public_key { let pk_storage_key = pk_key(&address); - self.storage - .write(&pk_storage_key, pk.try_to_vec().unwrap()) + self.wl_storage + .write_bytes(&pk_storage_key, pk.try_to_vec().unwrap()) .unwrap(); } for (key, value) in storage { - self.storage.write(&key, value).unwrap(); + self.wl_storage.write_bytes(&key, value).unwrap(); } // When using a faucet WASM, initialize its PoW challenge storage @@ -209,7 +215,7 @@ where .faucet_withdrawal_limit .unwrap_or_else(|| token::Amount::whole(1_000)); testnet_pow::init_faucet_storage( - &mut self.storage, + &mut self.wl_storage, &address, difficulty, withdrawal_limit, @@ -223,9 +229,7 @@ where { let address: address::Address = (&public_key).into(); let pk_storage_key = pk_key(&address); - self.storage - .write(&pk_storage_key, public_key.try_to_vec().unwrap()) - .unwrap(); + self.wl_storage.write(&pk_storage_key, public_key).unwrap(); } // Initialize genesis token accounts @@ -258,16 +262,13 @@ where ); } - self.storage - .write(&Key::validity_predicate(&address), vp_code) + self.wl_storage + .write_bytes(&Key::validity_predicate(&address), vp_code) .unwrap(); for (owner, amount) in balances { - self.storage - .write( - &token::balance_key(&address, &owner), - amount.try_to_vec().unwrap(), - ) + self.wl_storage + .write(&token::balance_key(&address, &owner), amount) .unwrap(); } } @@ -299,55 +300,40 @@ where } let addr = &validator.pos_data.address; - self.storage - .write(&Key::validity_predicate(addr), vp_code) + self.wl_storage + .write_bytes(&Key::validity_predicate(addr), vp_code) .expect("Unable to write user VP"); // Validator account key let pk_key = pk_key(addr); - self.storage - .write( - &pk_key, - validator - .account_key - .try_to_vec() - .expect("encode public key"), - ) + self.wl_storage + .write(&pk_key, &validator.account_key) .expect("Unable to set genesis user public key"); // Account balance (tokens no staked in PoS) - self.storage + self.wl_storage .write( - &token::balance_key(&self.storage.native_token, addr), - validator - .non_staked_balance - .try_to_vec() - .expect("encode token amount"), + &token::balance_key( + &self.wl_storage.storage.native_token, + addr, + ), + validator.non_staked_balance, ) .expect("Unable to set genesis balance"); - self.storage - .write( - &protocol_pk_key(addr), - validator - .protocol_key - .try_to_vec() - .expect("encode protocol public key"), - ) + self.wl_storage + .write(&protocol_pk_key(addr), &validator.protocol_key) .expect("Unable to set genesis user protocol public key"); - self.storage + self.wl_storage .write( &dkg_session_keys::dkg_pk_key(addr), - validator - .dkg_public_key - .try_to_vec() - .expect("encode public DKG session key"), + &validator.dkg_public_key, ) .expect("Unable to set genesis user public DKG session key"); } // PoS system depends on epoch being initialized - let (current_epoch, _gas) = self.storage.get_current_epoch(); + let (current_epoch, _gas) = self.wl_storage.storage.get_current_epoch(); pos::init_genesis_storage( - &mut self.storage, + &mut self.wl_storage, &genesis.pos_params, genesis .validators @@ -355,7 +341,7 @@ where .map(|validator| &validator.pos_data), current_epoch, ); - ibc::init_genesis_storage(&mut self.storage); + ibc::init_genesis_storage(&mut self.wl_storage.storage); // Set the initial validator set for validator in genesis.validators { @@ -372,6 +358,11 @@ where ); response.validators.push(abci_validator); } + + self.wl_storage + .commit_genesis() + .expect("Must be able to commit genesis state"); + Ok(response) } } diff --git a/apps/src/lib/node/ledger/shell/mod.rs b/apps/src/lib/node/ledger/shell/mod.rs index 40a3f5d1156..377277919f0 100644 --- a/apps/src/lib/node/ledger/shell/mod.rs +++ b/apps/src/lib/node/ledger/shell/mod.rs @@ -30,8 +30,9 @@ use namada::ledger::pos::namada_proof_of_stake::types::{ use namada::ledger::pos::namada_proof_of_stake::PosBase; use namada::ledger::storage::write_log::WriteLog; use namada::ledger::storage::{ - DBIter, Sha256Hasher, Storage, StorageHasher, DB, + DBIter, Sha256Hasher, Storage, StorageHasher, WlStorage, DB, }; +use namada::ledger::storage_api::StorageRead; use namada::ledger::{ibc, pos, protocol}; use namada::proto::{self, Tx}; use namada::types::address; @@ -195,12 +196,10 @@ where /// The id of the current chain #[allow(dead_code)] chain_id: ChainId, - /// The persistent storage - pub(super) storage: Storage, + /// The persistent storage with write log + pub(super) wl_storage: WlStorage, /// Gas meter for the current block gas_meter: BlockGasMeter, - /// Write log for the current block - write_log: WriteLog, /// Byzantine validators given from ABCI++ `prepare_proposal` are stored in /// this field. They will be slashed when we finalize the block. byzantine_validators: Vec, @@ -315,11 +314,14 @@ where TendermintMode::Seed => ShellMode::Seed, }; + let wl_storage = WlStorage { + storage, + write_log: WriteLog::default(), + }; Self { chain_id, - storage, + wl_storage, gas_meter: BlockGasMeter::default(), - write_log: WriteLog::default(), byzantine_validators: vec![], base_dir, wasm_dir, @@ -354,14 +356,14 @@ where /// Iterate over the wrapper txs in order #[allow(dead_code)] fn iter_tx_queue(&mut self) -> impl Iterator { - self.storage.tx_queue.iter() + self.wl_storage.storage.tx_queue.iter() } /// Load the Merkle root hash and the height of the last committed block, if /// any. This is returned when ABCI sends an `info` request. pub fn last_state(&mut self) -> response::Info { let mut response = response::Info::default(); - let result = self.storage.get_state(); + let result = self.wl_storage.storage.get_state(); match result { Some((root, height)) => { @@ -389,7 +391,7 @@ where where T: Clone + BorshDeserialize, { - let result = self.storage.read(key); + let result = self.wl_storage.storage.read(key); match result { Ok((bytes, _gas)) => match bytes { @@ -405,7 +407,7 @@ where /// Read the bytes for a storage key dropping any error pub fn read_storage_key_bytes(&self, key: &Key) -> Option> { - let result = self.storage.read(key); + let result = self.wl_storage.storage.read(key); match result { Ok((bytes, _gas)) => bytes, @@ -418,8 +420,8 @@ where if !self.byzantine_validators.is_empty() { let byzantine_validators = mem::take(&mut self.byzantine_validators); - let pos_params = self.storage.read_pos_params(); - let current_epoch = self.storage.block.epoch; + let pos_params = self.wl_storage.read_pos_params(); + let current_epoch = self.wl_storage.storage.block.epoch; for evidence in byzantine_validators { tracing::info!("Processing evidence {evidence:?}."); let evidence_height = match u64::try_from(evidence.height) { @@ -433,6 +435,7 @@ where } }; let evidence_epoch = match self + .wl_storage .storage .block .pred_epochs @@ -489,7 +492,7 @@ where } }; let validator = match self - .storage + .wl_storage .read_validator_address_raw_hash(&validator_raw_hash) { Some(validator) => validator, @@ -508,7 +511,7 @@ where evidence_epoch, evidence_height ); - if let Err(err) = self.storage.slash( + if let Err(err) = self.wl_storage.slash( &pos_params, current_epoch, evidence_epoch, @@ -547,22 +550,23 @@ where pub fn commit(&mut self) -> response::Commit { let mut response = response::Commit::default(); // commit changes from the write-log to storage - self.write_log - .commit_block(&mut self.storage) + self.wl_storage + .write_log + .commit_block(&mut self.wl_storage.storage) .expect("Expected committing block write log success"); // store the block's data in DB - self.storage.commit().unwrap_or_else(|e| { + self.wl_storage.commit_block().unwrap_or_else(|e| { tracing::error!( "Encountered a storage error while committing a block {:?}", e ) }); - let root = self.storage.merkle_root(); + let root = self.wl_storage.storage.merkle_root(); tracing::info!( "Committed block hash: {}, height: {}", root, - self.storage.last_height, + self.wl_storage.storage.last_height, ); response.data = root.0; response @@ -640,7 +644,7 @@ where TxIndex::default(), &mut gas_meter, &mut write_log, - &self.storage, + &self.wl_storage.storage, &mut vp_wasm_cache, &mut tx_wasm_cache, ) @@ -675,21 +679,18 @@ where genesis::genesis_config::open_genesis_config(genesis_path).unwrap(), ); self.mode.get_validator_address().map(|addr| { - let pk_bytes = self - .storage + let sk: common::SecretKey = self + .wl_storage .read(&pk_key(addr)) .expect( "A validator should have a public key associated with \ it's established account", ) - .0 .expect( "A validator should have a public key associated with \ it's established account", ); - let pk = common::SecretKey::deserialize(&mut pk_bytes.as_slice()) - .expect("Validator's public key should be deserializable") - .ref_to(); + let pk = sk.ref_to(); wallet.find_key_by_pk(&pk).expect( "A validator's established keypair should be stored in its \ wallet", @@ -707,13 +708,13 @@ where if let Some(solution) = &tx.pow_solution { if let (Some(faucet_address), _gas) = namada::ledger::parameters::read_faucet_account_parameter( - &self.storage, + &self.wl_storage.storage, ) .expect("Must be able to read faucet account parameter") { let source = Address::from(&tx.pk); return solution - .validate(&self.storage, &faucet_address, source) + .validate(&self.wl_storage, &faucet_address, source) .expect("Must be able to validate PoW solutions"); } } @@ -725,7 +726,7 @@ where fn get_wrapper_tx_fees(&self) -> token::Amount { let (fees, _gas) = namada::ledger::parameters::read_wrapper_tx_fees_parameter( - &self.storage, + &self.wl_storage.storage, ) .expect("Must be able to read wrapper tx fees parameter"); fees.unwrap_or(token::Amount::whole(MIN_FEE)) @@ -741,14 +742,14 @@ where if let Some(solution) = &tx.pow_solution { if let (Some(faucet_address), _gas) = namada::ledger::parameters::read_faucet_account_parameter( - &self.storage, + &self.wl_storage.storage, ) .expect("Must be able to read faucet account parameter") { let source = Address::from(&tx.pk); return solution .invalidate_if_valid( - &mut self.storage, + &mut self.wl_storage, &faucet_address, &source, ) @@ -922,11 +923,15 @@ mod test_utils { /// in the current block proposal #[cfg(test)] pub fn enqueue_tx(&mut self, wrapper: WrapperTx) { - self.shell.storage.tx_queue.push(WrapperTxInQueue { - tx: wrapper, - #[cfg(not(feature = "mainnet"))] - has_valid_pow: false, - }); + self.shell + .wl_storage + .storage + .tx_queue + .push(WrapperTxInQueue { + tx: wrapper, + #[cfg(not(feature = "mainnet"))] + has_valid_pow: false, + }); } } @@ -1005,7 +1010,7 @@ mod test_utils { #[cfg(not(feature = "mainnet"))] None, ); - shell.storage.tx_queue.push(WrapperTxInQueue { + shell.wl_storage.storage.tx_queue.push(WrapperTxInQueue { tx: wrapper, #[cfg(not(feature = "mainnet"))] has_valid_pow: false, @@ -1018,6 +1023,7 @@ mod test_utils { let pred_epochs = Default::default(); let address_gen = EstablishedAddressGen::new("test"); shell + .wl_storage .storage .db .write_block(BlockStateWrite { @@ -1031,7 +1037,7 @@ mod test_utils { next_epoch_min_start_time: DateTimeUtc::now(), address_gen: &address_gen, results: &BlockResults::default(), - tx_queue: &shell.storage.tx_queue, + tx_queue: &shell.wl_storage.storage.tx_queue, }) .expect("Test failed"); @@ -1052,6 +1058,6 @@ mod test_utils { tx_wasm_compilation_cache, address::nam(), ); - assert!(!shell.storage.tx_queue.is_empty()); + assert!(!shell.wl_storage.storage.tx_queue.is_empty()); } } diff --git a/apps/src/lib/node/ledger/shell/prepare_proposal.rs b/apps/src/lib/node/ledger/shell/prepare_proposal.rs index 04953fa0299..1783a127fd7 100644 --- a/apps/src/lib/node/ledger/shell/prepare_proposal.rs +++ b/apps/src/lib/node/ledger/shell/prepare_proposal.rs @@ -98,7 +98,7 @@ where .collect(); // decrypt the wrapper txs included in the previous block - let decrypted_txs = self.storage.tx_queue.iter().map( + let decrypted_txs = self.wl_storage.storage.tx_queue.iter().map( |WrapperTxInQueue { tx, #[cfg(not(feature = "mainnet"))] @@ -225,7 +225,7 @@ mod test_prepare_proposal { WrapperTx::new( Fee { amount: 0.into(), - token: shell.storage.native_token.clone(), + token: shell.wl_storage.storage.native_token.clone(), }, &keypair, Epoch(0), @@ -285,7 +285,7 @@ mod test_prepare_proposal { let wrapper_tx = WrapperTx::new( Fee { amount: 0.into(), - token: shell.storage.native_token.clone(), + token: shell.wl_storage.storage.native_token.clone(), }, &keypair, Epoch(0), diff --git a/apps/src/lib/node/ledger/shell/process_proposal.rs b/apps/src/lib/node/ledger/shell/process_proposal.rs index e75254499a6..11deec9e13f 100644 --- a/apps/src/lib/node/ledger/shell/process_proposal.rs +++ b/apps/src/lib/node/ledger/shell/process_proposal.rs @@ -43,7 +43,7 @@ where /// Check all the given txs. pub fn process_txs(&self, txs: &[Vec]) -> Vec { - let mut tx_queue_iter = self.storage.tx_queue.iter(); + let mut tx_queue_iter = self.wl_storage.storage.tx_queue.iter(); txs.iter() .map(|tx_bytes| { self.process_single_tx(tx_bytes, &mut tx_queue_iter) @@ -239,7 +239,7 @@ mod test_process_proposal { let wrapper = WrapperTx::new( Fee { amount: 0.into(), - token: shell.storage.native_token.clone(), + token: shell.wl_storage.storage.native_token.clone(), }, &keypair, Epoch(0), @@ -288,7 +288,7 @@ mod test_process_proposal { let mut wrapper = WrapperTx::new( Fee { amount: 100.into(), - token: shell.storage.native_token.clone(), + token: shell.wl_storage.storage.native_token.clone(), }, &keypair, Epoch(0), @@ -372,7 +372,7 @@ mod test_process_proposal { let wrapper = WrapperTx::new( Fee { amount: 1.into(), - token: shell.storage.native_token.clone(), + token: shell.wl_storage.storage.native_token.clone(), }, &keypair, Epoch(0), @@ -413,10 +413,11 @@ mod test_process_proposal { let keypair = crate::wallet::defaults::daewon_keypair(); // reduce address balance to match the 100 token fee let balance_key = token::balance_key( - &shell.storage.native_token, + &shell.wl_storage.storage.native_token, &Address::from(&keypair.ref_to()), ); shell + .wl_storage .storage .write(&balance_key, Amount::whole(99).try_to_vec().unwrap()) .unwrap(); @@ -427,8 +428,8 @@ mod test_process_proposal { ); let wrapper = WrapperTx::new( Fee { - amount: Amount::whole(100), - token: shell.storage.native_token.clone(), + amount: Amount::whole(1_000_100), + token: shell.wl_storage.storage.native_token.clone(), }, &keypair, Epoch(0), @@ -478,7 +479,7 @@ mod test_process_proposal { let wrapper = WrapperTx::new( Fee { amount: i.into(), - token: shell.storage.native_token.clone(), + token: shell.wl_storage.storage.native_token.clone(), }, &keypair, Epoch(0), @@ -548,7 +549,7 @@ mod test_process_proposal { let wrapper = WrapperTx::new( Fee { amount: 0.into(), - token: shell.storage.native_token.clone(), + token: shell.wl_storage.storage.native_token.clone(), }, &keypair, Epoch(0), @@ -609,7 +610,7 @@ mod test_process_proposal { let mut wrapper = WrapperTx::new( Fee { amount: 0.into(), - token: shell.storage.native_token.clone(), + token: shell.wl_storage.storage.native_token.clone(), }, &keypair, Epoch(0), @@ -664,7 +665,7 @@ mod test_process_proposal { let wrapper = WrapperTx { fee: Fee { amount: 0.into(), - token: shell.storage.native_token.clone(), + token: shell.wl_storage.storage.native_token.clone(), }, pk: keypair.ref_to(), epoch: Epoch(0), diff --git a/apps/src/lib/node/ledger/shell/queries.rs b/apps/src/lib/node/ledger/shell/queries.rs index 77715246ab8..4f63924b0f4 100644 --- a/apps/src/lib/node/ledger/shell/queries.rs +++ b/apps/src/lib/node/ledger/shell/queries.rs @@ -1,6 +1,6 @@ //! Shell methods for querying state -use borsh::{BorshDeserialize, BorshSerialize}; +use borsh::BorshSerialize; use ferveo_common::TendermintValidator; use namada::ledger::pos::into_tm_voting_power; use namada::ledger::queries::{RequestCtx, ResponseQuery}; @@ -23,7 +23,7 @@ where /// INVARIANT: This method must be stateless. pub fn query(&self, query: request::Query) -> response::Query { let ctx = RequestCtx { - storage: &self.storage, + wl_storage: &self.wl_storage, event_log: self.event_log(), vp_wasm_cache: self.vp_wasm_cache.read_only(), tx_wasm_cache: self.tx_wasm_cache.read_only(), @@ -32,7 +32,7 @@ where // Convert request to domain-type let request = match namada::ledger::queries::RequestQuery::try_from_tm( - &self.storage, + &self.wl_storage, query, ) { Ok(request) => request, @@ -70,7 +70,7 @@ where owner: &Address, ) -> token::Amount { let balance = storage_api::StorageRead::read( - &self.storage, + &self.wl_storage, &token::balance_key(token, owner), ); // Storage read must not fail, but there might be no value, in which @@ -90,11 +90,11 @@ where .try_to_vec() .expect("Serializing public key should not fail"); // get the current epoch - let (current_epoch, _) = self.storage.get_current_epoch(); + let (current_epoch, _) = self.wl_storage.storage.get_current_epoch(); // get the PoS params - let pos_params = self.storage.read_pos_params(); + let pos_params = self.wl_storage.read_pos_params(); // get the active validator set - self.storage + self.wl_storage .read_validator_set() .get(current_epoch) .expect("Validators for the next epoch should be known") @@ -102,34 +102,26 @@ where .iter() .find(|validator| { let pk_key = key::protocol_pk_key(&validator.address); - match self.storage.read(&pk_key) { - Ok((Some(bytes), _)) => bytes == pk_bytes, + match self.wl_storage.read_bytes(&pk_key) { + Ok(Some(bytes)) => bytes == pk_bytes, _ => false, } }) .map(|validator| { let dkg_key = key::dkg_session_keys::dkg_pk_key(&validator.address); - let bytes = self - .storage + let dkg_publickey: DkgPublicKey = self + .wl_storage .read(&dkg_key) .expect("Validator should have public dkg key") - .0 .expect("Validator should have public dkg key"); - let dkg_publickey = - &::deserialize( - &mut bytes.as_ref(), - ) - .expect( - "DKG public key in storage should be deserializable", - ); TendermintValidator { power: into_tm_voting_power( pos_params.tm_votes_per_token, validator.bonded_stake, ) as u64, address: validator.address.to_string(), - public_key: dkg_publickey.into(), + public_key: (&dkg_publickey).into(), } }) } diff --git a/apps/src/lib/node/ledger/storage/mod.rs b/apps/src/lib/node/ledger/storage/mod.rs index 1bd1446c519..a5d0fed81d7 100644 --- a/apps/src/lib/node/ledger/storage/mod.rs +++ b/apps/src/lib/node/ledger/storage/mod.rs @@ -50,10 +50,9 @@ fn new_blake2b() -> Blake2b { #[cfg(test)] mod tests { - use borsh::BorshSerialize; use itertools::Itertools; - use namada::ledger::storage::types; - use namada::ledger::storage_api; + use namada::ledger::storage::{types, WlStorage}; + use namada::ledger::storage_api::{self, StorageWrite}; use namada::types::chain::ChainId; use namada::types::storage::{BlockHash, BlockHeight, Key}; use namada::types::{address, storage}; @@ -132,7 +131,7 @@ mod tests { storage .write(&key, value_bytes.clone()) .expect("write failed"); - storage.commit().expect("commit failed"); + storage.commit_block().expect("commit failed"); // save the last state and drop the storage let root = storage.merkle_root().0; @@ -187,7 +186,7 @@ mod tests { .expect("write failed"); expected.push((key.to_string(), value_bytes)); } - storage.commit().expect("commit failed"); + storage.commit_block().expect("commit failed"); let (iter, gas) = storage.iter_prefix(&prefix); assert_eq!(gas, prefix.len() as u64); @@ -302,7 +301,7 @@ mod tests { } else { storage.delete(&key)?; } - storage.commit()?; + storage.commit_block()?; } // 2. We try to read from these heights to check that we get back @@ -353,28 +352,29 @@ mod tests { fn test_persistent_storage_prefix_iter() { let db_path = TempDir::new().expect("Unable to create a temporary DB directory"); - let mut storage = PersistentStorage::open( + let storage = PersistentStorage::open( db_path.path(), ChainId::default(), address::nam(), None, ); + let mut storage = WlStorage { + storage, + write_log: Default::default(), + }; let prefix = storage::Key::parse("prefix").unwrap(); let mismatched_prefix = storage::Key::parse("different").unwrap(); // We'll write sub-key in some random order to check prefix iter's order - let sub_keys = [2_i32, 1, i32::MAX, -1, 260, -2, i32::MIN, 5, 0]; + let sub_keys = [2_i32, -1, 260, -2, 5, 0]; for i in sub_keys.iter() { let key = prefix.push(i).unwrap(); - let value = i.try_to_vec().unwrap(); - storage.write(&key, value).unwrap(); + storage.write(&key, i).unwrap(); let key = mismatched_prefix.push(i).unwrap(); - let value = (i / 2).try_to_vec().unwrap(); - storage.write(&key, value).unwrap(); + storage.write(&key, i / 2).unwrap(); } - storage.commit().unwrap(); // Then try to iterate over their prefix let iter = storage_api::iter_prefix(&storage, &prefix) @@ -386,6 +386,66 @@ mod tests { .iter() .sorted() .map(|i| (prefix.push(i).unwrap(), *i)); + itertools::assert_equal(iter, expected.clone()); + + // Commit genesis state + storage.commit_genesis().unwrap(); + + // Again, try to iterate over their prefix + let iter = storage_api::iter_prefix(&storage, &prefix) + .unwrap() + .map(Result::unwrap); + itertools::assert_equal(iter, expected); + + let more_sub_keys = [1_i32, i32::MIN, -10, 123, i32::MAX, 10]; + debug_assert!( + !more_sub_keys.iter().any(|x| sub_keys.contains(x)), + "assuming no repetition" + ); + for i in more_sub_keys.iter() { + let key = prefix.push(i).unwrap(); + storage.write(&key, i).unwrap(); + + let key = mismatched_prefix.push(i).unwrap(); + storage.write(&key, i / 2).unwrap(); + } + + let iter = storage_api::iter_prefix(&storage, &prefix) + .unwrap() + .map(Result::unwrap); + + // The order has to be sorted by sub-key value + let merged = itertools::merge(sub_keys.iter(), more_sub_keys.iter()); + let expected = merged + .clone() + .sorted() + .map(|i| (prefix.push(i).unwrap(), *i)); + itertools::assert_equal(iter, expected); + + // Delete some keys + let delete_keys = [2, 0, -10, 123]; + for i in delete_keys.iter() { + let key = prefix.push(i).unwrap(); + storage.delete(&key).unwrap() + } + + // Check that iter_prefix doesn't return deleted keys anymore + let iter = storage_api::iter_prefix(&storage, &prefix) + .unwrap() + .map(Result::unwrap); + let expected = merged + .filter(|x| !delete_keys.contains(x)) + .sorted() + .map(|i| (prefix.push(i).unwrap(), *i)); + itertools::assert_equal(iter, expected.clone()); + + // Commit genesis state + storage.commit_genesis().unwrap(); + + // And check again + let iter = storage_api::iter_prefix(&storage, &prefix) + .unwrap() + .map(Result::unwrap); itertools::assert_equal(iter, expected); } } diff --git a/core/src/ledger/storage/mod.rs b/core/src/ledger/storage/mod.rs index 45f145ec870..66049b40148 100644 --- a/core/src/ledger/storage/mod.rs +++ b/core/src/ledger/storage/mod.rs @@ -6,6 +6,8 @@ pub mod merkle_tree; pub mod mockdb; pub mod traits; pub mod types; +mod wl_storage; +pub mod write_log; use core::fmt::Debug; use std::collections::BTreeMap; @@ -28,14 +30,15 @@ use rayon::iter::{ use rayon::prelude::ParallelSlice; use thiserror::Error; pub use traits::{Sha256Hasher, StorageHasher}; +pub use wl_storage::{ + iter_prefix_post, iter_prefix_pre, PrefixIter, WlStorage, +}; use crate::ledger::gas::MIN_STORAGE_GAS; use crate::ledger::parameters::{self, EpochDuration, Parameters}; use crate::ledger::storage::merkle_tree::{ Error as MerkleTreeError, MerkleRoot, }; -use crate::ledger::storage_api; -use crate::ledger::storage_api::{ResultExt, StorageRead, StorageWrite}; #[cfg(any(feature = "tendermint", feature = "tendermint-abcipp"))] use crate::tendermint::merkle::proof::Proof; use crate::types::address::{ @@ -301,6 +304,10 @@ pub trait DBIter<'iter> { /// The concrete type of the iterator type PrefixIter: Debug + Iterator, u64)>; + /// WARNING: This only works for values that have been committed to DB. + /// To be able to see values written or deleted, but not yet committed, + /// use the `StorageWithWriteLog`. + /// /// Read account subspace key value pairs with the given prefix from the DB, /// ordered by the storage keys. fn iter_prefix(&'iter self, prefix: &Key) -> Self::PrefixIter; @@ -429,7 +436,7 @@ where } /// Persist the current block's state to the database - pub fn commit(&mut self) -> Result<()> { + pub fn commit_block(&mut self) -> Result<()> { let state = BlockStateWrite { merkle_tree_stores: self.block.tree.stores(), header: self.header.as_ref(), @@ -503,7 +510,11 @@ where } } - /// Returns a prefix iterator, ordered by storage keys, and the gas cost + /// WARNING: This only works for values that have been committed to DB. + /// To be able to see values written or deleted, but not yet committed, + /// use the `StorageWithWriteLog`. + /// + /// Returns a prefix iterator, ordered by storage keys, and the gas cost. pub fn iter_prefix( &self, prefix: &Key, @@ -992,116 +1003,6 @@ where } } -impl StorageRead for Storage -where - D: DB + for<'iter_> DBIter<'iter_>, - H: StorageHasher, -{ - type PrefixIter<'iter> = >::PrefixIter -where - Self: 'iter - ; - - fn read_bytes( - &self, - key: &crate::types::storage::Key, - ) -> std::result::Result>, storage_api::Error> { - self.db.read_subspace_val(key).into_storage_result() - } - - fn has_key( - &self, - key: &crate::types::storage::Key, - ) -> std::result::Result { - self.block.tree.has_key(key).into_storage_result() - } - - fn iter_prefix<'iter>( - &'iter self, - prefix: &crate::types::storage::Key, - ) -> std::result::Result, storage_api::Error> { - Ok(self.db.iter_prefix(prefix)) - } - - fn iter_next<'iter>( - &'iter self, - iter: &mut Self::PrefixIter<'iter>, - ) -> std::result::Result)>, storage_api::Error> - { - Ok(iter.next().map(|(key, val, _gas)| (key, val))) - } - - fn get_chain_id(&self) -> std::result::Result { - Ok(self.chain_id.to_string()) - } - - fn get_block_height( - &self, - ) -> std::result::Result { - Ok(self.block.height) - } - - fn get_block_hash( - &self, - ) -> std::result::Result { - Ok(self.block.hash.clone()) - } - - fn get_block_epoch( - &self, - ) -> std::result::Result { - Ok(self.block.epoch) - } - - fn get_tx_index(&self) -> std::result::Result { - Ok(self.tx_index) - } - - fn get_native_token( - &self, - ) -> std::result::Result { - Ok(self.native_token.clone()) - } -} - -impl StorageWrite for Storage -where - D: DB + for<'iter> DBIter<'iter>, - H: StorageHasher, -{ - fn write_bytes( - &mut self, - key: &crate::types::storage::Key, - val: impl AsRef<[u8]>, - ) -> storage_api::Result<()> { - // Note that this method is the same as `Storage::write`, but without - // gas and storage bytes len diff accounting, because it can only be - // used by the protocol that has a direct mutable access to storage - let val = val.as_ref(); - self.block.tree.update(key, val).into_storage_result()?; - let _ = self - .db - .write_subspace_val(self.block.height, key, val) - .into_storage_result()?; - Ok(()) - } - - fn delete( - &mut self, - key: &crate::types::storage::Key, - ) -> storage_api::Result<()> { - // Note that this method is the same as `Storage::delete`, but without - // gas and storage bytes len diff accounting, because it can only be - // used by the protocol that has a direct mutable access to storage - self.block.tree.delete(key).into_storage_result()?; - let _ = self - .db - .delete_subspace_val(self.block.height, key) - .into_storage_result()?; - Ok(()) - } -} - impl From for Error { fn from(error: MerkleTreeError) -> Self { Self::MerkleTreeError(error) @@ -1115,7 +1016,15 @@ pub mod testing { use super::*; use crate::ledger::storage::traits::Sha256Hasher; use crate::types::address; - /// Storage with a mock DB for testing + + /// `WlStorage` with a mock DB for testing + pub type TestWlStorage = WlStorage; + + /// Storage with a mock DB for testing. + /// + /// Prefer to use [`TestWlStorage`], which implements + /// `storage_api::StorageRead + StorageWrite` with properly working + /// `prefix_iter`. pub type TestStorage = Storage; impl Default for TestStorage { @@ -1150,6 +1059,16 @@ pub mod testing { } } } + + #[allow(clippy::derivable_impls)] + impl Default for TestWlStorage { + fn default() -> Self { + Self { + write_log: Default::default(), + storage: Default::default(), + } + } + } } #[cfg(test)] diff --git a/core/src/ledger/storage/wl_storage.rs b/core/src/ledger/storage/wl_storage.rs new file mode 100644 index 00000000000..a5c559936c3 --- /dev/null +++ b/core/src/ledger/storage/wl_storage.rs @@ -0,0 +1,324 @@ +//! Storage with write log. + +use std::iter::Peekable; + +use crate::ledger::storage::write_log::{self, WriteLog}; +use crate::ledger::storage::{DBIter, Storage, StorageHasher, DB}; +use crate::ledger::storage_api::{ResultExt, StorageRead, StorageWrite}; +use crate::ledger::{gas, storage_api}; +use crate::types::address::Address; +use crate::types::storage; + +/// Storage with write log that allows to implement prefix iterator that works +/// with changes not yet committed to the DB. +#[derive(Debug)] +pub struct WlStorage +where + D: DB + for<'iter> DBIter<'iter>, + H: StorageHasher, +{ + /// Write log + pub write_log: WriteLog, + /// Storage provides access to DB + pub storage: Storage, +} + +impl WlStorage +where + D: 'static + DB + for<'iter> DBIter<'iter>, + H: StorageHasher, +{ + /// Combine storage with write-log + pub fn new(write_log: WriteLog, storage: Storage) -> Self { + Self { write_log, storage } + } + + /// Commit the genesis state to DB. This should only be used before any + /// blocks are produced. + pub fn commit_genesis(&mut self) -> storage_api::Result<()> { + self.write_log + .commit_genesis(&mut self.storage) + .into_storage_result() + } + + /// Commit the current transaction's write log to the block when it's + /// accepted by all the triggered validity predicates. Starts a new + /// transaction write log. + pub fn commit_tx(&mut self) { + self.write_log.commit_tx() + } + + /// Drop the current transaction's write log when it's declined by any of + /// the triggered validity predicates. Starts a new transaction write log. + pub fn drop_tx(&mut self) { + self.write_log.drop_tx() + } + + /// Commit the current block's write log to the storage and commit the block + /// to DB. Starts a new block write log. + pub fn commit_block(&mut self) -> storage_api::Result<()> { + self.write_log + .commit_block(&mut self.storage) + .into_storage_result()?; + self.storage.commit_block().into_storage_result() + } +} + +/// Prefix iterator for [`WlStorage`]. +#[derive(Debug)] +pub struct PrefixIter<'iter, D> +where + D: DB + DBIter<'iter>, +{ + /// Peekable storage iterator + pub storage_iter: Peekable<>::PrefixIter>, + /// Peekable write log iterator + pub write_log_iter: Peekable, +} + +/// Iterate write-log storage items prior to a tx execution, matching the +/// given prefix. Returns the iterator and gas cost. +pub fn iter_prefix_pre<'iter, D, H>( + // We cannot use e.g. `&'iter WlStorage`, because it doesn't live long + // enough - the lifetime of the `PrefixIter` must depend on the lifetime of + // references to the `WriteLog` and `Storage`. + write_log: &'iter WriteLog, + storage: &'iter Storage, + prefix: &storage::Key, +) -> (PrefixIter<'iter, D>, u64) +where + D: DB + for<'iter_> DBIter<'iter_>, + H: StorageHasher, +{ + let storage_iter = storage.db.iter_prefix(prefix).peekable(); + let write_log_iter = write_log.iter_prefix_pre(prefix).peekable(); + ( + PrefixIter { + storage_iter, + write_log_iter, + }, + gas::MIN_STORAGE_GAS, + ) +} + +/// Iterate write-log storage items posterior to a tx execution, matching the +/// given prefix. Returns the iterator and gas cost. +pub fn iter_prefix_post<'iter, D, H>( + // We cannot use e.g. `&'iter WlStorage`, because it doesn't live long + // enough - the lifetime of the `PrefixIter` must depend on the lifetime of + // references to the `WriteLog` and `Storage`. + write_log: &'iter WriteLog, + storage: &'iter Storage, + prefix: &storage::Key, +) -> (PrefixIter<'iter, D>, u64) +where + D: DB + for<'iter_> DBIter<'iter_>, + H: StorageHasher, +{ + let storage_iter = storage.db.iter_prefix(prefix).peekable(); + let write_log_iter = write_log.iter_prefix_post(prefix).peekable(); + ( + PrefixIter { + storage_iter, + write_log_iter, + }, + gas::MIN_STORAGE_GAS, + ) +} + +impl<'iter, D> Iterator for PrefixIter<'iter, D> +where + D: DB + DBIter<'iter>, +{ + type Item = (String, Vec, u64); + + fn next(&mut self) -> Option { + enum Next { + ReturnWl { advance_storage: bool }, + ReturnStorage, + } + loop { + let what: Next; + { + let storage_peeked = self.storage_iter.peek(); + let wl_peeked = self.write_log_iter.peek(); + match (storage_peeked, wl_peeked) { + (None, None) => return None, + (None, Some(_)) => { + what = Next::ReturnWl { + advance_storage: false, + }; + } + (Some(_), None) => { + what = Next::ReturnStorage; + } + (Some((storage_key, _, _)), Some((wl_key, _))) => { + let wl_key = wl_key.to_string(); + if &wl_key <= storage_key { + what = Next::ReturnWl { + advance_storage: &wl_key == storage_key, + }; + } else { + what = Next::ReturnStorage; + } + } + } + } + match what { + Next::ReturnWl { advance_storage } => { + if advance_storage { + let _ = self.storage_iter.next(); + } + + if let Some((key, modification)) = + self.write_log_iter.next() + { + match modification { + write_log::StorageModification::Write { value } + | write_log::StorageModification::Temp { value } => { + let gas = value.len() as u64; + return Some((key.to_string(), value, gas)); + } + write_log::StorageModification::InitAccount { + vp, + } => { + let gas = vp.len() as u64; + return Some((key.to_string(), vp, gas)); + } + write_log::StorageModification::Delete => { + continue; + } + } + } + } + Next::ReturnStorage => { + if let Some(next) = self.storage_iter.next() { + return Some(next); + } + } + } + } + } +} + +impl StorageRead for WlStorage +where + D: DB + for<'iter> DBIter<'iter>, + H: StorageHasher, +{ + type PrefixIter<'iter> = PrefixIter<'iter, D> where Self: 'iter; + + fn read_bytes( + &self, + key: &storage::Key, + ) -> storage_api::Result>> { + // try to read from the write log first + let (log_val, _gas) = self.write_log.read(key); + match log_val { + Some(&write_log::StorageModification::Write { ref value }) => { + Ok(Some(value.clone())) + } + Some(&write_log::StorageModification::Delete) => Ok(None), + Some(&write_log::StorageModification::InitAccount { + ref vp, + .. + }) => Ok(Some(vp.clone())), + Some(&write_log::StorageModification::Temp { ref value }) => { + Ok(Some(value.clone())) + } + None => { + // when not found in write log, try to read from the storage + self.storage.db.read_subspace_val(key).into_storage_result() + } + } + } + + fn has_key(&self, key: &storage::Key) -> storage_api::Result { + // try to read from the write log first + let (log_val, _gas) = self.write_log.read(key); + match log_val { + Some(&write_log::StorageModification::Write { .. }) + | Some(&write_log::StorageModification::InitAccount { .. }) + | Some(&write_log::StorageModification::Temp { .. }) => Ok(true), + Some(&write_log::StorageModification::Delete) => { + // the given key has been deleted + Ok(false) + } + None => { + // when not found in write log, try to check the storage + self.storage.block.tree.has_key(key).into_storage_result() + } + } + } + + fn iter_prefix<'iter>( + &'iter self, + prefix: &storage::Key, + ) -> storage_api::Result> { + let (iter, _gas) = + iter_prefix_post(&self.write_log, &self.storage, prefix); + Ok(iter) + } + + fn iter_next<'iter>( + &'iter self, + iter: &mut Self::PrefixIter<'iter>, + ) -> storage_api::Result)>> { + Ok(iter.next().map(|(key, val, _gas)| (key, val))) + } + + fn get_chain_id(&self) -> std::result::Result { + Ok(self.storage.chain_id.to_string()) + } + + fn get_block_height( + &self, + ) -> std::result::Result { + Ok(self.storage.block.height) + } + + fn get_block_hash( + &self, + ) -> std::result::Result { + Ok(self.storage.block.hash.clone()) + } + + fn get_block_epoch( + &self, + ) -> std::result::Result { + Ok(self.storage.block.epoch) + } + + fn get_tx_index( + &self, + ) -> std::result::Result { + Ok(self.storage.tx_index) + } + + fn get_native_token(&self) -> storage_api::Result
{ + Ok(self.storage.native_token.clone()) + } +} + +impl StorageWrite for WlStorage +where + D: DB + for<'iter> DBIter<'iter>, + H: StorageHasher, +{ + fn write_bytes( + &mut self, + key: &storage::Key, + val: impl AsRef<[u8]>, + ) -> storage_api::Result<()> { + let _ = self + .write_log + .write(key, val.as_ref().to_vec()) + .into_storage_result(); + Ok(()) + } + + fn delete(&mut self, key: &storage::Key) -> storage_api::Result<()> { + let _ = self.write_log.delete(key).into_storage_result(); + Ok(()) + } +} diff --git a/shared/src/ledger/storage/write_log.rs b/core/src/ledger/storage/write_log.rs similarity index 88% rename from shared/src/ledger/storage/write_log.rs rename to core/src/ledger/storage/write_log.rs index 6e78612f569..a754d876165 100644 --- a/shared/src/ledger/storage/write_log.rs +++ b/core/src/ledger/storage/write_log.rs @@ -3,6 +3,7 @@ use std::collections::{BTreeSet, HashMap, HashSet}; +use itertools::Itertools; use thiserror::Error; use crate::ledger; @@ -71,6 +72,21 @@ pub struct WriteLog { ibc_event: Option, } +/// Write log prefix iterator +#[derive(Debug)] +pub struct PrefixIter { + /// The concrete iterator for modifications sorted by storage keys + pub iter: std::vec::IntoIter<(storage::Key, StorageModification)>, +} + +impl Iterator for PrefixIter { + type Item = (storage::Key, StorageModification); + + fn next(&mut self) -> Option { + self.iter.next() + } +} + impl Default for WriteLog { fn default() -> Self { Self { @@ -285,7 +301,7 @@ impl WriteLog { pub fn get_partitioned_keys( &self, ) -> (BTreeSet<&storage::Key>, HashSet<&Address>) { - use itertools::{Either, Itertools}; + use itertools::Either; self.tx_write_log.iter().partition_map(|(key, value)| { match (key.is_validity_predicate(), value) { (Some(address), StorageModification::InitAccount { .. }) => { @@ -322,6 +338,53 @@ impl WriteLog { self.ibc_event.as_ref() } + /// Commit the current genesis tx's write log to the storage. + pub fn commit_genesis( + &mut self, + storage: &mut Storage, + ) -> Result<()> + where + DB: 'static + + ledger::storage::DB + + for<'iter> ledger::storage::DBIter<'iter>, + H: StorageHasher, + { + // This whole function is almost the same as `commit_block`, except that + // we commit the state directly from `tx_write_log` + let mut batch = Storage::::batch(); + for (key, entry) in self.tx_write_log.iter() { + match entry { + StorageModification::Write { value } => { + storage + .batch_write_subspace_val( + &mut batch, + key, + value.clone(), + ) + .map_err(Error::StorageError)?; + } + StorageModification::Delete => { + storage + .batch_delete_subspace_val(&mut batch, key) + .map_err(Error::StorageError)?; + } + StorageModification::InitAccount { vp } => { + storage + .batch_write_subspace_val(&mut batch, key, vp.clone()) + .map_err(Error::StorageError)?; + } + // temporary value isn't persisted + StorageModification::Temp { .. } => {} + } + } + storage.exec_batch(batch).map_err(Error::StorageError)?; + if let Some(address_gen) = self.address_gen.take() { + storage.address_gen = address_gen + } + self.tx_write_log.clear(); + Ok(()) + } + /// Commit the current transaction's write log to the block when it's /// accepted by all the triggered validity predicates. Starts a new /// transaction write log. @@ -424,6 +487,41 @@ impl WriteLog { } (verifiers, changed_keys) } + + /// Iterate modifications prior to the current transaction, whose storage + /// key matches the given prefix, sorted by their storage key. + pub fn iter_prefix_pre(&self, prefix: &storage::Key) -> PrefixIter { + let mut matches = HashMap::new(); + for (key, modification) in &self.block_write_log { + if key.split_prefix(prefix).is_some() { + matches.insert(key.clone(), modification.clone()); + } + } + let iter = matches + .into_iter() + .sorted_unstable_by_key(|(key, _val)| key.clone()); + PrefixIter { iter } + } + + /// Iterate modifications posterior of the current tx, whose storage key + /// matches the given prefix, sorted by their storage key. + pub fn iter_prefix_post(&self, prefix: &storage::Key) -> PrefixIter { + let mut matches = HashMap::new(); + for (key, modification) in &self.block_write_log { + if key.split_prefix(prefix).is_some() { + matches.insert(key.clone(), modification.clone()); + } + } + for (key, modification) in &self.tx_write_log { + if key.split_prefix(prefix).is_some() { + matches.insert(key.clone(), modification.clone()); + } + } + let iter = matches + .into_iter() + .sorted_unstable_by_key(|(key, _val)| key.clone()); + PrefixIter { iter } + } } #[cfg(test)] @@ -682,12 +780,12 @@ mod tests { /// Helpers for testing with write log. #[cfg(any(test, feature = "testing"))] pub mod testing { - use namada_core::types::address::testing::arb_address; - use namada_core::types::storage::testing::arb_key; use proptest::collection; use proptest::prelude::{any, prop_oneof, Just, Strategy}; use super::*; + use crate::types::address::testing::arb_address; + use crate::types::storage::testing::arb_key; /// Generate an arbitrary tx write log of [`HashMap`]. diff --git a/core/src/ledger/storage_api/collections/lazy_map.rs b/core/src/ledger/storage_api/collections/lazy_map.rs index ec4a958002d..28802b79eb0 100644 --- a/core/src/ledger/storage_api/collections/lazy_map.rs +++ b/core/src/ledger/storage_api/collections/lazy_map.rs @@ -512,11 +512,11 @@ where #[cfg(test)] mod test { use super::*; - use crate::ledger::storage::testing::TestStorage; + use crate::ledger::storage::testing::TestWlStorage; #[test] fn test_lazy_map_basics() -> storage_api::Result<()> { - let mut storage = TestStorage::default(); + let mut storage = TestWlStorage::default(); let key = storage::Key::parse("test").unwrap(); let lazy_map = LazyMap::::open(key); diff --git a/core/src/ledger/storage_api/collections/lazy_vec.rs b/core/src/ledger/storage_api/collections/lazy_vec.rs index 0e0a5ab03bf..47b5c95c754 100644 --- a/core/src/ledger/storage_api/collections/lazy_vec.rs +++ b/core/src/ledger/storage_api/collections/lazy_vec.rs @@ -476,11 +476,11 @@ where #[cfg(test)] mod test { use super::*; - use crate::ledger::storage::testing::TestStorage; + use crate::ledger::storage::testing::TestWlStorage; #[test] fn test_lazy_vec_basics() -> storage_api::Result<()> { - let mut storage = TestStorage::default(); + let mut storage = TestWlStorage::default(); let key = storage::Key::parse("test").unwrap(); let lazy_vec = LazyVec::::open(key); diff --git a/core/src/ledger/testnet_pow.rs b/core/src/ledger/testnet_pow.rs index e40eb7a96e5..bed2273a706 100644 --- a/core/src/ledger/testnet_pow.rs +++ b/core/src/ledger/testnet_pow.rs @@ -523,14 +523,15 @@ mod test_with_tx_and_vp_env { tx_env.spawn_accounts([&faucet_address, &source]); init_faucet_storage( - &mut tx_env.storage, + &mut tx_env.wl_storage, &faucet_address, difficulty, withdrawal_limit, )?; + tx_env.commit_genesis(); let challenge = Challenge::new( - &mut tx_env.storage, + &mut tx_env.wl_storage, &faucet_address, source.clone(), )?; diff --git a/proof_of_stake/src/storage.rs b/proof_of_stake/src/storage.rs index 5e11165c55a..e61fe381d4a 100644 --- a/proof_of_stake/src/storage.rs +++ b/proof_of_stake/src/storage.rs @@ -1,7 +1,7 @@ //! Proof-of-Stake storage keys and storage integration via [`PosBase`] trait. use namada_core::ledger::storage::types::{decode, encode}; -use namada_core::ledger::storage::{self, Storage, StorageHasher}; +use namada_core::ledger::storage::{self, StorageHasher, WlStorage}; use namada_core::types::address::Address; use namada_core::types::storage::{DbKeySeg, Key, KeySeg}; use namada_core::types::{key, token}; @@ -345,7 +345,7 @@ pub fn get_validator_address_from_bond(key: &Key) -> Option
{ } } -impl PosBase for Storage +impl PosBase for WlStorage where D: storage::DB + for<'iter> storage::DBIter<'iter>, H: StorageHasher, @@ -355,11 +355,11 @@ where super::SLASH_POOL_ADDRESS; fn staking_token_address(&self) -> namada_core::types::address::Address { - self.native_token.clone() + self.storage.native_token.clone() } fn read_pos_params(&self) -> PosParams { - let (value, _gas) = self.read(¶ms_key()).unwrap(); + let (value, _gas) = self.storage.read(¶ms_key()).unwrap(); decode(value.unwrap()).unwrap() } @@ -368,6 +368,7 @@ where raw_hash: impl AsRef, ) -> Option { let (value, _gas) = self + .storage .read(&validator_address_raw_hash_key(raw_hash)) .unwrap(); value.map(|value| decode(value).unwrap()) @@ -377,8 +378,10 @@ where &self, key: &namada_core::types::address::Address, ) -> Option { - let (value, _gas) = - self.read(&validator_consensus_key_key(key)).unwrap(); + let (value, _gas) = self + .storage + .read(&validator_consensus_key_key(key)) + .unwrap(); value.map(|value| decode(value).unwrap()) } @@ -386,7 +389,8 @@ where &self, key: &namada_core::types::address::Address, ) -> Option { - let (value, _gas) = self.read(&validator_state_key(key)).unwrap(); + let (value, _gas) = + self.storage.read(&validator_state_key(key)).unwrap(); value.map(|value| decode(value).unwrap()) } @@ -394,7 +398,8 @@ where &self, key: &namada_core::types::address::Address, ) -> Option { - let (value, _gas) = self.read(&validator_deltas_key(key)).unwrap(); + let (value, _gas) = + self.storage.read(&validator_deltas_key(key)).unwrap(); value.map(|value| decode(value).unwrap()) } @@ -402,7 +407,8 @@ where &self, key: &namada_core::types::address::Address, ) -> types::Slashes { - let (value, _gas) = self.read(&validator_slashes_key(key)).unwrap(); + let (value, _gas) = + self.storage.read(&validator_slashes_key(key)).unwrap(); value .map(|value| decode(value).unwrap()) .unwrap_or_default() @@ -412,8 +418,10 @@ where &self, key: &namada_core::types::address::Address, ) -> CommissionRates { - let (value, _gas) = - self.read(&validator_commission_rate_key(key)).unwrap(); + let (value, _gas) = self + .storage + .read(&validator_commission_rate_key(key)) + .unwrap(); decode(value.unwrap()).unwrap() } @@ -422,23 +430,24 @@ where key: &namada_core::types::address::Address, ) -> Decimal { let (value, _gas) = self + .storage .read(&validator_max_commission_rate_change_key(key)) .unwrap(); decode(value.unwrap()).unwrap() } fn read_validator_set(&self) -> ValidatorSets { - let (value, _gas) = self.read(&validator_set_key()).unwrap(); + let (value, _gas) = self.storage.read(&validator_set_key()).unwrap(); decode(value.unwrap()).unwrap() } fn read_total_deltas(&self) -> TotalDeltas { - let (value, _gas) = self.read(&total_deltas_key()).unwrap(); + let (value, _gas) = self.storage.read(&total_deltas_key()).unwrap(); decode(value.unwrap()).unwrap() } fn write_pos_params(&mut self, params: &PosParams) { - self.write(¶ms_key(), encode(params)).unwrap(); + self.storage.write(¶ms_key(), encode(params)).unwrap(); } fn write_validator_address_raw_hash( @@ -447,7 +456,8 @@ where consensus_key: &namada_core::types::key::common::PublicKey, ) { let raw_hash = key::tm_consensus_key_raw_hash(consensus_key); - self.write(&validator_address_raw_hash_key(raw_hash), encode(address)) + self.storage + .write(&validator_address_raw_hash_key(raw_hash), encode(address)) .unwrap(); } @@ -456,7 +466,8 @@ where key: &namada_core::types::address::Address, value: &CommissionRates, ) { - self.write(&validator_commission_rate_key(key), encode(value)) + self.storage + .write(&validator_commission_rate_key(key), encode(value)) .unwrap(); } @@ -465,11 +476,12 @@ where key: &namada_core::types::address::Address, value: &rust_decimal::Decimal, ) { - self.write( - &validator_max_commission_rate_change_key(key), - encode(value), - ) - .unwrap(); + self.storage + .write( + &validator_max_commission_rate_change_key(key), + encode(value), + ) + .unwrap(); } fn write_validator_consensus_key( @@ -477,7 +489,8 @@ where key: &namada_core::types::address::Address, value: &ValidatorConsensusKeys, ) { - self.write(&validator_consensus_key_key(key), encode(value)) + self.storage + .write(&validator_consensus_key_key(key), encode(value)) .unwrap(); } @@ -486,7 +499,8 @@ where key: &namada_core::types::address::Address, value: &ValidatorStates, ) { - self.write(&validator_state_key(key), encode(value)) + self.storage + .write(&validator_state_key(key), encode(value)) .unwrap(); } @@ -495,7 +509,8 @@ where key: &namada_core::types::address::Address, value: &ValidatorDeltas, ) { - self.write(&validator_deltas_key(key), encode(value)) + self.storage + .write(&validator_deltas_key(key), encode(value)) .unwrap(); } @@ -506,20 +521,25 @@ where ) { let mut slashes = PosBase::read_validator_slashes(self, validator); slashes.push(value); - self.write(&validator_slashes_key(validator), encode(&slashes)) + self.storage + .write(&validator_slashes_key(validator), encode(&slashes)) .unwrap(); } fn write_bond(&mut self, key: &BondId, value: &Bonds) { - self.write(&bond_key(key), encode(value)).unwrap(); + self.storage.write(&bond_key(key), encode(value)).unwrap(); } fn write_validator_set(&mut self, value: &ValidatorSets) { - self.write(&validator_set_key(), encode(value)).unwrap(); + self.storage + .write(&validator_set_key(), encode(value)) + .unwrap(); } fn write_total_deltas(&mut self, value: &TotalDeltas) { - self.write(&total_deltas_key(), encode(value)).unwrap(); + self.storage + .write(&total_deltas_key(), encode(value)) + .unwrap(); } fn credit_tokens( @@ -530,6 +550,7 @@ where ) { let key = token::balance_key(token, target); let new_balance = match self + .storage .read(&key) .expect("Unable to read token balance for PoS system") { @@ -540,7 +561,8 @@ where } _ => amount, }; - self.write(&key, encode(&new_balance)) + self.storage + .write(&key, encode(&new_balance)) .expect("Unable to write token balance for PoS system"); } @@ -554,6 +576,7 @@ where let src_key = token::balance_key(token, src); let dest_key = token::balance_key(token, dest); if let (Some(src_balance), _gas) = self + .storage .read(&src_key) .expect("Unable to read token balance for PoS system") { @@ -569,15 +592,18 @@ where return; } src_balance.spend(&amount); - let (dest_balance, _gas) = self.read(&dest_key).unwrap_or_default(); + let (dest_balance, _gas) = + self.storage.read(&dest_key).unwrap_or_default(); let mut dest_balance: namada_core::types::token::Amount = dest_balance .and_then(|b| decode(b).ok()) .unwrap_or_default(); dest_balance.receive(&amount); - self.write(&src_key, encode(&src_balance)) + self.storage + .write(&src_key, encode(&src_balance)) .expect("Unable to write token balance for PoS system"); - self.write(&dest_key, encode(&dest_balance)) + self.storage + .write(&dest_key, encode(&dest_balance)) .expect("Unable to write token balance for PoS system"); } else { tracing::error!( @@ -710,7 +736,7 @@ macro_rules! impl_pos_read_only { } impl_pos_read_only! { - impl PosReadOnly for Storage + impl PosReadOnly for WlStorage where DB: storage::DB + for<'iter> storage::DBIter<'iter> +'static, H: StorageHasher +'static, diff --git a/shared/src/ledger/ibc/vp/mod.rs b/shared/src/ledger/ibc/vp/mod.rs index 8a807ce7549..108942b548f 100644 --- a/shared/src/ledger/ibc/vp/mod.rs +++ b/shared/src/ledger/ibc/vp/mod.rs @@ -548,20 +548,53 @@ mod tests { #[test] fn test_create_client() { - let (storage, mut write_log) = insert_init_states(); + let storage = TestStorage::default(); + let mut write_log = WriteLog::default(); let height = Height::new(0, 1); let header = MockHeader { height, timestamp: Timestamp::now(), }; + let client_id = get_client_id(); + // insert client type, state, and consensus state + let client_type_key = client_type_key(&client_id); + let client_type = ClientType::Mock.as_str().as_bytes().to_vec(); + write_log + .write(&client_type_key, client_type) + .expect("write failed"); let client_state = MockClientState::new(header).wrap_any(); let consensus_state = MockConsensusState::new(header).wrap_any(); let msg = MsgCreateAnyClient { - client_state, - consensus_state, + client_state: client_state.clone(), + consensus_state: consensus_state.clone(), signer: Signer::new("account0"), }; + let client_state_key = client_state_key(&get_client_id()); + let bytes = client_state.encode_vec().expect("encoding failed"); + write_log + .write(&client_state_key, bytes) + .expect("write failed"); + let consensus_key = consensus_state_key(&client_id, height); + let bytes = consensus_state.encode_vec().expect("encoding failed"); + write_log + .write(&consensus_key, bytes) + .expect("write failed"); + // insert update time and height + let client_update_time_key = client_update_timestamp_key(&client_id); + let bytes = TmTime::now().encode_vec().expect("encoding failed"); + write_log + .write(&client_update_time_key, bytes) + .expect("write failed"); + let client_update_height_key = client_update_height_key(&client_id); + let host_height = Height::new(10, 100); + write_log + .write( + &client_update_height_key, + host_height.encode_vec().expect("encoding failed"), + ) + .expect("write failed"); + let event = make_create_client_event(&get_client_id(), &msg); write_log.set_ibc_event(event.try_into().unwrap()); @@ -574,7 +607,6 @@ mod tests { let (vp_wasm_cache, _vp_cache_dir) = wasm::compilation_cache::common::testing::cache(); let mut keys_changed = BTreeSet::new(); - let client_state_key = client_state_key(&get_client_id()); keys_changed.insert(client_state_key); let verifiers = BTreeSet::new(); @@ -1400,7 +1432,6 @@ mod tests { let (storage, mut write_log) = insert_init_states(); // insert a port set_port(&mut write_log, 0); - write_log.commit_tx(); let tx_index = TxIndex::default(); let tx_code = vec![]; @@ -1442,7 +1473,6 @@ mod tests { // insert a port let index = 0; set_port(&mut write_log, index); - write_log.commit_tx(); let tx_index = TxIndex::default(); let tx_code = vec![]; @@ -1888,7 +1918,6 @@ mod tests { ); let ack = PacketAck::result_success().encode_to_vec(); write_log.write(&ack_key, ack).expect("write failed"); - write_log.commit_tx(); let tx_index = TxIndex::default(); let tx_code = vec![]; @@ -1939,7 +1968,6 @@ mod tests { ack_key(&get_port_id(), &get_channel_id(), Sequence::from(1)); let ack = PacketAck::result_success().encode_to_vec(); write_log.write(&ack_key, ack).expect("write failed"); - write_log.commit_tx(); let tx_index = TxIndex::default(); let tx_code = vec![]; diff --git a/shared/src/ledger/native_vp/governance/mod.rs b/shared/src/ledger/native_vp/governance/mod.rs index f6b3187073a..b383d67484b 100644 --- a/shared/src/ledger/native_vp/governance/mod.rs +++ b/shared/src/ledger/native_vp/governance/mod.rs @@ -531,7 +531,7 @@ where /// Validate a governance parameter pub fn is_valid_parameter(&self, tx_data: &[u8]) -> Result { - utils::is_proposal_accepted(self.ctx.storage, tx_data) + utils::is_proposal_accepted(&self.ctx.pre(), tx_data) .map_err(Error::NativeVpError) } diff --git a/shared/src/ledger/native_vp/governance/utils.rs b/shared/src/ledger/native_vp/governance/utils.rs index 4fd0a682174..611edd82e7c 100644 --- a/shared/src/ledger/native_vp/governance/utils.rs +++ b/shared/src/ledger/native_vp/governance/utils.rs @@ -1,7 +1,6 @@ //! Governance utility functions use std::collections::HashMap; -use std::str::FromStr; use borsh::BorshDeserialize; use namada_proof_of_stake::PosReadOnly; @@ -9,11 +8,10 @@ use thiserror::Error; use crate::ledger::governance::storage as gov_storage; use crate::ledger::pos::BondId; -use crate::ledger::storage::{DBIter, Storage, StorageHasher, DB}; use crate::ledger::storage_api; use crate::types::address::Address; use crate::types::governance::{ProposalVote, TallyResult, VotePower}; -use crate::types::storage::{Epoch, Key}; +use crate::types::storage::Epoch; use crate::types::token; /// Proposal structure holding votes information necessary to compute the @@ -75,14 +73,13 @@ impl ProposalEvent { } /// Return a proposal result - accepted only when the result is `Ok(true)`. -pub fn compute_tally( - storage: &Storage, +pub fn compute_tally( + storage: &S, epoch: Epoch, votes: Votes, ) -> storage_api::Result where - D: DB + for<'iter> DBIter<'iter> + Sync + 'static, - H: StorageHasher + Sync + 'static, + S: storage_api::StorageRead + PosReadOnly, { let total_stake: VotePower = storage.total_stake(epoch)?.into(); @@ -119,20 +116,20 @@ where } /// Prepare Votes structure to compute proposal tally -pub fn get_proposal_votes( - storage: &Storage, +pub fn get_proposal_votes( + storage: &S, epoch: Epoch, proposal_id: u64, ) -> storage_api::Result where - D: DB + for<'iter> DBIter<'iter> + Sync + 'static, - H: StorageHasher + Sync + 'static, + S: storage_api::StorageRead + PosReadOnly, { let validators = storage.validator_addresses(epoch)?; let vote_prefix_key = gov_storage::get_proposal_vote_prefix_key(proposal_id); - let (vote_iter, _) = storage.iter_prefix(&vote_prefix_key); + let vote_iter = + storage_api::iter_prefix::(storage, &vote_prefix_key)?; let mut yay_validators = HashMap::new(); let mut yay_delegators: HashMap> = @@ -140,59 +137,51 @@ where let mut nay_delegators: HashMap> = HashMap::new(); - for (key, vote_bytes, _) in vote_iter { - let vote_key = Key::from_str(key.as_str()).ok(); - let vote = ProposalVote::try_from_slice(&vote_bytes[..]).ok(); - match (vote_key, vote) { - (Some(key), Some(vote)) => { - let voter_address = gov_storage::get_voter_address(&key); - match voter_address { - Some(voter_address) => { - if vote.is_yay() && validators.contains(voter_address) { - let amount: VotePower = storage - .validator_stake(voter_address, epoch)? - .into(); - yay_validators - .insert(voter_address.clone(), amount); - } else if !validators.contains(voter_address) { - let validator_address = - gov_storage::get_vote_delegation_address(&key); - match validator_address { - Some(validator) => { - let bond_id = BondId { - source: voter_address.clone(), - validator: validator.clone(), - }; - let amount = - storage.bond_amount(&bond_id, epoch)?; - if amount != token::Amount::default() { - if vote.is_yay() { - let entry = yay_delegators - .entry(voter_address.to_owned()) - .or_default(); - entry.insert( - validator.to_owned(), - VotePower::from(amount), - ); - } else { - let entry = nay_delegators - .entry(voter_address.to_owned()) - .or_default(); - entry.insert( - validator.to_owned(), - VotePower::from(amount), - ); - } - } + for next_vote in vote_iter { + let (vote_key, vote) = next_vote?; + let voter_address = gov_storage::get_voter_address(&vote_key); + match voter_address { + Some(voter_address) => { + if vote.is_yay() && validators.contains(voter_address) { + let amount: VotePower = + storage.validator_stake(voter_address, epoch)?.into(); + yay_validators.insert(voter_address.clone(), amount); + } else if !validators.contains(voter_address) { + let validator_address = + gov_storage::get_vote_delegation_address(&vote_key); + match validator_address { + Some(validator) => { + let bond_id = BondId { + source: voter_address.clone(), + validator: validator.clone(), + }; + let amount = + storage.bond_amount(&bond_id, epoch)?; + if amount != token::Amount::default() { + if vote.is_yay() { + let entry = yay_delegators + .entry(voter_address.to_owned()) + .or_default(); + entry.insert( + validator.to_owned(), + VotePower::from(amount), + ); + } else { + let entry = nay_delegators + .entry(voter_address.to_owned()) + .or_default(); + entry.insert( + validator.to_owned(), + VotePower::from(amount), + ); } - None => continue, } } + None => continue, } - None => continue, } } - _ => continue, + None => continue, } } diff --git a/shared/src/ledger/native_vp/mod.rs b/shared/src/ledger/native_vp/mod.rs index e14378c3d35..231405dde54 100644 --- a/shared/src/ledger/native_vp/mod.rs +++ b/shared/src/ledger/native_vp/mod.rs @@ -176,7 +176,7 @@ where H: 'static + StorageHasher, CA: 'static + WasmCacheAccess, { - type PrefixIter<'iter> = >::PrefixIter where Self: 'iter; + type PrefixIter<'iter> = storage::PrefixIter<'iter, DB> where Self: 'iter; fn read_bytes( &self, @@ -198,18 +198,21 @@ where vp_host_fns::has_key_pre( &mut self.ctx.gas_meter.borrow_mut(), self.ctx.storage, + self.ctx.write_log, key, ) .into_storage_result() } - fn iter_next<'iter>( + fn iter_prefix<'iter>( &'iter self, - iter: &mut Self::PrefixIter<'iter>, - ) -> Result)>, storage_api::Error> { - vp_host_fns::iter_pre_next::( + prefix: &crate::types::storage::Key, + ) -> Result, storage_api::Error> { + vp_host_fns::iter_prefix_pre( &mut self.ctx.gas_meter.borrow_mut(), - iter, + self.ctx.write_log, + self.ctx.storage, + prefix, ) .into_storage_result() } @@ -217,11 +220,12 @@ where // ---- Methods below are implemented in `self.ctx`, because they are // the same in `pre/post` ---- - fn iter_prefix<'iter>( + fn iter_next<'iter>( &'iter self, - prefix: &crate::types::storage::Key, - ) -> Result, storage_api::Error> { - self.ctx.iter_prefix(prefix) + iter: &mut Self::PrefixIter<'iter>, + ) -> Result)>, storage_api::Error> { + vp_host_fns::iter_next::(&mut self.ctx.gas_meter.borrow_mut(), iter) + .into_storage_result() } fn get_chain_id(&self) -> Result { @@ -256,7 +260,7 @@ where H: 'static + StorageHasher, CA: 'static + WasmCacheAccess, { - type PrefixIter<'iter> = >::PrefixIter where Self:'iter; + type PrefixIter<'iter> = storage::PrefixIter<'iter, DB> where Self: 'iter; fn read_bytes( &self, @@ -284,14 +288,15 @@ where .into_storage_result() } - fn iter_next<'iter>( + fn iter_prefix<'iter>( &'iter self, - iter: &mut Self::PrefixIter<'iter>, - ) -> Result)>, storage_api::Error> { - vp_host_fns::iter_post_next::( + prefix: &crate::types::storage::Key, + ) -> Result, storage_api::Error> { + vp_host_fns::iter_prefix_post( &mut self.ctx.gas_meter.borrow_mut(), self.ctx.write_log, - iter, + self.ctx.storage, + prefix, ) .into_storage_result() } @@ -299,11 +304,12 @@ where // ---- Methods below are implemented in `self.ctx`, because they are // the same in `pre/post` ---- - fn iter_prefix<'iter>( + fn iter_next<'iter>( &'iter self, - prefix: &crate::types::storage::Key, - ) -> Result, storage_api::Error> { - self.ctx.iter_prefix(prefix) + iter: &mut Self::PrefixIter<'iter>, + ) -> Result)>, storage_api::Error> { + vp_host_fns::iter_next::(&mut self.ctx.gas_meter.borrow_mut(), iter) + .into_storage_result() } fn get_chain_id(&self) -> Result { @@ -339,7 +345,7 @@ where { type Post = CtxPostStorageRead<'view, 'a, DB, H, CA>; type Pre = CtxPreStorageRead<'view, 'a, DB, H, CA>; - type PrefixIter<'iter> = >::PrefixIter where Self: 'iter; + type PrefixIter<'iter> = storage::PrefixIter<'iter, DB> where Self: 'iter; fn pre(&'view self) -> Self::Pre { CtxPreStorageRead { ctx: self } @@ -426,8 +432,9 @@ where &'iter self, prefix: &Key, ) -> Result, storage_api::Error> { - vp_host_fns::iter_prefix( + vp_host_fns::iter_prefix_pre( &mut self.gas_meter.borrow_mut(), + self.write_log, self.storage, prefix, ) diff --git a/shared/src/ledger/native_vp/parameters.rs b/shared/src/ledger/native_vp/parameters.rs index 0a762ca4f67..6c41ba8dc3d 100644 --- a/shared/src/ledger/native_vp/parameters.rs +++ b/shared/src/ledger/native_vp/parameters.rs @@ -52,7 +52,7 @@ where let key_type: KeyType = key.into(); match key_type { KeyType::PARAMETER => governance::utils::is_proposal_accepted( - self.ctx.storage, + &self.ctx.pre(), tx_data, ) .unwrap_or(false), diff --git a/shared/src/ledger/native_vp/slash_fund.rs b/shared/src/ledger/native_vp/slash_fund.rs index 49507056470..4caf0f3401f 100644 --- a/shared/src/ledger/native_vp/slash_fund.rs +++ b/shared/src/ledger/native_vp/slash_fund.rs @@ -61,7 +61,7 @@ where return true; } governance::utils::is_proposal_accepted( - self.ctx.storage, + &self.ctx.pre(), tx_data, ) .unwrap_or(false) diff --git a/shared/src/ledger/pos/mod.rs b/shared/src/ledger/pos/mod.rs index 2878f89fb2f..bcf8517aef5 100644 --- a/shared/src/ledger/pos/mod.rs +++ b/shared/src/ledger/pos/mod.rs @@ -10,7 +10,6 @@ use namada_proof_of_stake::PosBase; use rust_decimal::Decimal; pub use vp::PosVP; -use crate::ledger::storage::{self as ledger_storage, Storage, StorageHasher}; use crate::types::address::{Address, InternalAddress}; use crate::types::storage::Epoch; @@ -32,14 +31,13 @@ pub fn into_tm_voting_power( } /// Initialize storage in the genesis block. -pub fn init_genesis_storage<'a, DB, H>( - storage: &mut Storage, +pub fn init_genesis_storage<'a, S>( + storage: &mut S, params: &'a PosParams, validators: impl Iterator + Clone + 'a, current_epoch: Epoch, ) where - DB: ledger_storage::DB + for<'iter> ledger_storage::DBIter<'iter>, - H: StorageHasher, + S: PosBase, { storage .init_genesis(params, validators, current_epoch) diff --git a/shared/src/ledger/pos/vp.rs b/shared/src/ledger/pos/vp.rs index 2a2e87bdba2..92e1837412e 100644 --- a/shared/src/ledger/pos/vp.rs +++ b/shared/src/ledger/pos/vp.rs @@ -123,7 +123,7 @@ where for key in keys_changed { if is_params_key(key) { return governance::utils::is_proposal_accepted( - self.ctx.storage, + &self.ctx.pre(), tx_data, ) .map_err(Error::NativeVpError); diff --git a/shared/src/ledger/queries/mod.rs b/shared/src/ledger/queries/mod.rs index 131e5ce572f..565100b81b4 100644 --- a/shared/src/ledger/queries/mod.rs +++ b/shared/src/ledger/queries/mod.rs @@ -58,7 +58,7 @@ where H: 'static + StorageHasher + Sync, { if request.height != BlockHeight(0) - && request.height != ctx.storage.last_height + && request.height != ctx.wl_storage.storage.last_height { return Err(storage_api::Error::new_const( "This query doesn't support arbitrary block heights, only the \ @@ -159,7 +159,7 @@ mod testing { use super::*; use crate::ledger::events::log::EventLog; - use crate::ledger::storage::testing::TestStorage; + use crate::ledger::storage::testing::TestWlStorage; use crate::types::storage::BlockHeight; use crate::vm::wasm::{self, TxCache, VpCache}; use crate::vm::WasmCacheRoAccess; @@ -172,7 +172,7 @@ mod testing { /// RPC router pub rpc: RPC, /// storage - pub storage: TestStorage, + pub wl_storage: TestWlStorage, /// event log pub event_log: EventLog, /// VP wasm compilation cache @@ -193,7 +193,7 @@ mod testing { /// Initialize a test client for the given root RPC router pub fn new(rpc: RPC) -> Self { // Initialize the `TestClient` - let storage = TestStorage::default(); + let wl_storage = TestWlStorage::default(); let event_log = EventLog::default(); let (vp_wasm_cache, vp_cache_dir) = wasm::compilation_cache::common::testing::cache(); @@ -201,7 +201,7 @@ mod testing { wasm::compilation_cache::common::testing::cache(); Self { rpc, - storage, + wl_storage, event_log, vp_wasm_cache: vp_wasm_cache.read_only(), tx_wasm_cache: tx_wasm_cache.read_only(), @@ -236,7 +236,7 @@ mod testing { prove, }; let ctx = RequestCtx { - storage: &self.storage, + wl_storage: &self.wl_storage, event_log: &self.event_log, vp_wasm_cache: self.vp_wasm_cache.clone(), tx_wasm_cache: self.tx_wasm_cache.clone(), diff --git a/shared/src/ledger/queries/router.rs b/shared/src/ledger/queries/router.rs index 9bf1116a5c6..b05be9bb559 100644 --- a/shared/src/ledger/queries/router.rs +++ b/shared/src/ledger/queries/router.rs @@ -1007,7 +1007,7 @@ mod test { }; let ctx = RequestCtx { event_log: &client.event_log, - storage: &client.storage, + wl_storage: &client.wl_storage, vp_wasm_cache: client.vp_wasm_cache.clone(), tx_wasm_cache: client.tx_wasm_cache.clone(), storage_read_past_height_limit: None, diff --git a/shared/src/ledger/queries/shell.rs b/shared/src/ledger/queries/shell.rs index 61f43a6289c..43a51a1b0f2 100644 --- a/shared/src/ledger/queries/shell.rs +++ b/shared/src/ledger/queries/shell.rs @@ -90,7 +90,7 @@ where TxIndex(0), &mut gas_meter, &mut write_log, - ctx.storage, + &ctx.wl_storage.storage, &mut ctx.vp_wasm_cache, &mut ctx.tx_wasm_cache, ) @@ -111,9 +111,11 @@ where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, { - let (iter, _gas) = ctx.storage.iter_results(); - let mut results = - vec![BlockResults::default(); ctx.storage.block.height.0 as usize + 1]; + let (iter, _gas) = ctx.wl_storage.storage.iter_results(); + let mut results = vec![ + BlockResults::default(); + ctx.wl_storage.storage.block.height.0 as usize + 1 + ]; iter.for_each(|(key, value, _gas)| { let key = key .parse::() @@ -135,8 +137,12 @@ where H: 'static + StorageHasher + Sync, { // Conversion values are constructed on request - if let Some((addr, epoch, conv, pos)) = - ctx.storage.conversion_state.assets.get(&asset_type) + if let Some((addr, epoch, conv, pos)) = ctx + .wl_storage + .storage + .conversion_state + .assets + .get(&asset_type) { Ok(( addr.clone(), @@ -144,7 +150,7 @@ where Into::::into( conv.clone(), ), - ctx.storage.conversion_state.tree.path(*pos), + ctx.wl_storage.storage.conversion_state.tree.path(*pos), )) } else { Err(storage_api::Error::new(std::io::Error::new( @@ -171,7 +177,7 @@ where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, { - let data = ctx.storage.last_epoch; + let data = ctx.wl_storage.storage.last_epoch; Ok(data) } @@ -189,7 +195,9 @@ where H: 'static + StorageHasher + Sync, { if let Some(past_height_limit) = ctx.storage_read_past_height_limit { - if request.height.0 + past_height_limit < ctx.storage.last_height.0 { + if request.height.0 + past_height_limit + < ctx.wl_storage.storage.last_height.0 + { return Err(storage_api::Error::new(std::io::Error::new( std::io::ErrorKind::InvalidInput, format!( @@ -202,6 +210,7 @@ where } match ctx + .wl_storage .storage .read_with_height(&storage_key, request.height) .into_storage_result()? @@ -209,6 +218,7 @@ where (Some(value), _gas) => { let proof = if request.prove { let proof = ctx + .wl_storage .storage .get_existence_proof(&storage_key, &value, request.height) .into_storage_result()?; @@ -225,6 +235,7 @@ where (None, _gas) => { let proof = if request.prove { let proof = ctx + .wl_storage .storage .get_non_existence_proof(&storage_key, request.height) .into_storage_result()?; @@ -252,7 +263,7 @@ where { require_latest_height(&ctx, request)?; - let iter = storage_api::iter_prefix_bytes(ctx.storage, &storage_key)?; + let iter = storage_api::iter_prefix_bytes(ctx.wl_storage, &storage_key)?; let data: storage_api::Result> = iter .map(|iter_result| { let (key, value) = iter_result?; @@ -264,6 +275,7 @@ where let mut ops = vec![]; for PrefixValue { key, value } in &data { let mut proof: crate::tendermint::merkle::proof::Proof = ctx + .wl_storage .storage .get_existence_proof(key, value, request.height) .into_storage_result()?; @@ -291,7 +303,7 @@ where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, { - let data = StorageRead::has_key(ctx.storage, &storage_key)?; + let data = StorageRead::has_key(ctx.wl_storage, &storage_key)?; Ok(data) } @@ -370,7 +382,7 @@ mod test { // Request last committed epoch let read_epoch = RPC.shell().epoch(&client).await.unwrap(); - let current_epoch = client.storage.last_epoch; + let current_epoch = client.wl_storage.storage.last_epoch; assert_eq!(current_epoch, read_epoch); // Request dry run tx @@ -415,7 +427,10 @@ mod test { // Then write some balance ... let balance = token::Amount::from(1000); - StorageWrite::write(&mut client.storage, &balance_key, balance)?; + StorageWrite::write(&mut client.wl_storage, &balance_key, balance)?; + // It has to be committed to be visible in a query + client.wl_storage.commit_tx(); + client.wl_storage.commit_block().unwrap(); // ... there should be the same value now let read_balance = RPC .shell() diff --git a/shared/src/ledger/queries/types.rs b/shared/src/ledger/queries/types.rs index 0a15319af1c..fd65edcfd92 100644 --- a/shared/src/ledger/queries/types.rs +++ b/shared/src/ledger/queries/types.rs @@ -1,5 +1,7 @@ +use namada_core::ledger::storage::WlStorage; + use crate::ledger::events::log::EventLog; -use crate::ledger::storage::{DBIter, Storage, StorageHasher, DB}; +use crate::ledger::storage::{DBIter, StorageHasher, DB}; use crate::ledger::storage_api; use crate::tendermint::merkle::proof::Proof; use crate::types::storage::BlockHeight; @@ -16,8 +18,8 @@ where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, { - /// Reference to the ledger's [`Storage`]. - pub storage: &'shell Storage, + /// Reference to the ledger's [`WlStorage`]. + pub wl_storage: &'shell WlStorage, /// Log of events emitted by `FinalizeBlock` ABCI calls. pub event_log: &'shell EventLog, /// Cache of VP wasm compiled artifacts. @@ -146,7 +148,7 @@ impl RequestQuery { /// to use the latest committed block height as per tendermint ABCI Query /// spec. A negative block height will cause an error. pub fn try_from_tm( - storage: &Storage, + storage: &WlStorage, crate::tendermint_proto::abci::RequestQuery { data, path, @@ -161,7 +163,7 @@ impl RequestQuery { let height = match height { 0 => { // `0` means last committed height - storage.last_height + storage.storage.last_height } _ => BlockHeight(height.try_into().map_err(|_| { format!("Query height cannot be negative, got: {}", height) diff --git a/shared/src/ledger/queries/vp/pos.rs b/shared/src/ledger/queries/vp/pos.rs index 5c3fcf80df4..b6b63874b50 100644 --- a/shared/src/ledger/queries/vp/pos.rs +++ b/shared/src/ledger/queries/vp/pos.rs @@ -43,7 +43,7 @@ where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, { - ctx.storage.is_validator(&addr) + ctx.wl_storage.is_validator(&addr) } /// Get all the validator known addresses. These validators may be in any state, @@ -56,8 +56,8 @@ where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, { - let epoch = epoch.unwrap_or(ctx.storage.last_epoch); - ctx.storage.validator_addresses(epoch) + let epoch = epoch.unwrap_or(ctx.wl_storage.storage.last_epoch); + ctx.wl_storage.validator_addresses(epoch) } /// Get the total stake of a validator at the given epoch or current when @@ -72,8 +72,8 @@ where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, { - let epoch = epoch.unwrap_or(ctx.storage.last_epoch); - ctx.storage.validator_stake(&validator, epoch) + let epoch = epoch.unwrap_or(ctx.wl_storage.storage.last_epoch); + ctx.wl_storage.validator_stake(&validator, epoch) } /// Get the total stake in PoS system at the given epoch or current when `None`. @@ -85,8 +85,8 @@ where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, { - let epoch = epoch.unwrap_or(ctx.storage.last_epoch); - ctx.storage.total_stake(epoch) + let epoch = epoch.unwrap_or(ctx.wl_storage.storage.last_epoch); + ctx.wl_storage.total_stake(epoch) } /// Get the total bond amount for the given bond ID (this may be delegation or @@ -102,13 +102,13 @@ where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, { - let epoch = epoch.unwrap_or(ctx.storage.last_epoch); + let epoch = epoch.unwrap_or(ctx.wl_storage.storage.last_epoch); let bond_id = BondId { source: owner, validator, }; - ctx.storage.bond_amount(&bond_id, epoch) + ctx.wl_storage.bond_amount(&bond_id, epoch) } /// Find all the validator addresses to whom the given `owner` address has @@ -125,7 +125,7 @@ where let mut delegations: HashSet
= HashSet::new(); for iter_result in - storage_api::iter_prefix_bytes(ctx.storage, &bonds_prefix)? + storage_api::iter_prefix_bytes(ctx.wl_storage, &bonds_prefix)? { let (key, _bonds_bytes) = iter_result?; let validator_address = pos::get_validator_address_from_bond(&key) diff --git a/shared/src/ledger/storage/mod.rs b/shared/src/ledger/storage/mod.rs index d55a96db390..0578a8286c6 100644 --- a/shared/src/ledger/storage/mod.rs +++ b/shared/src/ledger/storage/mod.rs @@ -1,7 +1,7 @@ //! Ledger's state storage with key-value backed store and a merkle tree -pub mod write_log; - #[cfg(any(test, feature = "testing"))] pub use namada_core::ledger::storage::mockdb; -pub use namada_core::ledger::storage::{traits, *}; +pub use namada_core::ledger::storage::{ + traits, write_log, PrefixIter, WlStorage, *, +}; diff --git a/shared/src/ledger/vp_host_fns.rs b/shared/src/ledger/vp_host_fns.rs index c678744ccf1..5bcbd69cf41 100644 --- a/shared/src/ledger/vp_host_fns.rs +++ b/shared/src/ledger/vp_host_fns.rs @@ -157,16 +157,32 @@ pub fn read_temp( pub fn has_key_pre( gas_meter: &mut VpGasMeter, storage: &Storage, + write_log: &WriteLog, key: &Key, ) -> EnvResult where DB: storage::DB + for<'iter> storage::DBIter<'iter>, H: StorageHasher, { - let (present, gas) = - storage.has_key(key).map_err(RuntimeError::StorageError)?; + // Try to read from the write log first + let (log_val, gas) = write_log.read_pre(key); add_gas(gas_meter, gas)?; - Ok(present) + match log_val { + Some(&write_log::StorageModification::Write { .. }) => Ok(true), + Some(&write_log::StorageModification::Delete) => { + // The given key has been deleted + Ok(false) + } + Some(&write_log::StorageModification::InitAccount { .. }) => Ok(true), + Some(&write_log::StorageModification::Temp { .. }) => Ok(true), + None => { + // When not found in write log, try to check the storage + let (present, gas) = + storage.has_key(key).map_err(RuntimeError::StorageError)?; + add_gas(gas_meter, gas)?; + Ok(present) + } + } } /// Storage `has_key` in posterior state (after tx execution). It will try to @@ -295,71 +311,51 @@ where Ok(storage.native_token.clone()) } -/// Storage prefix iterator, ordered by storage keys. It will try to get an -/// iterator from the storage. -pub fn iter_prefix<'a, DB, H>( +/// Storage prefix iterator for prior state (before tx execution), ordered by +/// storage keys. It will try to get an iterator from the storage. +pub fn iter_prefix_pre<'a, DB, H>( gas_meter: &mut VpGasMeter, + write_log: &'a WriteLog, storage: &'a Storage, prefix: &Key, -) -> EnvResult<>::PrefixIter> +) -> EnvResult> where DB: storage::DB + for<'iter> storage::DBIter<'iter>, H: StorageHasher, { - let (iter, gas) = storage.iter_prefix(prefix); + let (iter, gas) = storage::iter_prefix_pre(write_log, storage, prefix); add_gas(gas_meter, gas)?; Ok(iter) } -/// Storage prefix iterator for prior state (before tx execution). It will try -/// to read from the storage. -pub fn iter_pre_next( +/// Storage prefix iterator for posterior state (after tx execution), ordered by +/// storage keys. It will try to get an iterator from the storage. +pub fn iter_prefix_post<'a, DB, H>( gas_meter: &mut VpGasMeter, - iter: &mut >::PrefixIter, -) -> EnvResult)>> + write_log: &'a WriteLog, + storage: &'a Storage, + prefix: &Key, +) -> EnvResult> where DB: storage::DB + for<'iter> storage::DBIter<'iter>, + H: StorageHasher, { - if let Some((key, val, gas)) = iter.next() { - add_gas(gas_meter, gas)?; - return Ok(Some((key, val))); - } - Ok(None) + let (iter, gas) = storage::iter_prefix_post(write_log, storage, prefix); + add_gas(gas_meter, gas)?; + Ok(iter) } -/// Storage prefix iterator next for posterior state (after tx execution). It -/// will try to read from the write log first and if no entry found then from -/// the storage. -pub fn iter_post_next( +/// Get the next item in a storage prefix iterator (pre or post). +pub fn iter_next( gas_meter: &mut VpGasMeter, - write_log: &WriteLog, - iter: &mut >::PrefixIter, + iter: &mut storage::PrefixIter, ) -> EnvResult)>> where DB: storage::DB + for<'iter> storage::DBIter<'iter>, { - for (key, val, iter_gas) in iter { - let (log_val, log_gas) = write_log.read( - &Key::parse(key.clone()).map_err(RuntimeError::StorageDataError)?, - ); - add_gas(gas_meter, iter_gas + log_gas)?; - match log_val { - Some(&write_log::StorageModification::Write { ref value }) => { - return Ok(Some((key, value.clone()))); - } - Some(&write_log::StorageModification::Delete) => { - // check the next because the key has already deleted - continue; - } - Some(&write_log::StorageModification::InitAccount { .. }) => { - // a VP of a new account doesn't need to be iterated - continue; - } - Some(&write_log::StorageModification::Temp { .. }) => { - return Err(RuntimeError::ReadTemporaryValueError); - } - None => return Ok(Some((key, val))), - } + if let Some((key, val, gas)) = iter.next() { + add_gas(gas_meter, gas)?; + return Ok(Some((key, val))); } Ok(None) } diff --git a/shared/src/vm/host_env.rs b/shared/src/vm/host_env.rs index ee8e8a36014..2ceaee746ff 100644 --- a/shared/src/vm/host_env.rs +++ b/shared/src/vm/host_env.rs @@ -696,7 +696,7 @@ pub fn tx_iter_prefix( ) -> TxResult where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: 'static + storage::DB + for<'iter> storage::DBIter<'iter>, H: StorageHasher, CA: WasmCacheAccess, { @@ -706,15 +706,17 @@ where .map_err(|e| TxRuntimeError::MemoryError(Box::new(e)))?; tx_add_gas(env, gas)?; - tracing::debug!("tx_iter_prefix {}, prefix {}", prefix, prefix_ptr); + tracing::debug!("tx_iter_prefix {}", prefix); let prefix = Key::parse(prefix).map_err(TxRuntimeError::StorageDataError)?; + let write_log = unsafe { env.ctx.write_log.get() }; let storage = unsafe { env.ctx.storage.get() }; - let iterators = unsafe { env.ctx.iterators.get() }; - let (iter, gas) = storage.iter_prefix(&prefix); + let (iter, gas) = storage::iter_prefix_post(write_log, storage, &prefix); tx_add_gas(env, gas)?; + + let iterators = unsafe { env.ctx.iterators.get() }; Ok(iterators.insert(iter).id()) } @@ -1183,7 +1185,9 @@ where let key = Key::parse(key).map_err(vp_host_fns::RuntimeError::StorageDataError)?; let storage = unsafe { env.ctx.storage.get() }; - let present = vp_host_fns::has_key_pre(gas_meter, storage, &key)?; + let write_log = unsafe { env.ctx.write_log.get() }; + let present = + vp_host_fns::has_key_pre(gas_meter, storage, write_log, &key)?; Ok(HostEnvResult::from(present).to_i64()) } @@ -1220,17 +1224,18 @@ where Ok(HostEnvResult::from(present).to_i64()) } -/// Storage prefix iterator function exposed to the wasm VM VP environment. -/// It will try to get an iterator from the storage and return the corresponding -/// ID of the iterator, ordered by storage keys. -pub fn vp_iter_prefix( +/// Storage prefix iterator function for prior state (before tx execution) +/// exposed to the wasm VM VP environment. It will try to get an iterator from +/// the storage and return the corresponding ID of the iterator, ordered by +/// storage keys. +pub fn vp_iter_prefix_pre( env: &VpVmEnv, prefix_ptr: u64, prefix_len: u64, ) -> vp_host_fns::EnvResult where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: 'static + storage::DB + for<'iter> storage::DBIter<'iter>, H: StorageHasher, EVAL: VpEvaluator, CA: WasmCacheAccess, @@ -1242,63 +1247,63 @@ where let gas_meter = unsafe { env.ctx.gas_meter.get() }; vp_host_fns::add_gas(gas_meter, gas)?; + tracing::debug!("vp_iter_prefix_pre {}", prefix); + let prefix = Key::parse(prefix) .map_err(vp_host_fns::RuntimeError::StorageDataError)?; - tracing::debug!("vp_iter_prefix {}", prefix); + let write_log = unsafe { env.ctx.write_log.get() }; let storage = unsafe { env.ctx.storage.get() }; - let iter = vp_host_fns::iter_prefix(gas_meter, storage, &prefix)?; + let iter = + vp_host_fns::iter_prefix_pre(gas_meter, write_log, storage, &prefix)?; + let iterators = unsafe { env.ctx.iterators.get() }; Ok(iterators.insert(iter).id()) } -/// Storage prefix iterator for prior state (before tx execution) function -/// exposed to the wasm VM VP environment. It will try to read from the storage. -/// -/// Returns `-1` when the key is not present, or the length of the data when -/// the key is present (the length may be `0`). -pub fn vp_iter_pre_next( +/// Storage prefix iterator function for posterior state (after tx execution) +/// exposed to the wasm VM VP environment. It will try to get an iterator from +/// the storage and return the corresponding ID of the iterator, ordered by +/// storage keys. +pub fn vp_iter_prefix_post( env: &VpVmEnv, - iter_id: u64, -) -> vp_host_fns::EnvResult + prefix_ptr: u64, + prefix_len: u64, +) -> vp_host_fns::EnvResult where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: 'static + storage::DB + for<'iter> storage::DBIter<'iter>, H: StorageHasher, EVAL: VpEvaluator, CA: WasmCacheAccess, { - tracing::debug!("vp_iter_pre_next iter_id {}", iter_id); + let (prefix, gas) = env + .memory + .read_string(prefix_ptr, prefix_len as _) + .map_err(|e| vp_host_fns::RuntimeError::MemoryError(Box::new(e)))?; + let gas_meter = unsafe { env.ctx.gas_meter.get() }; + vp_host_fns::add_gas(gas_meter, gas)?; + + tracing::debug!("vp_iter_prefix_post {}", prefix); + + let prefix = Key::parse(prefix) + .map_err(vp_host_fns::RuntimeError::StorageDataError)?; + + let write_log = unsafe { env.ctx.write_log.get() }; + let storage = unsafe { env.ctx.storage.get() }; + let iter = + vp_host_fns::iter_prefix_post(gas_meter, write_log, storage, &prefix)?; let iterators = unsafe { env.ctx.iterators.get() }; - let iter_id = PrefixIteratorId::new(iter_id); - if let Some(iter) = iterators.get_mut(iter_id) { - let gas_meter = unsafe { env.ctx.gas_meter.get() }; - if let Some((key, val)) = - vp_host_fns::iter_pre_next::(gas_meter, iter)? - { - let key_val = KeyVal { key, val } - .try_to_vec() - .map_err(vp_host_fns::RuntimeError::EncodingError)?; - let len: i64 = key_val - .len() - .try_into() - .map_err(vp_host_fns::RuntimeError::NumConversionError)?; - let result_buffer = unsafe { env.ctx.result_buffer.get() }; - result_buffer.replace(key_val); - return Ok(len); - } - } - Ok(HostEnvResult::Fail.to_i64()) + Ok(iterators.insert(iter).id()) } -/// Storage prefix iterator next for posterior state (after tx execution) -/// function exposed to the wasm VM VP environment. It will try to read from the -/// write log first and if no entry found then from the storage. +/// Storage prefix iterator for prior or posterior state function +/// exposed to the wasm VM VP environment. /// /// Returns `-1` when the key is not present, or the length of the data when /// the key is present (the length may be `0`). -pub fn vp_iter_post_next( +pub fn vp_iter_next( env: &VpVmEnv, iter_id: u64, ) -> vp_host_fns::EnvResult @@ -1309,16 +1314,13 @@ where EVAL: VpEvaluator, CA: WasmCacheAccess, { - tracing::debug!("vp_iter_post_next iter_id {}", iter_id); + tracing::debug!("vp_iter_next iter_id {}", iter_id); let iterators = unsafe { env.ctx.iterators.get() }; let iter_id = PrefixIteratorId::new(iter_id); if let Some(iter) = iterators.get_mut(iter_id) { let gas_meter = unsafe { env.ctx.gas_meter.get() }; - let write_log = unsafe { env.ctx.write_log.get() }; - if let Some((key, val)) = - vp_host_fns::iter_post_next::(gas_meter, write_log, iter)? - { + if let Some((key, val)) = vp_host_fns::iter_next(gas_meter, iter)? { let key_val = KeyVal { key, val } .try_to_vec() .map_err(vp_host_fns::RuntimeError::EncodingError)?; diff --git a/shared/src/vm/prefix_iter.rs b/shared/src/vm/prefix_iter.rs index e30cc728649..49e58b33ddf 100644 --- a/shared/src/vm/prefix_iter.rs +++ b/shared/src/vm/prefix_iter.rs @@ -3,6 +3,8 @@ use std::collections::HashMap; +use namada_core::ledger::storage::PrefixIter; + use crate::ledger::storage; /// A temporary iterators storage, used during a wasm run after which it's @@ -10,18 +12,18 @@ use crate::ledger::storage; #[derive(Debug)] pub struct PrefixIterators<'iter, DB> where - DB: storage::DBIter<'iter>, + DB: storage::DB + storage::DBIter<'iter>, { index: PrefixIteratorId, - iterators: HashMap, + iterators: HashMap>, } impl<'iter, DB> PrefixIterators<'iter, DB> where - DB: storage::DBIter<'iter>, + DB: storage::DB + storage::DBIter<'iter>, { /// Insert a new prefix iterator to the temporary storage. - pub fn insert(&mut self, iter: DB::PrefixIter) -> PrefixIteratorId { + pub fn insert(&mut self, iter: PrefixIter<'iter, DB>) -> PrefixIteratorId { let id = self.index; self.iterators.insert(id, iter); self.index = id.next_id(); @@ -32,7 +34,7 @@ where pub fn next( &mut self, id: PrefixIteratorId, - ) -> Option<::Item> { + ) -> Option< as Iterator>::Item> { self.iterators.get_mut(&id).and_then(|i| i.next()) } @@ -40,14 +42,14 @@ where pub fn get_mut( &mut self, id: PrefixIteratorId, - ) -> Option<&mut DB::PrefixIter> { + ) -> Option<&mut PrefixIter<'iter, DB>> { self.iterators.get_mut(&id) } } impl<'iter, DB> Default for PrefixIterators<'iter, DB> where - DB: storage::DBIter<'iter>, + DB: storage::DB + storage::DBIter<'iter>, { fn default() -> Self { Self { diff --git a/shared/src/vm/wasm/host_env.rs b/shared/src/vm/wasm/host_env.rs index 28fb2cc12a3..74c87ed69b9 100644 --- a/shared/src/vm/wasm/host_env.rs +++ b/shared/src/vm/wasm/host_env.rs @@ -108,9 +108,9 @@ where "namada_vp_result_buffer" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_result_buffer), "namada_vp_has_key_pre" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_has_key_pre), "namada_vp_has_key_post" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_has_key_post), - "namada_vp_iter_prefix" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_iter_prefix), - "namada_vp_iter_pre_next" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_iter_pre_next), - "namada_vp_iter_post_next" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_iter_post_next), + "namada_vp_iter_prefix_pre" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_iter_prefix_pre), + "namada_vp_iter_prefix_post" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_iter_prefix_pre), + "namada_vp_iter_next" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_iter_next), "namada_vp_get_chain_id" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_get_chain_id), "namada_vp_get_tx_index" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_get_tx_index), "namada_vp_get_block_height" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_get_block_height), diff --git a/shared/src/vm/wasm/run.rs b/shared/src/vm/wasm/run.rs index 233a5f72a0d..0efdac499cb 100644 --- a/shared/src/vm/wasm/run.rs +++ b/shared/src/vm/wasm/run.rs @@ -290,7 +290,7 @@ fn run_vp( } /// Validity predicate wasm evaluator for `eval` host function calls. -#[derive(Default)] +#[derive(Default, Debug)] pub struct VpEvalWasm where DB: storage::DB + for<'iter> storage::DBIter<'iter>, diff --git a/tests/src/native_vp/mod.rs b/tests/src/native_vp/mod.rs index 28dad14557b..6baf214a3be 100644 --- a/tests/src/native_vp/mod.rs +++ b/tests/src/native_vp/mod.rs @@ -49,8 +49,8 @@ impl TestNativeVpEnv { let ctx = Ctx { iterators: Default::default(), gas_meter: Default::default(), - storage: &self.tx_env.storage, - write_log: &self.tx_env.write_log, + storage: &self.tx_env.wl_storage.storage, + write_log: &self.tx_env.wl_storage.write_log, tx: &self.tx_env.tx, tx_index: &self.tx_env.tx_index, vp_wasm_cache: self.tx_env.vp_wasm_cache.clone(), diff --git a/tests/src/native_vp/pos.rs b/tests/src/native_vp/pos.rs index 69b7502a505..29460107bb9 100644 --- a/tests/src/native_vp/pos.rs +++ b/tests/src/native_vp/pos.rs @@ -117,17 +117,20 @@ pub fn init_pos( tx_host_env::with(|tx_env| { // Ensure that all the used // addresses exist - let native_token = tx_env.storage.native_token.clone(); + let native_token = tx_env.wl_storage.storage.native_token.clone(); tx_env.spawn_accounts([&native_token]); for validator in genesis_validators { tx_env.spawn_accounts([&validator.address]); } - tx_env.storage.block.epoch = start_epoch; + tx_env.wl_storage.storage.block.epoch = start_epoch; // Initialize PoS storage tx_env - .storage + .wl_storage .init_genesis(params, genesis_validators.iter(), start_epoch) .unwrap(); + + // Commit changes in WL to genesis state + tx_env.commit_genesis(); }); } @@ -248,7 +251,7 @@ mod tests { if !test_state.is_current_tx_valid { // Clear out the changes tx_host_env::with(|env| { - env.write_log.drop_tx(); + env.wl_storage.drop_tx(); }); } @@ -262,13 +265,13 @@ mod tests { tx_host_env::with(|env| { // Clear out the changes if !test_state.is_current_tx_valid { - env.write_log.drop_tx(); + env.wl_storage.drop_tx(); } // Also commit the last transaction(s) changes, if any env.commit_tx_and_block(); - env.storage.block.epoch = - env.storage.block.epoch.next(); + env.wl_storage.storage.block.epoch = + env.wl_storage.storage.block.epoch.next(); }); // Starting a new tx @@ -298,7 +301,7 @@ mod tests { // Clear out the invalid changes tx_host_env::with(|env| { - env.write_log.drop_tx(); + env.wl_storage.drop_tx(); }) } } @@ -851,7 +854,7 @@ pub mod testing { // Reset the gas meter on each change, so that we never run // out in this test env.gas_meter.reset(); - env.storage.block.epoch + env.wl_storage.storage.block.epoch }); println!("Current epoch {}", current_epoch); diff --git a/tests/src/storage_api/collections/lazy_map.rs b/tests/src/storage_api/collections/lazy_map.rs index afff09bbf1d..5268cf8b100 100644 --- a/tests/src/storage_api/collections/lazy_map.rs +++ b/tests/src/storage_api/collections/lazy_map.rs @@ -241,7 +241,7 @@ mod tests { match &transition { Transition::CommitTx => { // commit the tx without committing the block - tx_host_env::with(|env| env.write_log.commit_tx()); + tx_host_env::with(|env| env.wl_storage.commit_tx()); } Transition::CommitTxAndBlock => { // commit the tx and the block diff --git a/tests/src/storage_api/collections/lazy_vec.rs b/tests/src/storage_api/collections/lazy_vec.rs index 65e08b4ca74..b93c04885cb 100644 --- a/tests/src/storage_api/collections/lazy_vec.rs +++ b/tests/src/storage_api/collections/lazy_vec.rs @@ -235,7 +235,7 @@ mod tests { match &transition { Transition::CommitTx => { // commit the tx without committing the block - tx_host_env::with(|env| env.write_log.commit_tx()); + tx_host_env::with(|env| env.wl_storage.commit_tx()); } Transition::CommitTxAndBlock => { // commit the tx and the block diff --git a/tests/src/storage_api/collections/nested_lazy_map.rs b/tests/src/storage_api/collections/nested_lazy_map.rs index 9951eb318bd..b0ff35094c2 100644 --- a/tests/src/storage_api/collections/nested_lazy_map.rs +++ b/tests/src/storage_api/collections/nested_lazy_map.rs @@ -254,7 +254,7 @@ mod tests { match &transition { Transition::CommitTx => { // commit the tx without committing the block - tx_host_env::with(|env| env.write_log.commit_tx()); + tx_host_env::with(|env| env.wl_storage.commit_tx()); } Transition::CommitTxAndBlock => { // commit the tx and the block diff --git a/tests/src/vm_host_env/ibc.rs b/tests/src/vm_host_env/ibc.rs index 88f2d205a9d..1e604801860 100644 --- a/tests/src/vm_host_env/ibc.rs +++ b/tests/src/vm_host_env/ibc.rs @@ -115,6 +115,7 @@ pub fn validate_ibc_vp_from_tx<'a>( tx: &'a Tx, ) -> std::result::Result { let (verifiers, keys_changed) = tx_env + .wl_storage .write_log .verifiers_and_changed_keys(&tx_env.verifiers); let addr = Address::Internal(InternalAddress::Ibc); @@ -129,8 +130,8 @@ pub fn validate_ibc_vp_from_tx<'a>( let ctx = Ctx::new( &ADDRESS, - &tx_env.storage, - &tx_env.write_log, + &tx_env.wl_storage.storage, + &tx_env.wl_storage.write_log, tx, &TxIndex(0), VpGasMeter::new(0), @@ -150,6 +151,7 @@ pub fn validate_token_vp_from_tx<'a>( target: &Key, ) -> std::result::Result { let (verifiers, keys_changed) = tx_env + .wl_storage .write_log .verifiers_and_changed_keys(&tx_env.verifiers); if !keys_changed.contains(target) { @@ -164,8 +166,8 @@ pub fn validate_token_vp_from_tx<'a>( let ctx = Ctx::new( &ADDRESS, - &tx_env.storage, - &tx_env.write_log, + &tx_env.wl_storage.storage, + &tx_env.wl_storage.write_log, tx, &TxIndex(0), VpGasMeter::new(0), @@ -181,10 +183,14 @@ pub fn validate_token_vp_from_tx<'a>( /// Initialize the test storage. Requires initialized [`tx_host_env::ENV`]. pub fn init_storage() -> (Address, Address) { tx_host_env::with(|env| { - init_genesis_storage(&mut env.storage); + init_genesis_storage(&mut env.wl_storage.storage); // block header to check timeout timestamp - env.storage.set_header(tm_dummy_header()).unwrap(); - env.storage + env.wl_storage + .storage + .set_header(tm_dummy_header()) + .unwrap(); + env.wl_storage + .storage .begin_block(BlockHash::default(), BlockHeight(1)) .unwrap(); }); diff --git a/tests/src/vm_host_env/mod.rs b/tests/src/vm_host_env/mod.rs index 4ab7b6eca76..3f4adf6df5b 100644 --- a/tests/src/vm_host_env/mod.rs +++ b/tests/src/vm_host_env/mod.rs @@ -166,10 +166,8 @@ mod tests { tx_host_env::with(|env| { for i in sub_keys.iter() { let key = prefix.push(i).unwrap(); - let value = i.try_to_vec().unwrap(); - env.storage.write(&key, value).unwrap(); + env.wl_storage.write(&key, i).unwrap(); } - env.storage.commit().unwrap(); }); // Then try to iterate over their prefix @@ -234,23 +232,35 @@ mod tests { assert_eq!( tx::ctx().get_chain_id().unwrap(), - tx_host_env::with(|env| env.storage.get_chain_id().0) + tx_host_env::with(|env| env.wl_storage.storage.get_chain_id().0) ); assert_eq!( tx::ctx().get_block_height().unwrap(), - tx_host_env::with(|env| env.storage.get_block_height().0) + tx_host_env::with(|env| env + .wl_storage + .storage + .get_block_height() + .0) ); assert_eq!( tx::ctx().get_block_hash().unwrap(), - tx_host_env::with(|env| env.storage.get_block_hash().0) + tx_host_env::with(|env| env.wl_storage.storage.get_block_hash().0) ); assert_eq!( tx::ctx().get_block_epoch().unwrap(), - tx_host_env::with(|env| env.storage.get_current_epoch().0) + tx_host_env::with(|env| env + .wl_storage + .storage + .get_current_epoch() + .0) ); assert_eq!( tx::ctx().get_native_token().unwrap(), - tx_host_env::with(|env| env.storage.native_token.clone()) + tx_host_env::with(|env| env + .wl_storage + .storage + .native_token + .clone()) ); } @@ -264,10 +274,7 @@ mod tests { let key_raw = "key"; let key = storage::Key::parse(key_raw).unwrap(); let value = "test".to_string(); - let value_raw = value.try_to_vec().unwrap(); - vp_host_env::with(|env| { - env.write_log.write(&key, value_raw.clone()).unwrap() - }); + vp_host_env::with(|env| env.wl_storage.write(&key, &value).unwrap()); let read_pre_value: Option = vp::CTX.read_pre(&key).unwrap(); assert_eq!(None, read_pre_value); @@ -282,16 +289,16 @@ mod tests { let addr = address::testing::established_address_1(); let addr_key = storage::Key::from(addr.to_db_key()); - // Write some value to storage + // Write some value to storage ... let existing_key = addr_key.join(&Key::parse("existing_key_raw").unwrap()); let existing_value = vec![2_u8; 1000]; - // Values written to storage have to be encoded with Borsh - let existing_value_encoded = existing_value.try_to_vec().unwrap(); tx_env - .storage - .write(&existing_key, existing_value_encoded) + .wl_storage + .write(&existing_key, &existing_value) .unwrap(); + // ... and commit it + tx_env.wl_storage.commit_tx(); // In a transaction, write override the existing key's value and add // another key-value @@ -371,13 +378,13 @@ mod tests { // We'll write sub-key in some random order to check prefix iter's order let sub_keys = [2_i32, 1, i32::MAX, -1, 260, -2, i32::MIN, 5, 0]; - // Write some values to storage + // Write some values to storage ... for i in sub_keys.iter() { let key = prefix.push(i).unwrap(); - let value = i.try_to_vec().unwrap(); - tx_env.storage.write(&key, value).unwrap(); + tx_env.wl_storage.write(&key, i).unwrap(); } - tx_env.storage.commit().unwrap(); + // ... and commit them + tx_env.wl_storage.commit_tx(); // In a transaction, write override the existing key's value and add // another key-value @@ -411,7 +418,10 @@ mod tests { .map(|item| item.unwrap()); // The order in post also has to be sorted - let expected_post = sub_keys.iter().sorted().map(|i| { + let mut expected_keys = sub_keys.to_vec(); + // Add value from `new_key` + expected_keys.push(11); + let expected_post = expected_keys.iter().sorted().map(|i| { let val = if *i == 5 { 100 } else { *i }; (prefix.push(i).unwrap(), val) }); @@ -428,7 +438,7 @@ mod tests { let pk_key = key::pk_key(&addr); let keypair = key::testing::keypair_1(); let pk = keypair.ref_to(); - env.storage + env.wl_storage .write(&pk_key, pk.try_to_vec().unwrap()) .unwrap(); // Initialize the environment @@ -475,23 +485,35 @@ mod tests { assert_eq!( vp::CTX.get_chain_id().unwrap(), - vp_host_env::with(|env| env.storage.get_chain_id().0) + vp_host_env::with(|env| env.wl_storage.storage.get_chain_id().0) ); assert_eq!( vp::CTX.get_block_height().unwrap(), - vp_host_env::with(|env| env.storage.get_block_height().0) + vp_host_env::with(|env| env + .wl_storage + .storage + .get_block_height() + .0) ); assert_eq!( vp::CTX.get_block_hash().unwrap(), - vp_host_env::with(|env| env.storage.get_block_hash().0) + vp_host_env::with(|env| env.wl_storage.storage.get_block_hash().0) ); assert_eq!( vp::CTX.get_block_epoch().unwrap(), - vp_host_env::with(|env| env.storage.get_current_epoch().0) + vp_host_env::with(|env| env + .wl_storage + .storage + .get_current_epoch() + .0) ); assert_eq!( vp::CTX.get_native_token().unwrap(), - vp_host_env::with(|env| env.storage.native_token.clone()) + vp_host_env::with(|env| env + .wl_storage + .storage + .native_token + .clone()) ); } @@ -566,7 +588,7 @@ mod tests { IbcError::ClientError(_), )); // drop the transaction - env.write_log.drop_tx(); + env.wl_storage.drop_tx(); // Start a transaction to create a new client tx_host_env::set(env); @@ -593,10 +615,14 @@ mod tests { // Commit env.commit_tx_and_block(); // update the block height for the following client update - env.storage + env.wl_storage + .storage .begin_block(BlockHash::default(), BlockHeight(2)) .unwrap(); - env.storage.set_header(tm_dummy_header()).unwrap(); + env.wl_storage + .storage + .set_header(tm_dummy_header()) + .unwrap(); // Start an invalid transaction tx_host_env::set(env); @@ -644,7 +670,7 @@ mod tests { IbcError::ClientError(_), )); // drop the transaction - env.write_log.drop_tx(); + env.wl_storage.drop_tx(); // Start a transaction to update the client tx_host_env::set(env); @@ -670,10 +696,14 @@ mod tests { // Commit env.commit_tx_and_block(); // update the block height for the following client update - env.storage + env.wl_storage + .storage .begin_block(BlockHash::default(), BlockHeight(3)) .unwrap(); - env.storage.set_header(tm_dummy_header()).unwrap(); + env.wl_storage + .storage + .set_header(tm_dummy_header()) + .unwrap(); // Start a transaction to upgrade the client tx_host_env::set(env); @@ -707,7 +737,10 @@ mod tests { let (client_id, client_state, writes) = ibc::prepare_client(); writes.into_iter().for_each(|(key, val)| { tx_host_env::with(|env| { - env.storage.write(&key, &val).expect("write error"); + env.wl_storage + .storage + .write(&key, &val) + .expect("write error"); }); }); @@ -748,7 +781,7 @@ mod tests { IbcError::ConnectionError(_), )); // drop the transaction - env.write_log.drop_tx(); + env.wl_storage.drop_tx(); // Start a transaction for ConnectionOpenInit tx_host_env::set(env); @@ -774,7 +807,10 @@ mod tests { // Commit env.commit_tx_and_block(); // set a block header again - env.storage.set_header(tm_dummy_header()).unwrap(); + env.wl_storage + .storage + .set_header(tm_dummy_header()) + .unwrap(); // Start the next transaction for ConnectionOpenAck tx_host_env::set(env); @@ -809,7 +845,10 @@ mod tests { let mut env = tx_host_env::take(); let (client_id, client_state, writes) = ibc::prepare_client(); writes.into_iter().for_each(|(key, val)| { - env.storage.write(&key, &val).expect("write error"); + env.wl_storage + .storage + .write(&key, &val) + .expect("write error"); }); // Start a transaction for ConnectionOpenTry @@ -836,7 +875,10 @@ mod tests { // Commit env.commit_tx_and_block(); // set a block header again - env.storage.set_header(tm_dummy_header()).unwrap(); + env.wl_storage + .storage + .set_header(tm_dummy_header()) + .unwrap(); // Start the next transaction for ConnectionOpenConfirm tx_host_env::set(env); @@ -873,7 +915,10 @@ mod tests { writes.extend(conn_writes); writes.into_iter().for_each(|(key, val)| { tx_host_env::with(|env| { - env.storage.write(&key, &val).expect("write error"); + env.wl_storage + .storage + .write(&key, &val) + .expect("write error"); }); }); @@ -915,7 +960,7 @@ mod tests { IbcError::ChannelError(_), )); // drop the transaction - env.write_log.drop_tx(); + env.wl_storage.drop_tx(); // Start an invalid transaction tx_host_env::set(env); @@ -962,7 +1007,7 @@ mod tests { IbcError::ChannelError(_), )); // drop the transaction - env.write_log.drop_tx(); + env.wl_storage.drop_tx(); // Start a transaction for ChannelOpenInit tx_host_env::set(env); @@ -1023,7 +1068,10 @@ mod tests { writes.extend(conn_writes); writes.into_iter().for_each(|(key, val)| { tx_host_env::with(|env| { - env.storage.write(&key, &val).expect("write error"); + env.wl_storage + .storage + .write(&key, &val) + .expect("write error"); }); }); @@ -1089,7 +1137,10 @@ mod tests { writes.extend(channel_writes); writes.into_iter().for_each(|(key, val)| { tx_host_env::with(|env| { - env.storage.write(&key, &val).expect("write error"); + env.wl_storage + .storage + .write(&key, &val) + .expect("write error"); }); }); @@ -1129,7 +1180,10 @@ mod tests { writes.extend(channel_writes); writes.into_iter().for_each(|(key, val)| { tx_host_env::with(|env| { - env.storage.write(&key, &val).expect("write error"); + env.wl_storage + .storage + .write(&key, &val) + .expect("write error"); }); }); @@ -1170,7 +1224,10 @@ mod tests { writes.extend(channel_writes); writes.into_iter().for_each(|(key, val)| { tx_host_env::with(|env| { - env.storage.write(&key, &val).expect("write error"); + env.wl_storage + .storage + .write(&key, &val) + .expect("write error"); }); }); @@ -1260,7 +1317,10 @@ mod tests { writes.insert(key, init_bal.try_to_vec().unwrap()); writes.into_iter().for_each(|(key, val)| { tx_host_env::with(|env| { - env.storage.write(&key, &val).expect("write error"); + env.wl_storage + .storage + .write(&key, &val) + .expect("write error"); }); }); @@ -1319,7 +1379,10 @@ mod tests { writes.into_iter().for_each(|(key, val)| { tx_host_env::with(|env| { - env.storage.write(&key, &val).expect("write error"); + env.wl_storage + .storage + .write(&key, &val) + .expect("write error"); }); }); @@ -1377,7 +1440,10 @@ mod tests { writes.extend(channel_writes); writes.into_iter().for_each(|(key, val)| { tx_host_env::with(|env| { - env.storage.write(&key, &val).expect("write error"); + env.wl_storage + .storage + .write(&key, &val) + .expect("write error"); }); }); // escrow in advance @@ -1389,7 +1455,10 @@ mod tests { ); let val = Amount::from(1_000_000_000u64).try_to_vec().unwrap(); tx_host_env::with(|env| { - env.storage.write(&escrow, &val).expect("write error"); + env.wl_storage + .storage + .write(&escrow, &val) + .expect("write error"); }); // Set this chain as the source zone @@ -1448,7 +1517,10 @@ mod tests { writes.extend(channel_writes); writes.into_iter().for_each(|(key, val)| { tx_host_env::with(|env| { - env.storage.write(&key, &val).expect("write error"); + env.wl_storage + .storage + .write(&key, &val) + .expect("write error"); }); }); @@ -1523,7 +1595,10 @@ mod tests { writes.extend(channel_writes); writes.into_iter().for_each(|(key, val)| { tx_host_env::with(|env| { - env.storage.write(&key, &val).expect("write error"); + env.wl_storage + .storage + .write(&key, &val) + .expect("write error"); }); }); @@ -1574,7 +1649,10 @@ mod tests { writes.extend(channel_writes); writes.into_iter().for_each(|(key, val)| { tx_host_env::with(|env| { - env.storage.write(&key, &val).expect("write error"); + env.wl_storage + .storage + .write(&key, &val) + .expect("write error"); }) }); @@ -1647,7 +1725,10 @@ mod tests { writes.extend(channel_writes); writes.into_iter().for_each(|(key, val)| { tx_host_env::with(|env| { - env.storage.write(&key, &val).expect("write error"); + env.wl_storage + .storage + .write(&key, &val) + .expect("write error"); }) }); diff --git a/tests/src/vm_host_env/tx.rs b/tests/src/vm_host_env/tx.rs index 0f7040941db..6c3ccd55ae7 100644 --- a/tests/src/vm_host_env/tx.rs +++ b/tests/src/vm_host_env/tx.rs @@ -1,12 +1,12 @@ use std::borrow::Borrow; use std::collections::BTreeSet; -use derivative::Derivative; use namada::ledger::gas::BlockGasMeter; use namada::ledger::parameters::{self, EpochDuration}; use namada::ledger::storage::mockdb::MockDB; use namada::ledger::storage::testing::TestStorage; use namada::ledger::storage::write_log::WriteLog; +use namada::ledger::storage::{Sha256Hasher, WlStorage}; use namada::proto::Tx; use namada::types::address::Address; use namada::types::storage::{Key, TxIndex}; @@ -41,12 +41,9 @@ pub mod tx_host_env { } /// Host environment structures required for transactions. -#[derive(Derivative)] -#[derivative(Debug)] +#[derive(Debug)] pub struct TestTxEnv { - #[derivative(Debug = "ignore")] - pub storage: TestStorage, - pub write_log: WriteLog, + pub wl_storage: WlStorage, pub iterators: PrefixIterators<'static, MockDB>, pub verifiers: BTreeSet
, pub gas_meter: BlockGasMeter, @@ -66,8 +63,10 @@ impl Default for TestTxEnv { wasm::compilation_cache::common::testing::cache(); Self { - storage: TestStorage::default(), - write_log: WriteLog::default(), + wl_storage: WlStorage { + storage: TestStorage::default(), + write_log: WriteLog::default(), + }, iterators: PrefixIterators::default(), gas_meter: BlockGasMeter::default(), tx_index: TxIndex::default(), @@ -84,11 +83,14 @@ impl Default for TestTxEnv { impl TestTxEnv { pub fn all_touched_storage_keys(&self) -> BTreeSet { - self.write_log.get_keys() + self.wl_storage.write_log.get_keys() } pub fn get_verifiers(&self) -> BTreeSet
{ - self.write_log.verifiers_and_changed_keys(&self.verifiers).0 + self.wl_storage + .write_log + .verifiers_and_changed_keys(&self.verifiers) + .0 } pub fn init_parameters( @@ -98,18 +100,18 @@ impl TestTxEnv { tx_whitelist: Option>, ) { let _ = parameters::update_epoch_parameter( - &mut self.storage, + &mut self.wl_storage.storage, &epoch_duration.unwrap_or(EpochDuration { min_num_of_blocks: 1, min_duration: DurationSecs(5), }), ); let _ = parameters::update_tx_whitelist_parameter( - &mut self.storage, + &mut self.wl_storage.storage, tx_whitelist.unwrap_or_default(), ); let _ = parameters::update_vp_whitelist_parameter( - &mut self.storage, + &mut self.wl_storage.storage, vp_whitelist.unwrap_or_default(), ); } @@ -133,16 +135,23 @@ impl TestTxEnv { } let key = Key::validity_predicate(address.borrow()); let vp_code = vec![]; - self.storage + self.wl_storage + .storage .write(&key, vp_code) .expect("Unable to write VP"); } } + /// Commit the genesis state. Typically, you'll want to call this after + /// setting up the initial state, before running a transaction. + pub fn commit_genesis(&mut self) { + self.wl_storage.commit_genesis().unwrap(); + } + pub fn commit_tx_and_block(&mut self) { - self.write_log.commit_tx(); - self.write_log - .commit_block(&mut self.storage) + self.wl_storage.commit_tx(); + self.wl_storage + .commit_block() .map_err(|err| println!("{:?}", err)) .ok(); self.iterators = PrefixIterators::default(); @@ -166,7 +175,8 @@ impl TestTxEnv { } None => token::balance_key(token, target), }; - self.storage + self.wl_storage + .storage .write(&storage_key, amount.try_to_vec().unwrap()) .unwrap(); } @@ -178,7 +188,8 @@ impl TestTxEnv { public_key: &key::common::PublicKey, ) { let storage_key = key::pk_key(address); - self.storage + self.wl_storage + .storage .write(&storage_key, public_key.try_to_vec().unwrap()) .unwrap(); } @@ -187,8 +198,8 @@ impl TestTxEnv { pub fn execute_tx(&mut self) -> Result<(), Error> { let empty_data = vec![]; wasm::run::tx( - &self.storage, - &mut self.write_log, + &self.wl_storage.storage, + &mut self.wl_storage.write_log, &mut self.gas_meter, &self.tx_index, &self.tx.code, @@ -278,16 +289,14 @@ mod native_tx_host_env { /// changes. pub fn set_from_vp_env(vp_env: TestVpEnv) { let TestVpEnv { - storage, - write_log, + wl_storage, tx, vp_wasm_cache, vp_cache_dir, .. } = vp_env; let tx_env = TestTxEnv { - storage, - write_log, + wl_storage, vp_wasm_cache, vp_cache_dir, tx, @@ -306,13 +315,12 @@ mod native_tx_host_env { #[no_mangle] extern "C" fn extern_fn_name( $($arg: $type),* ) { with(|TestTxEnv { - storage, - write_log, + wl_storage, iterators, verifiers, gas_meter, - result_buffer, - tx_index, + result_buffer, + tx_index, vp_wasm_cache, vp_cache_dir: _, tx_wasm_cache, @@ -321,8 +329,8 @@ mod native_tx_host_env { }: &mut TestTxEnv| { let tx_env = vm::host_env::testing::tx_env( - storage, - write_log, + &wl_storage.storage, + &mut wl_storage.write_log, iterators, verifiers, gas_meter, @@ -347,8 +355,7 @@ mod native_tx_host_env { extern "C" fn extern_fn_name( $($arg: $type),* ) -> $ret { with(|TestTxEnv { tx_index, - storage, - write_log, + wl_storage, iterators, verifiers, gas_meter, @@ -361,8 +368,8 @@ mod native_tx_host_env { }: &mut TestTxEnv| { let tx_env = vm::host_env::testing::tx_env( - storage, - write_log, + &wl_storage.storage, + &mut wl_storage.write_log, iterators, verifiers, gas_meter, diff --git a/tests/src/vm_host_env/vp.rs b/tests/src/vm_host_env/vp.rs index 88d9d6ca3cd..4d5bbf3ddec 100644 --- a/tests/src/vm_host_env/vp.rs +++ b/tests/src/vm_host_env/vp.rs @@ -4,6 +4,7 @@ use namada::ledger::gas::VpGasMeter; use namada::ledger::storage::mockdb::MockDB; use namada::ledger::storage::testing::TestStorage; use namada::ledger::storage::write_log::WriteLog; +use namada::ledger::storage::{Sha256Hasher, WlStorage}; use namada::proto::Tx; use namada::types::address::{self, Address}; use namada::types::storage::{self, Key, TxIndex}; @@ -35,10 +36,10 @@ pub mod vp_host_env { } /// Host environment structures required for transactions. +#[derive(Debug)] pub struct TestVpEnv { pub addr: Address, - pub storage: TestStorage, - pub write_log: WriteLog, + pub wl_storage: WlStorage, pub iterators: PrefixIterators<'static, MockDB>, pub gas_meter: VpGasMeter, pub tx: Tx, @@ -65,8 +66,10 @@ impl Default for TestVpEnv { Self { addr: address::testing::established_address_1(), - storage: TestStorage::default(), - write_log: WriteLog::default(), + wl_storage: WlStorage { + storage: TestStorage::default(), + write_log: WriteLog::default(), + }, iterators: PrefixIterators::default(), gas_meter: VpGasMeter::default(), tx: Tx::new(vec![], None), @@ -85,11 +88,14 @@ impl Default for TestVpEnv { impl TestVpEnv { pub fn all_touched_storage_keys(&self) -> BTreeSet { - self.write_log.get_keys() + self.wl_storage.write_log.get_keys() } pub fn get_verifiers(&self) -> BTreeSet
{ - self.write_log.verifiers_and_changed_keys(&self.verifiers).0 + self.wl_storage + .write_log + .verifiers_and_changed_keys(&self.verifiers) + .0 } } @@ -185,7 +191,7 @@ mod native_vp_host_env { // Write an empty validity predicate for the address, because it's used // to check if the address exists when we write into its storage let vp_key = Key::validity_predicate(&addr); - tx_env.storage.write(&vp_key, vec![]).unwrap(); + tx_env.wl_storage.storage.write(&vp_key, vec![]).unwrap(); tx_host_env::set(tx_env); apply_tx(&addr); @@ -193,6 +199,7 @@ mod native_vp_host_env { let tx_env = tx_host_env::take(); let verifiers_from_tx = &tx_env.verifiers; let (verifiers, keys_changed) = tx_env + .wl_storage .write_log .verifiers_and_changed_keys(verifiers_from_tx); if !verifiers.contains(&addr) { @@ -205,8 +212,7 @@ mod native_vp_host_env { let vp_env = TestVpEnv { addr, - storage: tx_env.storage, - write_log: tx_env.write_log, + wl_storage: tx_env.wl_storage, keys_changed, verifiers, ..Default::default() @@ -246,8 +252,7 @@ mod native_vp_host_env { extern "C" fn extern_fn_name( $($arg: $type),* ) { with(|TestVpEnv { addr, - storage, - write_log, + wl_storage, iterators, gas_meter, tx, @@ -264,8 +269,8 @@ mod native_vp_host_env { let env = vm::host_env::testing::vp_env( addr, - storage, - write_log, + &wl_storage.storage, + &wl_storage.write_log, iterators, gas_meter, tx, @@ -294,8 +299,7 @@ mod native_vp_host_env { extern "C" fn extern_fn_name( $($arg: $type),* ) -> $ret { with(|TestVpEnv { addr, - storage, - write_log, + wl_storage, iterators, gas_meter, tx, @@ -312,8 +316,8 @@ mod native_vp_host_env { let env = vm::host_env::testing::vp_env( addr, - storage, - write_log, + &wl_storage.storage, + &wl_storage.write_log, iterators, gas_meter, tx, @@ -343,9 +347,9 @@ mod native_vp_host_env { native_host_fn!(vp_result_buffer(result_ptr: u64)); native_host_fn!(vp_has_key_pre(key_ptr: u64, key_len: u64) -> i64); native_host_fn!(vp_has_key_post(key_ptr: u64, key_len: u64) -> i64); - native_host_fn!(vp_iter_prefix(prefix_ptr: u64, prefix_len: u64) -> u64); - native_host_fn!(vp_iter_pre_next(iter_id: u64) -> i64); - native_host_fn!(vp_iter_post_next(iter_id: u64) -> i64); + native_host_fn!(vp_iter_prefix_pre(prefix_ptr: u64, prefix_len: u64) -> u64); + native_host_fn!(vp_iter_prefix_post(prefix_ptr: u64, prefix_len: u64) -> u64); + native_host_fn!(vp_iter_next(iter_id: u64) -> i64); native_host_fn!(vp_get_chain_id(result_ptr: u64)); native_host_fn!(vp_get_block_height() -> u64); native_host_fn!(vp_get_tx_index() -> u32); diff --git a/vm_env/src/lib.rs b/vm_env/src/lib.rs index b38feb83f26..e275c840d10 100644 --- a/vm_env/src/lib.rs +++ b/vm_env/src/lib.rs @@ -139,23 +139,26 @@ pub mod vp { // Returns 1 if the key is present in posterior state, -1 otherwise. pub fn namada_vp_has_key_post(key_ptr: u64, key_len: u64) -> i64; - // Get an ID of a data iterator with key prefix, ordered by storage - // keys. - pub fn namada_vp_iter_prefix(prefix_ptr: u64, prefix_len: u64) -> u64; - - // Read variable-length prior state when we don't know the size - // up-front, returns the size of the value (can be 0), or -1 if - // the key is not present. If a value is found, it will be placed in the - // result buffer, because we cannot allocate a buffer for it before - // we know its size. - pub fn namada_vp_iter_pre_next(iter_id: u64) -> i64; - - // Read variable-length posterior state when we don't know the size - // up-front, returns the size of the value (can be 0), or -1 if the - // key is not present. If a value is found, it will be placed in the - // result buffer, because we cannot allocate a buffer for it before - // we know its size. - pub fn namada_vp_iter_post_next(iter_id: u64) -> i64; + // Get an ID of a data iterator with key prefix in prior state, ordered + // by storage keys. + pub fn namada_vp_iter_prefix_pre( + prefix_ptr: u64, + prefix_len: u64, + ) -> u64; + + // Get an ID of a data iterator with key prefix in posterior state, + // ordered by storage keys. + pub fn namada_vp_iter_prefix_post( + prefix_ptr: u64, + prefix_len: u64, + ) -> u64; + + // Read variable-length iterator's next value when we don't know the + // size up-front, returns the size of the value (can be 0), or + // -1 if the key is not present. If a value is found, it will be + // placed in the result buffer, because we cannot allocate a + // buffer for it before we know its size. + pub fn namada_vp_iter_next(iter_id: u64) -> i64; // Get the chain ID pub fn namada_vp_get_chain_id(result_ptr: u64); diff --git a/vp_prelude/src/lib.rs b/vp_prelude/src/lib.rs index 33cb5f900c5..0d0680a2e69 100644 --- a/vp_prelude/src/lib.rs +++ b/vp_prelude/src/lib.rs @@ -262,8 +262,7 @@ impl<'view> VpEnv<'view> for Ctx { &'iter self, prefix: &storage::Key, ) -> Result, Error> { - // Both `CtxPreStorageRead` and `CtxPostStorageRead` have the same impl - iter_prefix_impl(prefix) + iter_prefix_pre_impl(prefix) } fn eval( @@ -334,26 +333,26 @@ impl StorageRead for CtxPreStorageRead<'_> { Ok(HostEnvResult::is_success(found)) } + fn iter_prefix<'iter>( + &'iter self, + prefix: &storage::Key, + ) -> Result, Error> { + iter_prefix_pre_impl(prefix) + } + + // ---- Methods below share the same implementation in `pre/post` ---- + fn iter_next<'iter>( &'iter self, iter: &mut Self::PrefixIter<'iter>, ) -> Result)>, Error> { - let read_result = unsafe { namada_vp_iter_pre_next(iter.0) }; + let read_result = unsafe { namada_vp_iter_next(iter.0) }; Ok(read_key_val_bytes_from_buffer( read_result, namada_vp_result_buffer, )) } - // ---- Methods below share the same implementation in `pre/post` ---- - - fn iter_prefix<'iter>( - &'iter self, - prefix: &storage::Key, - ) -> Result, Error> { - iter_prefix_impl(prefix) - } - fn get_chain_id(&self) -> Result { get_chain_id() } @@ -397,26 +396,26 @@ impl StorageRead for CtxPostStorageRead<'_> { Ok(HostEnvResult::is_success(found)) } + fn iter_prefix<'iter>( + &'iter self, + prefix: &storage::Key, + ) -> Result, Error> { + iter_prefix_post_impl(prefix) + } + + // ---- Methods below share the same implementation in `pre/post` ---- + fn iter_next<'iter>( &'iter self, iter: &mut Self::PrefixIter<'iter>, ) -> Result)>, Error> { - let read_result = unsafe { namada_vp_iter_post_next(iter.0) }; + let read_result = unsafe { namada_vp_iter_next(iter.0) }; Ok(read_key_val_bytes_from_buffer( read_result, namada_vp_result_buffer, )) } - // ---- Methods below share the same implementation in `pre/post` ---- - - fn iter_prefix<'iter>( - &'iter self, - prefix: &storage::Key, - ) -> Result, Error> { - iter_prefix_impl(prefix) - } - fn get_chain_id(&self) -> Result { get_chain_id() } @@ -442,12 +441,22 @@ impl StorageRead for CtxPostStorageRead<'_> { } } -fn iter_prefix_impl( +fn iter_prefix_pre_impl( + prefix: &storage::Key, +) -> Result)>, Error> { + let prefix = prefix.to_string(); + let iter_id = unsafe { + namada_vp_iter_prefix_pre(prefix.as_ptr() as _, prefix.len() as _) + }; + Ok(KeyValIterator(iter_id, PhantomData)) +} + +fn iter_prefix_post_impl( prefix: &storage::Key, ) -> Result)>, Error> { let prefix = prefix.to_string(); let iter_id = unsafe { - namada_vp_iter_prefix(prefix.as_ptr() as _, prefix.len() as _) + namada_vp_iter_prefix_post(prefix.as_ptr() as _, prefix.len() as _) }; Ok(KeyValIterator(iter_id, PhantomData)) } diff --git a/wasm/checksums.json b/wasm/checksums.json index c36511452a6..425b24bc202 100644 --- a/wasm/checksums.json +++ b/wasm/checksums.json @@ -1,20 +1,20 @@ { - "tx_bond.wasm": "tx_bond.c69b0b6b85a8340473dace3e927bc01be12cb5ae82d3367938d0005522a29479.wasm", - "tx_change_validator_commission.wasm": "tx_change_validator_commission.97b0d6f07c9db41320f1006e12e726098c93aad75eb843a575222c8f305462e7.wasm", - "tx_ibc.wasm": "tx_ibc.3637ae4b46f854b288c01e74eccc63b7e45c9c2d1ee3098b698f4039a6010705.wasm", - "tx_init_account.wasm": "tx_init_account.7aa4dbbf0ecad2d079766c47bf6c6d210523d1c4d74d118a02cde6427460a8c8.wasm", - "tx_init_proposal.wasm": "tx_init_proposal.4a977c3d205b68114c6ec8f4ae8d933b768f146759a35de21796395626ca5d43.wasm", - "tx_init_validator.wasm": "tx_init_validator.34b54635942c83de4e4dc94445753ffe55942ebef8a95393ffb10d487e681822.wasm", - "tx_reveal_pk.wasm": "tx_reveal_pk.054ff210ee793575d75a757bc5c28f88bae037bce99ceb27727e5e3b4c301116.wasm", - "tx_transfer.wasm": "tx_transfer.3fda6e26b50e7aa4b1d6e37fc631d5c55bb9370e6fac71f64f2be137b42df549.wasm", - "tx_unbond.wasm": "tx_unbond.5f4aae1a399f6d556cdf0c85df84b44e4725f0241f7d9ed276f44da08f0f0c84.wasm", - "tx_update_vp.wasm": "tx_update_vp.db4d8c11658c5f8e99fc39f0112be1ee480ab1f0045010d323ee3081a7afe802.wasm", - "tx_vote_proposal.wasm": "tx_vote_proposal.50c5a13ff8218b1ba79fa7644542af5a9b0bb60316aaa7a630574f9f901f3962.wasm", - "tx_withdraw.wasm": "tx_withdraw.7ff9162d8c5cd40411fff38c2aed47fe0df952fc67f7a9a0c29f624b56d6d88a.wasm", - "vp_implicit.wasm": "vp_implicit.3c4257215de3845937d477f54bdf490bf1cf21acd688852e82277401902882f4.wasm", - "vp_masp.wasm": "vp_masp.08137fd20390a40f106c4ab8ae3a6e548002dff189042aee858c6e4bf10f94e5.wasm", - "vp_testnet_faucet.wasm": "vp_testnet_faucet.ddd3135b67e2c116d6915862b93342db9708b9c5b44f5443545910fcea11f5fc.wasm", - "vp_token.wasm": "vp_token.b9622cb39e0141c3a8b9c6d5cba395ca2baf335f1b376079f66ba2bf6b8cd770.wasm", - "vp_user.wasm": "vp_user.f5a3a256d5a4fd12ea736c816e92402beceb1dfea54627209ce408862e290e7c.wasm", - "vp_validator.wasm": "vp_validator.c444b17e75c3a3f7fedf321476a3a7dd4950b131cf8f416a277f57d523613680.wasm" + "tx_bond.wasm": "tx_bond.dd3099e3d4408d5cfbc2d5a4f8b7e8784d30fafe25653852cd314e00a0ab3a90.wasm", + "tx_change_validator_commission.wasm": "tx_change_validator_commission.f3cd1434d0e0651d7a916e0eedad7d006e893d735485ca71e43b56e305cb929a.wasm", + "tx_ibc.wasm": "tx_ibc.3f4db4d3270a00fb03107c82fac975101c213c7a26dce4222311f43e0d1f56a9.wasm", + "tx_init_account.wasm": "tx_init_account.a325187897761411205170dad5d6e62b3f23843358178045b11b5bc855d91021.wasm", + "tx_init_proposal.wasm": "tx_init_proposal.e5ecc4d33efbfdfe4f09f716fa9c115cadfb540c546910e16c1edc3d8a201b9f.wasm", + "tx_init_validator.wasm": "tx_init_validator.5d7bc76bb3fbba0758e6ac7d0737782370c34e5e0de5f4e04a97a35dff6033ab.wasm", + "tx_reveal_pk.wasm": "tx_reveal_pk.9a351c937882e1bf5dc6a45740f47d18b82d89d626785d0795bba53f1940c18a.wasm", + "tx_transfer.wasm": "tx_transfer.c5e6a611da195185fe1002146601f8194076b6a6df2fcfaa5dabd054c11ba6ef.wasm", + "tx_unbond.wasm": "tx_unbond.122252b1faf2c6cd0c4138c1c28e5f1a4c25a8a32d9cae113b9a34796e276ca1.wasm", + "tx_update_vp.wasm": "tx_update_vp.6f3acf7e2e77589187815367eca221e5496bb6f6da5453a58de953dd4eca00cf.wasm", + "tx_vote_proposal.wasm": "tx_vote_proposal.b2c6a0b4a46013587b682c9089af6ff869ec1bd4a6855e011caf5775a07a44a1.wasm", + "tx_withdraw.wasm": "tx_withdraw.d515807101fa226f1688054e72c102b48d95a17fc59cd8f5a226d99b404ef288.wasm", + "vp_implicit.wasm": "vp_implicit.f1428855df5d67bb915a8081d45cd4b70a3fd1e6f1de88a5dc20d7c0c56b5cd1.wasm", + "vp_masp.wasm": "vp_masp.6f5676f0544badb01c7097e2f74c689473fcb684d732f2745f9c347465d06b3f.wasm", + "vp_testnet_faucet.wasm": "vp_testnet_faucet.24f8abc18751fd40cbc40c92346011bb4db8d17bc9cf7b5a416f17cd707cbb13.wasm", + "vp_token.wasm": "vp_token.7522b17d69915cfdbb80fd28be7fc9c16551969b8dece33d95e8cc1c564b5f24.wasm", + "vp_user.wasm": "vp_user.2bf9f9ad541d5c3554812d71e703b8fc1a994b7f3a5fd7226ea1936002094ffe.wasm", + "vp_validator.wasm": "vp_validator.71369ffbb9d12e8696fb6ea69e16c2cc9b40cdd79d93f4289c5799397eb8f175.wasm" } \ No newline at end of file diff --git a/wasm/wasm_source/src/tx_bond.rs b/wasm/wasm_source/src/tx_bond.rs index 219a6126304..d91868028c8 100644 --- a/wasm/wasm_source/src/tx_bond.rs +++ b/wasm/wasm_source/src/tx_bond.rs @@ -85,7 +85,7 @@ mod tests { // Ensure that the bond's source has enough tokens for the bond let target = bond.source.as_ref().unwrap_or(&bond.validator); - let native_token = tx_env.storage.native_token.clone(); + let native_token = tx_env.wl_storage.storage.native_token.clone(); tx_env.credit_tokens(target, &native_token, None, bond.amount); native_token }); diff --git a/wasm/wasm_source/src/tx_unbond.rs b/wasm/wasm_source/src/tx_unbond.rs index 70033286bf1..3392d6c174f 100644 --- a/wasm/wasm_source/src/tx_unbond.rs +++ b/wasm/wasm_source/src/tx_unbond.rs @@ -83,7 +83,7 @@ mod tests { init_pos(&genesis_validators[..], &pos_params, Epoch(0)); let native_token = tx_host_env::with(|tx_env| { - let native_token = tx_env.storage.native_token.clone(); + let native_token = tx_env.wl_storage.storage.native_token.clone(); if is_delegation { let source = unbond.source.as_ref().unwrap(); tx_env.spawn_accounts([source]); diff --git a/wasm/wasm_source/src/tx_withdraw.rs b/wasm/wasm_source/src/tx_withdraw.rs index e29415f800d..89ede199d40 100644 --- a/wasm/wasm_source/src/tx_withdraw.rs +++ b/wasm/wasm_source/src/tx_withdraw.rs @@ -92,7 +92,7 @@ mod tests { init_pos(&genesis_validators[..], &pos_params, Epoch(0)); let native_token = tx_host_env::with(|tx_env| { - let native_token = tx_env.storage.native_token.clone(); + let native_token = tx_env.wl_storage.storage.native_token.clone(); if is_delegation { let source = withdraw.source.as_ref().unwrap(); tx_env.spawn_accounts([source]); @@ -134,11 +134,12 @@ mod tests { // withdraw the unbonded tokens tx_host_env::with(|env| { for _ in 0..pos_params.unbonding_len { - env.storage.block.epoch = env.storage.block.epoch.next(); + env.wl_storage.storage.block.epoch = + env.wl_storage.storage.block.epoch.next(); } }); assert_eq!( - tx_host_env::with(|env| env.storage.block.epoch), + tx_host_env::with(|env| env.wl_storage.storage.block.epoch), Epoch(pos_params.unbonding_len) ); diff --git a/wasm/wasm_source/src/vp_testnet_faucet.rs b/wasm/wasm_source/src/vp_testnet_faucet.rs index 8e4e79719f6..1b8802df6e9 100644 --- a/wasm/wasm_source/src/vp_testnet_faucet.rs +++ b/wasm/wasm_source/src/vp_testnet_faucet.rs @@ -291,7 +291,7 @@ mod tests { let vp_owner = address::testing::established_address_1(); let difficulty = testnet_pow::Difficulty::try_new(0).unwrap(); let withdrawal_limit = token::Amount::from(MAX_FREE_DEBIT as u64); - testnet_pow::init_faucet_storage(&mut tx_env.storage, &vp_owner, difficulty, withdrawal_limit).unwrap(); + testnet_pow::init_faucet_storage(&mut tx_env.wl_storage, &vp_owner, difficulty, withdrawal_limit).unwrap(); let target = address::testing::established_address_2(); let token = address::nam(); @@ -303,6 +303,7 @@ mod tests { // Credit the tokens to the VP owner before running the transaction to // be able to transfer from it tx_env.credit_tokens(&vp_owner, &token, None, amount); + tx_env.commit_genesis(); // Initialize VP environment from a transaction vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |address| { @@ -330,7 +331,7 @@ mod tests { let vp_owner = address::testing::established_address_1(); let difficulty = testnet_pow::Difficulty::try_new(0).unwrap(); let withdrawal_limit = token::Amount::from(MAX_FREE_DEBIT as u64); - testnet_pow::init_faucet_storage(&mut tx_env.storage, &vp_owner, difficulty, withdrawal_limit).unwrap(); + testnet_pow::init_faucet_storage(&mut tx_env.wl_storage, &vp_owner, difficulty, withdrawal_limit).unwrap(); let target = address::testing::established_address_2(); let target_key = key::testing::keypair_1(); @@ -343,9 +344,10 @@ mod tests { // Credit the tokens to the VP owner before running the transaction to // be able to transfer from it tx_env.credit_tokens(&vp_owner, &token, None, amount); + tx_env.commit_genesis(); // Construct a PoW solution like a client would - let challenge = testnet_pow::Challenge::new(&mut tx_env.storage, &vp_owner, target.clone()).unwrap(); + let challenge = testnet_pow::Challenge::new(&mut tx_env.wl_storage, &vp_owner, target.clone()).unwrap(); let solution = challenge.solve(); let solution_bytes = solution.try_to_vec().unwrap(); // The signature itself doesn't matter and is not being checked in this @@ -391,7 +393,7 @@ mod tests { // Init the VP let difficulty = testnet_pow::Difficulty::try_new(0).unwrap(); let withdrawal_limit = token::Amount::from(MAX_FREE_DEBIT as u64); - testnet_pow::init_faucet_storage(&mut tx_env.storage, &vp_owner, difficulty, withdrawal_limit).unwrap(); + testnet_pow::init_faucet_storage(&mut tx_env.wl_storage, &vp_owner, difficulty, withdrawal_limit).unwrap(); let keypair = key::testing::keypair_1(); let public_key = &keypair.ref_to(); diff --git a/wasm_for_tests/tx_memory_limit.wasm b/wasm_for_tests/tx_memory_limit.wasm index 3a83b286320..8950a670889 100755 Binary files a/wasm_for_tests/tx_memory_limit.wasm and b/wasm_for_tests/tx_memory_limit.wasm differ diff --git a/wasm_for_tests/tx_mint_tokens.wasm b/wasm_for_tests/tx_mint_tokens.wasm index cb7822f98c2..593db7f9829 100755 Binary files a/wasm_for_tests/tx_mint_tokens.wasm and b/wasm_for_tests/tx_mint_tokens.wasm differ diff --git a/wasm_for_tests/tx_no_op.wasm b/wasm_for_tests/tx_no_op.wasm index 105a68cd1b7..3e0b1ef9970 100755 Binary files a/wasm_for_tests/tx_no_op.wasm and b/wasm_for_tests/tx_no_op.wasm differ diff --git a/wasm_for_tests/tx_proposal_code.wasm b/wasm_for_tests/tx_proposal_code.wasm index e32dbb1fb37..2c7db93b860 100755 Binary files a/wasm_for_tests/tx_proposal_code.wasm and b/wasm_for_tests/tx_proposal_code.wasm differ diff --git a/wasm_for_tests/tx_read_storage_key.wasm b/wasm_for_tests/tx_read_storage_key.wasm index dace8bca099..173cce21c3d 100755 Binary files a/wasm_for_tests/tx_read_storage_key.wasm and b/wasm_for_tests/tx_read_storage_key.wasm differ diff --git a/wasm_for_tests/tx_write_storage_key.wasm b/wasm_for_tests/tx_write_storage_key.wasm index e9cfb959e91..5b3178be754 100755 Binary files a/wasm_for_tests/tx_write_storage_key.wasm and b/wasm_for_tests/tx_write_storage_key.wasm differ diff --git a/wasm_for_tests/vp_always_false.wasm b/wasm_for_tests/vp_always_false.wasm index 67b7d774744..d1da1d4c628 100755 Binary files a/wasm_for_tests/vp_always_false.wasm and b/wasm_for_tests/vp_always_false.wasm differ diff --git a/wasm_for_tests/vp_always_true.wasm b/wasm_for_tests/vp_always_true.wasm index 86c64710ce1..c9ae9778129 100755 Binary files a/wasm_for_tests/vp_always_true.wasm and b/wasm_for_tests/vp_always_true.wasm differ diff --git a/wasm_for_tests/vp_eval.wasm b/wasm_for_tests/vp_eval.wasm index 04ad77434b8..1af66f64026 100755 Binary files a/wasm_for_tests/vp_eval.wasm and b/wasm_for_tests/vp_eval.wasm differ diff --git a/wasm_for_tests/vp_memory_limit.wasm b/wasm_for_tests/vp_memory_limit.wasm index e19204b86f4..38ce00f3f5c 100755 Binary files a/wasm_for_tests/vp_memory_limit.wasm and b/wasm_for_tests/vp_memory_limit.wasm differ diff --git a/wasm_for_tests/vp_read_storage_key.wasm b/wasm_for_tests/vp_read_storage_key.wasm index 781e9e365bc..feab835c87b 100755 Binary files a/wasm_for_tests/vp_read_storage_key.wasm and b/wasm_for_tests/vp_read_storage_key.wasm differ