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