sui_adapter_latest/
temporary_store.rs

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