namada_state/
write_log.rs

1//! Write log is temporary storage for modifications performed by a transaction.
2//! before they are committed to the ledger's storage.
3
4use std::collections::{BTreeMap, BTreeSet};
5
6use itertools::Itertools;
7use namada_core::address::{Address, EstablishedAddressGen};
8use namada_core::arith::checked;
9use namada_core::collections::{HashMap, HashSet};
10use namada_core::hash::Hash;
11use namada_core::{arith, storage};
12use namada_events::extend::{InnerTxHash, TxHash};
13use namada_events::{Event, EventToEmit, EventType};
14use namada_gas::{
15    Gas, MEMORY_ACCESS_GAS_PER_BYTE, STORAGE_DELETE_GAS_PER_BYTE,
16    STORAGE_WRITE_GAS_PER_BYTE,
17};
18use namada_tx::data::InnerTxId;
19use patricia_tree::map::StringPatriciaMap;
20use thiserror::Error;
21
22#[allow(missing_docs)]
23#[derive(Error, Debug)]
24pub enum Error {
25    #[error("Trying to update a temporary value")]
26    UpdateTemporaryValue,
27    #[error(
28        "Trying to update a validity predicate that a new account that's not \
29         yet committed to storage"
30    )]
31    UpdateVpOfNewAccount,
32    #[error("Trying to delete a validity predicate")]
33    DeleteVp,
34    #[error("Trying to write a temporary value after deleting")]
35    WriteTempAfterDelete,
36    #[error("Trying to write a temporary value after writing")]
37    WriteTempAfterWrite,
38    #[error("Replay protection key: {0}")]
39    ReplayProtection(String),
40    #[error("Arithmetic {0}")]
41    Arith(#[from] arith::Error),
42    #[error("Sized-diff overflowed")]
43    SizeDiffOverflow,
44    #[error("Value length overflowed")]
45    ValueLenOverflow,
46}
47
48impl From<Error> for crate::Error {
49    fn from(value: Error) -> Self {
50        crate::Error::new(value)
51    }
52}
53
54/// Result for functions that may fail
55pub type Result<T> = std::result::Result<T, Error>;
56
57/// A storage modification
58#[derive(Clone, Debug, PartialEq, Eq)]
59pub enum StorageModification {
60    /// Write a new value
61    Write {
62        /// Value bytes
63        value: Vec<u8>,
64    },
65    /// Delete an existing key-value
66    Delete,
67    /// Initialize a new account with established address and a given validity
68    /// predicate hash. The key for `InitAccount` inside the [`WriteLog`] must
69    /// point to its validity predicate.
70    InitAccount {
71        /// Validity predicate hash bytes
72        vp_code_hash: Hash,
73    },
74}
75
76/// The write log for a transaction. This allows managing the result of a single
77/// inner transaction inside a batch
78#[derive(Debug, Clone, PartialEq, Eq)]
79pub(crate) struct TxWriteLog {
80    // The generator of established addresses
81    address_gen: Option<EstablishedAddressGen>,
82    // The storage modifications for the current transaction
83    write_log: HashMap<storage::Key, StorageModification>,
84    // Temporary key-values for the current transaction that are dropped after
85    // tx and its verifying VPs execution is done
86    tx_temp_log: HashMap<storage::Key, Vec<u8>>,
87    /// The events emitted by the current transaction
88    events: WriteLogEvents,
89}
90
91impl Default for TxWriteLog {
92    fn default() -> Self {
93        Self {
94            address_gen: None,
95            write_log: HashMap::with_capacity(100),
96            tx_temp_log: HashMap::with_capacity(1),
97            events: WriteLogEvents {
98                tree: StringPatriciaMap::new(),
99            },
100        }
101    }
102}
103
104/// The write log for an already evaluated transaction of a batch. This allows
105/// managing the result of a single inner transaction inside a batch
106#[derive(Debug, Clone, PartialEq, Eq)]
107pub(crate) struct BatchedTxWriteLog {
108    // The generator of established addresses
109    address_gen: Option<EstablishedAddressGen>,
110    // The storage modifications for the transaction
111    write_log: HashMap<storage::Key, StorageModification>,
112}
113
114/// Log of events in the write log.
115#[derive(Debug, Clone)]
116pub(crate) struct WriteLogEvents {
117    pub tree: StringPatriciaMap<HashSet<Event>>,
118}
119
120impl std::cmp::PartialEq for WriteLogEvents {
121    fn eq(&self, other: &WriteLogEvents) -> bool {
122        if self.tree.len() != other.tree.len() {
123            return false;
124        }
125
126        self.tree.iter().all(|(event_type, event_set)| {
127            other
128                .tree
129                .get(event_type)
130                .map(|other_event_set| event_set == other_event_set)
131                .unwrap_or_default()
132        })
133    }
134}
135
136impl std::cmp::Eq for WriteLogEvents {}
137
138/// The write log storage
139#[derive(Debug, Clone, PartialEq, Eq)]
140pub struct WriteLog {
141    /// The generator of established addresses
142    pub(crate) block_address_gen: Option<EstablishedAddressGen>,
143    /// All the storage modification accepted by validity predicates are stored
144    /// in block write-log, before being committed to the storage
145    pub(crate) block_write_log: HashMap<storage::Key, StorageModification>,
146    /// The write log of the transactions of the current batch
147    /// INVARIANT: this has to be sorted by the insertion
148    /// order to correctly read values
149    pub(crate) batch_write_log: Vec<BatchedTxWriteLog>,
150    // The write log of the current active transaction
151    pub(crate) tx_write_log: TxWriteLog,
152    /// Storage modifications for the replay protection storage, cannot be
153    /// managed in the normal write log because we need to commit them
154    /// sometimes even on batch failure
155    pub(crate) replay_protection: HashSet<Hash>,
156}
157
158/// Write log prefix iterator
159#[derive(Debug)]
160pub struct PrefixIter {
161    /// The concrete iterator for modifications sorted by storage keys
162    pub iter:
163        std::collections::btree_map::IntoIter<String, StorageModification>,
164}
165
166impl Iterator for PrefixIter {
167    type Item = (String, StorageModification);
168
169    fn next(&mut self) -> Option<Self::Item> {
170        self.iter.next()
171    }
172}
173
174impl Default for WriteLog {
175    fn default() -> Self {
176        Self {
177            block_address_gen: None,
178            block_write_log: HashMap::with_capacity(100_000),
179            batch_write_log: Vec::with_capacity(5),
180            tx_write_log: Default::default(),
181            replay_protection: HashSet::with_capacity(1_000),
182        }
183    }
184}
185
186impl WriteLog {
187    /// Read a non-temp value at the given key and return the value and the gas
188    /// cost, returns [`None`] if the key is not present in the write log
189    pub fn read(
190        &self,
191        key: &storage::Key,
192    ) -> std::result::Result<(Option<&StorageModification>, Gas), arith::Error>
193    {
194        // try to read from tx write log first
195        match self
196            .tx_write_log
197            .write_log
198            .get(key)
199            .or_else(|| {
200                // If not found, then try to read from batch write log,
201                // following the insertion order
202                self.batch_write_log
203                    .iter()
204                    .rev()
205                    .find_map(|log| log.write_log.get(key))
206            })
207            .or_else(|| {
208                // if not found, then try to read from block write log
209                self.block_write_log.get(key)
210            }) {
211            Some(v) => {
212                let gas = match &v {
213                    StorageModification::Write { value } => {
214                        checked!(key.len() + value.len())?
215                    }
216                    StorageModification::Delete => key.len(),
217                    StorageModification::InitAccount { vp_code_hash } => {
218                        checked!(key.len() + vp_code_hash.len())?
219                    }
220                } as u64;
221                Ok((
222                    Some(v),
223                    checked!(gas * MEMORY_ACCESS_GAS_PER_BYTE)?.into(),
224                ))
225            }
226            None => {
227                let gas = key.len() as u64;
228                Ok((None, checked!(gas * MEMORY_ACCESS_GAS_PER_BYTE)?.into()))
229            }
230        }
231    }
232
233    /// Read a value before the latest tx execution at the given key and return
234    /// the value and the gas cost, returns [`None`] if the key is not present
235    /// in the write log
236    pub fn read_pre(
237        &self,
238        key: &storage::Key,
239    ) -> std::result::Result<(Option<&StorageModification>, Gas), arith::Error>
240    {
241        for bucket in self
242            .batch_write_log
243            .iter()
244            .rev()
245            .map(|batch_log| &batch_log.write_log)
246            .chain([&self.block_write_log])
247        {
248            if let Some(v) = bucket.get(key) {
249                let gas = match &v {
250                    StorageModification::Write { value } => {
251                        checked!(key.len() + value.len())?
252                    }
253                    StorageModification::Delete => key.len(),
254                    StorageModification::InitAccount { vp_code_hash } => {
255                        checked!(key.len() + vp_code_hash.len())?
256                    }
257                } as u64;
258                return Ok((
259                    Some(v),
260                    checked!(gas * MEMORY_ACCESS_GAS_PER_BYTE)?.into(),
261                ));
262            }
263        }
264        let gas = key.len() as u64;
265        Ok((None, checked!(gas * MEMORY_ACCESS_GAS_PER_BYTE)?.into()))
266    }
267
268    /// Read a temp value at the given key and return the value and the gas
269    /// cost, returns [`None`] if the key is not present in the temp write
270    /// log
271    pub fn read_temp(
272        &self,
273        key: &storage::Key,
274    ) -> Result<(Option<&Vec<u8>>, Gas)> {
275        // try to read from tx write log first
276        match self.tx_write_log.tx_temp_log.get(key) {
277            Some(value) => {
278                let gas = checked!(key.len() + value.len())? as u64;
279
280                Ok((
281                    Some(value),
282                    checked!(gas * MEMORY_ACCESS_GAS_PER_BYTE)?.into(),
283                ))
284            }
285            None => {
286                let gas = key.len() as u64;
287                Ok((None, checked!(gas * MEMORY_ACCESS_GAS_PER_BYTE)?.into()))
288            }
289        }
290    }
291
292    /// Write a key and a value and return the gas cost and the size difference
293    /// Fails with [`Error::UpdateVpOfNewAccount`] when attempting to update a
294    /// validity predicate of a new account that's not yet committed to storage.
295    /// Fails with [`Error::UpdateTemporaryValue`] when attempting to update a
296    /// temporary value.
297    pub fn write(
298        &mut self,
299        key: &storage::Key,
300        value: Vec<u8>,
301    ) -> Result<(Gas, i64)> {
302        let len = value.len();
303        if self.tx_write_log.tx_temp_log.contains_key(key) {
304            return Err(Error::UpdateTemporaryValue);
305        }
306        let len_signed =
307            i64::try_from(len).map_err(|_| Error::ValueLenOverflow)?;
308        let size_diff = match self.tx_write_log.write_log.get(key) {
309            Some(prev) => match &prev {
310                StorageModification::Write { value } => {
311                    let val_len = i64::try_from(value.len())
312                        .map_err(|_| Error::ValueLenOverflow)?;
313                    checked!(len_signed - val_len)?
314                }
315                StorageModification::Delete => len_signed,
316                StorageModification::InitAccount { .. } => {
317                    // NOTE: errors from host functions force a shudown of the
318                    // wasm environment without the need for cooperation from
319                    // the wasm code (tx or vp), so there's no need to return
320                    // gas in case of an error because execution will terminate
321                    // anyway and this cannot be exploited to keep the vm
322                    // running
323                    return Err(Error::UpdateVpOfNewAccount);
324                }
325            },
326            // set just the length of the value because we don't know if
327            // the previous value exists on the storage
328            None => len_signed,
329        };
330
331        self.tx_write_log
332            .write_log
333            .insert(key.clone(), StorageModification::Write { value });
334
335        let gas = checked!(key.len() + len)? as u64;
336        Ok((
337            checked!(gas * STORAGE_WRITE_GAS_PER_BYTE)?.into(),
338            size_diff,
339        ))
340    }
341
342    /// Write a key and a value.
343    /// Fails with [`Error::UpdateVpOfNewAccount`] when attempting to update a
344    /// validity predicate of a new account that's not yet committed to storage.
345    /// Fails with [`Error::UpdateTemporaryValue`] when attempting to update a
346    /// temporary value.
347    pub fn protocol_write(
348        &mut self,
349        key: &storage::Key,
350        value: Vec<u8>,
351    ) -> Result<()> {
352        if self.tx_write_log.tx_temp_log.contains_key(key) {
353            return Err(Error::UpdateTemporaryValue);
354        }
355        if let Some(prev) = self
356            .block_write_log
357            .insert(key.clone(), StorageModification::Write { value })
358        {
359            match prev {
360                StorageModification::InitAccount { .. } => {
361                    return Err(Error::UpdateVpOfNewAccount);
362                }
363                StorageModification::Write { .. }
364                | StorageModification::Delete => {}
365            }
366        }
367        Ok(())
368    }
369
370    /// Write a key and a value and return the gas cost and the size difference
371    /// Fails with [`Error::WriteTempAfterWrite`] when attempting to update a
372    /// temporary value after writing.
373    /// Fails with [`Error::UpdateVpOfNewAccount`] when attempting to update a
374    /// validity predicate of a new account that's not yet committed to storage.
375    /// Fails with [`Error::WriteTempAfterDelete`] when attempting to update a
376    /// temporary value after deleting.
377    pub fn write_temp(
378        &mut self,
379        key: &storage::Key,
380        value: Vec<u8>,
381    ) -> Result<(Gas, i64)> {
382        if let Some(prev) = self.tx_write_log.write_log.get(key) {
383            match prev {
384                StorageModification::Write { .. } => {
385                    // Cannot overwrite a write request with a temporary one
386                    return Err(Error::WriteTempAfterWrite);
387                }
388                StorageModification::Delete => {
389                    return Err(Error::WriteTempAfterDelete);
390                }
391                StorageModification::InitAccount { .. } => {
392                    return Err(Error::UpdateVpOfNewAccount);
393                }
394            }
395        }
396
397        let len = value.len();
398        let len_signed =
399            i64::try_from(len).map_err(|_| Error::ValueLenOverflow)?;
400        let size_diff = match self.tx_write_log.tx_temp_log.get(key) {
401            Some(prev) => {
402                let prev_len = i64::try_from(prev.len())
403                    .map_err(|_| Error::ValueLenOverflow)?;
404                checked!(len_signed - prev_len)?
405            }
406            // set just the length of the value because we don't know if
407            // the previous value exists on the storage
408            None => len_signed,
409        };
410
411        self.tx_write_log.tx_temp_log.insert(key.clone(), value);
412
413        // Temp writes are not propagated to db so just charge the cost of
414        // accessing storage
415        let gas = checked!(key.len() + len)? as u64;
416        Ok((
417            checked!(gas * MEMORY_ACCESS_GAS_PER_BYTE)?.into(),
418            size_diff,
419        ))
420    }
421
422    /// Delete a key and its value, and return the gas cost and the size
423    /// difference.
424    /// Fails with [`Error::DeleteVp`] for a validity predicate key, which are
425    /// not possible to delete.
426    pub fn delete(&mut self, key: &storage::Key) -> Result<(Gas, i64)> {
427        if key.is_validity_predicate().is_some() {
428            return Err(Error::DeleteVp);
429        }
430        let size_diff = match self.tx_write_log.write_log.get(key) {
431            Some(prev) => match &prev {
432                StorageModification::Write { value } => value.len(),
433                StorageModification::Delete => 0,
434                StorageModification::InitAccount { .. } => {
435                    return Err(Error::DeleteVp);
436                }
437            },
438            // set 0 because we don't know if the previous value exists on the
439            // storage
440            None => 0,
441        };
442
443        self.tx_write_log
444            .write_log
445            .insert(key.clone(), StorageModification::Delete);
446        let gas = checked!(key.len() + size_diff)? as u64;
447        let size_diff = i64::try_from(size_diff)
448            .ok()
449            .and_then(i64::checked_neg)
450            .ok_or(Error::SizeDiffOverflow)?;
451        Ok((
452            checked!(gas * STORAGE_DELETE_GAS_PER_BYTE)?.into(),
453            size_diff,
454        ))
455    }
456
457    /// Delete a key and its value.
458    /// Fails with [`Error::DeleteVp`] for a validity predicate key, which are
459    /// not possible to delete.
460    pub fn protocol_delete(&mut self, key: &storage::Key) -> Result<()> {
461        if key.is_validity_predicate().is_some() {
462            return Err(Error::DeleteVp);
463        }
464        if let Some(prev) = self
465            .block_write_log
466            .insert(key.clone(), StorageModification::Delete)
467        {
468            match prev {
469                StorageModification::InitAccount { .. } => {
470                    return Err(Error::DeleteVp);
471                }
472                StorageModification::Write { .. }
473                | StorageModification::Delete => {}
474            }
475        };
476        Ok(())
477    }
478
479    /// Initialize a new account and return the gas cost.
480    pub fn init_account(
481        &mut self,
482        storage_address_gen: &EstablishedAddressGen,
483        vp_code_hash: Hash,
484        entropy_source: &[u8],
485    ) -> (Address, Gas) {
486        // If we've previously generated a new account, we use the local copy of
487        // the generator. Otherwise, we create a new copy from the storage
488        let address_gen = self
489            .tx_write_log
490            .address_gen
491            .get_or_insert_with(|| storage_address_gen.clone());
492        let addr = address_gen.generate_address(entropy_source);
493        let key = storage::Key::validity_predicate(&addr);
494        let gas = ((key
495            .len()
496            .checked_add(vp_code_hash.len())
497            .expect("Cannot overflow")) as u64)
498            .checked_mul(STORAGE_WRITE_GAS_PER_BYTE)
499            .expect("Canno overflow");
500        self.tx_write_log
501            .write_log
502            .insert(key, StorageModification::InitAccount { vp_code_hash });
503        (addr, gas.into())
504    }
505
506    /// Set an event and return the gas cost. Returns `None` on gas u64
507    /// overflow.
508    pub fn emit_event<E: EventToEmit>(&mut self, event: E) -> Option<Gas> {
509        self.emit_event_with_tx_hashes(event, None)
510    }
511
512    /// Set an event and return the gas cost.
513    ///
514    /// Returns `None` on gas u64 overflow. The optional tx hashes
515    /// are included as new attributes in the event, free of charge.
516    pub fn emit_event_with_tx_hashes<E: EventToEmit>(
517        &mut self,
518        event: E,
519        inner_tx_id: Option<InnerTxId<'_>>,
520    ) -> Option<Gas> {
521        let mut event = event.into();
522        let gas_cost = event.emission_gas_cost(MEMORY_ACCESS_GAS_PER_BYTE);
523        if gas_cost.as_ref().is_some() {
524            let event_type = event.kind().to_string();
525            if !self.tx_write_log.events.tree.contains_key(&event_type) {
526                self.tx_write_log
527                    .events
528                    .tree
529                    .insert(&event_type, HashSet::new());
530            }
531            if let Some(inner_tx_id) = inner_tx_id {
532                event.extend(TxHash(inner_tx_id.wrapper_hash()));
533                event.extend(InnerTxHash(inner_tx_id.inner_hash()));
534            }
535            self.tx_write_log
536                .events
537                .tree
538                .get_mut(&event_type)
539                .unwrap()
540                .insert(event);
541        }
542        gas_cost.map(|gas| gas.into())
543    }
544
545    /// Get the non-temporary storage keys changed and accounts keys initialized
546    /// in the current transaction. The account keys point to the validity
547    /// predicates of the newly created accounts.
548    pub fn get_keys(&self) -> BTreeSet<storage::Key> {
549        self.tx_write_log
550            .write_log
551            .iter()
552            .map(|(key, _modification)| key.clone())
553            .collect()
554    }
555
556    /// Get the storage keys changed in the current transaction (left) and
557    /// the addresses of accounts initialized in the current transaction
558    /// (right). The first vector excludes keys of validity predicates of
559    /// newly initialized accounts, but may include keys of other data
560    /// written into newly initialized accounts.
561    pub fn get_partitioned_keys(
562        &self,
563    ) -> (BTreeSet<&storage::Key>, HashSet<&Address>) {
564        use itertools::Either;
565        self.tx_write_log
566            .write_log
567            .iter()
568            .partition_map(|(key, value)| {
569                match (key.is_validity_predicate(), value) {
570                    (
571                        Some(address),
572                        StorageModification::InitAccount { .. },
573                    ) => Either::Right(address),
574                    _ => Either::Left(key),
575                }
576            })
577    }
578
579    /// Get the addresses of accounts initialized in the current transaction.
580    pub fn get_initialized_accounts(&self) -> Vec<Address> {
581        self.tx_write_log
582            .write_log
583            .iter()
584            .filter_map(|(key, value)| {
585                match (key.is_validity_predicate(), value) {
586                    (
587                        Some(address),
588                        StorageModification::InitAccount { .. },
589                    ) => Some(address.clone()),
590                    _ => None,
591                }
592            })
593            .collect()
594    }
595
596    /// Take the events of the current transaction
597    pub fn take_events(&mut self) -> BTreeSet<Event> {
598        std::mem::take(&mut self.tx_write_log.events.tree)
599            .into_iter()
600            .flat_map(|(_, event_set)| event_set)
601            .collect()
602    }
603
604    /// Get events emitted by the current transaction of
605    /// a certain type.
606    #[inline]
607    pub fn lookup_events_with_prefix<'this, 'ty: 'this>(
608        &'this self,
609        event_type: &'ty EventType,
610    ) -> impl Iterator<Item = &'this Event> + 'this {
611        self.tx_write_log
612            .events
613            .tree
614            .iter_prefix(event_type)
615            .flat_map(|(_, event_set)| event_set)
616    }
617
618    /// Get events emitted by the current transaction of
619    /// type `E`.
620    #[inline]
621    pub fn get_events_of<E: EventToEmit>(
622        &self,
623    ) -> impl Iterator<Item = &Event> {
624        self.tx_write_log
625            .events
626            .tree
627            .iter_prefix(E::DOMAIN)
628            .flat_map(|(_, event_set)| event_set)
629    }
630
631    /// Get all events emitted by the current transaction.
632    #[inline]
633    pub fn get_events(&self) -> impl Iterator<Item = &Event> {
634        self.tx_write_log.events.tree.values().flatten()
635    }
636
637    /// Commit the current transaction's write log to the batch when it's
638    /// accepted by all the triggered validity predicates. Starts a new
639    /// transaction write log.
640    pub fn commit_tx_to_batch(&mut self) {
641        let tx_write_log = std::mem::take(&mut self.tx_write_log);
642        let batched_log = BatchedTxWriteLog {
643            address_gen: tx_write_log.address_gen,
644            write_log: tx_write_log.write_log,
645        };
646
647        self.batch_write_log.push(batched_log);
648    }
649
650    /// Drop the current transaction's write log and IBC events when it's
651    /// declined by any of the triggered validity predicates. Starts a new
652    /// transaction write log and clears the temp write log.
653    pub fn drop_tx(&mut self) {
654        self.tx_write_log = Default::default();
655    }
656
657    /// Commit the current tx and the entire batch to the block log.
658    pub fn commit_batch_and_current_tx(&mut self) {
659        self.commit_tx_to_batch();
660        self.commit_batch_only();
661    }
662
663    /// Commit the entire batch to the block log. Doesn't handle the tx write
664    /// log which might still contain some data and needs to be handled
665    /// separately.
666    pub fn commit_batch_only(&mut self) {
667        for log in std::mem::take(&mut self.batch_write_log) {
668            self.block_write_log.extend(log.write_log);
669            self.block_address_gen = log.address_gen;
670        }
671    }
672
673    /// Drop the current tx and the entire batch log.
674    pub fn drop_batch(&mut self) {
675        self.drop_tx();
676        self.batch_write_log = Default::default();
677    }
678
679    /// Get the verifiers set whose validity predicates should validate the
680    /// current transaction changes and the storage keys that have been
681    /// modified created, updated and deleted via the write log.
682    ///
683    /// Note that some storage keys may comprise of multiple addresses, in which
684    /// case every address will be included in the verifiers set.
685    pub fn verifiers_and_changed_keys(
686        &self,
687        verifiers_from_tx: &BTreeSet<Address>,
688    ) -> (BTreeSet<Address>, BTreeSet<storage::Key>) {
689        let changed_keys: BTreeSet<storage::Key> = self.get_keys();
690        let initialized_accounts = self.get_initialized_accounts();
691        let mut verifiers = verifiers_from_tx.clone();
692
693        // get changed keys grouped by the address
694        for key in changed_keys.iter() {
695            if let Some(addr) = key.fst_address() {
696                // We can skip insert when the address has been added from the
697                // Tx above. Also skip if it's an address of a newly initialized
698                // account, because anything can be written into an account's
699                // storage in the same tx in which it's initialized (there is no
700                // VP in the state prior to tx execution).
701                if !verifiers_from_tx.contains(addr)
702                    && !initialized_accounts.contains(addr)
703                {
704                    // Add the address as a verifier
705                    verifiers.insert(addr.clone());
706                }
707            }
708        }
709        (verifiers, changed_keys)
710    }
711
712    /// Iterate modifications prior to the current transaction, whose storage
713    /// key matches the given prefix, sorted by their storage key.
714    pub fn iter_prefix_pre(&self, prefix: &storage::Key) -> PrefixIter {
715        let mut matches = BTreeMap::new();
716
717        for (key, modification) in self.block_write_log.iter().chain(
718            self.batch_write_log
719                .iter()
720                .flat_map(|batch_log| batch_log.write_log.iter()),
721        ) {
722            if key.split_prefix(prefix).is_some() {
723                matches.insert(key.to_string(), modification.clone());
724            }
725        }
726
727        let iter = matches.into_iter();
728        PrefixIter { iter }
729    }
730
731    /// Iterate modifications posterior of the current tx, whose storage key
732    /// matches the given prefix, sorted by their storage key.
733    pub fn iter_prefix_post(&self, prefix: &storage::Key) -> PrefixIter {
734        let mut matches = BTreeMap::new();
735
736        for (key, modification) in self.block_write_log.iter().chain(
737            self.batch_write_log
738                .iter()
739                .flat_map(|batch_log| batch_log.write_log.iter())
740                .chain(self.tx_write_log.write_log.iter()),
741        ) {
742            if key.split_prefix(prefix).is_some() {
743                matches.insert(key.to_string(), modification.clone());
744            }
745        }
746
747        let iter = matches.into_iter();
748        PrefixIter { iter }
749    }
750
751    /// Check if the given tx hash has already been processed
752    pub fn has_replay_protection_entry(&self, hash: &Hash) -> bool {
753        self.replay_protection.contains(hash)
754    }
755
756    /// Write the transaction hash
757    pub fn write_tx_hash(&mut self, hash: Hash) -> Result<()> {
758        if !self.replay_protection.insert(hash) {
759            // Cannot write an hash if it's already present in the set
760            return Err(Error::ReplayProtection(format!(
761                "Requested a write of hash {hash} which has already been \
762                 processed"
763            )));
764        }
765
766        Ok(())
767    }
768
769    /// Remove the transaction hash because redundant
770    pub(crate) fn redundant_tx_hash(&mut self, hash: &Hash) -> Result<()> {
771        if !self.replay_protection.swap_remove(hash) {
772            return Err(Error::ReplayProtection(format!(
773                "Requested a redundant modification on hash {hash} which is \
774                 unknown"
775            )));
776        }
777        Ok(())
778    }
779}
780
781#[allow(clippy::cast_possible_wrap)]
782#[cfg(test)]
783mod tests {
784    use assert_matches::assert_matches;
785    use namada_core::address;
786    use pretty_assertions::assert_eq;
787    use proptest::prelude::*;
788
789    use super::*;
790    use crate::StateRead;
791
792    #[test]
793    fn test_crud_value() {
794        let mut write_log = WriteLog::default();
795        let key =
796            storage::Key::parse("key").expect("cannot parse the key string");
797
798        // read a non-existing key
799        let (value, gas) = write_log.read(&key).unwrap();
800        assert!(value.is_none());
801        assert_eq!(
802            gas,
803            ((key.len() as u64) * MEMORY_ACCESS_GAS_PER_BYTE).into()
804        );
805
806        // delete a non-existing key
807        let (gas, diff) = write_log.delete(&key).unwrap();
808        assert_eq!(
809            gas,
810            (key.len() as u64 * STORAGE_DELETE_GAS_PER_BYTE).into()
811        );
812        assert_eq!(diff, 0);
813
814        // insert a value
815        let inserted = "inserted".as_bytes().to_vec();
816        let (gas, diff) = write_log.write(&key, inserted.clone()).unwrap();
817        assert_eq!(
818            gas,
819            ((key.len() + inserted.len()) as u64 * STORAGE_WRITE_GAS_PER_BYTE)
820                .into()
821        );
822        assert_eq!(diff, inserted.len() as i64);
823
824        // read the value
825        let (value, gas) = write_log.read(&key).unwrap();
826        match value.expect("no read value") {
827            StorageModification::Write { value } => {
828                assert_eq!(*value, inserted)
829            }
830            _ => panic!("unexpected read result"),
831        }
832        assert_eq!(
833            gas,
834            (((key.len() + inserted.len()) as u64)
835                * MEMORY_ACCESS_GAS_PER_BYTE)
836                .into()
837        );
838
839        // update the value
840        let updated = "updated".as_bytes().to_vec();
841        let (gas, diff) = write_log.write(&key, updated.clone()).unwrap();
842        assert_eq!(
843            gas,
844            ((key.len() + updated.len()) as u64 * STORAGE_WRITE_GAS_PER_BYTE)
845                .into()
846        );
847        assert_eq!(diff, updated.len() as i64 - inserted.len() as i64);
848
849        // delete the key
850        let (gas, diff) = write_log.delete(&key).unwrap();
851        assert_eq!(
852            gas,
853            ((key.len() + updated.len()) as u64 * STORAGE_DELETE_GAS_PER_BYTE)
854                .into()
855        );
856        assert_eq!(diff, -(updated.len() as i64));
857
858        // delete the deleted key again
859        let (gas, diff) = write_log.delete(&key).unwrap();
860        assert_eq!(
861            gas,
862            (key.len() as u64 * STORAGE_DELETE_GAS_PER_BYTE).into()
863        );
864        assert_eq!(diff, 0);
865
866        // read the deleted key
867        let (value, gas) = write_log.read(&key).unwrap();
868        match &value.expect("no read value") {
869            StorageModification::Delete => {}
870            _ => panic!("unexpected result"),
871        }
872        assert_eq!(gas, (key.len() as u64 * MEMORY_ACCESS_GAS_PER_BYTE).into());
873
874        // insert again
875        let reinserted = "reinserted".as_bytes().to_vec();
876        let (gas, diff) = write_log.write(&key, reinserted.clone()).unwrap();
877        assert_eq!(
878            gas,
879            ((key.len() + reinserted.len()) as u64
880                * STORAGE_WRITE_GAS_PER_BYTE)
881                .into()
882        );
883        assert_eq!(diff, reinserted.len() as i64);
884    }
885
886    #[test]
887    fn test_crud_account() {
888        let mut write_log = WriteLog::default();
889        let address_gen = EstablishedAddressGen::new("test");
890
891        // init
892        let init_vp = "initialized".as_bytes().to_vec();
893        let vp_hash = Hash::sha256(init_vp);
894        let (addr, gas) = write_log.init_account(&address_gen, vp_hash, &[]);
895        let vp_key = storage::Key::validity_predicate(&addr);
896        assert_eq!(
897            gas,
898            ((vp_key.len() + vp_hash.len()) as u64
899                * STORAGE_WRITE_GAS_PER_BYTE)
900                .into()
901        );
902
903        // read
904        let (value, gas) = write_log.read(&vp_key).unwrap();
905        match value.expect("no read value") {
906            StorageModification::InitAccount { vp_code_hash } => {
907                assert_eq!(*vp_code_hash, vp_hash)
908            }
909            _ => panic!("unexpected result"),
910        }
911        assert_eq!(
912            gas,
913            ((vp_key.len() + vp_hash.len()) as u64
914                * MEMORY_ACCESS_GAS_PER_BYTE)
915                .into()
916        );
917
918        // get all
919        let (_changed_keys, init_accounts) = write_log.get_partitioned_keys();
920        assert!(init_accounts.contains(&&addr));
921        assert_eq!(init_accounts.len(), 1);
922    }
923
924    #[test]
925    fn test_update_initialized_account_should_fail() {
926        let mut write_log = WriteLog::default();
927        let address_gen = EstablishedAddressGen::new("test");
928
929        let init_vp = "initialized".as_bytes().to_vec();
930        let vp_hash = Hash::sha256(init_vp);
931        let (addr, _) = write_log.init_account(&address_gen, vp_hash, &[]);
932        let vp_key = storage::Key::validity_predicate(&addr);
933
934        // update should fail
935        let updated_vp = "updated".as_bytes().to_vec();
936        let updated_vp_hash = Hash::sha256(updated_vp);
937        let result = write_log
938            .write(&vp_key, updated_vp_hash.to_vec())
939            .unwrap_err();
940        assert_matches!(result, Error::UpdateVpOfNewAccount);
941    }
942
943    #[test]
944    fn test_delete_initialized_account_should_fail() {
945        let mut write_log = WriteLog::default();
946        let address_gen = EstablishedAddressGen::new("test");
947
948        let init_vp = "initialized".as_bytes().to_vec();
949        let vp_hash = Hash::sha256(init_vp);
950        let (addr, _) = write_log.init_account(&address_gen, vp_hash, &[]);
951        let vp_key = storage::Key::validity_predicate(&addr);
952
953        // delete should fail
954        let result = write_log.delete(&vp_key).unwrap_err();
955        assert_matches!(result, Error::DeleteVp);
956    }
957
958    #[test]
959    fn test_delete_vp_should_fail() {
960        let mut write_log = WriteLog::default();
961        let addr = address::testing::established_address_1();
962        let vp_key = storage::Key::validity_predicate(&addr);
963
964        // delete should fail
965        let result = write_log.delete(&vp_key).unwrap_err();
966        assert_matches!(result, Error::DeleteVp);
967    }
968
969    #[test]
970    fn test_commit() {
971        let mut state = crate::testing::TestState::default();
972        let address_gen = EstablishedAddressGen::new("test");
973
974        let key1 =
975            storage::Key::parse("key1").expect("cannot parse the key string");
976        let key2 =
977            storage::Key::parse("key2").expect("cannot parse the key string");
978        let key3 =
979            storage::Key::parse("key3").expect("cannot parse the key string");
980        let key4 =
981            storage::Key::parse("key4").expect("cannot parse the key string");
982
983        // initialize an account
984        let vp1 = Hash::sha256("vp1".as_bytes());
985        let (addr1, _) = state.write_log.init_account(&address_gen, vp1, &[]);
986        state.write_log.commit_batch_and_current_tx();
987
988        // write values
989        let val1 = "val1".as_bytes().to_vec();
990        let _ = state.write_log.write(&key1, val1.clone()).unwrap();
991        let _ = state.write_log.write(&key2, val1.clone()).unwrap();
992        let _ = state.write_log.write(&key3, val1.clone()).unwrap();
993        let _ = state.write_log.write_temp(&key4, val1.clone()).unwrap();
994        state.write_log.commit_batch_and_current_tx();
995
996        // these values are not written due to drop_tx
997        let val2 = "val2".as_bytes().to_vec();
998        let _ = state.write_log.write(&key1, val2.clone()).unwrap();
999        let _ = state.write_log.write(&key2, val2.clone()).unwrap();
1000        let _ = state.write_log.write(&key3, val2).unwrap();
1001        state.write_log.drop_tx();
1002
1003        // deletes and updates values
1004        let val3 = "val3".as_bytes().to_vec();
1005        let _ = state.write_log.delete(&key2).unwrap();
1006        let _ = state.write_log.write(&key3, val3.clone()).unwrap();
1007        state.write_log.commit_batch_and_current_tx();
1008
1009        // commit a block
1010        state.commit_block().expect("commit failed");
1011
1012        let (vp_code_hash, _gas) = state
1013            .validity_predicate::<namada_parameters::Store<()>>(&addr1)
1014            .expect("vp read failed");
1015        assert_eq!(vp_code_hash, Some(vp1));
1016        let (value, _) = state.db_read(&key1).expect("read failed");
1017        assert_eq!(value.expect("no read value"), val1);
1018        let (value, _) = state.db_read(&key2).expect("read failed");
1019        assert!(value.is_none());
1020        let (value, _) = state.db_read(&key3).expect("read failed");
1021        assert_eq!(value.expect("no read value"), val3);
1022        let (value, _) = state.db_read(&key4).expect("read failed");
1023        assert_eq!(value, None);
1024    }
1025
1026    #[test]
1027    fn test_replay_protection_commit() {
1028        let mut state = crate::testing::TestState::default();
1029
1030        {
1031            let write_log = state.write_log_mut();
1032            // write some replay protection keys
1033            write_log
1034                .write_tx_hash(Hash::sha256("tx1".as_bytes()))
1035                .unwrap();
1036            write_log
1037                .write_tx_hash(Hash::sha256("tx2".as_bytes()))
1038                .unwrap();
1039            write_log
1040                .write_tx_hash(Hash::sha256("tx3".as_bytes()))
1041                .unwrap();
1042        }
1043
1044        // commit a block
1045        state.commit_block().expect("commit failed");
1046
1047        assert!(state.write_log.replay_protection.is_empty());
1048        for tx in ["tx1", "tx2", "tx3"] {
1049            let hash = Hash::sha256(tx.as_bytes());
1050            assert!(
1051                state
1052                    .has_replay_protection_entry(&hash)
1053                    .expect("read failed")
1054            );
1055        }
1056
1057        {
1058            let write_log = state.write_log_mut();
1059            // write some replay protection keys
1060            write_log
1061                .write_tx_hash(Hash::sha256("tx4".as_bytes()))
1062                .unwrap();
1063            write_log
1064                .write_tx_hash(Hash::sha256("tx5".as_bytes()))
1065                .unwrap();
1066            write_log
1067                .write_tx_hash(Hash::sha256("tx6".as_bytes()))
1068                .unwrap();
1069
1070            // Mark one hash as redundant
1071            write_log
1072                .redundant_tx_hash(&Hash::sha256("tx4".as_bytes()))
1073                .unwrap();
1074        }
1075
1076        // commit a block
1077        state.commit_block().expect("commit failed");
1078
1079        assert!(state.write_log.replay_protection.is_empty());
1080        for tx in ["tx1", "tx2", "tx3", "tx5", "tx6"] {
1081            assert!(
1082                state
1083                    .has_replay_protection_entry(&Hash::sha256(tx.as_bytes()))
1084                    .expect("read failed")
1085            );
1086        }
1087        assert!(
1088            !state
1089                .has_replay_protection_entry(&Hash::sha256("tx4".as_bytes()))
1090                .expect("read failed")
1091        );
1092        {
1093            let write_log = state.write_log_mut();
1094            write_log
1095                .write_tx_hash(Hash::sha256("tx7".as_bytes()))
1096                .unwrap();
1097
1098            // mark as redundant a missing hash and check that it fails
1099            assert!(
1100                state
1101                    .write_log
1102                    .redundant_tx_hash(&Hash::sha256("tx8".as_bytes()))
1103                    .is_err()
1104            );
1105
1106            // Do not assert the state of replay protection because this
1107            // error will actually trigger a shut down of the node. Also, since
1108            // we write the values before validating them, the state would be
1109            // wrong
1110        }
1111    }
1112
1113    // Test that writing a value on top of a temporary write is not allowed
1114    #[test]
1115    fn test_write_after_temp_disallowed() {
1116        let mut state = crate::testing::TestState::default();
1117
1118        let key1 =
1119            storage::Key::parse("key1").expect("cannot parse the key string");
1120        let val1 = "val1".as_bytes().to_vec();
1121        // Test from tx_write_log
1122        let _ = state.write_log.write_temp(&key1, val1.clone()).unwrap();
1123        assert!(matches!(
1124            state.write_log.write(&key1, val1.clone()),
1125            Err(Error::UpdateTemporaryValue)
1126        ));
1127    }
1128
1129    // Test that a temporary write on top of a write is not allowed
1130    #[test]
1131    fn test_write_temp_after_write_disallowed() {
1132        let mut state = crate::testing::TestState::default();
1133
1134        let key1 =
1135            storage::Key::parse("key1").expect("cannot parse the key string");
1136        let val1 = "val1".as_bytes().to_vec();
1137        // Test from tx_write_log
1138        let _ = state.write_log.write(&key1, val1.clone()).unwrap();
1139        assert!(matches!(
1140            state.write_log.write_temp(&key1, val1.clone()),
1141            Err(Error::WriteTempAfterWrite)
1142        ));
1143    }
1144
1145    // Test that a temporary write on top of a delete is not allowed
1146    #[test]
1147    fn test_write_temp_after_delete_disallowed() {
1148        let mut state = crate::testing::TestState::default();
1149
1150        let key1 =
1151            storage::Key::parse("key1").expect("cannot parse the key string");
1152        let val1 = "val1".as_bytes().to_vec();
1153        // Test from tx_write_log
1154        let _ = state.write_log.delete(&key1).unwrap();
1155        assert!(matches!(
1156            state.write_log.write_temp(&key1, val1.clone()),
1157            Err(Error::WriteTempAfterDelete)
1158        ));
1159    }
1160
1161    prop_compose! {
1162        fn arb_verifiers_changed_key_tx_all_key()
1163            (verifiers_from_tx in testing::arb_verifiers_from_tx())
1164            (tx_write_log in testing::arb_tx_write_log(verifiers_from_tx.clone()),
1165                verifiers_from_tx in Just(verifiers_from_tx))
1166        -> (BTreeSet<Address>, HashMap<storage::Key, StorageModification>) {
1167            (verifiers_from_tx, tx_write_log)
1168        }
1169    }
1170
1171    proptest! {
1172        #[test]
1173        fn verifiers_changed_key_tx_all_key(
1174            (verifiers_from_tx, write_log) in arb_verifiers_changed_key_tx_all_key(),
1175        ) {
1176            verifiers_changed_key_tx_all_key_aux(verifiers_from_tx, write_log)
1177        }
1178
1179        #[test]
1180        fn test_batched_txs(modifications in testing::arb_batched_txs()) {
1181            test_batched_txs_aux(modifications)
1182        }
1183    }
1184
1185    /// Test [`WriteLog::verifiers_changed_keys`] that:
1186    /// 1. Every address from `verifiers_from_tx` is included in the verifiers
1187    ///    set.
1188    /// 2. Every address included in the first segment of changed storage keys
1189    ///    is included in the verifiers set.
1190    /// 3. Addresses of newly initialized accounts are not verifiers, so that
1191    ///    anything can be written into an account's storage in the same tx in
1192    ///    which it's initialized.
1193    /// 4. The validity predicates of all the newly initialized accounts are
1194    ///    included in the changed keys set.
1195    fn verifiers_changed_key_tx_all_key_aux(
1196        verifiers_from_tx: BTreeSet<Address>,
1197        write_log: HashMap<storage::Key, StorageModification>,
1198    ) {
1199        let write_log = WriteLog {
1200            tx_write_log: super::TxWriteLog {
1201                write_log,
1202                ..Default::default()
1203            },
1204            ..WriteLog::default()
1205        };
1206
1207        let (verifiers, changed_keys) =
1208            write_log.verifiers_and_changed_keys(&verifiers_from_tx);
1209
1210        println!("verifiers_from_tx {:#?}", verifiers_from_tx);
1211        for verifier_from_tx in verifiers_from_tx {
1212            // Test for 1.
1213            assert!(verifiers.contains(&verifier_from_tx));
1214        }
1215
1216        let (_changed_keys, initialized_accounts) =
1217            write_log.get_partitioned_keys();
1218        for key in changed_keys.iter() {
1219            if let Some(addr_from_key) = key.fst_address() {
1220                if !initialized_accounts.contains(addr_from_key) {
1221                    // Test for 2.
1222                    assert!(verifiers.contains(addr_from_key));
1223                }
1224            }
1225        }
1226
1227        println!("verifiers {:#?}", verifiers);
1228        println!("changed_keys {:#?}", changed_keys);
1229        println!("initialized_accounts {:#?}", initialized_accounts);
1230        for initialized_account in initialized_accounts {
1231            // Test for 3.
1232            assert!(!verifiers.contains(initialized_account));
1233            // Test for 4.
1234            let vp_key = storage::Key::validity_predicate(initialized_account);
1235            assert!(changed_keys.contains(&vp_key));
1236        }
1237    }
1238
1239    /// Test that for a batched tx the result of reading modified keys from
1240    /// write log matches the result of prefix iterators.
1241    fn test_batched_txs_aux(
1242        txs: Vec<HashMap<storage::Key, StorageModification>>,
1243    ) {
1244        let mut write_log = WriteLog::default();
1245
1246        for tx in txs {
1247            // Write tx modifications
1248            write_log.tx_write_log.write_log = tx;
1249
1250            // Collect all the keys in batch from previous txs
1251            let keys: HashSet<storage::Key> = write_log
1252                .batch_write_log
1253                .iter()
1254                .flat_map(|batch| batch.write_log.keys())
1255                .cloned()
1256                .collect();
1257
1258            // Iterate through all the modified keys to check prior state
1259            for key in keys.clone() {
1260                // Read the modification associated with this key from prior
1261                // state
1262                let (modification, _gas) = write_log.read_pre(&key).unwrap();
1263                let modification = modification.unwrap();
1264
1265                // Prefix iter prior state for this key and assert that the
1266                // values match
1267                let mut found_match = false;
1268                for (key_str, modification_from_iter) in
1269                    write_log.iter_prefix_pre(&key)
1270                {
1271                    // This key might be a prefix of a matched key
1272                    if key_str == key.to_string() {
1273                        found_match = true;
1274                        assert_eq!(modification, &modification_from_iter);
1275                    } else {
1276                        assert!(key_str.contains(&key.to_string()));
1277                    }
1278                }
1279                assert!(
1280                    found_match,
1281                    "Expected key {key} not found in iter_prefix_pre"
1282                );
1283            }
1284
1285            // And then commit them to batch
1286            write_log.commit_tx_to_batch();
1287
1288            // Collect all the keys in batch and tx logs
1289            let keys: HashSet<storage::Key> = write_log
1290                .batch_write_log
1291                .iter()
1292                .flat_map(|batch| batch.write_log.keys())
1293                .chain(write_log.tx_write_log.write_log.keys())
1294                .cloned()
1295                .collect();
1296
1297            // Iterate through all the modified keys again to check posterior
1298            // state
1299            for key in keys.clone() {
1300                // Read the modification associated with this key from posterior
1301                // state
1302                let (modification, _gas) = write_log.read(&key).unwrap();
1303                let modification = modification.unwrap();
1304
1305                // Prefix iter posterior state for this key and assert that the
1306                // values match
1307                let mut found_match = false;
1308                for (key_str, modification_from_iter) in
1309                    write_log.iter_prefix_post(&key)
1310                {
1311                    // This key might be a prefix of a matched key
1312                    if key_str == key.to_string() {
1313                        found_match = true;
1314                        assert_eq!(modification, &modification_from_iter);
1315                    } else {
1316                        assert!(key_str.contains(&key.to_string()));
1317                    }
1318                }
1319                assert!(
1320                    found_match,
1321                    "Expected key {key} not found in iter_prefix_post"
1322                );
1323            }
1324        }
1325    }
1326}
1327
1328/// Helpers for testing with write log.
1329#[cfg(any(test, feature = "testing"))]
1330pub mod testing {
1331    use namada_core::address::testing::arb_address;
1332    use namada_core::hash::HASH_LENGTH;
1333    use namada_core::storage::testing::arb_key;
1334    use proptest::collection;
1335    use proptest::prelude::{Just, Strategy, any, prop_oneof};
1336
1337    use super::*;
1338
1339    /// Generate an arbitrary tx write log of [`HashMap<storage::Key,
1340    /// StorageModification>`].
1341    pub fn arb_tx_write_log(
1342        verifiers_from_tx: BTreeSet<Address>,
1343    ) -> impl Strategy<Value = HashMap<storage::Key, StorageModification>> + 'static
1344    {
1345        arb_key().prop_flat_map(move |key| {
1346            // If the key is a validity predicate key and its owner is in the
1347            // verifier set, we must not generate `InitAccount` for it.
1348            let can_init_account = key
1349                .is_validity_predicate()
1350                .map(|owner| !verifiers_from_tx.contains(owner))
1351                .unwrap_or_default();
1352            collection::hash_map(
1353                Just(key),
1354                arb_storage_modification(can_init_account),
1355                0..100,
1356            )
1357            .prop_map(|map| map.into_iter().collect())
1358        })
1359    }
1360
1361    /// Generate modifications for batched txs where each `Vec` entry is
1362    /// intended to be a single tx and each tx touches the same set of common
1363    /// keys in addition to some other random keys.
1364    pub fn arb_batched_txs()
1365    -> impl Strategy<Value = Vec<HashMap<storage::Key, StorageModification>>>
1366    + 'static {
1367        const COMMON_KEYS_LEN: usize = 10;
1368
1369        let common_keys = collection::vec(arb_key(), COMMON_KEYS_LEN);
1370        (
1371            // Generate some keys to be modified by all txs
1372            common_keys,
1373            // Vec of txs
1374            collection::vec(
1375                // For each common key generate some modifications in each tx
1376                (
1377                    collection::vec(
1378                        arb_storage_modification(false),
1379                        COMMON_KEYS_LEN,
1380                    ),
1381                    // Then add some more random key modification to each tx
1382                    collection::hash_map(
1383                        arb_key(),
1384                        arb_storage_modification(false),
1385                        1..10,
1386                    ),
1387                ),
1388                1..10,
1389            ),
1390        )
1391            .prop_map(|(common_keys, modifications)| {
1392                modifications
1393                    .into_iter()
1394                    .map(|(common_key_modifications, other_modifications)| {
1395                        common_keys
1396                            .clone()
1397                            .into_iter()
1398                            .zip(common_key_modifications)
1399                            .chain(other_modifications)
1400                            .collect::<HashMap<_, _>>()
1401                    })
1402                    .collect::<Vec<_>>()
1403            })
1404    }
1405
1406    /// Generate arbitrary verifiers from tx of [`BTreeSet<Address>`].
1407    pub fn arb_verifiers_from_tx() -> impl Strategy<Value = BTreeSet<Address>> {
1408        collection::btree_set(arb_address(), 0..10)
1409    }
1410
1411    /// Generate an arbitrary [`StorageModification`].
1412    pub fn arb_storage_modification(
1413        can_init_account: bool,
1414    ) -> impl Strategy<Value = StorageModification> {
1415        if can_init_account {
1416            prop_oneof![
1417                any::<Vec<u8>>()
1418                    .prop_map(|value| StorageModification::Write { value }),
1419                Just(StorageModification::Delete),
1420                any::<[u8; HASH_LENGTH]>().prop_map(|hash| {
1421                    StorageModification::InitAccount {
1422                        vp_code_hash: Hash(hash),
1423                    }
1424                }),
1425            ]
1426            .boxed()
1427        } else {
1428            prop_oneof![
1429                any::<Vec<u8>>()
1430                    .prop_map(|value| StorageModification::Write { value }),
1431                Just(StorageModification::Delete),
1432            ]
1433            .boxed()
1434        }
1435    }
1436}