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