massa_ledger_exports/
ledger_changes.rs

1// Copyright (c) 2022 MASSA LABS <info@massa.net>
2
3//! This file provides structures representing changes to ledger entries
4
5use crate::ledger_entry::LedgerEntry;
6use massa_models::{
7    address::Address,
8    amount::{Amount, AmountDeserializer, AmountSerializer},
9    bytecode::{Bytecode, BytecodeDeserializer, BytecodeSerializer},
10    prehash::PreHashMap,
11    serialization::{VecU8Deserializer, VecU8Serializer},
12    types::{
13        Applicable, SetOrDelete, SetOrDeleteDeserializer, SetOrDeleteSerializer, SetOrKeep,
14        SetOrKeepDeserializer, SetOrKeepSerializer, SetUpdateOrDelete,
15    },
16};
17use massa_serialization::{
18    Deserializer, SerializeError, Serializer, U64VarIntDeserializer, U64VarIntSerializer,
19};
20use nom::{
21    error::{context, ContextError, ParseError},
22    multi::length_count,
23    sequence::tuple,
24    IResult, Parser,
25};
26use serde::{Deserialize, Serialize};
27use serde_with::serde_as;
28use std::{
29    collections::{hash_map, BTreeMap},
30    ops::Bound::Included,
31};
32
33/// represents an update to one or more fields of a `LedgerEntry`
34#[serde_as]
35#[derive(Default, Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
36pub struct LedgerEntryUpdate {
37    /// change the balance
38    pub balance: SetOrKeep<Amount>,
39    /// change the executable bytecode
40    pub bytecode: SetOrKeep<Bytecode>,
41    /// change datastore entries
42    #[serde_as(as = "Vec<(_, _)>")]
43    pub datastore: BTreeMap<Vec<u8>, SetOrDelete<Vec<u8>>>,
44}
45
46/// Serializer for `datastore` field of `LedgerEntryUpdate`
47pub struct DatastoreUpdateSerializer {
48    u64_serializer: U64VarIntSerializer,
49    vec_u8_serializer: VecU8Serializer,
50    value_serializer: SetOrDeleteSerializer<Vec<u8>, VecU8Serializer>,
51}
52
53impl DatastoreUpdateSerializer {
54    /// Creates a new `DatastoreUpdateSerializer`
55    pub fn new() -> Self {
56        Self {
57            u64_serializer: U64VarIntSerializer::new(),
58            vec_u8_serializer: VecU8Serializer::new(),
59            value_serializer: SetOrDeleteSerializer::new(VecU8Serializer::new()),
60        }
61    }
62}
63
64impl Default for DatastoreUpdateSerializer {
65    fn default() -> Self {
66        Self::new()
67    }
68}
69
70impl Serializer<BTreeMap<Vec<u8>, SetOrDelete<Vec<u8>>>> for DatastoreUpdateSerializer {
71    /// ## Example
72    /// ```rust
73    /// use std::collections::BTreeMap;
74    /// use massa_ledger_exports::{DatastoreUpdateSerializer};
75    /// use massa_serialization::Serializer;
76    /// use massa_models::types::SetOrDelete;
77    ///
78    /// let serializer = DatastoreUpdateSerializer::new();
79    /// let mut buffer = Vec::new();
80    /// let mut datastore = BTreeMap::new();
81    /// datastore.insert(vec![1, 2, 3], SetOrDelete::Set(vec![4, 5, 6]));
82    /// datastore.insert(vec![3, 4, 5], SetOrDelete::Delete);
83    /// serializer.serialize(&datastore, &mut buffer).unwrap();
84    /// ```
85    fn serialize(
86        &self,
87        value: &BTreeMap<Vec<u8>, SetOrDelete<Vec<u8>>>,
88        buffer: &mut Vec<u8>,
89    ) -> Result<(), SerializeError> {
90        let entry_count: u64 = value.len().try_into().map_err(|err| {
91            SerializeError::GeneralError(format!(
92                "too many entries in ConsensusLedgerSubset: {}",
93                err
94            ))
95        })?;
96        self.u64_serializer.serialize(&entry_count, buffer)?;
97        for (key, value) in value.iter() {
98            self.vec_u8_serializer.serialize(key, buffer)?;
99            self.value_serializer.serialize(value, buffer)?;
100        }
101        Ok(())
102    }
103}
104
105/// Serializer for `datastore` field of `LedgerEntryUpdate`
106pub struct DatastoreUpdateDeserializer {
107    length_deserializer: U64VarIntDeserializer,
108    key_deserializer: VecU8Deserializer,
109    value_deserializer: SetOrDeleteDeserializer<Vec<u8>, VecU8Deserializer>,
110}
111
112impl DatastoreUpdateDeserializer {
113    /// Creates a new `DatastoreUpdateDeserializer`
114    pub fn new(
115        max_datastore_key_length: u8,
116        max_datastore_value_length: u64,
117        max_datastore_entry_count: u64,
118    ) -> Self {
119        Self {
120            length_deserializer: U64VarIntDeserializer::new(
121                Included(u64::MIN),
122                Included(max_datastore_entry_count),
123            ),
124            key_deserializer: VecU8Deserializer::new(
125                Included(u64::MIN),
126                Included(max_datastore_key_length as u64),
127            ),
128            value_deserializer: SetOrDeleteDeserializer::new(VecU8Deserializer::new(
129                Included(u64::MIN),
130                Included(max_datastore_value_length),
131            )),
132        }
133    }
134}
135
136impl Deserializer<BTreeMap<Vec<u8>, SetOrDelete<Vec<u8>>>> for DatastoreUpdateDeserializer {
137    /// ## Example
138    /// ```rust
139    /// use std::collections::BTreeMap;
140    /// use massa_ledger_exports::{DatastoreUpdateDeserializer, DatastoreUpdateSerializer};
141    /// use massa_serialization::{Serializer, Deserializer, DeserializeError};
142    /// use massa_models::types::SetOrDelete;
143    ///
144    /// let serializer = DatastoreUpdateSerializer::new();
145    /// let deserializer = DatastoreUpdateDeserializer::new(255, 255, 255);
146    /// let mut buffer = Vec::new();
147    /// let mut datastore = BTreeMap::new();
148    /// datastore.insert(vec![1, 2, 3], SetOrDelete::Set(vec![4, 5, 6]));
149    /// datastore.insert(vec![3, 4, 5], SetOrDelete::Delete);
150    /// serializer.serialize(&datastore, &mut buffer).unwrap();
151    /// let (rest, deserialized) = deserializer.deserialize::<DeserializeError>(&buffer).unwrap();
152    /// assert_eq!(rest.len(), 0);
153    /// assert_eq!(deserialized, datastore);
154    /// ```
155    fn deserialize<'a, E: ParseError<&'a [u8]> + ContextError<&'a [u8]>>(
156        &self,
157        buffer: &'a [u8],
158    ) -> IResult<&'a [u8], BTreeMap<Vec<u8>, SetOrDelete<Vec<u8>>>, E> {
159        context(
160            "Failed Datastore deserialization",
161            length_count(
162                context("Failed length deserialization", |input| {
163                    self.length_deserializer.deserialize(input)
164                }),
165                |input| {
166                    tuple((
167                        context("Failed key deserialization", |input| {
168                            self.key_deserializer.deserialize(input)
169                        }),
170                        context("Failed value deserialization", |input| {
171                            self.value_deserializer.deserialize(input)
172                        }),
173                    ))(input)
174                },
175            ),
176        )
177        .map(|elems| elems.into_iter().collect())
178        .parse(buffer)
179    }
180}
181
182/// Serializer for `LedgerEntryUpdate`
183pub struct LedgerEntryUpdateSerializer {
184    balance_serializer: SetOrKeepSerializer<Amount, AmountSerializer>,
185    bytecode_serializer: SetOrKeepSerializer<Bytecode, BytecodeSerializer>,
186    datastore_serializer: DatastoreUpdateSerializer,
187}
188
189impl LedgerEntryUpdateSerializer {
190    /// Creates a new `LedgerEntryUpdateSerializer`
191    pub fn new() -> Self {
192        Self {
193            balance_serializer: SetOrKeepSerializer::new(AmountSerializer::new()),
194            bytecode_serializer: SetOrKeepSerializer::new(BytecodeSerializer::new()),
195            datastore_serializer: DatastoreUpdateSerializer::new(),
196        }
197    }
198}
199
200impl Default for LedgerEntryUpdateSerializer {
201    fn default() -> Self {
202        Self::new()
203    }
204}
205
206impl Serializer<LedgerEntryUpdate> for LedgerEntryUpdateSerializer {
207    /// ## Example
208    /// ```
209    /// use massa_serialization::Serializer;
210    /// use massa_models::{prehash::PreHashMap, address::Address, amount::Amount, bytecode::Bytecode};
211    /// use std::str::FromStr;
212    /// use std::collections::BTreeMap;
213    /// use massa_models::types::{SetOrDelete, SetOrKeep};
214    /// use massa_ledger_exports::{LedgerEntryUpdate, LedgerEntryUpdateSerializer};
215    ///
216    /// let key = "hello world".as_bytes().to_vec();
217    /// let mut datastore = BTreeMap::default();
218    /// datastore.insert(key, SetOrDelete::Set(vec![1, 2, 3]));
219    /// let amount = Amount::from_str("1").unwrap();
220    /// let bytecode = Bytecode(vec![1, 2, 3]);
221    /// let ledger_entry = LedgerEntryUpdate {
222    ///    balance: SetOrKeep::Keep,
223    ///    bytecode: SetOrKeep::Set(bytecode.clone()),
224    ///    datastore,
225    /// };
226    /// let mut serialized = Vec::new();
227    /// let serializer = LedgerEntryUpdateSerializer::new();
228    /// serializer.serialize(&ledger_entry, &mut serialized).unwrap();
229    /// ```
230    fn serialize(
231        &self,
232        value: &LedgerEntryUpdate,
233        buffer: &mut Vec<u8>,
234    ) -> Result<(), SerializeError> {
235        self.balance_serializer.serialize(&value.balance, buffer)?;
236        self.bytecode_serializer
237            .serialize(&value.bytecode, buffer)?;
238        self.datastore_serializer
239            .serialize(&value.datastore, buffer)?;
240        Ok(())
241    }
242}
243
244/// Deserializer for `LedgerEntryUpdate`
245pub struct LedgerEntryUpdateDeserializer {
246    amount_deserializer: SetOrKeepDeserializer<Amount, AmountDeserializer>,
247    bytecode_deserializer: SetOrKeepDeserializer<Bytecode, BytecodeDeserializer>,
248    datastore_deserializer: DatastoreUpdateDeserializer,
249}
250
251impl LedgerEntryUpdateDeserializer {
252    /// Creates a new `LedgerEntryUpdateDeserializer`
253    pub fn new(
254        max_datastore_key_length: u8,
255        max_datastore_value_length: u64,
256        max_datastore_entry_count: u64,
257        max_bytecode_size: u64,
258    ) -> Self {
259        Self {
260            amount_deserializer: SetOrKeepDeserializer::new(AmountDeserializer::new(
261                Included(Amount::MIN),
262                Included(Amount::MAX),
263            )),
264            bytecode_deserializer: SetOrKeepDeserializer::new(BytecodeDeserializer::new(
265                max_bytecode_size,
266            )),
267            datastore_deserializer: DatastoreUpdateDeserializer::new(
268                max_datastore_key_length,
269                max_datastore_value_length,
270                max_datastore_entry_count,
271            ),
272        }
273    }
274}
275
276impl Deserializer<LedgerEntryUpdate> for LedgerEntryUpdateDeserializer {
277    /// ## Example
278    /// ```
279    /// use massa_serialization::{Deserializer, Serializer, DeserializeError};
280    /// use massa_models::{prehash::PreHashMap, address::Address, amount::Amount, bytecode::Bytecode};
281    /// use std::str::FromStr;
282    /// use massa_models::types::{SetOrDelete, SetOrKeep};
283    /// use std::collections::BTreeMap;
284    /// use massa_ledger_exports::{LedgerEntryUpdate, LedgerEntryUpdateSerializer, LedgerEntryUpdateDeserializer};
285    ///
286    /// let key = "hello world".as_bytes().to_vec();
287    /// let mut datastore = BTreeMap::default();
288    /// datastore.insert(key, SetOrDelete::Set(vec![1, 2, 3]));
289    /// let amount = Amount::from_str("1").unwrap();
290    /// let bytecode = Bytecode(vec![1, 2, 3]);
291    /// let ledger_entry = LedgerEntryUpdate {
292    ///    balance: SetOrKeep::Keep,
293    ///    bytecode: SetOrKeep::Set(bytecode.clone()),
294    ///    datastore,
295    /// };
296    /// let mut serialized = Vec::new();
297    /// let serializer = LedgerEntryUpdateSerializer::new();
298    /// let deserializer = LedgerEntryUpdateDeserializer::new(255, 10000, 10000, 10000);
299    /// serializer.serialize(&ledger_entry, &mut serialized).unwrap();
300    /// let (rest, ledger_entry_deser) = deserializer.deserialize::<DeserializeError>(&serialized).unwrap();
301    /// assert!(rest.is_empty());
302    /// assert_eq!(ledger_entry, ledger_entry_deser);
303    /// ```
304    fn deserialize<'a, E: ParseError<&'a [u8]> + ContextError<&'a [u8]>>(
305        &self,
306        buffer: &'a [u8],
307    ) -> IResult<&'a [u8], LedgerEntryUpdate, E> {
308        context(
309            "Failed LedgerEntryUpdate deserialization",
310            tuple((
311                context("Failed balance deserialization", |input| {
312                    self.amount_deserializer.deserialize(input)
313                }),
314                context("Failed bytecode deserialization", |input| {
315                    self.bytecode_deserializer.deserialize(input)
316                }),
317                context("Failed datastore deserialization", |input| {
318                    self.datastore_deserializer.deserialize(input)
319                }),
320            )),
321        )
322        .map(|(balance, bytecode, datastore)| LedgerEntryUpdate {
323            balance,
324            bytecode,
325            datastore,
326        })
327        .parse(buffer)
328    }
329}
330
331impl Applicable<LedgerEntryUpdate> for LedgerEntryUpdate {
332    /// extends the `LedgerEntryUpdate` with another one
333    fn apply(&mut self, update: LedgerEntryUpdate) {
334        self.balance.apply(update.balance);
335        self.bytecode.apply(update.bytecode);
336        self.datastore.extend(update.datastore);
337    }
338}
339
340/// represents a list of changes to multiple ledger entries
341#[derive(Default, Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
342pub struct LedgerChanges(
343    pub PreHashMap<Address, SetUpdateOrDelete<LedgerEntry, LedgerEntryUpdate>>,
344);
345
346impl Applicable<LedgerChanges> for LedgerChanges {
347    /// extends the current `LedgerChanges` with another one
348    fn apply(&mut self, changes: LedgerChanges) {
349        for (addr, change) in changes.0 {
350            match self.0.entry(addr) {
351                hash_map::Entry::Occupied(mut occ) => {
352                    // apply incoming change if a change on this entry already exists
353                    occ.get_mut().apply(change);
354                }
355                hash_map::Entry::Vacant(vac) => {
356                    // otherwise insert the incoming change
357                    vac.insert(change);
358                }
359            }
360        }
361    }
362}
363
364impl LedgerChanges {
365    /// Get an item from the `LedgerChanges`
366    pub fn get(
367        &self,
368        addr: &Address,
369    ) -> Option<&SetUpdateOrDelete<LedgerEntry, LedgerEntryUpdate>> {
370        self.0.get(addr)
371    }
372
373    /// Retrieves all the bytcode updates contained in the current changes
374    pub fn get_bytecode_updates(&self) -> Vec<Bytecode> {
375        let mut v = Vec::new();
376        for (_addr, change) in self.0.iter() {
377            match change {
378                SetUpdateOrDelete::Set(LedgerEntry { bytecode, .. }) => {
379                    // When creating a new address all fields are filled and bytecode is empty
380                    if !bytecode.0.is_empty() {
381                        v.push(bytecode.clone())
382                    }
383                }
384                SetUpdateOrDelete::Update(entry_update) => {
385                    if let SetOrKeep::Set(bytecode) = entry_update.bytecode.clone() {
386                        if !bytecode.0.is_empty() {
387                            v.push(bytecode);
388                        }
389                    }
390                }
391                SetUpdateOrDelete::Delete => (),
392            }
393        }
394        v
395    }
396
397    /// Create a new, empty address.
398    /// Overwrites the address if it is already there.
399    pub fn create_address(&mut self, address: &Address) {
400        self.0
401            .insert(*address, SetUpdateOrDelete::Set(LedgerEntry::default()));
402    }
403
404    /// Tries to return the balance of an entry
405    /// or gets it from a function if the entry's status is unknown.
406    ///
407    /// This function is used as an optimization:
408    /// if the value can be deduced unambiguously from the `LedgerChanges`,
409    /// no need to dig further (for example in the `FinalLedger`).
410    ///
411    /// # Arguments
412    /// * `addr`: address for which to get the value
413    /// * `f`: fallback function with no arguments and returning `Option<Amount>`
414    ///
415    /// # Returns
416    /// * Some(v) if a value is present, where v is a copy of the value
417    /// * None if the value is absent
418    /// * f() if the value is unknown
419    pub fn get_balance_or_else<F: FnOnce() -> Option<Amount>>(
420        &self,
421        addr: &Address,
422        f: F,
423    ) -> Option<Amount> {
424        // Get the changes for the provided address
425        match self.0.get(addr) {
426            // This entry is being replaced by a new one: get the balance from the new entry
427            Some(SetUpdateOrDelete::Set(v)) => Some(v.balance),
428
429            // This entry is being updated
430            Some(SetUpdateOrDelete::Update(LedgerEntryUpdate { balance, .. })) => match balance {
431                // The update sets a new balance: return it
432                SetOrKeep::Set(v) => Some(*v),
433                // The update keeps the old balance.
434                // We therefore have no info on the absolute value of the balance.
435                // We call the fallback function and return its output.
436                SetOrKeep::Keep => f(),
437            },
438
439            // This entry is being deleted: return None.
440            Some(SetUpdateOrDelete::Delete) => None,
441
442            // This entry is not being changed.
443            // We therefore have no info on the absolute value of the balance.
444            // We call the fallback function and return its output.
445            None => f(),
446        }
447    }
448
449    /// Tries to return the executable bytecode of an entry
450    /// or gets it from a function if the entry's status is unknown.
451    ///
452    /// This function is used as an optimization:
453    /// if the value can be deduced unambiguously from the `LedgerChanges`,
454    /// no need to dig further (for example in the `FinalLedger`).
455    ///
456    /// # Arguments
457    /// * `addr`: address for which to get the value
458    /// * `f`: fallback function with no arguments and returning `Option<Vec<u8>>`
459    ///
460    /// # Returns
461    /// * Some(v) if a value is present, where v is a copy of the value
462    /// * None if the value is absent
463    /// * f() if the value is unknown
464    pub fn get_bytecode_or_else<F: FnOnce() -> Option<Bytecode>>(
465        &self,
466        addr: &Address,
467        f: F,
468    ) -> Option<Bytecode> {
469        // Get the changes to the provided address
470        match self.0.get(addr) {
471            // This entry is being replaced by a new one: get the bytecode from the new entry
472            Some(SetUpdateOrDelete::Set(v)) => Some(v.bytecode.clone()),
473
474            // This entry is being updated
475            Some(SetUpdateOrDelete::Update(LedgerEntryUpdate { bytecode, .. })) => match bytecode {
476                // The update sets a new bytecode: return it
477                SetOrKeep::Set(v) => Some(v.clone()),
478
479                // The update keeps the old bytecode.
480                // We therefore have no info on the absolute value of the bytecode.
481                // We call the fallback function and return its output.
482                SetOrKeep::Keep => f(),
483            },
484
485            // This entry is being deleted: return None.
486            Some(SetUpdateOrDelete::Delete) => None,
487
488            // This entry is not being changed.
489            // We therefore have no info on the absolute contents of the bytecode.
490            // We call the fallback function and return its output.
491            None => f(),
492        }
493    }
494
495    /// Tries to return whether an entry exists
496    /// or gets the information from a function if the entry's status is unknown.
497    ///
498    /// This function is used as an optimization:
499    /// if the result can be deduced unambiguously from the `LedgerChanges`,
500    /// no need to dig further (for example in the `FinalLedger`).
501    ///
502    /// # Arguments
503    /// * `addr`: address to search for
504    /// * `f`: fallback function with no arguments and returning a boolean
505    ///
506    /// # Returns
507    /// * true if the entry exists
508    /// * false if the value is absent
509    /// * f() if the value's existence is unknown
510    pub fn entry_exists_or_else<F: FnOnce() -> bool>(&self, addr: &Address, f: F) -> bool {
511        // Get the changes for the provided address
512        match self.0.get(addr) {
513            // The entry is being replaced by a new one: it exists
514            Some(SetUpdateOrDelete::Set(_)) => true,
515
516            // The entry is being updated:
517            // assume it exists because it will be created on update if it doesn't
518            Some(SetUpdateOrDelete::Update(_)) => true,
519
520            // The entry is being deleted: it doesn't exist anymore
521            Some(SetUpdateOrDelete::Delete) => false,
522
523            // This entry is not being changed.
524            // We therefore have no info on its existence.
525            // We call the fallback function and return its output.
526            None => f(),
527        }
528    }
529
530    /// Set the balance of an address.
531    /// If the address doesn't exist, its ledger entry is created.
532    ///
533    /// # Arguments
534    /// * `addr`: target address
535    /// * `balance`: balance to set for the provided address
536    pub fn set_balance(&mut self, addr: Address, balance: Amount) {
537        // Get the changes for the entry associated to the provided address
538        match self.0.entry(addr) {
539            // That entry is being changed
540            hash_map::Entry::Occupied(mut occ) => {
541                match occ.get_mut() {
542                    // The entry is being replaced by a new one
543                    SetUpdateOrDelete::Set(v) => {
544                        // update the balance of the replacement entry
545                        v.balance = balance;
546                    }
547
548                    // The entry is being updated
549                    SetUpdateOrDelete::Update(u) => {
550                        // Make sure the update sets the balance of the entry to its new value
551                        u.balance = SetOrKeep::Set(balance);
552                    }
553
554                    // The entry is being deleted
555                    d @ SetUpdateOrDelete::Delete => {
556                        // Replace that deletion with a replacement by a new default entry
557                        // for which the balance was properly set
558                        *d = SetUpdateOrDelete::Set(LedgerEntry {
559                            balance,
560                            ..Default::default()
561                        });
562                    }
563                }
564            }
565
566            // This entry is not being changed
567            hash_map::Entry::Vacant(vac) => {
568                // Induce an Update to the entry that sets the balance to its new value
569                vac.insert(SetUpdateOrDelete::Update(LedgerEntryUpdate {
570                    balance: SetOrKeep::Set(balance),
571                    ..Default::default()
572                }));
573            }
574        }
575    }
576
577    /// Set the executable bytecode of an address.
578    /// If the address doesn't exist, its ledger entry is created.
579    ///
580    /// # Parameters
581    /// * `addr`: target address
582    /// * `bytecode`: executable bytecode to assign to that address
583    pub fn set_bytecode(&mut self, addr: Address, bytecode: Bytecode) {
584        // Get the current changes being applied to the entry associated to that address
585        match self.0.entry(addr) {
586            // There are changes currently being applied to the entry
587            hash_map::Entry::Occupied(mut occ) => {
588                match occ.get_mut() {
589                    // The entry is being replaced by a new one
590                    SetUpdateOrDelete::Set(v) => {
591                        // update the bytecode of the replacement entry
592                        v.bytecode = bytecode;
593                    }
594
595                    // The entry is being updated
596                    SetUpdateOrDelete::Update(u) => {
597                        // Ensure that the update includes setting the bytecode to its new value
598                        u.bytecode = SetOrKeep::Set(bytecode);
599                    }
600
601                    // The entry is being deleted
602                    d @ SetUpdateOrDelete::Delete => {
603                        // Replace that deletion with a replacement by a new default entry
604                        // for which the bytecode was properly set
605                        *d = SetUpdateOrDelete::Set(LedgerEntry {
606                            bytecode,
607                            ..Default::default()
608                        });
609                    }
610                }
611            }
612
613            // This entry is not being changed
614            hash_map::Entry::Vacant(vac) => {
615                // Induce an Update to the entry that sets the bytecode to its new value
616                vac.insert(SetUpdateOrDelete::Update(LedgerEntryUpdate {
617                    bytecode: SetOrKeep::Set(bytecode),
618                    ..Default::default()
619                }));
620            }
621        }
622    }
623
624    /// Tries to return a datastore entry for a given address,
625    /// or gets it from a function if the value's status is unknown.
626    ///
627    /// This function is used as an optimization:
628    /// if the result can be deduced unambiguously from the `LedgerChanges`,
629    /// no need to dig further (for example in the `FinalLedger`).
630    ///
631    /// # Arguments
632    /// * `addr`: target address
633    /// * `key`: datastore key
634    /// * `f`: fallback function with no arguments and returning `Option<Vec<u8>>`
635    ///
636    /// # Returns
637    /// * Some(v) if the value was found, where v is a copy of the value
638    /// * None if the value is absent
639    /// * f() if the value is unknown
640    pub fn get_data_entry_or_else<F: FnOnce() -> Option<Vec<u8>>>(
641        &self,
642        addr: &Address,
643        key: &[u8],
644        f: F,
645    ) -> Option<Vec<u8>> {
646        // Get the current changes being applied to the ledger entry associated to that address
647        match self.0.get(addr) {
648            // This ledger entry is being replaced by a new one:
649            // get the datastore entry from the new ledger entry
650            Some(SetUpdateOrDelete::Set(v)) => v.datastore.get(key).cloned(),
651
652            // This ledger entry is being updated
653            Some(SetUpdateOrDelete::Update(LedgerEntryUpdate { datastore, .. })) => {
654                // Get the update being applied to that datastore entry
655                match datastore.get(key) {
656                    // A new datastore value is being set: return a clone of it
657                    Some(SetOrDelete::Set(v)) => Some(v.clone()),
658
659                    // This datastore entry is being deleted: return None
660                    Some(SetOrDelete::Delete) => None,
661
662                    // There are no changes to this particular datastore entry.
663                    // We therefore have no info on the absolute contents of the datastore entry.
664                    // We call the fallback function and return its output.
665                    None => f(),
666                }
667            }
668
669            // This ledger entry is being deleted: return None
670            Some(SetUpdateOrDelete::Delete) => None,
671
672            // This ledger entry is not being changed.
673            // We therefore have no info on the absolute contents of its datastore entry.
674            // We call the fallback function and return its output.
675            None => f(),
676        }
677    }
678
679    /// Tries to return whether the ledger changes contain a write for the given address
680    /// and optionally if a datastore key write also exists in the address's datastore.
681    /// Notes:
682    /// - A ledger entry could be written to without any changes on the values associated,
683    ///   for example if the value was changed multiple times in the same slot.
684    /// - This code assumes Delete cannot be shadowed by Set operations in the same slot, which may not be the case
685    ///   when / if we allow full entry Delete given the current LedgerChanges::Delete handling. In that case, a rework may be necessary.
686    ///
687    /// # Arguments
688    /// * `addr`: target address
689    /// * `key`: optional datastore key
690    ///
691    /// # Returns
692    /// * true if the address and, optionally the datastore key, exists in the ledger changes
693    pub fn has_writes(&self, addr: &Address, key: Option<Vec<u8>>) -> bool {
694        // Get the current changes being applied to the ledger entry associated to that address
695        match self.0.get(addr) {
696            // This ledger entry is being replaced by a new one:
697            // check if the new ledger entry has a datastore entry for the provided key
698            Some(SetUpdateOrDelete::Set(v)) => key.is_none_or(|k| v.datastore.contains_key(&k)),
699
700            // This ledger entry is being updated
701            Some(SetUpdateOrDelete::Update(LedgerEntryUpdate { datastore, .. })) => {
702                // Check if the update being applied to that datastore entry
703                key.is_none_or(|k| datastore.contains_key(&k))
704            }
705
706            // This ledger entry is being deleted: return true
707            Some(SetUpdateOrDelete::Delete) => true,
708
709            // This ledger entry is not being changed.
710            None => false,
711        }
712    }
713
714    /// Tries to return whether a datastore entry exists for a given address,
715    /// or gets it from a function if the datastore entry's status is unknown.
716    ///
717    /// This function is used as an optimization:
718    /// if the result can be deduced unambiguously from the `LedgerChanges`,
719    /// no need to dig further (for example in the `FinalLedger`).
720    ///
721    /// # Arguments
722    /// * `addr`: target address
723    /// * `key`: datastore key
724    /// * `f`: fallback function with no arguments and returning a boolean
725    ///
726    /// # Returns
727    /// * true if the ledger entry exists and the key is present in its datastore
728    /// * false if the ledger entry is absent, or if the key is not in its datastore
729    /// * f() if the existence of the ledger entry or datastore entry is unknown
730    pub fn has_data_entry_or_else<F: FnOnce() -> bool>(
731        &self,
732        addr: &Address,
733        key: &[u8],
734        f: F,
735    ) -> bool {
736        // Get the current changes being applied to the ledger entry associated to that address
737        match self.0.get(addr) {
738            // This ledger entry is being replaced by a new one:
739            // check if the replacement ledger entry has the key in its datastore
740            Some(SetUpdateOrDelete::Set(v)) => v.datastore.contains_key(key),
741
742            // This ledger entry is being updated
743            Some(SetUpdateOrDelete::Update(LedgerEntryUpdate { datastore, .. })) => {
744                // Get the update being applied to that datastore entry
745                match datastore.get(key) {
746                    // A new datastore value is being set: the datastore entry exists
747                    Some(SetOrDelete::Set(_)) => true,
748
749                    // The datastore entry is being deletes: it doesn't exist anymore
750                    Some(SetOrDelete::Delete) => false,
751
752                    // There are no changes to this particular datastore entry.
753                    // We therefore have no info on its existence.
754                    // We call the fallback function and return its output.
755                    None => f(),
756                }
757            }
758
759            // This ledger entry is being deleted: it has no datastore anymore
760            Some(SetUpdateOrDelete::Delete) => false,
761
762            // This ledger entry is not being changed.
763            // We therefore have no info on its datastore.
764            // We call the fallback function and return its output.
765            None => f(),
766        }
767    }
768
769    /// Set a datastore entry for a given address.
770    /// If the address doesn't exist, its ledger entry is created.
771    /// If the datastore entry exists, its value is replaced, otherwise it is created.
772    ///
773    /// # Arguments
774    /// * `addr`: target address
775    /// * `key`: datastore key
776    /// * `data`: datastore value to set
777    pub fn set_data_entry(&mut self, addr: Address, key: Vec<u8>, data: Vec<u8>) {
778        // Get the changes being applied to the ledger entry associated to that address
779        match self.0.entry(addr) {
780            // There are changes currently being applied to the ledger entry
781            hash_map::Entry::Occupied(mut occ) => {
782                match occ.get_mut() {
783                    // The ledger entry is being replaced by a new one
784                    SetUpdateOrDelete::Set(v) => {
785                        // Insert the value in the datastore of the replacement entry
786                        // Any existing value is overwritten
787                        v.datastore.insert(key, data);
788                    }
789
790                    // The ledger entry is being updated
791                    SetUpdateOrDelete::Update(u) => {
792                        // Ensure that the update includes setting the datastore entry
793                        u.datastore.insert(key, SetOrDelete::Set(data));
794                    }
795
796                    // The ledger entry is being deleted
797                    d @ SetUpdateOrDelete::Delete => {
798                        // Replace that ledger entry deletion with a replacement by a new default ledger entry
799                        // for which the datastore contains the (key, value) to insert.
800                        *d = SetUpdateOrDelete::Set(LedgerEntry {
801                            datastore: vec![(key, data)].into_iter().collect(),
802                            ..Default::default()
803                        });
804                    }
805                }
806            }
807
808            // This ledger entry is not being changed
809            hash_map::Entry::Vacant(vac) => {
810                // Induce an Update to the ledger entry that sets the datastore entry to the desired value
811                vac.insert(SetUpdateOrDelete::Update(LedgerEntryUpdate {
812                    datastore: vec![(key, SetOrDelete::Set(data))].into_iter().collect(),
813                    ..Default::default()
814                }));
815            }
816        }
817    }
818
819    /// Deletes a datastore entry for a given address.
820    /// Does nothing if the entry is missing.
821    ///
822    /// # Arguments
823    /// * `addr`: target address
824    /// * `key`: datastore key
825    pub fn delete_data_entry(&mut self, addr: Address, key: Vec<u8>) {
826        // Get the changes being applied to the ledger entry associated to that address
827        match self.0.entry(addr) {
828            // There are changes currently being applied to the ledger entry
829            hash_map::Entry::Occupied(mut occ) => {
830                match occ.get_mut() {
831                    // The ledger entry is being replaced by a new one
832                    SetUpdateOrDelete::Set(v) => {
833                        // Delete the entry in the datastore of the replacement entry
834                        v.datastore.remove(&key);
835                    }
836
837                    // The ledger entry is being updated
838                    SetUpdateOrDelete::Update(u) => {
839                        // Ensure that the update includes deleting the datastore entry
840                        u.datastore.insert(key, SetOrDelete::Delete);
841                    }
842
843                    // The ledger entry is being deleted
844                    SetUpdateOrDelete::Delete => {
845                        // Do nothing because the whole ledger entry is being deleted
846                    }
847                }
848            }
849
850            // This ledger entry is not being changed
851            hash_map::Entry::Vacant(vac) => {
852                // Induce an Update to the ledger entry that deletes the datastore entry
853                vac.insert(SetUpdateOrDelete::Update(LedgerEntryUpdate {
854                    datastore: vec![(key, SetOrDelete::Delete)].into_iter().collect(),
855                    ..Default::default()
856                }));
857            }
858        }
859    }
860}