namada_state/
lib.rs

1//! Ledger's state storage with key-value backed store and a merkle tree
2
3#![doc(html_favicon_url = "https://dev.namada.net/master/favicon.png")]
4#![doc(html_logo_url = "https://dev.namada.net/master/rustdoc-logo.png")]
5#![deny(rustdoc::broken_intra_doc_links)]
6#![deny(rustdoc::private_intra_doc_links)]
7#![warn(
8    missing_docs,
9    rust_2018_idioms,
10    clippy::cast_sign_loss,
11    clippy::cast_possible_truncation,
12    clippy::cast_possible_wrap,
13    clippy::cast_lossless,
14    clippy::arithmetic_side_effects,
15    clippy::dbg_macro,
16    clippy::print_stdout,
17    clippy::print_stderr
18)]
19
20mod in_memory;
21pub mod prefix_iter;
22mod wl_state;
23pub mod write_log;
24
25use std::fmt::Debug;
26use std::iter::Peekable;
27
28pub use in_memory::{
29    BlockStorage, InMemory, LastBlock, ProcessProposalCachedResult,
30};
31use namada_core::address::Address;
32use namada_core::arith::checked;
33use namada_core::chain::ChainId;
34pub use namada_core::chain::{
35    BLOCK_HASH_LENGTH, BLOCK_HEIGHT_LENGTH, BlockHash, BlockHeader,
36    BlockHeight, Epoch, Epochs,
37};
38use namada_core::eth_bridge_pool::is_pending_transfer_key;
39use namada_core::hash::Hash;
40pub use namada_core::hash::Sha256Hasher;
41pub use namada_core::storage::{
42    BlockResults, EPOCH_TYPE_LENGTH, EthEventsQueue, Key, KeySeg, TxIndex,
43};
44use namada_core::tendermint::merkle::proof::ProofOps;
45use namada_gas::{
46    Gas, MEMORY_ACCESS_GAS_PER_BYTE, STORAGE_ACCESS_GAS_PER_BYTE,
47};
48use namada_merkle_tree::Error as MerkleTreeError;
49pub use namada_merkle_tree::{
50    self as merkle_tree, MembershipProof, MerkleTree, MerkleTreeStoresRead,
51    MerkleTreeStoresWrite, StoreRef, StoreType, ics23_specs,
52};
53pub use namada_storage as storage;
54pub use namada_storage::conversion_state::{
55    ConversionLeaf, ConversionState, ReadConversionState, WithConversionState,
56};
57pub use namada_storage::types::{KVBytes, PatternIterator, PrefixIterator};
58pub use namada_storage::{
59    BlockStateRead, BlockStateWrite, DB, DBIter, DBWriteBatch, DbError,
60    DbResult, Error, OptionExt, Result, ResultExt, StorageHasher, StorageRead,
61    StorageWrite, collections, iter_prefix, iter_prefix_bytes,
62    iter_prefix_with_filter, iter_prefix_with_filter_map, mockdb, tx_queue,
63};
64use namada_systems::parameters;
65use thiserror::Error;
66use wl_state::TxWlState;
67pub use wl_state::{FullAccessState, TempWlState, WlState};
68use write_log::WriteLog;
69
70/// We delay epoch change 2 blocks to keep it in sync with Tendermint, because
71/// it has 2 blocks delay on validator set update.
72pub const EPOCH_SWITCH_BLOCKS_DELAY: u32 = 2;
73
74/// Common trait for read-only access to write log, DB and in-memory state.
75pub trait StateRead: StorageRead + Debug {
76    /// DB type
77    type D: 'static + DB + for<'iter> DBIter<'iter>;
78    /// DB hasher type
79    type H: 'static + StorageHasher;
80
81    /// Borrow `WriteLog`
82    fn write_log(&self) -> &WriteLog;
83
84    /// Borrow `DB`
85    fn db(&self) -> &Self::D;
86
87    /// Borrow `InMemory` state
88    fn in_mem(&self) -> &InMemory<Self::H>;
89
90    /// Try to charge a given gas amount. Returns an error on out-of-gas.
91    fn charge_gas(&self, gas: Gas) -> Result<()>;
92
93    /// Check if the given key is present in storage. Returns the result and the
94    /// gas cost.
95    fn db_has_key(&self, key: &storage::Key) -> Result<(bool, Gas)> {
96        let len = key.len() as u64;
97        Ok((
98            self.db().read_subspace_val(key)?.is_some(),
99            checked!(len * STORAGE_ACCESS_GAS_PER_BYTE)?.into(),
100        ))
101    }
102
103    /// Returns a value from the specified subspace and the gas cost
104    fn db_read(&self, key: &storage::Key) -> Result<(Option<Vec<u8>>, Gas)> {
105        tracing::trace!("storage read key {}", key);
106
107        match self.db().read_subspace_val(key)? {
108            Some(v) => {
109                let len = checked!(key.len() + v.len())? as u64;
110                let gas = checked!(len * STORAGE_ACCESS_GAS_PER_BYTE)?;
111                Ok((Some(v), gas.into()))
112            }
113            None => {
114                let len = key.len() as u64;
115                let gas = checked!(len * STORAGE_ACCESS_GAS_PER_BYTE)?;
116                Ok((None, gas.into()))
117            }
118        }
119    }
120
121    /// WARNING: This only works for values that have been committed to DB.
122    /// To be able to see values written or deleted, but not yet committed,
123    /// use the `StorageWithWriteLog`.
124    ///
125    /// Returns a prefix iterator, ordered by storage keys, and the gas cost.
126    fn db_iter_prefix(
127        &self,
128        prefix: &Key,
129    ) -> Result<(<Self::D as DBIter<'_>>::PrefixIter, Gas)> {
130        let len = prefix.len() as u64;
131        Ok((
132            self.db().iter_prefix(Some(prefix)),
133            checked!(len * STORAGE_ACCESS_GAS_PER_BYTE)?.into(),
134        ))
135    }
136
137    /// Returns an iterator over the block results
138    fn db_iter_results(&self) -> (<Self::D as DBIter<'_>>::PrefixIter, Gas) {
139        (self.db().iter_results(), Gas::default())
140    }
141
142    /// Get the hash of a validity predicate for the given account address and
143    /// the gas cost for reading it.
144    fn validity_predicate<Params: parameters::Keys>(
145        &self,
146        addr: &Address,
147    ) -> Result<(Option<Hash>, Gas)> {
148        let key = if let Address::Implicit(_) = addr {
149            Params::implicit_vp_key()
150        } else {
151            Key::validity_predicate(addr)
152        };
153        match self.db_read(&key)? {
154            (Some(value), gas) => {
155                let vp_code_hash = Hash::try_from(&value[..])?;
156                Ok((Some(vp_code_hash), gas))
157            }
158            (None, gas) => Ok((None, gas)),
159        }
160    }
161
162    /// Get the block header
163    fn get_block_header(
164        &self,
165        height: Option<BlockHeight>,
166    ) -> Result<(Option<BlockHeader>, Gas)> {
167        match height {
168            Some(h) if h == self.in_mem().get_block_height().0 => {
169                let header = self.in_mem().header.clone();
170                let gas = if header.is_some() {
171                    let len = BlockHeader::encoded_len() as u64;
172                    checked!(len * MEMORY_ACCESS_GAS_PER_BYTE)?
173                } else {
174                    MEMORY_ACCESS_GAS_PER_BYTE
175                };
176                Ok((header, gas.into()))
177            }
178            Some(h) => match self.db().read_block_header(h)? {
179                Some(header) => {
180                    let len = BlockHeader::encoded_len() as u64;
181                    let gas = checked!(len * STORAGE_ACCESS_GAS_PER_BYTE)?;
182                    Ok((Some(header), gas.into()))
183                }
184                None => Ok((None, STORAGE_ACCESS_GAS_PER_BYTE.into())),
185            },
186            None => Ok((
187                self.in_mem().header.clone(),
188                STORAGE_ACCESS_GAS_PER_BYTE.into(),
189            )),
190        }
191    }
192}
193
194/// Common trait for write log, DB and in-memory state.
195pub trait State: StateRead + StorageWrite {
196    /// Borrow mutable `WriteLog`
197    fn write_log_mut(&mut self) -> &mut WriteLog;
198
199    /// Splitting borrow to get mutable reference to `WriteLog`, immutable
200    /// reference to the `InMemory` state and DB when in need of both (avoids
201    /// complain from the borrow checker)
202    fn split_borrow(&mut self)
203    -> (&mut WriteLog, &InMemory<Self::H>, &Self::D);
204
205    /// Write the provided tx hash to write log.
206    fn write_tx_hash(&mut self, hash: Hash) -> write_log::Result<()> {
207        self.write_log_mut().write_tx_hash(hash)
208    }
209}
210
211/// Perform storage writes and deletions to write-log at tx level.
212pub trait TxWrites: StateRead {
213    /// Performs storage writes at the tx level of the write-log.
214    fn with_tx_writes(&mut self) -> TxWlState<'_, Self::D, Self::H>;
215}
216
217/// Implement [`trait StorageRead`] using its [`trait StateRead`]
218/// implementation.
219#[macro_export]
220macro_rules! impl_storage_read {
221    ($($type:ty)*) => {
222        impl<D, H> StorageRead for $($type)*
223        where
224            D: 'static + DB + for<'iter> DBIter<'iter>,
225            H: 'static + StorageHasher,
226        {
227            type PrefixIter<'iter> = PrefixIter<'iter, D> where Self: 'iter;
228
229            fn read_bytes(
230                &self,
231                key: &storage::Key,
232            ) -> namada_storage::Result<Option<Vec<u8>>> {
233                // try to read from the write log first
234                let (log_val, gas) = self.write_log().read(key)?;
235                self.charge_gas(gas).into_storage_result()?;
236                match &log_val {
237                    Some(write_log::StorageModification::Write { value }) => {
238                        Ok(Some(value.clone()))
239                    }
240                    Some(write_log::StorageModification::Delete) => Ok(None),
241                    Some(write_log::StorageModification::InitAccount {
242                        vp_code_hash,
243                    }) => Ok(Some(vp_code_hash.to_vec())),
244                    None => {
245                        // when not found in write log try to read from the storage
246                        let (value, gas) = self.db_read(key).into_storage_result()?;
247                        self.charge_gas(gas).into_storage_result()?;
248                        Ok(value)
249                    }
250                }
251            }
252
253            fn has_key(&self, key: &storage::Key) -> namada_storage::Result<bool> {
254                // try to read from the write log first
255                let (log_val, gas) = self.write_log().read(key)?;
256                self.charge_gas(gas).into_storage_result()?;
257                match log_val {
258                    Some(&write_log::StorageModification::Write { .. })
259                    | Some(&write_log::StorageModification::InitAccount { .. }) => Ok(true),
260                    Some(&write_log::StorageModification::Delete) => {
261                        // the given key has been deleted
262                        Ok(false)
263                    }
264                    None => {
265                        // when not found in write log try to check the storage
266                        let (present, gas) = self.db_has_key(key).into_storage_result()?;
267                        self.charge_gas(gas).into_storage_result()?;
268                        Ok(present)
269                    }
270                }
271            }
272
273            fn iter_prefix<'iter>(
274                &'iter self,
275                prefix: &storage::Key,
276            ) -> namada_storage::Result<Self::PrefixIter<'iter>> {
277                let (iter, gas) =
278                    iter_prefix_post(self.write_log(), self.db(), prefix)?;
279                self.charge_gas(gas).into_storage_result()?;
280                Ok(iter)
281            }
282
283            fn iter_next<'iter>(
284                &'iter self,
285                iter: &mut Self::PrefixIter<'iter>,
286            ) -> namada_storage::Result<Option<(String, Vec<u8>)>> {
287                iter.next().map(|(key, val, gas)| {
288                    self.charge_gas(gas).into_storage_result()?;
289                    Ok((key, val))
290                }).transpose()
291            }
292
293            fn get_chain_id(
294                &self,
295            ) -> std::result::Result<ChainId, namada_storage::Error> {
296                let (chain_id, gas) = self.in_mem().get_chain_id();
297                self.charge_gas(gas).into_storage_result()?;
298                Ok(chain_id)
299            }
300
301            fn get_block_height(
302                &self,
303            ) -> std::result::Result<BlockHeight, namada_storage::Error> {
304                let (height, gas) = self.in_mem().get_block_height();
305                self.charge_gas(gas).into_storage_result()?;
306                Ok(height)
307            }
308
309            fn get_block_header(
310                &self,
311                height: BlockHeight,
312            ) -> std::result::Result<Option<BlockHeader>, namada_storage::Error>
313            {
314                let (header, gas) =
315                    StateRead::get_block_header(self, Some(height)).into_storage_result()?;
316                self.charge_gas(gas).into_storage_result()?;
317                Ok(header)
318            }
319
320            fn get_block_epoch(
321                &self,
322            ) -> std::result::Result<Epoch, namada_storage::Error> {
323                let (epoch, gas) = self.in_mem().get_current_epoch();
324                self.charge_gas(gas).into_storage_result()?;
325                Ok(epoch)
326            }
327
328            fn get_pred_epochs(&self) -> namada_storage::Result<Epochs> {
329                self.charge_gas(
330                    namada_gas::STORAGE_ACCESS_GAS_PER_BYTE.into(),
331                ).into_storage_result()?;
332                Ok(self.in_mem().block.pred_epochs.clone())
333            }
334
335            fn get_tx_index(
336                &self,
337            ) -> std::result::Result<storage::TxIndex, namada_storage::Error> {
338                self.charge_gas(
339                    namada_gas::STORAGE_ACCESS_GAS_PER_BYTE.into(),
340                ).into_storage_result()?;
341                Ok(self.in_mem().tx_index)
342            }
343
344            fn get_native_token(&self) -> namada_storage::Result<Address> {
345                self.charge_gas(
346                    namada_gas::STORAGE_ACCESS_GAS_PER_BYTE.into(),
347                ).into_storage_result()?;
348                Ok(self.in_mem().native_token.clone())
349            }
350        }
351    }
352}
353
354/// Implement [`trait StorageWrite`] using its [`trait State`] implementation.
355#[macro_export]
356macro_rules! impl_storage_write {
357    ($($type:ty)*) => {
358        impl<D, H> StorageWrite for $($type)*
359        where
360            D: 'static + DB + for<'iter> DBIter<'iter>,
361            H: 'static + StorageHasher,
362        {
363            fn write_bytes(
364                &mut self,
365                key: &storage::Key,
366                val: impl AsRef<[u8]>,
367            ) -> namada_storage::Result<()> {
368                let (gas, _size_diff) = self
369                    .write_log_mut()
370                    .write(key, val.as_ref().to_vec())
371                    .into_storage_result()?;
372                self.charge_gas(gas).into_storage_result()?;
373                Ok(())
374            }
375
376            fn delete(&mut self, key: &storage::Key) -> namada_storage::Result<()> {
377                let (gas, _size_diff) = self
378                    .write_log_mut()
379                    .delete(key)
380                    .into_storage_result()?;
381                self.charge_gas(gas).into_storage_result()?;
382                Ok(())
383            }
384        }
385    };
386}
387
388// Note: `FullAccessState` writes to a write-log at block-level, while all the
389// other `StorageWrite` impls write at tx-level.
390macro_rules! impl_storage_write_by_protocol {
391    ($($type:ty)*) => {
392        impl<D, H> StorageWrite for $($type)*
393        where
394            D: 'static + DB + for<'iter> DBIter<'iter>,
395            H: 'static + StorageHasher,
396        {
397            fn write_bytes(
398                &mut self,
399                key: &storage::Key,
400                val: impl AsRef<[u8]>,
401            ) -> namada_storage::Result<()> {
402                self
403                    .write_log_mut()
404                    .protocol_write(key, val.as_ref().to_vec())
405                    .into_storage_result()?;
406                Ok(())
407            }
408
409            fn delete(&mut self, key: &storage::Key) -> namada_storage::Result<()> {
410                self
411                    .write_log_mut()
412                    .protocol_delete(key)
413                    .into_storage_result()?;
414                Ok(())
415            }
416        }
417    };
418}
419
420impl_storage_read!(FullAccessState<D, H>);
421impl_storage_read!(WlState<D, H>);
422impl_storage_read!(TempWlState<'_, D, H>);
423impl_storage_read!(TxWlState<'_, D, H>);
424impl_storage_write_by_protocol!(FullAccessState<D, H>);
425impl_storage_write_by_protocol!(WlState<D, H>);
426impl_storage_write_by_protocol!(TempWlState<'_, D, H>);
427impl_storage_write!(TxWlState<'_, D, H>);
428
429#[allow(missing_docs)]
430#[derive(Error, Debug)]
431pub enum StateError {
432    #[error("Merkle tree at the height {height} is not stored")]
433    NoMerkleTree { height: BlockHeight },
434    #[error("{0}")]
435    Gas(namada_gas::Error),
436}
437
438impl From<StateError> for Error {
439    fn from(value: StateError) -> Self {
440        Error::new(value)
441    }
442}
443
444/// Prefix iterator for [`StorageRead`] implementations.
445#[derive(Debug)]
446pub struct PrefixIter<'iter, D>
447where
448    D: DB + DBIter<'iter>,
449{
450    /// Peekable storage iterator
451    pub storage_iter: Peekable<<D as DBIter<'iter>>::PrefixIter>,
452    /// Peekable write log iterator
453    pub write_log_iter: Peekable<write_log::PrefixIter>,
454}
455
456/// Iterate write-log storage items prior to a tx execution, matching the
457/// given prefix. Returns the iterator and gas cost.
458pub fn iter_prefix_pre<'a, D>(
459    // We cannot use e.g. `&'a State`, because it doesn't live long
460    // enough - the lifetime of the `PrefixIter` must depend on the lifetime of
461    // references to the `WriteLog` and `DB`.
462    write_log: &'a WriteLog,
463    db: &'a D,
464    prefix: &storage::Key,
465) -> namada_storage::Result<(PrefixIter<'a, D>, Gas)>
466where
467    D: DB + for<'iter> DBIter<'iter>,
468{
469    let storage_iter = db.iter_prefix(Some(prefix)).peekable();
470    let write_log_iter = write_log.iter_prefix_pre(prefix).peekable();
471    let len = prefix.len() as u64;
472    Ok((
473        PrefixIter::<D> {
474            storage_iter,
475            write_log_iter,
476        },
477        checked!(len * STORAGE_ACCESS_GAS_PER_BYTE)?.into(),
478    ))
479}
480
481/// Iterate write-log storage items posterior to a tx execution, matching the
482/// given prefix. Returns the iterator and gas cost.
483pub fn iter_prefix_post<'a, D>(
484    // We cannot use e.g. `&'a State`, because it doesn't live long
485    // enough - the lifetime of the `PrefixIter` must depend on the lifetime of
486    // references to the `WriteLog` and `DB`.
487    write_log: &'a WriteLog,
488    db: &'a D,
489    prefix: &storage::Key,
490) -> namada_storage::Result<(PrefixIter<'a, D>, Gas)>
491where
492    D: DB + for<'iter> DBIter<'iter>,
493{
494    let storage_iter = db.iter_prefix(Some(prefix)).peekable();
495    let write_log_iter = write_log.iter_prefix_post(prefix).peekable();
496    let len = prefix.len() as u64;
497    Ok((
498        PrefixIter::<D> {
499            storage_iter,
500            write_log_iter,
501        },
502        checked!(len * STORAGE_ACCESS_GAS_PER_BYTE)?.into(),
503    ))
504}
505
506impl<'iter, D> Iterator for PrefixIter<'iter, D>
507where
508    D: DB + DBIter<'iter>,
509{
510    type Item = (String, Vec<u8>, Gas);
511
512    fn next(&mut self) -> Option<Self::Item> {
513        enum Next {
514            ReturnWl { advance_storage: bool },
515            ReturnStorage,
516        }
517        loop {
518            let what: Next;
519            {
520                let storage_peeked = self.storage_iter.peek();
521                let wl_peeked = self.write_log_iter.peek();
522                match (storage_peeked, wl_peeked) {
523                    (None, None) => return None,
524                    (None, Some(_)) => {
525                        what = Next::ReturnWl {
526                            advance_storage: false,
527                        };
528                    }
529                    (Some(_), None) => {
530                        what = Next::ReturnStorage;
531                    }
532                    (Some((storage_key, _, _)), Some((wl_key, _))) => {
533                        if wl_key <= storage_key {
534                            what = Next::ReturnWl {
535                                advance_storage: wl_key == storage_key,
536                            };
537                        } else {
538                            what = Next::ReturnStorage;
539                        }
540                    }
541                }
542            }
543            match what {
544                Next::ReturnWl { advance_storage } => {
545                    if advance_storage {
546                        let _ = self.storage_iter.next();
547                    }
548
549                    if let Some((key, modification)) =
550                        self.write_log_iter.next()
551                    {
552                        match modification {
553                            write_log::StorageModification::Write { value } => {
554                                let gas = value.len() as u64;
555                                return Some((key, value, gas.into()));
556                            }
557                            write_log::StorageModification::InitAccount {
558                                vp_code_hash,
559                            } => {
560                                let gas = vp_code_hash.len() as u64;
561                                return Some((
562                                    key,
563                                    vp_code_hash.to_vec(),
564                                    gas.into(),
565                                ));
566                            }
567                            write_log::StorageModification::Delete => {
568                                continue;
569                            }
570                        }
571                    }
572                }
573                Next::ReturnStorage => {
574                    if let Some(next) = self.storage_iter.next() {
575                        return Some(next);
576                    }
577                }
578            }
579        }
580    }
581}
582
583/// Helpers for testing components that depend on storage
584#[cfg(any(test, feature = "testing"))]
585pub mod testing {
586
587    use std::num::NonZeroUsize;
588
589    use clru::CLruCache;
590    use namada_core::address;
591    use namada_core::address::EstablishedAddressGen;
592    use namada_core::time::DateTimeUtc;
593    pub use namada_storage::testing::{PrefixIter, *};
594    use namada_storage::tx_queue::ExpiredTxsQueue;
595    use storage::types::CommitOnlyData;
596
597    use super::mockdb::MockDB;
598    use super::*;
599
600    /// A full-access state with a `MockDB` nd sha256 hasher.
601    pub type TestState = FullAccessState<MockDB, Sha256Hasher>;
602
603    impl Default for TestState {
604        fn default() -> Self {
605            Self(WlState {
606                write_log: Default::default(),
607                db: MockDB::default(),
608                in_mem: Default::default(),
609                diff_key_filter: diff_all_keys,
610            })
611        }
612    }
613
614    fn diff_all_keys(_key: &storage::Key) -> bool {
615        true
616    }
617
618    /// In memory State for testing.
619    pub type InMemoryState = InMemory<Sha256Hasher>;
620
621    impl Default for InMemoryState {
622        fn default() -> Self {
623            let chain_id = ChainId::default();
624            let tree = MerkleTree::default();
625            let block = BlockStorage {
626                tree,
627                height: BlockHeight::default(),
628                epoch: Epoch::default(),
629                pred_epochs: Epochs::default(),
630                results: BlockResults::default(),
631            };
632            Self {
633                chain_id,
634                block,
635                header: None,
636                last_block: None,
637                last_epoch: Epoch::default(),
638                next_epoch_min_start_height: BlockHeight::default(),
639                #[allow(clippy::disallowed_methods)]
640                next_epoch_min_start_time: DateTimeUtc::now(),
641                address_gen: EstablishedAddressGen::new(
642                    "Test address generator seed",
643                ),
644                update_epoch_blocks_delay: None,
645                tx_index: TxIndex::default(),
646                conversion_state: ConversionState::default(),
647                expired_txs_queue: ExpiredTxsQueue::default(),
648                native_token: address::testing::nam(),
649                ethereum_height: None,
650                eth_events_queue: EthEventsQueue::default(),
651                storage_read_past_height_limit: Some(1000),
652                commit_only_data: CommitOnlyData::default(),
653                block_proposals_cache: CLruCache::new(
654                    NonZeroUsize::new(10).unwrap(),
655                ),
656            }
657        }
658    }
659}
660
661#[allow(
662    clippy::arithmetic_side_effects,
663    clippy::cast_sign_loss,
664    clippy::cast_possible_wrap
665)]
666#[cfg(test)]
667mod tests {
668    use std::collections::BTreeMap;
669
670    use chrono::{TimeZone, Utc};
671    use merkle_tree::NO_DIFF_KEY_PREFIX;
672    use namada_core::address::InternalAddress;
673    use namada_core::borsh::{BorshDeserialize, BorshSerializeExt};
674    use namada_core::keccak::KeccakHash;
675    use namada_core::parameters::{EpochDuration, Parameters};
676    use namada_core::storage::DbKeySeg;
677    use namada_core::time::{self, DateTimeUtc, Duration};
678    use proptest::prelude::*;
679    use proptest::test_runner::Config;
680    // Use `RUST_LOG=info` (or another tracing level) and `--nocapture` to
681    // see `tracing` logs from tests
682    use test_log::test;
683
684    use super::testing::*;
685    use super::*;
686
687    prop_compose! {
688        /// Setup test input data with arbitrary epoch duration, epoch start
689        /// height and time, and a block height and time that are greater than
690        /// the epoch start height and time, and the change to be applied to
691        /// the epoch duration parameters.
692        fn arb_and_epoch_duration_start_and_block()
693        (
694            start_height in 0..1000_u64,
695            start_time in 0..10000_i64,
696            min_num_of_blocks in 1..10_u64,
697            min_duration in 1..100_i64,
698        )
699        (
700            min_num_of_blocks in Just(min_num_of_blocks),
701            min_duration in Just(min_duration),
702            start_height in Just(start_height),
703            start_time in Just(start_time),
704            block_height in start_height + 1..(start_height + 2 * min_num_of_blocks),
705            block_time in start_time + 1..(start_time + 2 * min_duration),
706            // Delta will be applied on the `min_num_of_blocks` parameter
707            min_blocks_delta in -(min_num_of_blocks as i64 - 1)..5,
708            // Delta will be applied on the `min_duration` parameter
709            min_duration_delta in -(min_duration - 1)..50,
710        ) -> (EpochDuration, BlockHeight, DateTimeUtc, BlockHeight, DateTimeUtc,
711                i64, i64) {
712            let epoch_duration = EpochDuration {
713                min_num_of_blocks,
714                min_duration: Duration::seconds(min_duration).into(),
715            };
716            (epoch_duration,
717                BlockHeight(start_height), Utc.timestamp_opt(start_time, 0).single().expect("expected valid timestamp").into(),
718                BlockHeight(block_height), Utc.timestamp_opt(block_time, 0).single().expect("expected valid timestamp").into(),
719                min_blocks_delta, min_duration_delta)
720        }
721    }
722
723    proptest! {
724        #![proptest_config(Config {
725            cases: 10,
726            .. Config::default()
727        })]
728        /// Test that:
729        /// 1. When the minimum blocks have been created since the epoch
730        ///    start height and minimum time passed since the epoch start time,
731        ///    a new epoch must start.
732        /// 2. When the epoch duration parameters change, the current epoch's
733        ///    duration doesn't change, but the next one does.
734        #[test]
735        fn update_epoch_after_its_duration(
736            (epoch_duration, start_height, start_time, block_height, block_time,
737            min_blocks_delta, min_duration_delta)
738            in arb_and_epoch_duration_start_and_block())
739        {
740            let mut state =TestState::default();
741            state.in_mem_mut().next_epoch_min_start_height=
742                        start_height + epoch_duration.min_num_of_blocks;
743            state.in_mem_mut().next_epoch_min_start_time=
744                        start_time + epoch_duration.min_duration;
745            let mut parameters = Parameters {
746                max_tx_bytes: 1024 * 1024,
747                max_proposal_bytes: Default::default(),
748                max_block_gas: 20_000_000,
749                epoch_duration: epoch_duration.clone(),
750                vp_allowlist: vec![],
751                tx_allowlist: vec![],
752                implicit_vp_code_hash: Some(Hash::zero()),
753                epochs_per_year: 100,
754                masp_epoch_multiplier: 2,
755                masp_fee_payment_gas_limit: 20_000,
756                gas_scale: 10_000_000,
757                minimum_gas_price: BTreeMap::default(),
758                is_native_token_transferable: true,
759            };
760            // Initialize pred_epochs to the current height
761            let height = state.in_mem().block.height;
762            state
763                .in_mem_mut()
764                .block
765                .pred_epochs
766                .new_epoch(height);
767
768            let epoch_before = state.in_mem().last_epoch;
769            assert_eq!(epoch_before, state.in_mem().block.epoch);
770
771            // Try to apply the epoch update
772            state.update_epoch(block_height, block_time, &parameters).unwrap();
773
774            // Test for 1.
775            if block_height.0 - start_height.0
776                >= epoch_duration.min_num_of_blocks
777                && time::duration_passed(
778                    block_time,
779                    start_time,
780                    epoch_duration.min_duration,
781                )
782            {
783                // Update will now be enqueued for 2 blocks in the future
784                assert_eq!(state.in_mem().block.epoch, epoch_before);
785                assert_eq!(state.in_mem().update_epoch_blocks_delay, Some(2));
786
787                let block_height = block_height + 1;
788                let block_time = block_time + Duration::seconds(1);
789                state.update_epoch(block_height, block_time, &parameters).unwrap();
790                assert_eq!(state.in_mem().block.epoch, epoch_before);
791                assert_eq!(state.in_mem().update_epoch_blocks_delay, Some(1));
792
793                let block_height = block_height + 1;
794                let block_time = block_time + Duration::seconds(1);
795                state.update_epoch(block_height, block_time, &parameters).unwrap();
796                assert_eq!(state.in_mem().block.epoch, epoch_before.next());
797                assert!(state.in_mem().update_epoch_blocks_delay.is_none());
798
799                assert_eq!(state.in_mem().next_epoch_min_start_height,
800                    block_height + epoch_duration.min_num_of_blocks);
801                assert_eq!(state.in_mem().next_epoch_min_start_time,
802                    block_time + epoch_duration.min_duration);
803                assert_eq!(
804                    state.in_mem().block.pred_epochs.get_epoch(BlockHeight(block_height.0 - 1)),
805                    Some(epoch_before));
806                assert_eq!(
807                    state.in_mem().block.pred_epochs.get_epoch(block_height),
808                    Some(epoch_before.next()));
809            } else {
810                assert!(state.in_mem().update_epoch_blocks_delay.is_none());
811                assert_eq!(state.in_mem().block.epoch, epoch_before);
812                assert_eq!(
813                    state.in_mem().block.pred_epochs.get_epoch(BlockHeight(block_height.0 - 1)),
814                    Some(epoch_before));
815                assert_eq!(
816                    state.in_mem().block.pred_epochs.get_epoch(block_height),
817                    Some(epoch_before));
818            }
819            // Last epoch should only change when the block is committed
820            assert_eq!(state.in_mem().last_epoch, epoch_before);
821
822            // Update the epoch duration parameters
823            parameters.epoch_duration.min_num_of_blocks =
824                (parameters.epoch_duration.min_num_of_blocks as i64 + min_blocks_delta) as u64;
825            let min_duration: i64 = parameters.epoch_duration.min_duration.0 as _;
826            parameters.epoch_duration.min_duration =
827                Duration::seconds(min_duration + min_duration_delta).into();
828
829            // Test for 2.
830            let epoch_before = state.in_mem().block.epoch;
831            let height_of_update = state.in_mem().next_epoch_min_start_height.0 ;
832            let time_of_update = state.in_mem().next_epoch_min_start_time;
833            let height_before_update = BlockHeight(height_of_update - 1);
834            let height_of_update = BlockHeight(height_of_update);
835            let time_before_update = time_of_update - Duration::seconds(1);
836
837            // No update should happen before both epoch duration conditions are
838            // satisfied
839            state.update_epoch(height_before_update, time_before_update, &parameters).unwrap();
840            assert_eq!(state.in_mem().block.epoch, epoch_before);
841            assert!(state.in_mem().update_epoch_blocks_delay.is_none());
842            state.update_epoch(height_of_update, time_before_update, &parameters).unwrap();
843            assert_eq!(state.in_mem().block.epoch, epoch_before);
844            assert!(state.in_mem().update_epoch_blocks_delay.is_none());
845            state.update_epoch(height_before_update, time_of_update, &parameters).unwrap();
846            assert_eq!(state.in_mem().block.epoch, epoch_before);
847            assert!(state.in_mem().update_epoch_blocks_delay.is_none());
848
849            // Update should be enqueued for 2 blocks in the future starting at or after this height and time
850            state.update_epoch(height_of_update, time_of_update, &parameters).unwrap();
851            assert_eq!(state.in_mem().block.epoch, epoch_before);
852            assert_eq!(state.in_mem().update_epoch_blocks_delay, Some(2));
853
854            // Increment the block height and time to simulate new blocks now
855            let height_of_update = height_of_update + 1;
856            let time_of_update = time_of_update + Duration::seconds(1);
857            state.update_epoch(height_of_update, time_of_update, &parameters).unwrap();
858            assert_eq!(state.in_mem().block.epoch, epoch_before);
859            assert_eq!(state.in_mem().update_epoch_blocks_delay, Some(1));
860
861            let height_of_update = height_of_update + 1;
862            let time_of_update = time_of_update + Duration::seconds(1);
863            state.update_epoch(height_of_update, time_of_update, &parameters).unwrap();
864            assert_eq!(state.in_mem().block.epoch, epoch_before.next());
865            assert!(state.in_mem().update_epoch_blocks_delay.is_none());
866            // The next epoch's minimum duration should change
867            assert_eq!(state.in_mem().next_epoch_min_start_height,
868                height_of_update + parameters.epoch_duration.min_num_of_blocks);
869            assert_eq!(state.in_mem().next_epoch_min_start_time,
870                time_of_update + parameters.epoch_duration.min_duration);
871
872            // Increment the block height and time once more to make sure things reset
873            let height_of_update = height_of_update + 1;
874            let time_of_update = time_of_update + Duration::seconds(1);
875            state.update_epoch(height_of_update, time_of_update, &parameters).unwrap();
876            assert_eq!(state.in_mem().block.epoch, epoch_before.next());
877        }
878    }
879
880    fn test_key_1() -> Key {
881        Key::parse("testing1").unwrap()
882    }
883
884    fn test_key_2() -> Key {
885        Key::parse("testing2").unwrap()
886    }
887
888    fn diff_key_filter(key: &Key) -> bool {
889        key == &test_key_1()
890    }
891
892    #[test]
893    fn test_writing_without_diffs() {
894        let mut state = TestState::default();
895        assert_eq!(state.in_mem().block.height.0, 0);
896
897        (state.0.diff_key_filter) = diff_key_filter;
898
899        let key1 = test_key_1();
900        let val1 = 1u64;
901        let key2 = test_key_2();
902        let val2 = 2u64;
903
904        // Standard write of key-val-1
905        state.write(&key1, val1).unwrap();
906
907        // Read from State should return val1
908        let res = state.read::<u64>(&key1).unwrap().unwrap();
909        assert_eq!(res, val1);
910
911        // Read from DB shouldn't return val1 bc the block hasn't been
912        // committed
913        let (res, _) = state.db_read(&key1).unwrap();
914        assert!(res.is_none());
915
916        // Write key-val-2 without merklizing or diffs
917        state.write(&key2, val2).unwrap();
918
919        // Read from state should return val2
920        let res = state.read::<u64>(&key2).unwrap().unwrap();
921        assert_eq!(res, val2);
922
923        // Commit block and storage changes
924        state.commit_block().unwrap();
925        state.in_mem_mut().block.height =
926            state.in_mem().block.height.next_height();
927
928        // Read key1 from DB should return val1
929        let (res1, _) = state.db_read(&key1).unwrap();
930        let res1 = u64::try_from_slice(&res1.unwrap()).unwrap();
931        assert_eq!(res1, val1);
932
933        // Check merkle tree inclusion of key-val-1 explicitly
934        let is_merklized1 = state.in_mem().block.tree.has_key(&key1).unwrap();
935        assert!(is_merklized1);
936
937        // Key2 should be in storage. Confirm by reading from
938        // state and also by reading DB subspace directly
939        let res2 = state.read::<u64>(&key2).unwrap().unwrap();
940        assert_eq!(res2, val2);
941        let res2 = state.db().read_subspace_val(&key2).unwrap().unwrap();
942        let res2 = u64::try_from_slice(&res2).unwrap();
943        assert_eq!(res2, val2);
944
945        // Check merkle tree inclusion of key-val-2 explicitly
946        let is_merklized2 = state.in_mem().block.tree.has_key(&key2).unwrap();
947        assert!(!is_merklized2);
948        let no_diff_key2 =
949            Key::from(NO_DIFF_KEY_PREFIX.to_string().to_db_key()).join(&key2);
950        let is_merklized2 =
951            state.in_mem().block.tree.has_key(&no_diff_key2).unwrap();
952        assert!(is_merklized2);
953
954        // Check that the proper diffs exist for key-val-1
955        let res1 = state
956            .db()
957            .read_diffs_val(&key1, Default::default(), true)
958            .unwrap();
959        assert!(res1.is_none());
960
961        let res1 = state
962            .db()
963            .read_diffs_val(&key1, Default::default(), false)
964            .unwrap()
965            .unwrap();
966        let res1 = u64::try_from_slice(&res1).unwrap();
967        assert_eq!(res1, val1);
968
969        // Check that there are diffs for key-val-2 in block 0, since all keys
970        // need to have diffs for at least 1 block for rollback purposes
971        let res2 = state
972            .db()
973            .read_diffs_val(&key2, BlockHeight(0), true)
974            .unwrap();
975        assert!(res2.is_none());
976        let res2 = state
977            .db()
978            .read_diffs_val(&key2, BlockHeight(0), false)
979            .unwrap()
980            .unwrap();
981        let res2 = u64::try_from_slice(&res2).unwrap();
982        assert_eq!(res2, val2);
983
984        // Now delete the keys properly
985        state.delete(&key1).unwrap();
986        state.delete(&key2).unwrap();
987
988        // Commit the block again
989        state.commit_block().unwrap();
990        state.in_mem_mut().block.height =
991            state.in_mem().block.height.next_height();
992
993        // Check the key-vals are removed from the storage subspace
994        let res1 = state.read::<u64>(&key1).unwrap();
995        let res2 = state.read::<u64>(&key2).unwrap();
996        assert!(res1.is_none() && res2.is_none());
997        let res1 = state.db().read_subspace_val(&key1).unwrap();
998        let res2 = state.db().read_subspace_val(&key2).unwrap();
999        assert!(res1.is_none() && res2.is_none());
1000
1001        // Check that the key-vals don't exist in the merkle tree anymore
1002        let is_merklized1 = state.in_mem().block.tree.has_key(&key1).unwrap();
1003        let is_merklized2 =
1004            state.in_mem().block.tree.has_key(&no_diff_key2).unwrap();
1005        assert!(!is_merklized1 && !is_merklized2);
1006
1007        // Check that key-val-1 diffs are properly updated for blocks 0 and 1
1008        let res1 = state
1009            .db()
1010            .read_diffs_val(&key1, BlockHeight(0), true)
1011            .unwrap();
1012        assert!(res1.is_none());
1013
1014        let res1 = state
1015            .db()
1016            .read_diffs_val(&key1, BlockHeight(0), false)
1017            .unwrap()
1018            .unwrap();
1019        let res1 = u64::try_from_slice(&res1).unwrap();
1020        assert_eq!(res1, val1);
1021
1022        let res1 = state
1023            .db()
1024            .read_diffs_val(&key1, BlockHeight(1), true)
1025            .unwrap()
1026            .unwrap();
1027        let res1 = u64::try_from_slice(&res1).unwrap();
1028        assert_eq!(res1, val1);
1029
1030        let res1 = state
1031            .db()
1032            .read_diffs_val(&key1, BlockHeight(1), false)
1033            .unwrap();
1034        assert!(res1.is_none());
1035
1036        // Check that key-val-2 diffs don't exist for block 0 anymore
1037        let res2 = state
1038            .db()
1039            .read_diffs_val(&key2, BlockHeight(0), true)
1040            .unwrap();
1041        assert!(res2.is_none());
1042        let res2 = state
1043            .db()
1044            .read_diffs_val(&key2, BlockHeight(0), false)
1045            .unwrap();
1046        assert!(res2.is_none());
1047
1048        // Check that the block 1 diffs for key-val-2 include an "old" value of
1049        // val2 and no "new" value
1050        let res2 = state
1051            .db()
1052            .read_diffs_val(&key2, BlockHeight(1), true)
1053            .unwrap()
1054            .unwrap();
1055        let res2 = u64::try_from_slice(&res2).unwrap();
1056        assert_eq!(res2, val2);
1057        let res2 = state
1058            .db()
1059            .read_diffs_val(&key2, BlockHeight(1), false)
1060            .unwrap();
1061        assert!(res2.is_none());
1062    }
1063
1064    proptest! {
1065        // Generate arb valid input for `test_prefix_iters_aux`
1066        #![proptest_config(Config {
1067            cases: 10,
1068            .. Config::default()
1069        })]
1070        #[test]
1071        fn test_prefix_iters(
1072            key_vals in arb_key_vals(30),
1073        ) {
1074            test_prefix_iters_aux(key_vals)
1075        }
1076    }
1077
1078    /// Check the `prefix_iter_pre` and `prefix_iter_post` return expected
1079    /// values, generated in the input to this function
1080    fn test_prefix_iters_aux(kvs: Vec<KeyVal<i8>>) {
1081        let mut s = TestState::default();
1082
1083        // Partition the tx and storage kvs
1084        let (tx_kvs, rest): (Vec<_>, Vec<_>) = kvs
1085            .into_iter()
1086            .partition(|(_key, val)| matches!(val, Level::TxWriteLog(_)));
1087        // Partition the kvs to only apply block level first
1088        let (block_kvs, storage_kvs): (Vec<_>, Vec<_>) = rest
1089            .into_iter()
1090            .partition(|(_key, val)| matches!(val, Level::BlockWriteLog(_)));
1091
1092        // Apply the kvs in order of the levels
1093        apply_to_state(&mut s, &storage_kvs);
1094        apply_to_state(&mut s, &block_kvs);
1095        apply_to_state(&mut s, &tx_kvs);
1096
1097        // Collect the expected values in prior state - storage level then block
1098        let mut expected_pre = BTreeMap::new();
1099        for (key, val) in storage_kvs {
1100            if let Level::Storage(val) = val {
1101                expected_pre.insert(key, val);
1102            }
1103        }
1104        for (key, val) in &block_kvs {
1105            if let Level::BlockWriteLog(WlMod::Write(val)) = val {
1106                expected_pre.insert(key.clone(), *val);
1107            }
1108        }
1109        for (key, val) in &block_kvs {
1110            // Deletes have to be applied last
1111            if let Level::BlockWriteLog(WlMod::Delete) = val {
1112                expected_pre.remove(key);
1113            } else if let Level::BlockWriteLog(WlMod::DeletePrefix) = val {
1114                expected_pre.retain(|expected_key, _val| {
1115                    // Remove matching prefixes except for VPs
1116                    expected_key.is_validity_predicate().is_some()
1117                        || expected_key.split_prefix(key).is_none()
1118                })
1119            }
1120        }
1121
1122        // Collect the values from prior state prefix iterator
1123        let (iter_pre, _gas) =
1124            iter_prefix_pre(s.write_log(), s.db(), &storage::Key::default())
1125                .unwrap();
1126        let mut read_pre = BTreeMap::new();
1127        for (key, val, _gas) in iter_pre {
1128            let key = storage::Key::parse(key).unwrap();
1129            let val: i8 = BorshDeserialize::try_from_slice(&val).unwrap();
1130            read_pre.insert(key, val);
1131        }
1132
1133        // A helper for dbg
1134        let keys_to_string = |kvs: &BTreeMap<storage::Key, i8>| {
1135            kvs.iter()
1136                .map(|(key, val)| (key.to_string(), *val))
1137                .collect::<Vec<_>>()
1138        };
1139        dbg!(keys_to_string(&expected_pre), keys_to_string(&read_pre));
1140        // Clone the prior expected kvs for posterior state check
1141        let mut expected_post = expected_pre.clone();
1142        itertools::assert_equal(expected_pre, read_pre);
1143
1144        // Collect the expected values in posterior state - all the levels
1145        for (key, val) in &tx_kvs {
1146            if let Level::TxWriteLog(WlMod::Write(val)) = val {
1147                expected_post.insert(key.clone(), *val);
1148            }
1149        }
1150        for (key, val) in &tx_kvs {
1151            // Deletes have to be applied last
1152            if let Level::TxWriteLog(WlMod::Delete) = val {
1153                expected_post.remove(key);
1154            } else if let Level::TxWriteLog(WlMod::DeletePrefix) = val {
1155                expected_post.retain(|expected_key, _val| {
1156                    // Remove matching prefixes except for VPs
1157                    expected_key.is_validity_predicate().is_some()
1158                        || expected_key.split_prefix(key).is_none()
1159                })
1160            }
1161        }
1162
1163        // Collect the values from posterior state prefix iterator
1164        let (iter_post, _gas) =
1165            iter_prefix_post(s.write_log(), s.db(), &storage::Key::default())
1166                .unwrap();
1167        let mut read_post = BTreeMap::new();
1168        for (key, val, _gas) in iter_post {
1169            let key = storage::Key::parse(key).unwrap();
1170            let val: i8 = BorshDeserialize::try_from_slice(&val).unwrap();
1171            read_post.insert(key, val);
1172        }
1173        dbg!(keys_to_string(&expected_post), keys_to_string(&read_post));
1174        itertools::assert_equal(expected_post, read_post);
1175    }
1176
1177    fn apply_to_state(s: &mut TestState, kvs: &[KeyVal<i8>]) {
1178        // Apply writes first
1179        for (key, val) in kvs {
1180            match val {
1181                Level::TxWriteLog(WlMod::Delete | WlMod::DeletePrefix)
1182                | Level::BlockWriteLog(WlMod::Delete | WlMod::DeletePrefix) => {
1183                }
1184                Level::TxWriteLog(WlMod::Write(val)) => {
1185                    let _ = s
1186                        .write_log_mut()
1187                        .write(key, val.serialize_to_vec())
1188                        .unwrap();
1189                }
1190                Level::BlockWriteLog(WlMod::Write(val)) => {
1191                    s.write_log_mut()
1192                        // protocol only writes at block level
1193                        .protocol_write(key, val.serialize_to_vec())
1194                        .unwrap();
1195                }
1196                Level::Storage(val) => {
1197                    s.db_write(key, val.serialize_to_vec()).unwrap();
1198                }
1199            }
1200        }
1201        // Then apply deletions
1202        for (key, val) in kvs {
1203            match val {
1204                Level::TxWriteLog(WlMod::Delete) => {
1205                    let _ = s.write_log_mut().delete(key).unwrap();
1206                }
1207                Level::BlockWriteLog(WlMod::Delete) => {
1208                    s.delete(key).unwrap();
1209                }
1210                Level::TxWriteLog(WlMod::DeletePrefix) => {
1211                    // Find keys matching the prefix
1212                    let keys =
1213                        namada_storage::iter_prefix_bytes(s, key.clone())
1214                            .unwrap()
1215                            .map(|res| {
1216                                let (key, _val) = res.unwrap();
1217                                key
1218                            })
1219                            .collect::<Vec<storage::Key>>();
1220                    // Delete the matching keys
1221                    for key in keys {
1222                        // Skip validity predicates which cannot be deleted
1223                        if key.is_validity_predicate().is_none() {
1224                            let _ = s.write_log_mut().delete(&key).unwrap();
1225                        }
1226                    }
1227                }
1228                Level::BlockWriteLog(WlMod::DeletePrefix) => {
1229                    s.delete_prefix(key).unwrap();
1230                }
1231                _ => {}
1232            }
1233        }
1234    }
1235
1236    /// WlStorage key written in the write log or storage
1237    type KeyVal<VAL> = (storage::Key, Level<VAL>);
1238
1239    /// WlStorage write level
1240    #[derive(Clone, Copy, Debug)]
1241    enum Level<VAL> {
1242        TxWriteLog(WlMod<VAL>),
1243        BlockWriteLog(WlMod<VAL>),
1244        Storage(VAL),
1245    }
1246
1247    /// Write log modification
1248    #[derive(Clone, Copy, Debug)]
1249    enum WlMod<VAL> {
1250        Write(VAL),
1251        Delete,
1252        DeletePrefix,
1253    }
1254
1255    fn arb_key_vals(len: usize) -> impl Strategy<Value = Vec<KeyVal<i8>>> {
1256        // Start with some arb. storage key-vals
1257        let storage_kvs = prop::collection::vec(
1258            (storage::testing::arb_key(), any::<i8>()),
1259            1..len,
1260        )
1261        .prop_map(|kvs| {
1262            kvs.into_iter()
1263                .map(|(mut key, val)| {
1264                    if let DbKeySeg::AddressSeg(Address::Internal(
1265                        InternalAddress::EthBridgePool,
1266                    )) = key.segments[0]
1267                    {
1268                        // This is needed to be able to write this key to DB -
1269                        // the merkle tree's `BridgePoolTree::parse_key`
1270                        // requires a `KeccakHash` on the 2nd segment
1271                        key.segments.insert(
1272                            1,
1273                            DbKeySeg::StringSeg(
1274                                KeccakHash::default().to_string(),
1275                            ),
1276                        );
1277                    }
1278                    (key, Level::Storage(val))
1279                })
1280                .collect::<Vec<_>>()
1281        });
1282
1283        // Select some indices to override in write log
1284        let overrides = prop::collection::vec(
1285            (any::<prop::sample::Index>(), any::<i8>(), any::<bool>()),
1286            1..len / 2,
1287        );
1288
1289        // Select some indices to delete
1290        let deletes = prop::collection::vec(
1291            (any::<prop::sample::Index>(), any::<bool>()),
1292            1..len / 3,
1293        );
1294
1295        // Select some indices to delete prefix
1296        let delete_prefix = prop::collection::vec(
1297            (
1298                any::<prop::sample::Index>(),
1299                any::<bool>(),
1300                // An arbitrary number of key segments to drop from a selected
1301                // key to obtain the prefix. Because `arb_key` generates `2..5`
1302                // segments, we can drop one less of its upper bound.
1303                (2_usize..4),
1304            ),
1305            1..len / 4,
1306        );
1307
1308        // Combine them all together
1309        (storage_kvs, overrides, deletes, delete_prefix).prop_map(
1310            |(mut kvs, overrides, deletes, delete_prefix)| {
1311                for (ix, val, is_tx) in overrides {
1312                    let (key, _) = ix.get(&kvs);
1313                    let wl_mod = WlMod::Write(val);
1314                    let lvl = if is_tx {
1315                        Level::TxWriteLog(wl_mod)
1316                    } else {
1317                        Level::BlockWriteLog(wl_mod)
1318                    };
1319                    kvs.push((key.clone(), lvl));
1320                }
1321                for (ix, is_tx) in deletes {
1322                    let (key, _) = ix.get(&kvs);
1323                    // We have to skip validity predicate keys as they cannot be
1324                    // deleted
1325                    if key.is_validity_predicate().is_some() {
1326                        continue;
1327                    }
1328                    let wl_mod = WlMod::Delete;
1329                    let lvl = if is_tx {
1330                        Level::TxWriteLog(wl_mod)
1331                    } else {
1332                        Level::BlockWriteLog(wl_mod)
1333                    };
1334                    kvs.push((key.clone(), lvl));
1335                }
1336                for (ix, is_tx, num_of_seg_to_drop) in delete_prefix {
1337                    let (key, _) = ix.get(&kvs);
1338                    let wl_mod = WlMod::DeletePrefix;
1339                    let lvl = if is_tx {
1340                        Level::TxWriteLog(wl_mod)
1341                    } else {
1342                        Level::BlockWriteLog(wl_mod)
1343                    };
1344                    // Keep at least one segment
1345                    let num_of_seg_to_keep = std::cmp::max(
1346                        1,
1347                        key.segments
1348                            .len()
1349                            .checked_sub(num_of_seg_to_drop)
1350                            .unwrap_or_default(),
1351                    );
1352                    let prefix = storage::Key {
1353                        segments: key
1354                            .segments
1355                            .iter()
1356                            .take(num_of_seg_to_keep)
1357                            .cloned()
1358                            .collect(),
1359                    };
1360                    kvs.push((prefix, lvl));
1361                }
1362                kvs
1363            },
1364        )
1365    }
1366}