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}