cnidarium/
cache.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
use std::{any::Any, collections::BTreeMap, sync::Arc};

use tendermint::abci;

use crate::{
    store::{multistore::MultistoreConfig, substore::SubstoreConfig},
    StateWrite,
};

/// A cache of changes to the state of the blockchain.
///
/// A [`StateDelta`](crate::StateDelta) is `Cache` above a `StateRead`.
#[derive(Default, Debug)]
pub struct Cache {
    /// Unwritten changes to the consensus-critical state (stored in the JMT).
    pub(crate) unwritten_changes: BTreeMap<String, Option<Vec<u8>>>,
    /// Unwritten changes to non-consensus-critical state (stored in the nonverifiable storage).
    pub(crate) nonverifiable_changes: BTreeMap<Vec<u8>, Option<Vec<u8>>>,
    /// Unwritten changes to the object store.  A `None` value means a deletion.
    pub(crate) ephemeral_objects: BTreeMap<&'static str, Option<Box<dyn Any + Send + Sync>>>,
    /// A list of ABCI events that occurred while building this set of state changes.
    pub(crate) events: Vec<abci::Event>,
}

impl Cache {
    /// Inspect the cache of unwritten changes to the verifiable state.
    pub fn unwritten_changes(&self) -> &BTreeMap<String, Option<Vec<u8>>> {
        &self.unwritten_changes
    }

    /// Inspect the cache of unwritten changes to the nonverifiable state.
    pub fn nonverifiable_changes(&self) -> &BTreeMap<Vec<u8>, Option<Vec<u8>>> {
        &self.nonverifiable_changes
    }

    /// Merge the given cache with this one, taking its writes in place of ours.
    pub fn merge(&mut self, other: Cache) {
        // One might ask, why does this exist separately from `apply_to`?  The
        // answer is that `apply_to` takes a `StateWrite`, so we'd have to have
        // `Cache: StateWrite`, and that implies `Cache: StateRead`, but the
        // `StateRead` trait assumes asynchronous access, and in any case, we
        // probably don't want to be reading directly from a `Cache` (?)
        self.unwritten_changes.extend(other.unwritten_changes);
        self.nonverifiable_changes
            .extend(other.nonverifiable_changes);
        self.ephemeral_objects.extend(other.ephemeral_objects);
        self.events.extend(other.events);
    }

    /// Consume this cache, applying its writes to the given state.
    pub fn apply_to<S: StateWrite>(self, mut state: S) {
        for (key, value) in self.unwritten_changes {
            if let Some(value) = value {
                state.put_raw(key, value);
            } else {
                state.delete(key);
            }
        }

        for (key, value) in self.nonverifiable_changes {
            if let Some(value) = value {
                state.nonverifiable_put_raw(key, value);
            } else {
                state.nonverifiable_delete(key);
            }
        }

        // It's important to use object_merge here, so that we don't re-box all
        // of the objects, causing downcasting to fail.
        state.object_merge(self.ephemeral_objects);

        for event in self.events {
            state.record(event);
        }
    }

    /// Returns `true` if there are cached writes on top of the snapshot, and `false` otherwise.
    pub fn is_dirty(&self) -> bool {
        !(self.unwritten_changes.is_empty()
            && self.nonverifiable_changes.is_empty()
            && self.ephemeral_objects.is_empty())
    }

    /// Extracts and returns the ABCI events contained in this cache.
    pub fn take_events(&mut self) -> Vec<abci::Event> {
        std::mem::take(&mut self.events)
    }

    /// Consumes a `Cache` and returns a map of `SubstoreConfig` to `Cache` that
    /// corresponds to changes belonging to each substore. The keys in each `Cache`
    /// are truncated to remove the substore prefix.
    pub fn shard_by_prefix(
        self,
        prefixes: &MultistoreConfig,
    ) -> BTreeMap<Arc<SubstoreConfig>, Self> {
        let mut changes_by_substore = BTreeMap::new();
        for (key, some_value) in self.unwritten_changes.into_iter() {
            let (truncated_key, substore_config) = prefixes.route_key_str(&key);
            changes_by_substore
                .entry(substore_config)
                .or_insert_with(Cache::default)
                .unwritten_changes
                .insert(truncated_key.to_string(), some_value);
        }

        for (key, some_value) in self.nonverifiable_changes {
            let (truncated_key, substore_config) = prefixes.route_key_bytes(&key);
            changes_by_substore
                .entry(substore_config)
                .or_insert_with(Cache::default)
                .nonverifiable_changes
                .insert(truncated_key.to_vec(), some_value);
        }
        changes_by_substore
    }

    pub(crate) fn clone_changes(&self) -> Self {
        Self {
            unwritten_changes: self.unwritten_changes.clone(),
            nonverifiable_changes: self.nonverifiable_changes.clone(),
            ephemeral_objects: Default::default(),
            events: Default::default(),
        }
    }
}