iota_adapter_latest/
temporary_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, BTreeSet, HashSet};
6
7use iota_metrics::monitored_scope;
8use iota_protocol_config::ProtocolConfig;
9use iota_types::{
10    IOTA_DENY_LIST_OBJECT_ID, IOTA_SYSTEM_STATE_OBJECT_ID,
11    base_types::{
12        IotaAddress, ObjectID, ObjectRef, SequenceNumber, TransactionDigest, VersionDigest,
13    },
14    committee::EpochId,
15    deny_list_v1::check_coin_deny_list_v1_during_execution,
16    effects::{EffectsObjectChange, TransactionEffects, TransactionEvents},
17    error::{ExecutionError, IotaError, IotaResult},
18    execution::{
19        DynamicallyLoadedObjectMetadata, ExecutionResults, ExecutionResultsV1, SharedInput,
20    },
21    execution_config_utils::to_binary_config,
22    execution_status::ExecutionStatus,
23    fp_bail,
24    gas::GasCostSummary,
25    inner_temporary_store::InnerTemporaryStore,
26    iota_system_state::{AdvanceEpochParams, get_iota_system_state_wrapper},
27    is_system_package,
28    layout_resolver::LayoutResolver,
29    object::{Data, Object, Owner},
30    storage::{
31        BackingPackageStore, BackingStore, ChildObjectResolver, DenyListResult, PackageObject,
32        Storage,
33    },
34    transaction::InputObjects,
35};
36use move_core_types::{
37    account_address::AccountAddress, language_storage::StructTag, resolver::ResourceResolver,
38};
39use parking_lot::RwLock;
40
41use crate::gas_charger::GasCharger;
42
43pub struct TemporaryStore<'backing> {
44    // The backing store for retrieving Move packages onchain.
45    // When executing a Move call, the dependent packages are not going to be
46    // in the input objects. They will be fetched from the backing store.
47    // Also used for fetching the backing parent_sync to get the last known version for wrapped
48    // objects
49    store: &'backing dyn BackingStore,
50    tx_digest: TransactionDigest,
51    input_objects: BTreeMap<ObjectID, Object>,
52    /// The version to assign to all objects written by the transaction using
53    /// this store.
54    lamport_timestamp: SequenceNumber,
55    mutable_input_refs: BTreeMap<ObjectID, (VersionDigest, Owner)>, // Inputs that are mutable
56    execution_results: ExecutionResultsV1,
57    /// Objects that were loaded during execution (dynamic fields + received
58    /// objects).
59    loaded_runtime_objects: BTreeMap<ObjectID, DynamicallyLoadedObjectMetadata>,
60    /// A map from wrapped object to its container. Used during expensive
61    /// invariant checks.
62    wrapped_object_containers: BTreeMap<ObjectID, ObjectID>,
63    protocol_config: &'backing ProtocolConfig,
64
65    /// Every package that was loaded from DB store during execution.
66    /// These packages were not previously loaded into the temporary store.
67    runtime_packages_loaded_from_db: RwLock<BTreeMap<ObjectID, PackageObject>>,
68
69    /// The set of objects that we may receive during execution. Not guaranteed
70    /// to receive all, or any of the objects referenced in this set.
71    receiving_objects: Vec<ObjectRef>,
72
73    // TODO: Now that we track epoch here, there are a few places we don't need to pass it around.
74    /// The current epoch.
75    cur_epoch: EpochId,
76
77    /// The set of per-epoch config objects that were loaded during execution,
78    /// and are not in the input objects. This allows us to commit them to
79    /// the effects.
80    loaded_per_epoch_config_objects: RwLock<BTreeSet<ObjectID>>,
81}
82
83impl<'backing> TemporaryStore<'backing> {
84    /// Creates a new store associated with an authority store, and populates it
85    /// with initial objects.
86    pub fn new(
87        store: &'backing dyn BackingStore,
88        input_objects: InputObjects,
89        receiving_objects: Vec<ObjectRef>,
90        tx_digest: TransactionDigest,
91        protocol_config: &'backing ProtocolConfig,
92        cur_epoch: EpochId,
93    ) -> Self {
94        let mutable_input_refs = input_objects.mutable_inputs();
95        let lamport_timestamp = input_objects.lamport_timestamp(&receiving_objects);
96        let objects = input_objects.into_object_map();
97        #[cfg(debug_assertions)]
98        {
99            // Ensure that input objects and receiving objects must not overlap.
100            assert!(
101                objects
102                    .keys()
103                    .collect::<HashSet<_>>()
104                    .intersection(
105                        &receiving_objects
106                            .iter()
107                            .map(|oref| &oref.0)
108                            .collect::<HashSet<_>>()
109                    )
110                    .next()
111                    .is_none()
112            );
113        }
114        Self {
115            store,
116            tx_digest,
117            input_objects: objects,
118            lamport_timestamp,
119            mutable_input_refs,
120            execution_results: ExecutionResultsV1::default(),
121            protocol_config,
122            loaded_runtime_objects: BTreeMap::new(),
123            wrapped_object_containers: BTreeMap::new(),
124            runtime_packages_loaded_from_db: RwLock::new(BTreeMap::new()),
125            receiving_objects,
126            cur_epoch,
127            loaded_per_epoch_config_objects: RwLock::new(BTreeSet::new()),
128        }
129    }
130
131    // Helpers to access private fields
132    pub fn objects(&self) -> &BTreeMap<ObjectID, Object> {
133        &self.input_objects
134    }
135
136    pub fn update_object_version_and_prev_tx(&mut self) {
137        self.execution_results.update_version_and_previous_tx(
138            self.lamport_timestamp,
139            self.tx_digest,
140            &self.input_objects,
141        );
142
143        #[cfg(debug_assertions)]
144        {
145            self.check_invariants();
146        }
147    }
148
149    /// Break up the structure and return its internal stores (objects,
150    /// active_inputs, written, deleted)
151    pub fn into_inner(self) -> InnerTemporaryStore {
152        let results = self.execution_results;
153        InnerTemporaryStore {
154            input_objects: self.input_objects,
155            mutable_inputs: self.mutable_input_refs,
156            written: results.written_objects,
157            events: TransactionEvents {
158                data: results.user_events,
159            },
160            loaded_runtime_objects: self.loaded_runtime_objects,
161            runtime_packages_loaded_from_db: self.runtime_packages_loaded_from_db.into_inner(),
162            lamport_version: self.lamport_timestamp,
163            binary_config: to_binary_config(self.protocol_config),
164        }
165    }
166
167    /// For every object from active_inputs (i.e. all mutable objects), if they
168    /// are not mutated during the transaction execution, force mutating
169    /// them by incrementing the sequence number. This is required to
170    /// achieve safety.
171    pub(crate) fn ensure_active_inputs_mutated(&mut self) {
172        let mut to_be_updated = vec![];
173        for id in self.mutable_input_refs.keys() {
174            if !self.execution_results.modified_objects.contains(id) {
175                // We cannot update here but have to push to `to_be_updated` and update later
176                // because the for loop is holding a reference to `self`, and calling
177                // `self.mutate_input_object` requires a mutable reference to `self`.
178                to_be_updated.push(self.input_objects[id].clone());
179            }
180        }
181        for object in to_be_updated {
182            // The object must be mutated as it was present in the input objects
183            self.mutate_input_object(object.clone());
184        }
185    }
186
187    fn get_object_changes(&self) -> BTreeMap<ObjectID, EffectsObjectChange> {
188        let results = &self.execution_results;
189        let all_ids = results
190            .created_object_ids
191            .iter()
192            .chain(&results.deleted_object_ids)
193            .chain(&results.modified_objects)
194            .chain(results.written_objects.keys())
195            .collect::<BTreeSet<_>>();
196        all_ids
197            .into_iter()
198            .map(|id| {
199                (
200                    *id,
201                    EffectsObjectChange::new(
202                        self.get_object_modified_at(id)
203                            .map(|metadata| ((metadata.version, metadata.digest), metadata.owner)),
204                        results.written_objects.get(id),
205                        results.created_object_ids.contains(id),
206                        results.deleted_object_ids.contains(id),
207                    ),
208                )
209            })
210            .collect()
211    }
212
213    pub fn into_effects(
214        mut self,
215        shared_object_refs: Vec<SharedInput>,
216        transaction_digest: &TransactionDigest,
217        mut transaction_dependencies: BTreeSet<TransactionDigest>,
218        gas_cost_summary: GasCostSummary,
219        status: ExecutionStatus,
220        gas_charger: &mut GasCharger,
221        epoch: EpochId,
222    ) -> (InnerTemporaryStore, TransactionEffects) {
223        self.update_object_version_and_prev_tx();
224
225        // Regardless of execution status (including aborts), we insert the previous
226        // transaction for any successfully received objects during the
227        // transaction.
228        for (id, expected_version, expected_digest) in &self.receiving_objects {
229            // If the receiving object is in the loaded runtime objects, then that means
230            // that it was actually successfully loaded (so existed, and there
231            // was authenticated mutable access to it). So we insert the
232            // previous transaction as a dependency.
233            if let Some(obj_meta) = self.loaded_runtime_objects.get(id) {
234                // Check that the expected version, digest, and owner match the loaded version,
235                // digest, and owner. If they don't then don't register a dependency.
236                // This is because this could be "spoofed" by loading a dynamic object field.
237                let loaded_via_receive = obj_meta.version == *expected_version
238                    && obj_meta.digest == *expected_digest
239                    && obj_meta.owner.is_address_owned();
240                if loaded_via_receive {
241                    transaction_dependencies.insert(obj_meta.previous_transaction);
242                }
243            }
244        }
245
246        // In the case of special transactions that don't require a gas object,
247        // we don't really care about the effects to gas, just use the input for it.
248        // Gas coins are guaranteed to be at least size 1 and if more than 1
249        // the first coin is where all the others are merged.
250        let gas_coin = gas_charger.gas_coin();
251
252        let object_changes = self.get_object_changes();
253
254        let lamport_version = self.lamport_timestamp;
255        // TODO: Cleanup this clone. Potentially add unchanged_shraed_objects directly
256        // to InnerTempStore.
257        let loaded_per_epoch_config_objects = self.loaded_per_epoch_config_objects.read().clone();
258        let inner = self.into_inner();
259
260        let effects = TransactionEffects::new_from_execution_v1(
261            status,
262            epoch,
263            gas_cost_summary,
264            // TODO: Provide the list of read-only shared objects directly.
265            shared_object_refs,
266            loaded_per_epoch_config_objects,
267            *transaction_digest,
268            lamport_version,
269            object_changes,
270            gas_coin,
271            if inner.events.data.is_empty() {
272                None
273            } else {
274                Some(inner.events.digest())
275            },
276            transaction_dependencies.into_iter().collect(),
277        );
278
279        (inner, effects)
280    }
281
282    /// An internal check of the invariants (will only fire in debug)
283    #[cfg(debug_assertions)]
284    fn check_invariants(&self) {
285        // Check not both deleted and written
286        debug_assert!(
287            {
288                self.execution_results
289                    .written_objects
290                    .keys()
291                    .all(|id| !self.execution_results.deleted_object_ids.contains(id))
292            },
293            "Object both written and deleted."
294        );
295
296        // Check all mutable inputs are modified
297        debug_assert!(
298            {
299                self.mutable_input_refs
300                    .keys()
301                    .all(|id| self.execution_results.modified_objects.contains(id))
302            },
303            "Mutable input not modified."
304        );
305
306        debug_assert!(
307            {
308                self.execution_results
309                    .written_objects
310                    .values()
311                    .all(|obj| obj.previous_transaction == self.tx_digest)
312            },
313            "Object previous transaction not properly set",
314        );
315    }
316
317    /// Mutate a mutable input object. This is used to mutate input objects
318    /// outside of PT execution.
319    pub fn mutate_input_object(&mut self, object: Object) {
320        let id = object.id();
321        debug_assert!(self.input_objects.contains_key(&id));
322        debug_assert!(!object.is_immutable());
323        self.execution_results.modified_objects.insert(id);
324        self.execution_results.written_objects.insert(id, object);
325    }
326
327    /// Mutate a child object outside of PT. This should be used extremely
328    /// rarely. Currently it's only used by advance_epoch_safe_mode because
329    /// it's all native without PT. This should almost never be used
330    /// otherwise.
331    pub fn mutate_child_object(&mut self, old_object: Object, new_object: Object) {
332        let id = new_object.id();
333        let old_ref = old_object.compute_object_reference();
334        debug_assert_eq!(old_ref.0, id);
335        self.loaded_runtime_objects.insert(
336            id,
337            DynamicallyLoadedObjectMetadata {
338                version: old_ref.1,
339                digest: old_ref.2,
340                owner: old_object.owner,
341                storage_rebate: old_object.storage_rebate,
342                previous_transaction: old_object.previous_transaction,
343            },
344        );
345        self.execution_results.modified_objects.insert(id);
346        self.execution_results
347            .written_objects
348            .insert(id, new_object);
349    }
350
351    /// Upgrade system package during epoch change. This requires special
352    /// treatment since the system package to be upgraded is not in the
353    /// input objects. We could probably fix above to make it less special.
354    pub fn upgrade_system_package(&mut self, package: Object) {
355        let id = package.id();
356        assert!(package.is_package() && is_system_package(id));
357        self.execution_results.modified_objects.insert(id);
358        self.execution_results.written_objects.insert(id, package);
359    }
360
361    /// Crate a new objcet. This is used to create objects outside of PT
362    /// execution.
363    pub fn create_object(&mut self, object: Object) {
364        // Created mutable objects' versions are set to the store's lamport timestamp
365        // when it is committed to effects. Creating an object at a non-zero
366        // version risks violating the lamport timestamp invariant (that a
367        // transaction's lamport timestamp is strictly greater than all versions
368        // witnessed by the transaction).
369        debug_assert!(
370            object.is_immutable() || object.version() == SequenceNumber::MIN,
371            "Created mutable objects should not have a version set",
372        );
373        let id = object.id();
374        self.execution_results.created_object_ids.insert(id);
375        self.execution_results.written_objects.insert(id, object);
376    }
377
378    /// Delete a mutable input object. This is used to delete input objects
379    /// outside of PT execution.
380    pub fn delete_input_object(&mut self, id: &ObjectID) {
381        // there should be no deletion after write
382        debug_assert!(!self.execution_results.written_objects.contains_key(id));
383        debug_assert!(self.input_objects.contains_key(id));
384        self.execution_results.modified_objects.insert(*id);
385        self.execution_results.deleted_object_ids.insert(*id);
386    }
387
388    pub fn drop_writes(&mut self) {
389        self.execution_results.drop_writes();
390    }
391
392    pub fn read_object(&self, id: &ObjectID) -> Option<&Object> {
393        // there should be no read after delete
394        debug_assert!(!self.execution_results.deleted_object_ids.contains(id));
395        self.execution_results
396            .written_objects
397            .get(id)
398            .or_else(|| self.input_objects.get(id))
399    }
400
401    pub fn save_loaded_runtime_objects(
402        &mut self,
403        loaded_runtime_objects: BTreeMap<ObjectID, DynamicallyLoadedObjectMetadata>,
404    ) {
405        #[cfg(debug_assertions)]
406        {
407            for (id, v1) in &loaded_runtime_objects {
408                if let Some(v2) = self.loaded_runtime_objects.get(id) {
409                    assert_eq!(v1, v2);
410                }
411            }
412            for (id, v1) in &self.loaded_runtime_objects {
413                if let Some(v2) = loaded_runtime_objects.get(id) {
414                    assert_eq!(v1, v2);
415                }
416            }
417        }
418        // Merge the two maps because we may be calling the execution engine more than
419        // once (e.g. in advance epoch transaction, where we may be publishing a
420        // new system package).
421        self.loaded_runtime_objects.extend(loaded_runtime_objects);
422    }
423
424    pub fn save_wrapped_object_containers(
425        &mut self,
426        wrapped_object_containers: BTreeMap<ObjectID, ObjectID>,
427    ) {
428        #[cfg(debug_assertions)]
429        {
430            for (id, container1) in &wrapped_object_containers {
431                if let Some(container2) = self.wrapped_object_containers.get(id) {
432                    assert_eq!(container1, container2);
433                }
434            }
435            for (id, container1) in &self.wrapped_object_containers {
436                if let Some(container2) = wrapped_object_containers.get(id) {
437                    assert_eq!(container1, container2);
438                }
439            }
440        }
441        // Merge the two maps because we may be calling the execution engine more than
442        // once (e.g. in advance epoch transaction, where we may be publishing a
443        // new system package).
444        self.wrapped_object_containers
445            .extend(wrapped_object_containers);
446    }
447
448    pub fn estimate_effects_size_upperbound(&self) -> usize {
449        TransactionEffects::estimate_effects_size_upperbound_v1(
450            self.execution_results.written_objects.len(),
451            self.execution_results.modified_objects.len(),
452            self.input_objects.len(),
453        )
454    }
455
456    pub fn written_objects_size(&self) -> usize {
457        self.execution_results
458            .written_objects
459            .values()
460            .fold(0, |sum, obj| sum + obj.object_size_for_gas_metering())
461    }
462
463    /// If there are unmetered storage rebate (due to system transaction), we
464    /// put them into the storage rebate of 0x5 object.
465    /// TODO: This will not work for potential future new system transactions if
466    /// 0x5 is not in the input. We should fix this.
467    pub fn conserve_unmetered_storage_rebate(&mut self, unmetered_storage_rebate: u64) {
468        if unmetered_storage_rebate == 0 {
469            // If unmetered_storage_rebate is 0, we are most likely executing the genesis
470            // transaction. And in that case we cannot mutate the 0x5 object
471            // because it's newly created. And there is no storage rebate that
472            // needs distribution anyway.
473            return;
474        }
475        tracing::debug!(
476            "Amount of unmetered storage rebate from system tx: {:?}",
477            unmetered_storage_rebate
478        );
479        let mut system_state_wrapper = self
480            .read_object(&IOTA_SYSTEM_STATE_OBJECT_ID)
481            .expect("0x5 object must be mutated in system tx with unmetered storage rebate")
482            .clone();
483        // In unmetered execution, storage_rebate field of mutated object must be 0.
484        // If not, we would be dropping IOTA on the floor by overriding it.
485        assert_eq!(system_state_wrapper.storage_rebate, 0);
486        system_state_wrapper.storage_rebate = unmetered_storage_rebate;
487        self.mutate_input_object(system_state_wrapper);
488    }
489
490    /// Given an object ID, if it's not modified, returns None.
491    /// Otherwise returns its metadata, including version, digest, owner and
492    /// storage rebate. A modified object must be either a mutable input, or
493    /// a loaded child object. The only exception is when we upgrade system
494    /// packages, in which case the upgraded system packages are not part of
495    /// input, but are modified.
496    fn get_object_modified_at(
497        &self,
498        object_id: &ObjectID,
499    ) -> Option<DynamicallyLoadedObjectMetadata> {
500        if self.execution_results.modified_objects.contains(object_id) {
501            Some(
502                self.mutable_input_refs
503                    .get(object_id)
504                    .map(
505                        |((version, digest), owner)| DynamicallyLoadedObjectMetadata {
506                            version: *version,
507                            digest: *digest,
508                            owner: *owner,
509                            // It's guaranteed that a mutable input object is an input object.
510                            storage_rebate: self.input_objects[object_id].storage_rebate,
511                            previous_transaction: self.input_objects[object_id]
512                                .previous_transaction,
513                        },
514                    )
515                    .or_else(|| self.loaded_runtime_objects.get(object_id).cloned())
516                    .unwrap_or_else(|| {
517                        debug_assert!(is_system_package(*object_id));
518                        let package_obj =
519                            self.store.get_package_object(object_id).unwrap().unwrap();
520                        let obj = package_obj.object();
521                        DynamicallyLoadedObjectMetadata {
522                            version: obj.version(),
523                            digest: obj.digest(),
524                            owner: obj.owner,
525                            storage_rebate: obj.storage_rebate,
526                            previous_transaction: obj.previous_transaction,
527                        }
528                    }),
529            )
530        } else {
531            None
532        }
533    }
534}
535
536impl TemporaryStore<'_> {
537    // check that every object read is owned directly or indirectly by sender,
538    // sponsor, or a shared object input
539    pub fn check_ownership_invariants(
540        &self,
541        sender: &IotaAddress,
542        gas_charger: &mut GasCharger,
543        mutable_inputs: &HashSet<ObjectID>,
544        is_epoch_change: bool,
545    ) -> IotaResult<()> {
546        let gas_objs: HashSet<&ObjectID> = gas_charger.gas_coins().iter().map(|g| &g.0).collect();
547        // mark input objects as authenticated
548        let mut authenticated_for_mutation: HashSet<_> = self
549            .input_objects
550            .iter()
551            .filter_map(|(id, obj)| {
552                if gas_objs.contains(id) {
553                    // gas could be owned by either the sender (common case) or sponsor
554                    // (if this is a sponsored tx, which we do not know inside this function).
555                    // Either way, no object ownership chain should be rooted in a gas object
556                    // thus, consider object authenticated, but don't add it to authenticated_objs
557                    return None;
558                }
559                match &obj.owner {
560                    Owner::AddressOwner(a) => {
561                        assert!(sender == a, "Input object not owned by sender");
562                        Some(id)
563                    }
564                    Owner::Shared { .. } => Some(id),
565                    Owner::Immutable => {
566                        // object is authenticated, but it cannot own other objects,
567                        // so we should not add it to `authenticated_objs`
568                        // However, we would definitely want to add immutable objects
569                        // to the set of authenticated roots if we were doing runtime
570                        // checks inside the VM instead of after-the-fact in the temporary
571                        // store. Here, we choose not to add them because this will catch a
572                        // bug where we mutate or delete an object that belongs to an immutable
573                        // object (though it will show up somewhat opaquely as an authentication
574                        // failure), whereas adding the immutable object to the roots will prevent
575                        // us from catching this.
576                        None
577                    }
578                    Owner::ObjectOwner(_parent) => {
579                        unreachable!("Input objects must be address owned, shared, or immutable")
580                    }
581                }
582            })
583            .filter(|id| {
584                // remove any non-mutable inputs. This will remove deleted or readonly shared
585                // objects
586                mutable_inputs.contains(id)
587            })
588            .copied()
589            .collect();
590
591        // check all modified objects are authenticated (excluding gas objects)
592        let mut objects_to_authenticate = self
593            .execution_results
594            .modified_objects
595            .iter()
596            .filter(|id| !gas_objs.contains(id))
597            .copied()
598            .collect::<Vec<_>>();
599        // Map from an ObjectID to the ObjectID that covers it.
600        while let Some(to_authenticate) = objects_to_authenticate.pop() {
601            if authenticated_for_mutation.contains(&to_authenticate) {
602                // object has been authenticated
603                continue;
604            }
605            let wrapped_parent = self.wrapped_object_containers.get(&to_authenticate);
606            let parent = if let Some(container_id) = wrapped_parent {
607                // If the object is wrapped, then the container must be authenticated.
608                // For example, the ID is for a wrapped table or bag.
609                *container_id
610            } else {
611                let Some(old_obj) = self.store.get_object(&to_authenticate)? else {
612                    panic!(
613                        "
614                        Failed to load object {to_authenticate:?}. \n\
615                        If it cannot be loaded, \
616                        we would expect it to be in the wrapped object map: {:?}",
617                        &self.wrapped_object_containers
618                    )
619                };
620                match &old_obj.owner {
621                    Owner::ObjectOwner(parent) => ObjectID::from(*parent),
622                    Owner::AddressOwner(parent) => {
623                        // For Receiving<_> objects, the address owner is actually an object.
624                        // If it was actually an address, we should have caught it as an input and
625                        // it would already have been in authenticated_for_mutation
626                        ObjectID::from(*parent)
627                    }
628                    owner @ Owner::Shared { .. } => panic!(
629                        "Unauthenticated root at {to_authenticate:?} with owner {owner:?}\n\
630                        Potentially covering objects in: {authenticated_for_mutation:#?}",
631                    ),
632                    Owner::Immutable => {
633                        assert!(
634                            is_epoch_change,
635                            "Immutable objects cannot be written, except for \
636                            IOTA Framework/Move stdlib upgrades at epoch change boundaries"
637                        );
638                        // Note: this assumes that the only immutable objects an epoch change
639                        // tx can update are system packages,
640                        // but in principle we could allow others.
641                        assert!(
642                            is_system_package(to_authenticate),
643                            "Only system packages can be upgraded"
644                        );
645                        continue;
646                    }
647                }
648            };
649            // we now assume the object is authenticated and must check the parent
650            authenticated_for_mutation.insert(to_authenticate);
651            objects_to_authenticate.push(parent);
652        }
653        Ok(())
654    }
655}
656
657impl TemporaryStore<'_> {
658    /// Track storage gas for each mutable input object (including the gas coin)
659    /// and each created object. Compute storage refunds for each deleted
660    /// object. Will *not* charge anything, gas status keeps track of
661    /// storage cost and rebate. All objects will be updated with their new
662    /// (current) storage rebate/cost. `IotaGasStatus` `storage_rebate` and
663    /// `storage_gas_units` track the transaction overall storage rebate and
664    /// cost.
665    pub(crate) fn collect_storage_and_rebate(&mut self, gas_charger: &mut GasCharger) {
666        // Use two loops because we cannot mut iterate written while calling
667        // get_object_modified_at.
668        let old_storage_rebates: Vec<_> = self
669            .execution_results
670            .written_objects
671            .keys()
672            .map(|object_id| {
673                self.get_object_modified_at(object_id)
674                    .map(|metadata| metadata.storage_rebate)
675                    .unwrap_or_default()
676            })
677            .collect();
678        for (object, old_storage_rebate) in self
679            .execution_results
680            .written_objects
681            .values_mut()
682            .zip(old_storage_rebates)
683        {
684            // new object size
685            let new_object_size = object.object_size_for_gas_metering();
686            // track changes and compute the new object `storage_rebate`
687            let new_storage_rebate = gas_charger.track_storage_mutation(
688                object.id(),
689                new_object_size,
690                old_storage_rebate,
691            );
692            object.storage_rebate = new_storage_rebate;
693        }
694
695        self.collect_rebate(gas_charger);
696    }
697
698    pub(crate) fn collect_rebate(&self, gas_charger: &mut GasCharger) {
699        for object_id in &self.execution_results.modified_objects {
700            if self
701                .execution_results
702                .written_objects
703                .contains_key(object_id)
704            {
705                continue;
706            }
707            // get and track the deleted object `storage_rebate`
708            let storage_rebate = self
709                .get_object_modified_at(object_id)
710                // Unwrap is safe because this loop iterates through all modified objects.
711                .unwrap()
712                .storage_rebate;
713            gas_charger.track_storage_mutation(*object_id, 0, storage_rebate);
714        }
715    }
716
717    pub fn check_execution_results_consistency(&self) -> Result<(), ExecutionError> {
718        assert_invariant!(
719            self.execution_results
720                .created_object_ids
721                .iter()
722                .all(|id| !self.execution_results.deleted_object_ids.contains(id)
723                    && !self.execution_results.modified_objects.contains(id)),
724            "Created object IDs cannot also be deleted or modified"
725        );
726        assert_invariant!(
727            self.execution_results.modified_objects.iter().all(|id| {
728                self.mutable_input_refs.contains_key(id)
729                    || self.loaded_runtime_objects.contains_key(id)
730                    || is_system_package(*id)
731            }),
732            "A modified object must be either a mutable input, a loaded child object, or a system package"
733        );
734        Ok(())
735    }
736}
737//==============================================================================
738// Charge gas current - end
739//==============================================================================
740
741impl TemporaryStore<'_> {
742    pub fn advance_epoch_safe_mode(
743        &mut self,
744        params: &AdvanceEpochParams,
745        protocol_config: &ProtocolConfig,
746    ) {
747        let wrapper = get_iota_system_state_wrapper(self.store.as_object_store())
748            .expect("System state wrapper object must exist");
749        let (old_object, new_object) =
750            wrapper.advance_epoch_safe_mode(params, self.store.as_object_store(), protocol_config);
751        self.mutate_child_object(old_object, new_object);
752    }
753}
754
755type ModifiedObjectInfo<'a> = (
756    ObjectID,
757    // old object metadata, including version, digest, owner, and storage rebate.
758    Option<DynamicallyLoadedObjectMetadata>,
759    Option<&'a Object>,
760);
761
762impl TemporaryStore<'_> {
763    fn get_input_iota(
764        &self,
765        id: &ObjectID,
766        expected_version: SequenceNumber,
767        layout_resolver: &mut impl LayoutResolver,
768    ) -> Result<u64, ExecutionError> {
769        if let Some(obj) = self.input_objects.get(id) {
770            // the assumption here is that if it is in the input objects must be the right
771            // one
772            if obj.version() != expected_version {
773                invariant_violation!(
774                    "Version mismatching when resolving input object to check conservation--\
775                     expected {}, got {}",
776                    expected_version,
777                    obj.version(),
778                );
779            }
780            obj.get_total_iota(layout_resolver).map_err(|e| {
781                make_invariant_violation!(
782                    "Failed looking up input IOTA in IOTA conservation checking for input with \
783                         type {:?}: {e:#?}",
784                    obj.struct_tag(),
785                )
786            })
787        } else {
788            // not in input objects, must be a dynamic field
789            let Ok(Some(obj)) = self.store.get_object_by_key(id, expected_version) else {
790                invariant_violation!(
791                    "Failed looking up dynamic field {id} in IOTA conservation checking"
792                );
793            };
794            obj.get_total_iota(layout_resolver).map_err(|e| {
795                make_invariant_violation!(
796                    "Failed looking up input IOTA in IOTA conservation checking for type \
797                         {:?}: {e:#?}",
798                    obj.struct_tag(),
799                )
800            })
801        }
802    }
803
804    /// Return the list of all modified objects, for each object, returns
805    /// - Object ID,
806    /// - Input: If the object existed prior to this transaction, include their
807    ///   version and storage_rebate,
808    /// - Output: If a new version of the object is written, include the new
809    ///   object.
810    fn get_modified_objects(&self) -> Vec<ModifiedObjectInfo<'_>> {
811        self.execution_results
812            .modified_objects
813            .iter()
814            .map(|id| {
815                let metadata = self.get_object_modified_at(id);
816                let output = self.execution_results.written_objects.get(id);
817                (*id, metadata, output)
818            })
819            .chain(
820                self.execution_results
821                    .written_objects
822                    .iter()
823                    .filter_map(|(id, object)| {
824                        if self.execution_results.modified_objects.contains(id) {
825                            None
826                        } else {
827                            Some((*id, None, Some(object)))
828                        }
829                    }),
830            )
831            .collect()
832    }
833
834    /// Check that this transaction neither creates nor destroys IOTA. This
835    /// should hold for all txes except the epoch change tx, which mints
836    /// staking rewards equal to the gas fees burned in the previous epoch.
837    /// Specifically, this checks two key invariants about storage
838    /// fees and storage rebate:
839    ///
840    /// 1. all IOTA in storage rebate fields of input objects should flow either
841    ///    to the transaction storage rebate, or the transaction non-refundable
842    ///    storage rebate
843    /// 2. all IOTA charged for storage should flow into the storage rebate
844    ///    field of some output object
845    ///
846    /// This function is intended to be called *after* we have charged for
847    /// gas + applied the storage rebate to the gas object, but *before* we
848    /// have updated object versions.
849    pub fn check_iota_conserved(&self, gas_summary: &GasCostSummary) -> Result<(), ExecutionError> {
850        // total amount of IOTA in storage rebate of input objects
851        let mut total_input_rebate = 0;
852        // total amount of IOTA in storage rebate of output objects
853        let mut total_output_rebate = 0;
854        for (_, input, output) in self.get_modified_objects() {
855            if let Some(input) = input {
856                total_input_rebate += input.storage_rebate;
857            }
858            if let Some(object) = output {
859                total_output_rebate += object.storage_rebate;
860            }
861        }
862
863        if gas_summary.storage_cost == 0 {
864            // this condition is usually true when the transaction went OOG and no
865            // gas is left for storage charges.
866            // The storage cost has to be there at least for the gas coin which
867            // will not be deleted even when going to 0.
868            // However if the storage cost is 0 and if there is any object touched
869            // or deleted the value in input must be equal to the output plus rebate and
870            // non refundable.
871            // Rebate and non refundable will be positive when there are object deleted
872            // (gas smashing being the primary and possibly only example).
873            // A more typical condition is for all storage charges in summary to be 0 and
874            // then input and output must be the same value
875            if total_input_rebate
876                != total_output_rebate
877                    + gas_summary.storage_rebate
878                    + gas_summary.non_refundable_storage_fee
879            {
880                return Err(ExecutionError::invariant_violation(format!(
881                    "IOTA conservation failed -- no storage charges in gas summary \
882                        and total storage input rebate {} not equal  \
883                        to total storage output rebate {}",
884                    total_input_rebate, total_output_rebate,
885                )));
886            }
887        } else {
888            // all IOTA in storage rebate fields of input objects should flow either to
889            // the transaction storage rebate, or the non-refundable storage rebate pool
890            if total_input_rebate
891                != gas_summary.storage_rebate + gas_summary.non_refundable_storage_fee
892            {
893                return Err(ExecutionError::invariant_violation(format!(
894                    "IOTA conservation failed -- {} IOTA in storage rebate field of input objects, \
895                        {} IOTA in tx storage rebate or tx non-refundable storage rebate",
896                    total_input_rebate, gas_summary.non_refundable_storage_fee,
897                )));
898            }
899
900            // all IOTA charged for storage should flow into the storage rebate field
901            // of some output object
902            if gas_summary.storage_cost != total_output_rebate {
903                return Err(ExecutionError::invariant_violation(format!(
904                    "IOTA conservation failed -- {} IOTA charged for storage, \
905                        {} IOTA in storage rebate field of output objects",
906                    gas_summary.storage_cost, total_output_rebate
907                )));
908            }
909        }
910        Ok(())
911    }
912
913    /// Check that this transaction neither creates nor destroys IOTA.
914    /// This more expensive check will check a third invariant on top of the 2
915    /// performed by `check_iota_conserved` above:
916    ///
917    /// * all IOTA in input objects (including coins etc in the Move part of an
918    ///   object) should flow either to an output object, or be burned as part
919    ///   of computation fees or non-refundable storage rebate
920    ///
921    /// This function is intended to be called *after* we have charged for gas +
922    /// applied the storage rebate to the gas object, but *before* we have
923    /// updated object versions. The advance epoch transaction would mint
924    /// `epoch_fees` amount of IOTA, and burn `epoch_rebates` amount of IOTA.
925    /// We need these information for this check.
926    pub fn check_iota_conserved_expensive(
927        &self,
928        gas_summary: &GasCostSummary,
929        advance_epoch_gas_summary: Option<(u64, u64)>,
930        layout_resolver: &mut impl LayoutResolver,
931    ) -> Result<(), ExecutionError> {
932        // total amount of IOTA in input objects, including both coins and storage
933        // rebates
934        let mut total_input_iota = 0;
935        // total amount of IOTA in output objects, including both coins and storage
936        // rebates
937        let mut total_output_iota = 0;
938        for (id, input, output) in self.get_modified_objects() {
939            if let Some(input) = input {
940                total_input_iota += self.get_input_iota(&id, input.version, layout_resolver)?;
941            }
942            if let Some(object) = output {
943                total_output_iota += object.get_total_iota(layout_resolver).map_err(|e| {
944                    make_invariant_violation!(
945                        "Failed looking up output IOTA in IOTA conservation checking for \
946                         mutated type {:?}: {e:#?}",
947                        object.struct_tag(),
948                    )
949                })?;
950            }
951        }
952        // note: storage_cost flows into the storage_rebate field of the output objects,
953        // which is why it is not accounted for here.
954        // similarly, all of the storage_rebate *except* the storage_fund_rebate_inflow
955        // gets credited to the gas coin both computation costs and storage rebate
956        // inflow are
957        total_output_iota += gas_summary.computation_cost + gas_summary.non_refundable_storage_fee;
958        if let Some((epoch_fees, epoch_rebates)) = advance_epoch_gas_summary {
959            total_input_iota += epoch_fees;
960            total_output_iota += epoch_rebates;
961        }
962        if total_input_iota != total_output_iota {
963            return Err(ExecutionError::invariant_violation(format!(
964                "IOTA conservation failed: input={}, output={}, \
965                    this transaction either mints or burns IOTA",
966                total_input_iota, total_output_iota,
967            )));
968        }
969        Ok(())
970    }
971}
972
973impl ChildObjectResolver for TemporaryStore<'_> {
974    fn read_child_object(
975        &self,
976        parent: &ObjectID,
977        child: &ObjectID,
978        child_version_upper_bound: SequenceNumber,
979    ) -> IotaResult<Option<Object>> {
980        let obj_opt = self.execution_results.written_objects.get(child);
981        if obj_opt.is_some() {
982            Ok(obj_opt.cloned())
983        } else {
984            let _scope = monitored_scope("Execution::read_child_object");
985            self.store
986                .read_child_object(parent, child, child_version_upper_bound)
987        }
988    }
989
990    fn get_object_received_at_version(
991        &self,
992        owner: &ObjectID,
993        receiving_object_id: &ObjectID,
994        receive_object_at_version: SequenceNumber,
995        epoch_id: EpochId,
996    ) -> IotaResult<Option<Object>> {
997        // You should never be able to try and receive an object after deleting it or
998        // writing it in the same transaction since `Receiving` doesn't have
999        // copy.
1000        debug_assert!(
1001            !self
1002                .execution_results
1003                .written_objects
1004                .contains_key(receiving_object_id)
1005        );
1006        debug_assert!(
1007            !self
1008                .execution_results
1009                .deleted_object_ids
1010                .contains(receiving_object_id)
1011        );
1012        self.store.get_object_received_at_version(
1013            owner,
1014            receiving_object_id,
1015            receive_object_at_version,
1016            epoch_id,
1017        )
1018    }
1019}
1020
1021impl Storage for TemporaryStore<'_> {
1022    fn reset(&mut self) {
1023        self.drop_writes();
1024    }
1025
1026    fn read_object(&self, id: &ObjectID) -> Option<&Object> {
1027        TemporaryStore::read_object(self, id)
1028    }
1029
1030    /// Take execution results v1.
1031    fn record_execution_results(&mut self, results: ExecutionResults) {
1032        let ExecutionResults::V1(results) = results;
1033
1034        // It's important to merge instead of override results because it's
1035        // possible to execute PT more than once during tx execution.
1036        self.execution_results.merge_results(results);
1037    }
1038
1039    fn save_loaded_runtime_objects(
1040        &mut self,
1041        loaded_runtime_objects: BTreeMap<ObjectID, DynamicallyLoadedObjectMetadata>,
1042    ) {
1043        TemporaryStore::save_loaded_runtime_objects(self, loaded_runtime_objects)
1044    }
1045
1046    fn save_wrapped_object_containers(
1047        &mut self,
1048        wrapped_object_containers: BTreeMap<ObjectID, ObjectID>,
1049    ) {
1050        TemporaryStore::save_wrapped_object_containers(self, wrapped_object_containers)
1051    }
1052
1053    fn check_coin_deny_list(&self, written_objects: &BTreeMap<ObjectID, Object>) -> DenyListResult {
1054        let result = check_coin_deny_list_v1_during_execution(
1055            written_objects,
1056            self.cur_epoch,
1057            self.store.as_object_store(),
1058        );
1059        // The denylist object is only loaded if there are regulated transfers.
1060        // And also if we already have it in the input there is no need to commit it
1061        // again in the effects.
1062        if result.num_non_gas_coin_owners > 0
1063            && !self.input_objects.contains_key(&IOTA_DENY_LIST_OBJECT_ID)
1064        {
1065            self.loaded_per_epoch_config_objects
1066                .write()
1067                .insert(IOTA_DENY_LIST_OBJECT_ID);
1068        }
1069        result
1070    }
1071}
1072
1073impl BackingPackageStore for TemporaryStore<'_> {
1074    fn get_package_object(&self, package_id: &ObjectID) -> IotaResult<Option<PackageObject>> {
1075        // We first check the objects in the temporary store because in non-production
1076        // code path, it is possible to read packages that are just written in
1077        // the same transaction. This can happen for example when we run the
1078        // expensive conservation checks, where we may look into the types of
1079        // each written object in the output, and some of them need the
1080        // newly written packages for type checking.
1081        // In production path though, this should never happen.
1082        if let Some(obj) = self.execution_results.written_objects.get(package_id) {
1083            Ok(Some(PackageObject::new(obj.clone())))
1084        } else {
1085            self.store.get_package_object(package_id).inspect(|obj| {
1086                // Track object but leave unchanged
1087                if let Some(v) = obj {
1088                    if !self
1089                        .runtime_packages_loaded_from_db
1090                        .read()
1091                        .contains_key(package_id)
1092                    {
1093                        // TODO: Can this lock ever block execution?
1094                        // TODO: Another way to avoid the cost of maintaining this map is to not
1095                        // enable it in normal runs, and if a fork is detected, rerun it with a flag
1096                        // turned on and start populating this field.
1097                        self.runtime_packages_loaded_from_db
1098                            .write()
1099                            .insert(*package_id, v.clone());
1100                    }
1101                }
1102            })
1103        }
1104    }
1105}
1106
1107impl ResourceResolver for TemporaryStore<'_> {
1108    type Error = IotaError;
1109
1110    fn get_resource(
1111        &self,
1112        address: &AccountAddress,
1113        struct_tag: &StructTag,
1114    ) -> Result<Option<Vec<u8>>, Self::Error> {
1115        let object = match self.read_object(&ObjectID::from(*address)) {
1116            Some(x) => x,
1117            None => match self.read_object(&ObjectID::from(*address)) {
1118                None => return Ok(None),
1119                Some(x) => {
1120                    if !x.is_immutable() {
1121                        fp_bail!(IotaError::ExecutionInvariantViolation);
1122                    }
1123                    x
1124                }
1125            },
1126        };
1127
1128        match &object.data {
1129            Data::Move(m) => {
1130                assert!(
1131                    m.is_type(struct_tag),
1132                    "Invariant violation: ill-typed object in storage \
1133                    or bad object request from caller"
1134                );
1135                Ok(Some(m.contents().to_vec()))
1136            }
1137            other => unimplemented!(
1138                "Bad object lookup: expected Move object, but got {:?}",
1139                other
1140            ),
1141        }
1142    }
1143}