Skip to main content

sui_adapter_v1/
temporary_store.rs

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