simulacrum/store/
in_mem_store.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use std::collections::{BTreeMap, HashMap};
6
7use iota_config::genesis;
8use iota_types::{
9    base_types::{AuthorityName, IotaAddress, ObjectID, SequenceNumber},
10    committee::{Committee, EpochId},
11    crypto::{AccountKeyPair, AuthorityKeyPair},
12    digests::{ObjectDigest, TransactionDigest, TransactionEventsDigest},
13    effects::{TransactionEffects, TransactionEffectsAPI, TransactionEvents},
14    error::IotaError,
15    messages_checkpoint::{
16        CheckpointContents, CheckpointContentsDigest, CheckpointDigest, CheckpointSequenceNumber,
17        VerifiedCheckpoint,
18    },
19    object::{Object, Owner},
20    storage::{
21        BackingPackageStore, ChildObjectResolver, ObjectStore, PackageObject, get_module,
22        load_package_object_from_object_store,
23    },
24    transaction::VerifiedTransaction,
25};
26use move_binary_format::CompiledModule;
27use move_bytecode_utils::module_cache::GetModule;
28use move_core_types::{language_storage::ModuleId, resolver::ModuleResolver};
29
30use super::SimulatorStore;
31
32#[derive(Debug, Default)]
33pub struct InMemoryStore {
34    // Checkpoint data
35    checkpoints: BTreeMap<CheckpointSequenceNumber, VerifiedCheckpoint>,
36    checkpoint_digest_to_sequence_number: HashMap<CheckpointDigest, CheckpointSequenceNumber>,
37    checkpoint_contents: HashMap<CheckpointContentsDigest, CheckpointContents>,
38
39    // Transaction data
40    transactions: HashMap<TransactionDigest, VerifiedTransaction>,
41    effects: HashMap<TransactionDigest, TransactionEffects>,
42    events: HashMap<TransactionEventsDigest, TransactionEvents>,
43    // Map from transaction digest to events digest for easy lookup
44    events_tx_digest_index: HashMap<TransactionDigest, TransactionEventsDigest>,
45
46    // Committee data
47    epoch_to_committee: Vec<Committee>,
48
49    // Object data
50    live_objects: HashMap<ObjectID, SequenceNumber>,
51    objects: HashMap<ObjectID, BTreeMap<SequenceNumber, Object>>,
52}
53
54impl InMemoryStore {
55    pub fn new(genesis: &genesis::Genesis) -> Self {
56        let mut store = Self::default();
57        store.init_with_genesis(genesis);
58        store
59    }
60
61    pub fn get_checkpoint_by_sequence_number(
62        &self,
63        sequence_number: CheckpointSequenceNumber,
64    ) -> Option<&VerifiedCheckpoint> {
65        self.checkpoints.get(&sequence_number)
66    }
67
68    pub fn get_checkpoint_by_digest(
69        &self,
70        digest: &CheckpointDigest,
71    ) -> Option<&VerifiedCheckpoint> {
72        self.checkpoint_digest_to_sequence_number
73            .get(digest)
74            .and_then(|sequence_number| self.get_checkpoint_by_sequence_number(*sequence_number))
75    }
76
77    pub fn get_highest_checkpoint(&self) -> Option<&VerifiedCheckpoint> {
78        self.checkpoints
79            .last_key_value()
80            .map(|(_, checkpoint)| checkpoint)
81    }
82
83    pub fn get_checkpoint_contents(
84        &self,
85        digest: &CheckpointContentsDigest,
86    ) -> Option<&CheckpointContents> {
87        self.checkpoint_contents.get(digest)
88    }
89
90    pub fn get_committee_by_epoch(&self, epoch: EpochId) -> Option<&Committee> {
91        self.epoch_to_committee.get(epoch as usize)
92    }
93    pub fn get_transaction(&self, digest: &TransactionDigest) -> Option<&VerifiedTransaction> {
94        self.transactions.get(digest)
95    }
96
97    pub fn get_transaction_effects(
98        &self,
99        digest: &TransactionDigest,
100    ) -> Option<&TransactionEffects> {
101        self.effects.get(digest)
102    }
103
104    pub fn get_transaction_events(
105        &self,
106        digest: &TransactionEventsDigest,
107    ) -> Option<&TransactionEvents> {
108        self.events.get(digest)
109    }
110
111    pub fn get_object(&self, id: &ObjectID) -> Option<&Object> {
112        let version = self.live_objects.get(id)?;
113        self.get_object_at_version(id, *version)
114    }
115
116    pub fn get_object_at_version(&self, id: &ObjectID, version: SequenceNumber) -> Option<&Object> {
117        self.objects
118            .get(id)
119            .and_then(|versions| versions.get(&version))
120    }
121
122    pub fn get_system_state(&self) -> iota_types::iota_system_state::IotaSystemState {
123        iota_types::iota_system_state::get_iota_system_state(self).expect("system state must exist")
124    }
125
126    pub fn get_clock(&self) -> iota_types::clock::Clock {
127        self.get_object(&iota_types::IOTA_CLOCK_OBJECT_ID)
128            .expect("clock should exist")
129            .to_rust()
130            .expect("clock object should deserialize")
131    }
132
133    pub fn owned_objects(&self, owner: IotaAddress) -> impl Iterator<Item = &Object> {
134        self.live_objects
135            .iter()
136            .flat_map(|(id, version)| self.get_object_at_version(id, *version))
137            .filter(
138                move |object| matches!(object.owner, Owner::AddressOwner(addr) if addr == owner),
139            )
140    }
141}
142
143impl InMemoryStore {
144    pub fn insert_checkpoint(&mut self, checkpoint: VerifiedCheckpoint) {
145        if let Some(end_of_epoch_data) = &checkpoint.data().end_of_epoch_data {
146            let next_committee = end_of_epoch_data
147                .next_epoch_committee
148                .iter()
149                .cloned()
150                .collect();
151            let committee =
152                Committee::new(checkpoint.epoch().checked_add(1).unwrap(), next_committee);
153            self.insert_committee(committee);
154        }
155
156        self.checkpoint_digest_to_sequence_number
157            .insert(*checkpoint.digest(), *checkpoint.sequence_number());
158        self.checkpoints
159            .insert(*checkpoint.sequence_number(), checkpoint);
160    }
161
162    pub fn insert_checkpoint_contents(&mut self, contents: CheckpointContents) {
163        self.checkpoint_contents
164            .insert(*contents.digest(), contents);
165    }
166
167    pub fn insert_committee(&mut self, committee: Committee) {
168        let epoch = committee.epoch as usize;
169
170        if self.epoch_to_committee.get(epoch).is_some() {
171            return;
172        }
173
174        if self.epoch_to_committee.len() == epoch {
175            self.epoch_to_committee.push(committee);
176        } else {
177            panic!("committee was inserted into EpochCommitteeMap out of order");
178        }
179    }
180
181    pub fn insert_executed_transaction(
182        &mut self,
183        transaction: VerifiedTransaction,
184        effects: TransactionEffects,
185        events: TransactionEvents,
186        written_objects: BTreeMap<ObjectID, Object>,
187    ) {
188        let deleted_objects = effects.deleted();
189        let tx_digest = *effects.transaction_digest();
190        self.insert_transaction(transaction);
191        self.insert_transaction_effects(effects);
192        self.insert_events(&tx_digest, events);
193        self.update_objects(written_objects, deleted_objects);
194    }
195
196    pub fn insert_transaction(&mut self, transaction: VerifiedTransaction) {
197        self.transactions.insert(*transaction.digest(), transaction);
198    }
199
200    pub fn insert_transaction_effects(&mut self, effects: TransactionEffects) {
201        self.effects.insert(*effects.transaction_digest(), effects);
202    }
203
204    pub fn insert_events(&mut self, tx_digest: &TransactionDigest, events: TransactionEvents) {
205        self.events_tx_digest_index
206            .insert(*tx_digest, events.digest());
207        self.events.insert(events.digest(), events);
208    }
209
210    pub fn update_objects(
211        &mut self,
212        written_objects: BTreeMap<ObjectID, Object>,
213        deleted_objects: Vec<(ObjectID, SequenceNumber, ObjectDigest)>,
214    ) {
215        for (object_id, _, _) in deleted_objects {
216            self.live_objects.remove(&object_id);
217        }
218
219        for (object_id, object) in written_objects {
220            let version = object.version();
221            self.live_objects.insert(object_id, version);
222            self.objects
223                .entry(object_id)
224                .or_default()
225                .insert(version, object);
226        }
227    }
228}
229
230impl BackingPackageStore for InMemoryStore {
231    fn get_package_object(
232        &self,
233        package_id: &ObjectID,
234    ) -> iota_types::error::IotaResult<Option<PackageObject>> {
235        load_package_object_from_object_store(self, package_id)
236    }
237}
238
239impl ChildObjectResolver for InMemoryStore {
240    fn read_child_object(
241        &self,
242        parent: &ObjectID,
243        child: &ObjectID,
244        child_version_upper_bound: SequenceNumber,
245    ) -> iota_types::error::IotaResult<Option<Object>> {
246        let child_object = match crate::store::SimulatorStore::get_object(self, child) {
247            None => return Ok(None),
248            Some(obj) => obj,
249        };
250
251        let parent = *parent;
252        if child_object.owner != Owner::ObjectOwner(parent.into()) {
253            return Err(IotaError::InvalidChildObjectAccess {
254                object: *child,
255                given_parent: parent,
256                actual_owner: child_object.owner,
257            });
258        }
259
260        if child_object.version() > child_version_upper_bound {
261            return Err(IotaError::UnsupportedFeature {
262                error: "TODO InMemoryStorage::read_child_object does not yet support bounded reads"
263                    .to_owned(),
264            });
265        }
266
267        Ok(Some(child_object))
268    }
269
270    fn get_object_received_at_version(
271        &self,
272        owner: &ObjectID,
273        receiving_object_id: &ObjectID,
274        receive_object_at_version: SequenceNumber,
275        _epoch_id: EpochId,
276    ) -> iota_types::error::IotaResult<Option<Object>> {
277        let recv_object = match crate::store::SimulatorStore::get_object(self, receiving_object_id)
278        {
279            None => return Ok(None),
280            Some(obj) => obj,
281        };
282        if recv_object.owner != Owner::AddressOwner((*owner).into()) {
283            return Ok(None);
284        }
285
286        if recv_object.version() != receive_object_at_version {
287            return Ok(None);
288        }
289        Ok(Some(recv_object))
290    }
291}
292
293impl GetModule for InMemoryStore {
294    type Error = IotaError;
295    type Item = CompiledModule;
296
297    fn get_module_by_id(&self, id: &ModuleId) -> Result<Option<Self::Item>, Self::Error> {
298        Ok(self
299            .get_module(id)?
300            .map(|bytes| CompiledModule::deserialize_with_defaults(&bytes).unwrap()))
301    }
302}
303
304impl ModuleResolver for InMemoryStore {
305    type Error = IotaError;
306
307    fn get_module(&self, module_id: &ModuleId) -> Result<Option<Vec<u8>>, Self::Error> {
308        get_module(self, module_id)
309    }
310}
311
312impl ObjectStore for InMemoryStore {
313    fn get_object(
314        &self,
315        object_id: &ObjectID,
316    ) -> Result<Option<Object>, iota_types::storage::error::Error> {
317        Ok(self.get_object(object_id).cloned())
318    }
319
320    fn get_object_by_key(
321        &self,
322        object_id: &ObjectID,
323        version: iota_types::base_types::VersionNumber,
324    ) -> Result<Option<Object>, iota_types::storage::error::Error> {
325        Ok(self.get_object_at_version(object_id, version).cloned())
326    }
327}
328
329#[derive(Debug)]
330pub struct KeyStore {
331    validator_keys: BTreeMap<AuthorityName, AuthorityKeyPair>,
332    account_keys: BTreeMap<IotaAddress, AccountKeyPair>,
333}
334
335impl KeyStore {
336    pub fn from_network_config(
337        network_config: &iota_swarm_config::network_config::NetworkConfig,
338    ) -> Self {
339        use fastcrypto::traits::KeyPair;
340
341        let validator_keys = network_config
342            .validator_configs()
343            .iter()
344            .map(|config| {
345                (
346                    config.authority_public_key(),
347                    config.authority_key_pair().copy(),
348                )
349            })
350            .collect();
351
352        let account_keys = network_config
353            .account_keys
354            .iter()
355            .map(|key| (key.public().into(), key.copy()))
356            .collect();
357        Self {
358            validator_keys,
359            account_keys,
360        }
361    }
362
363    pub fn validator(&self, name: &AuthorityName) -> Option<&AuthorityKeyPair> {
364        self.validator_keys.get(name)
365    }
366
367    pub fn accounts(&self) -> impl Iterator<Item = (&IotaAddress, &AccountKeyPair)> {
368        self.account_keys.iter()
369    }
370}
371
372impl SimulatorStore for InMemoryStore {
373    fn get_checkpoint_by_sequence_number(
374        &self,
375        sequence_number: CheckpointSequenceNumber,
376    ) -> Option<VerifiedCheckpoint> {
377        self.get_checkpoint_by_sequence_number(sequence_number)
378            .cloned()
379    }
380
381    fn get_checkpoint_by_digest(&self, digest: &CheckpointDigest) -> Option<VerifiedCheckpoint> {
382        self.get_checkpoint_by_digest(digest).cloned()
383    }
384
385    fn get_highest_checkpoint(&self) -> Option<VerifiedCheckpoint> {
386        self.get_highest_checkpoint().cloned()
387    }
388
389    fn get_checkpoint_contents(
390        &self,
391        digest: &CheckpointContentsDigest,
392    ) -> Option<CheckpointContents> {
393        self.get_checkpoint_contents(digest).cloned()
394    }
395
396    fn get_committee_by_epoch(&self, epoch: EpochId) -> Option<Committee> {
397        self.get_committee_by_epoch(epoch).cloned()
398    }
399
400    fn get_transaction(&self, digest: &TransactionDigest) -> Option<VerifiedTransaction> {
401        self.get_transaction(digest).cloned()
402    }
403
404    fn get_transaction_effects(&self, digest: &TransactionDigest) -> Option<TransactionEffects> {
405        self.get_transaction_effects(digest).cloned()
406    }
407
408    fn get_transaction_events(
409        &self,
410        digest: &TransactionEventsDigest,
411    ) -> Option<TransactionEvents> {
412        self.get_transaction_events(digest).cloned()
413    }
414
415    fn get_transaction_events_by_tx_digest(
416        &self,
417        tx_digest: &TransactionDigest,
418    ) -> Option<TransactionEvents> {
419        self.events_tx_digest_index
420            .get(tx_digest)
421            .and_then(|x| self.events.get(x))
422            .cloned()
423    }
424
425    fn get_object(&self, id: &ObjectID) -> Option<Object> {
426        self.get_object(id).cloned()
427    }
428
429    fn get_object_at_version(&self, id: &ObjectID, version: SequenceNumber) -> Option<Object> {
430        self.get_object_at_version(id, version).cloned()
431    }
432
433    fn get_system_state(&self) -> iota_types::iota_system_state::IotaSystemState {
434        self.get_system_state()
435    }
436
437    fn get_clock(&self) -> iota_types::clock::Clock {
438        self.get_clock()
439    }
440
441    fn owned_objects(&self, owner: IotaAddress) -> Box<dyn Iterator<Item = Object> + '_> {
442        Box::new(self.owned_objects(owner).cloned())
443    }
444
445    fn insert_checkpoint(&mut self, checkpoint: VerifiedCheckpoint) {
446        self.insert_checkpoint(checkpoint)
447    }
448
449    fn insert_checkpoint_contents(&mut self, contents: CheckpointContents) {
450        self.insert_checkpoint_contents(contents)
451    }
452
453    fn insert_committee(&mut self, committee: Committee) {
454        self.insert_committee(committee)
455    }
456
457    fn insert_executed_transaction(
458        &mut self,
459        transaction: VerifiedTransaction,
460        effects: TransactionEffects,
461        events: TransactionEvents,
462        written_objects: BTreeMap<ObjectID, Object>,
463    ) {
464        self.insert_executed_transaction(transaction, effects, events, written_objects)
465    }
466
467    fn insert_transaction(&mut self, transaction: VerifiedTransaction) {
468        self.insert_transaction(transaction)
469    }
470
471    fn insert_transaction_effects(&mut self, effects: TransactionEffects) {
472        self.insert_transaction_effects(effects)
473    }
474
475    fn insert_events(&mut self, tx_digest: &TransactionDigest, events: TransactionEvents) {
476        self.insert_events(tx_digest, events)
477    }
478
479    fn update_objects(
480        &mut self,
481        written_objects: BTreeMap<ObjectID, Object>,
482        deleted_objects: Vec<(ObjectID, SequenceNumber, ObjectDigest)>,
483    ) {
484        self.update_objects(written_objects, deleted_objects)
485    }
486
487    fn backing_store(&self) -> &dyn iota_types::storage::BackingStore {
488        self
489    }
490}