Skip to main content

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