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