1use crate::gas_charger::GasCharger;
5use mysten_metrics::monitored_scope;
6use parking_lot::RwLock;
7use std::collections::{BTreeMap, BTreeSet, HashSet};
8use sui_protocol_config::ProtocolConfig;
9use sui_types::accumulator_event::AccumulatorEvent;
10use sui_types::base_types::VersionDigest;
11use sui_types::committee::EpochId;
12use sui_types::deny_list_v2::check_coin_deny_list_v2_during_execution;
13use sui_types::effects::{TransactionEffects, TransactionEvents};
14use sui_types::execution::{
15 DynamicallyLoadedObjectMetadata, ExecutionResults, ExecutionResultsV2, SharedInput,
16};
17use sui_types::execution_status::ExecutionStatus;
18use sui_types::inner_temporary_store::InnerTemporaryStore;
19use sui_types::layout_resolver::LayoutResolver;
20use sui_types::storage::{BackingStore, DenyListResult, PackageObject};
21use sui_types::sui_system_state::{AdvanceEpochParams, get_sui_system_state_wrapper};
22use sui_types::{
23 SUI_DENY_LIST_OBJECT_ID,
24 base_types::{ObjectID, ObjectRef, SequenceNumber, SuiAddress, TransactionDigest},
25 effects::EffectsObjectChange,
26 error::{ExecutionError, SuiResult},
27 gas::GasCostSummary,
28 object::Object,
29 object::Owner,
30 storage::{BackingPackageStore, ChildObjectResolver, ParentSync, Storage},
31 transaction::InputObjects,
32};
33use sui_types::{SUI_SYSTEM_STATE_OBJECT_ID, TypeTag, is_system_package};
34
35pub struct TemporaryStore<'backing> {
36 store: &'backing dyn BackingStore,
42 tx_digest: TransactionDigest,
43 input_objects: BTreeMap<ObjectID, Object>,
44 stream_ended_consensus_objects: BTreeMap<ObjectID, SequenceNumber >,
45 lamport_timestamp: SequenceNumber,
47 mutable_input_refs: BTreeMap<ObjectID, (VersionDigest, Owner)>, execution_results: ExecutionResultsV2,
49 loaded_runtime_objects: BTreeMap<ObjectID, DynamicallyLoadedObjectMetadata>,
51 wrapped_object_containers: BTreeMap<ObjectID, ObjectID>,
53 protocol_config: &'backing ProtocolConfig,
54
55 runtime_packages_loaded_from_db: RwLock<BTreeMap<ObjectID, PackageObject>>,
58
59 receiving_objects: Vec<ObjectRef>,
62
63 generated_runtime_ids: BTreeSet<ObjectID>,
67
68 cur_epoch: EpochId,
71
72 loaded_per_epoch_config_objects: RwLock<BTreeSet<ObjectID>>,
75}
76
77impl<'backing> TemporaryStore<'backing> {
78 pub fn new(
81 store: &'backing dyn BackingStore,
82 input_objects: InputObjects,
83 receiving_objects: Vec<ObjectRef>,
84 tx_digest: TransactionDigest,
85 protocol_config: &'backing ProtocolConfig,
86 cur_epoch: EpochId,
87 ) -> Self {
88 let mutable_input_refs = input_objects.mutable_inputs();
89 let lamport_timestamp = input_objects.lamport_timestamp(&receiving_objects);
90 let stream_ended_consensus_objects = input_objects.consensus_stream_ended_objects();
91 let objects = input_objects.into_object_map();
92 #[cfg(debug_assertions)]
93 {
94 assert!(
96 objects
97 .keys()
98 .collect::<HashSet<_>>()
99 .intersection(
100 &receiving_objects
101 .iter()
102 .map(|oref| &oref.0)
103 .collect::<HashSet<_>>()
104 )
105 .next()
106 .is_none()
107 );
108 }
109 Self {
110 store,
111 tx_digest,
112 input_objects: objects,
113 stream_ended_consensus_objects,
114 lamport_timestamp,
115 mutable_input_refs,
116 execution_results: ExecutionResultsV2::default(),
117 protocol_config,
118 loaded_runtime_objects: BTreeMap::new(),
119 wrapped_object_containers: BTreeMap::new(),
120 runtime_packages_loaded_from_db: RwLock::new(BTreeMap::new()),
121 receiving_objects,
122 generated_runtime_ids: BTreeSet::new(),
123 cur_epoch,
124 loaded_per_epoch_config_objects: RwLock::new(BTreeSet::new()),
125 }
126 }
127
128 pub fn objects(&self) -> &BTreeMap<ObjectID, Object> {
130 &self.input_objects
131 }
132
133 pub fn update_object_version_and_prev_tx(&mut self) {
134 self.execution_results.update_version_and_previous_tx(
135 self.lamport_timestamp,
136 self.tx_digest,
137 &self.input_objects,
138 self.protocol_config.reshare_at_same_initial_version(),
139 );
140
141 #[cfg(debug_assertions)]
142 {
143 self.check_invariants();
144 }
145 }
146
147 pub fn into_inner(self) -> InnerTemporaryStore {
149 let results = self.execution_results;
150 InnerTemporaryStore {
151 input_objects: self.input_objects,
152 stream_ended_consensus_objects: self.stream_ended_consensus_objects,
153 mutable_inputs: self.mutable_input_refs,
154 written: results.written_objects,
155 events: TransactionEvents {
156 data: results.user_events,
157 },
158 accumulator_events: results.accumulator_events,
159 loaded_runtime_objects: self.loaded_runtime_objects,
160 runtime_packages_loaded_from_db: self.runtime_packages_loaded_from_db.into_inner(),
161 lamport_version: self.lamport_timestamp,
162 binary_config: self.protocol_config.binary_config(None),
163 }
164 }
165
166 pub(crate) fn ensure_active_inputs_mutated(&mut self) {
170 let mut to_be_updated = vec![];
171 for id in self.mutable_input_refs.keys() {
172 if !self.execution_results.modified_objects.contains(id) {
173 to_be_updated.push(self.input_objects[id].clone());
177 }
178 }
179 for object in to_be_updated {
180 self.mutate_input_object(object.clone());
182 }
183 }
184
185 fn get_object_changes(&self) -> BTreeMap<ObjectID, EffectsObjectChange> {
186 let results = &self.execution_results;
187 let all_ids = results
188 .created_object_ids
189 .iter()
190 .chain(&results.deleted_object_ids)
191 .chain(&results.modified_objects)
192 .chain(results.written_objects.keys())
193 .collect::<BTreeSet<_>>();
194 all_ids
195 .into_iter()
196 .map(|id| {
197 (
198 *id,
199 EffectsObjectChange::new(
200 self.get_object_modified_at(id)
201 .map(|metadata| ((metadata.version, metadata.digest), metadata.owner)),
202 results.written_objects.get(id),
203 results.created_object_ids.contains(id),
204 results.deleted_object_ids.contains(id),
205 ),
206 )
207 })
208 .chain(results.accumulator_events.iter().cloned().map(
209 |AccumulatorEvent {
210 accumulator_obj,
211 write,
212 }| {
213 (
214 *accumulator_obj.inner(),
215 EffectsObjectChange::new_from_accumulator_write(write),
216 )
217 },
218 ))
219 .collect()
220 }
221
222 pub fn into_effects(
223 mut self,
224 shared_object_refs: Vec<SharedInput>,
225 transaction_digest: &TransactionDigest,
226 mut transaction_dependencies: BTreeSet<TransactionDigest>,
227 gas_cost_summary: GasCostSummary,
228 status: ExecutionStatus,
229 gas_charger: &mut GasCharger,
230 epoch: EpochId,
231 ) -> (InnerTemporaryStore, TransactionEffects) {
232 self.update_object_version_and_prev_tx();
233
234 for (id, expected_version, expected_digest) in &self.receiving_objects {
237 if let Some(obj_meta) = self.loaded_runtime_objects.get(id) {
241 let loaded_via_receive = obj_meta.version == *expected_version
245 && obj_meta.digest == *expected_digest
246 && obj_meta.owner.is_address_owned();
247 if loaded_via_receive {
248 transaction_dependencies.insert(obj_meta.previous_transaction);
249 }
250 }
251 }
252
253 assert!(self.protocol_config.enable_effects_v2());
254
255 let gas_coin = gas_charger.gas_coin();
260
261 let object_changes = self.get_object_changes();
262
263 let lamport_version = self.lamport_timestamp;
264 let loaded_per_epoch_config_objects = self.loaded_per_epoch_config_objects.read().clone();
266 let inner = self.into_inner();
267
268 let effects = TransactionEffects::new_from_execution_v2(
269 status,
270 epoch,
271 gas_cost_summary,
272 shared_object_refs,
274 loaded_per_epoch_config_objects,
275 *transaction_digest,
276 lamport_version,
277 object_changes,
278 gas_coin,
279 if inner.events.data.is_empty() {
280 None
281 } else {
282 Some(inner.events.digest())
283 },
284 transaction_dependencies.into_iter().collect(),
285 );
286
287 (inner, effects)
288 }
289
290 #[cfg(debug_assertions)]
292 fn check_invariants(&self) {
293 debug_assert!(
295 {
296 self.execution_results
297 .written_objects
298 .keys()
299 .all(|id| !self.execution_results.deleted_object_ids.contains(id))
300 },
301 "Object both written and deleted."
302 );
303
304 debug_assert!(
306 {
307 self.mutable_input_refs
308 .keys()
309 .all(|id| self.execution_results.modified_objects.contains(id))
310 },
311 "Mutable input not modified."
312 );
313
314 debug_assert!(
315 {
316 self.execution_results
317 .written_objects
318 .values()
319 .all(|obj| obj.previous_transaction == self.tx_digest)
320 },
321 "Object previous transaction not properly set",
322 );
323 }
324
325 pub fn mutate_input_object(&mut self, object: Object) {
327 let id = object.id();
328 debug_assert!(self.input_objects.contains_key(&id));
329 debug_assert!(!object.is_immutable());
330 self.execution_results.modified_objects.insert(id);
331 self.execution_results.written_objects.insert(id, object);
332 }
333
334 pub fn mutate_child_object(&mut self, old_object: Object, new_object: Object) {
338 let id = new_object.id();
339 let old_ref = old_object.compute_object_reference();
340 debug_assert_eq!(old_ref.0, id);
341 self.loaded_runtime_objects.insert(
342 id,
343 DynamicallyLoadedObjectMetadata {
344 version: old_ref.1,
345 digest: old_ref.2,
346 owner: old_object.owner.clone(),
347 storage_rebate: old_object.storage_rebate,
348 previous_transaction: old_object.previous_transaction,
349 },
350 );
351 self.execution_results.modified_objects.insert(id);
352 self.execution_results
353 .written_objects
354 .insert(id, new_object);
355 }
356
357 pub fn upgrade_system_package(&mut self, package: Object) {
361 let id = package.id();
362 assert!(package.is_package() && is_system_package(id));
363 self.execution_results.modified_objects.insert(id);
364 self.execution_results.written_objects.insert(id, package);
365 }
366
367 pub fn create_object(&mut self, object: Object) {
369 debug_assert!(
374 object.is_immutable() || object.version() == SequenceNumber::MIN,
375 "Created mutable objects should not have a version set",
376 );
377 let id = object.id();
378 self.execution_results.created_object_ids.insert(id);
379 self.execution_results.written_objects.insert(id, object);
380 }
381
382 pub fn delete_input_object(&mut self, id: &ObjectID) {
384 debug_assert!(!self.execution_results.written_objects.contains_key(id));
386 debug_assert!(self.input_objects.contains_key(id));
387 self.execution_results.modified_objects.insert(*id);
388 self.execution_results.deleted_object_ids.insert(*id);
389 }
390
391 pub fn drop_writes(&mut self) {
392 self.execution_results.drop_writes();
393 }
394
395 pub fn read_object(&self, id: &ObjectID) -> Option<&Object> {
396 debug_assert!(!self.execution_results.deleted_object_ids.contains(id));
398 self.execution_results
399 .written_objects
400 .get(id)
401 .or_else(|| self.input_objects.get(id))
402 }
403
404 pub fn save_loaded_runtime_objects(
405 &mut self,
406 loaded_runtime_objects: BTreeMap<ObjectID, DynamicallyLoadedObjectMetadata>,
407 ) {
408 #[cfg(debug_assertions)]
409 {
410 for (id, v1) in &loaded_runtime_objects {
411 if let Some(v2) = self.loaded_runtime_objects.get(id) {
412 assert_eq!(v1, v2);
413 }
414 }
415 for (id, v1) in &self.loaded_runtime_objects {
416 if let Some(v2) = loaded_runtime_objects.get(id) {
417 assert_eq!(v1, v2);
418 }
419 }
420 }
421 self.loaded_runtime_objects.extend(loaded_runtime_objects);
424 }
425
426 pub fn save_wrapped_object_containers(
427 &mut self,
428 wrapped_object_containers: BTreeMap<ObjectID, ObjectID>,
429 ) {
430 #[cfg(debug_assertions)]
431 {
432 for (id, container1) in &wrapped_object_containers {
433 if let Some(container2) = self.wrapped_object_containers.get(id) {
434 assert_eq!(container1, container2);
435 }
436 }
437 for (id, container1) in &self.wrapped_object_containers {
438 if let Some(container2) = wrapped_object_containers.get(id) {
439 assert_eq!(container1, container2);
440 }
441 }
442 }
443 self.wrapped_object_containers
446 .extend(wrapped_object_containers);
447 }
448
449 pub fn save_generated_object_ids(&mut self, generated_ids: BTreeSet<ObjectID>) {
450 #[cfg(debug_assertions)]
451 {
452 for id in &self.generated_runtime_ids {
453 assert!(!generated_ids.contains(id))
454 }
455 for id in &generated_ids {
456 assert!(!self.generated_runtime_ids.contains(id));
457 }
458 }
459 self.generated_runtime_ids.extend(generated_ids);
460 }
461
462 pub fn estimate_effects_size_upperbound(&self) -> usize {
463 TransactionEffects::estimate_effects_size_upperbound_v2(
464 self.execution_results.written_objects.len(),
465 self.execution_results.modified_objects.len(),
466 self.input_objects.len(),
467 )
468 }
469
470 pub fn written_objects_size(&self) -> usize {
471 self.execution_results
472 .written_objects
473 .values()
474 .fold(0, |sum, obj| sum + obj.object_size_for_gas_metering())
475 }
476
477 pub fn conserve_unmetered_storage_rebate(&mut self, unmetered_storage_rebate: u64) {
482 if unmetered_storage_rebate == 0 {
483 return;
487 }
488 tracing::debug!(
489 "Amount of unmetered storage rebate from system tx: {:?}",
490 unmetered_storage_rebate
491 );
492 let mut system_state_wrapper = self
493 .read_object(&SUI_SYSTEM_STATE_OBJECT_ID)
494 .expect("0x5 object must be mutated in system tx with unmetered storage rebate")
495 .clone();
496 assert_eq!(system_state_wrapper.storage_rebate, 0);
499 system_state_wrapper.storage_rebate = unmetered_storage_rebate;
500 self.mutate_input_object(system_state_wrapper);
501 }
502
503 fn get_object_modified_at(
509 &self,
510 object_id: &ObjectID,
511 ) -> Option<DynamicallyLoadedObjectMetadata> {
512 if self.execution_results.modified_objects.contains(object_id) {
513 Some(
514 self.mutable_input_refs
515 .get(object_id)
516 .map(
517 |((version, digest), owner)| DynamicallyLoadedObjectMetadata {
518 version: *version,
519 digest: *digest,
520 owner: owner.clone(),
521 storage_rebate: self.input_objects[object_id].storage_rebate,
523 previous_transaction: self.input_objects[object_id]
524 .previous_transaction,
525 },
526 )
527 .or_else(|| self.loaded_runtime_objects.get(object_id).cloned())
528 .unwrap_or_else(|| {
529 debug_assert!(is_system_package(*object_id));
530 let package_obj =
531 self.store.get_package_object(object_id).unwrap().unwrap();
532 let obj = package_obj.object();
533 DynamicallyLoadedObjectMetadata {
534 version: obj.version(),
535 digest: obj.digest(),
536 owner: obj.owner.clone(),
537 storage_rebate: obj.storage_rebate,
538 previous_transaction: obj.previous_transaction,
539 }
540 }),
541 )
542 } else {
543 None
544 }
545 }
546}
547
548impl TemporaryStore<'_> {
549 pub fn check_ownership_invariants(
552 &self,
553 sender: &SuiAddress,
554 sponsor: &Option<SuiAddress>,
555 gas_charger: &mut GasCharger,
556 mutable_inputs: &HashSet<ObjectID>,
557 is_epoch_change: bool,
558 ) -> SuiResult<()> {
559 let gas_objs: HashSet<&ObjectID> = gas_charger.gas_coins().iter().map(|g| &g.0).collect();
560 let gas_owner = sponsor.as_ref().unwrap_or(sender);
561
562 let mut authenticated_for_mutation: HashSet<_> = self
564 .input_objects
565 .iter()
566 .filter_map(|(id, obj)| {
567 match &obj.owner {
568 Owner::AddressOwner(a) => {
569 if gas_objs.contains(id) {
570 assert!(
572 a == gas_owner,
573 "Gas object must be owned by sender or sponsor"
574 );
575 } else {
576 assert!(sender == a, "Input object must be owned by sender");
577 }
578 Some(id)
579 }
580 Owner::Shared { .. } | Owner::ConsensusAddressOwner { .. } => Some(id),
581 Owner::Immutable => {
582 None
593 }
594 Owner::ObjectOwner(_parent) => {
595 unreachable!(
596 "Input objects must be address owned, shared, consensus, or immutable"
597 )
598 }
599 }
600 })
601 .filter(|id| {
602 mutable_inputs.contains(id)
605 })
606 .copied()
607 .chain(self.generated_runtime_ids.iter().copied())
610 .collect();
611
612 authenticated_for_mutation.insert((*sender).into());
614 if let Some(sponsor) = sponsor {
615 authenticated_for_mutation.insert((*sponsor).into());
616 }
617
618 let mut objects_to_authenticate = self
620 .execution_results
621 .modified_objects
622 .iter()
623 .copied()
624 .collect::<Vec<_>>();
625
626 while let Some(to_authenticate) = objects_to_authenticate.pop() {
627 if authenticated_for_mutation.contains(&to_authenticate) {
628 continue;
630 }
631
632 let parent = if let Some(container_id) =
633 self.wrapped_object_containers.get(&to_authenticate)
634 {
635 *container_id
637 } else {
638 let Some(old_obj) = self.store.get_object(&to_authenticate) else {
641 panic!(
642 "Failed to load object {to_authenticate:?}.\n \
643 If it cannot be loaded, we would expect it to be in the wrapped object map: {:#?}",
644 &self.wrapped_object_containers
645 )
646 };
647
648 match &old_obj.owner {
649 Owner::ObjectOwner(parent) => ObjectID::from(*parent),
652 Owner::AddressOwner(parent)
657 | Owner::ConsensusAddressOwner { owner: parent, .. } => {
658 ObjectID::from(*parent)
662 }
663 owner @ Owner::Shared { .. } => {
666 panic!(
667 "Unauthenticated root at {to_authenticate:?} with owner {owner:?}\n\
668 Potentially covering objects in: {authenticated_for_mutation:#?}"
669 );
670 }
671 Owner::Immutable => {
672 assert!(
673 is_epoch_change,
674 "Immutable objects cannot be written, except for \
675 Sui Framework/Move stdlib upgrades at epoch change boundaries"
676 );
677 assert!(
681 is_system_package(to_authenticate),
682 "Only system packages can be upgraded"
683 );
684 continue;
685 }
686 }
687 };
688
689 authenticated_for_mutation.insert(to_authenticate);
691 objects_to_authenticate.push(parent);
692 }
693 Ok(())
694 }
695}
696
697impl TemporaryStore<'_> {
698 pub(crate) fn collect_storage_and_rebate(&mut self, gas_charger: &mut GasCharger) {
705 let old_storage_rebates: Vec<_> = self
707 .execution_results
708 .written_objects
709 .keys()
710 .map(|object_id| {
711 self.get_object_modified_at(object_id)
712 .map(|metadata| metadata.storage_rebate)
713 .unwrap_or_default()
714 })
715 .collect();
716 for (object, old_storage_rebate) in self
717 .execution_results
718 .written_objects
719 .values_mut()
720 .zip(old_storage_rebates)
721 {
722 let new_object_size = object.object_size_for_gas_metering();
724 let new_storage_rebate = gas_charger.track_storage_mutation(
726 object.id(),
727 new_object_size,
728 old_storage_rebate,
729 );
730 object.storage_rebate = new_storage_rebate;
731 }
732
733 self.collect_rebate(gas_charger);
734 }
735
736 pub(crate) fn collect_rebate(&self, gas_charger: &mut GasCharger) {
737 for object_id in &self.execution_results.modified_objects {
738 if self
739 .execution_results
740 .written_objects
741 .contains_key(object_id)
742 {
743 continue;
744 }
745 let storage_rebate = self
747 .get_object_modified_at(object_id)
748 .unwrap()
750 .storage_rebate;
751 gas_charger.track_storage_mutation(*object_id, 0, storage_rebate);
752 }
753 }
754
755 pub fn check_execution_results_consistency(&self) -> Result<(), ExecutionError> {
756 assert_invariant!(
757 self.execution_results
758 .created_object_ids
759 .iter()
760 .all(|id| !self.execution_results.deleted_object_ids.contains(id)
761 && !self.execution_results.modified_objects.contains(id)),
762 "Created object IDs cannot also be deleted or modified"
763 );
764 assert_invariant!(
765 self.execution_results.modified_objects.iter().all(|id| {
766 self.mutable_input_refs.contains_key(id)
767 || self.loaded_runtime_objects.contains_key(id)
768 || is_system_package(*id)
769 }),
770 "A modified object must be either a mutable input, a loaded child object, or a system package"
771 );
772 Ok(())
773 }
774}
775impl TemporaryStore<'_> {
780 pub fn advance_epoch_safe_mode(
781 &mut self,
782 params: &AdvanceEpochParams,
783 protocol_config: &ProtocolConfig,
784 ) {
785 let wrapper = get_sui_system_state_wrapper(self.store.as_object_store())
786 .expect("System state wrapper object must exist");
787 let (old_object, new_object) =
788 wrapper.advance_epoch_safe_mode(params, self.store.as_object_store(), protocol_config);
789 self.mutate_child_object(old_object, new_object);
790 }
791}
792
793type ModifiedObjectInfo<'a> = (
794 ObjectID,
795 Option<DynamicallyLoadedObjectMetadata>,
797 Option<&'a Object>,
798);
799
800impl TemporaryStore<'_> {
801 fn get_input_sui(
802 &self,
803 id: &ObjectID,
804 expected_version: SequenceNumber,
805 layout_resolver: &mut impl LayoutResolver,
806 ) -> Result<u64, ExecutionError> {
807 if let Some(obj) = self.input_objects.get(id) {
808 if obj.version() != expected_version {
810 invariant_violation!(
811 "Version mismatching when resolving input object to check conservation--\
812 expected {}, got {}",
813 expected_version,
814 obj.version(),
815 );
816 }
817 obj.get_total_sui(layout_resolver).map_err(|e| {
818 make_invariant_violation!(
819 "Failed looking up input SUI in SUI conservation checking for input with \
820 type {:?}: {e:#?}",
821 obj.struct_tag(),
822 )
823 })
824 } else {
825 let Some(obj) = self.store.get_object_by_key(id, expected_version) else {
827 invariant_violation!(
828 "Failed looking up dynamic field {id} in SUI conservation checking"
829 );
830 };
831 obj.get_total_sui(layout_resolver).map_err(|e| {
832 make_invariant_violation!(
833 "Failed looking up input SUI in SUI conservation checking for type \
834 {:?}: {e:#?}",
835 obj.struct_tag(),
836 )
837 })
838 }
839 }
840
841 fn get_modified_objects(&self) -> Vec<ModifiedObjectInfo<'_>> {
846 self.execution_results
847 .modified_objects
848 .iter()
849 .map(|id| {
850 let metadata = self.get_object_modified_at(id);
851 let output = self.execution_results.written_objects.get(id);
852 (*id, metadata, output)
853 })
854 .chain(
855 self.execution_results
856 .written_objects
857 .iter()
858 .filter_map(|(id, object)| {
859 if self.execution_results.modified_objects.contains(id) {
860 None
861 } else {
862 Some((*id, None, Some(object)))
863 }
864 }),
865 )
866 .collect()
867 }
868
869 pub fn check_sui_conserved(
883 &self,
884 simple_conservation_checks: bool,
885 gas_summary: &GasCostSummary,
886 ) -> Result<(), ExecutionError> {
887 if !simple_conservation_checks {
888 return Ok(());
889 }
890 let mut total_input_rebate = 0;
892 let mut total_output_rebate = 0;
894 for (_, input, output) in self.get_modified_objects() {
895 if let Some(input) = input {
896 total_input_rebate += input.storage_rebate;
897 }
898 if let Some(object) = output {
899 total_output_rebate += object.storage_rebate;
900 }
901 }
902
903 if gas_summary.storage_cost == 0 {
904 if total_input_rebate
916 != total_output_rebate
917 + gas_summary.storage_rebate
918 + gas_summary.non_refundable_storage_fee
919 {
920 return Err(ExecutionError::invariant_violation(format!(
921 "SUI conservation failed -- no storage charges in gas summary \
922 and total storage input rebate {} not equal \
923 to total storage output rebate {}",
924 total_input_rebate, total_output_rebate,
925 )));
926 }
927 } else {
928 if total_input_rebate
931 != gas_summary.storage_rebate + gas_summary.non_refundable_storage_fee
932 {
933 return Err(ExecutionError::invariant_violation(format!(
934 "SUI conservation failed -- {} SUI in storage rebate field of input objects, \
935 {} SUI in tx storage rebate or tx non-refundable storage rebate",
936 total_input_rebate, gas_summary.non_refundable_storage_fee,
937 )));
938 }
939
940 if gas_summary.storage_cost != total_output_rebate {
943 return Err(ExecutionError::invariant_violation(format!(
944 "SUI conservation failed -- {} SUI charged for storage, \
945 {} SUI in storage rebate field of output objects",
946 gas_summary.storage_cost, total_output_rebate
947 )));
948 }
949 }
950 Ok(())
951 }
952
953 pub fn check_sui_conserved_expensive(
966 &self,
967 gas_summary: &GasCostSummary,
968 advance_epoch_gas_summary: Option<(u64, u64)>,
969 layout_resolver: &mut impl LayoutResolver,
970 ) -> Result<(), ExecutionError> {
971 let mut total_input_sui = 0;
973 let mut total_output_sui = 0;
975
976 total_input_sui += self.execution_results.settlement_input_sui;
980 total_output_sui += self.execution_results.settlement_output_sui;
981
982 for (id, input, output) in self.get_modified_objects() {
983 if let Some(input) = input {
984 total_input_sui += self.get_input_sui(&id, input.version, layout_resolver)?;
985 }
986 if let Some(object) = output {
987 total_output_sui += object.get_total_sui(layout_resolver).map_err(|e| {
988 make_invariant_violation!(
989 "Failed looking up output SUI in SUI conservation checking for \
990 mutated type {:?}: {e:#?}",
991 object.struct_tag(),
992 )
993 })?;
994 }
995 }
996
997 for event in &self.execution_results.accumulator_events {
998 let (input, output) = event.total_sui_in_event();
999 total_input_sui += input;
1000 total_output_sui += output;
1001 }
1002
1003 total_output_sui += gas_summary.computation_cost + gas_summary.non_refundable_storage_fee;
1008 if let Some((epoch_fees, epoch_rebates)) = advance_epoch_gas_summary {
1009 total_input_sui += epoch_fees;
1010 total_output_sui += epoch_rebates;
1011 }
1012 if total_input_sui != total_output_sui {
1013 return Err(ExecutionError::invariant_violation(format!(
1014 "SUI conservation failed: input={}, output={}, \
1015 this transaction either mints or burns SUI",
1016 total_input_sui, total_output_sui,
1017 )));
1018 }
1019 Ok(())
1020 }
1021}
1022
1023impl ChildObjectResolver for TemporaryStore<'_> {
1024 fn read_child_object(
1025 &self,
1026 parent: &ObjectID,
1027 child: &ObjectID,
1028 child_version_upper_bound: SequenceNumber,
1029 ) -> SuiResult<Option<Object>> {
1030 let obj_opt = self.execution_results.written_objects.get(child);
1031 if obj_opt.is_some() {
1032 Ok(obj_opt.cloned())
1033 } else {
1034 let _scope = monitored_scope("Execution::read_child_object");
1035 self.store
1036 .read_child_object(parent, child, child_version_upper_bound)
1037 }
1038 }
1039
1040 fn get_object_received_at_version(
1041 &self,
1042 owner: &ObjectID,
1043 receiving_object_id: &ObjectID,
1044 receive_object_at_version: SequenceNumber,
1045 epoch_id: EpochId,
1046 ) -> SuiResult<Option<Object>> {
1047 debug_assert!(
1050 !self
1051 .execution_results
1052 .written_objects
1053 .contains_key(receiving_object_id)
1054 );
1055 debug_assert!(
1056 !self
1057 .execution_results
1058 .deleted_object_ids
1059 .contains(receiving_object_id)
1060 );
1061 self.store.get_object_received_at_version(
1062 owner,
1063 receiving_object_id,
1064 receive_object_at_version,
1065 epoch_id,
1066 )
1067 }
1068}
1069
1070impl Storage for TemporaryStore<'_> {
1071 fn reset(&mut self) {
1072 self.drop_writes();
1073 }
1074
1075 fn read_object(&self, id: &ObjectID) -> Option<&Object> {
1076 TemporaryStore::read_object(self, id)
1077 }
1078
1079 fn record_execution_results(&mut self, results: ExecutionResults) {
1081 let ExecutionResults::V2(results) = results else {
1082 panic!("ExecutionResults::V2 expected in sui-execution v1 and above");
1083 };
1084 self.execution_results.merge_results(results);
1087 }
1088
1089 fn save_loaded_runtime_objects(
1090 &mut self,
1091 loaded_runtime_objects: BTreeMap<ObjectID, DynamicallyLoadedObjectMetadata>,
1092 ) {
1093 TemporaryStore::save_loaded_runtime_objects(self, loaded_runtime_objects)
1094 }
1095
1096 fn save_wrapped_object_containers(
1097 &mut self,
1098 wrapped_object_containers: BTreeMap<ObjectID, ObjectID>,
1099 ) {
1100 TemporaryStore::save_wrapped_object_containers(self, wrapped_object_containers)
1101 }
1102
1103 fn check_coin_deny_list(
1104 &self,
1105 receiving_funds_type_and_owners: BTreeMap<TypeTag, BTreeSet<SuiAddress>>,
1106 ) -> DenyListResult {
1107 let result = check_coin_deny_list_v2_during_execution(
1108 receiving_funds_type_and_owners,
1109 self.cur_epoch,
1110 self.store.as_object_store(),
1111 );
1112 if result.num_non_gas_coin_owners > 0
1115 && !self.input_objects.contains_key(&SUI_DENY_LIST_OBJECT_ID)
1116 {
1117 self.loaded_per_epoch_config_objects
1118 .write()
1119 .insert(SUI_DENY_LIST_OBJECT_ID);
1120 }
1121 result
1122 }
1123
1124 fn record_generated_object_ids(&mut self, generated_ids: BTreeSet<ObjectID>) {
1125 TemporaryStore::save_generated_object_ids(self, generated_ids)
1126 }
1127}
1128
1129impl BackingPackageStore for TemporaryStore<'_> {
1130 fn get_package_object(&self, package_id: &ObjectID) -> SuiResult<Option<PackageObject>> {
1131 if let Some(obj) = self.execution_results.written_objects.get(package_id) {
1138 Ok(Some(PackageObject::new(obj.clone())))
1139 } else {
1140 self.store.get_package_object(package_id).inspect(|obj| {
1141 if let Some(v) = obj
1143 && !self
1144 .runtime_packages_loaded_from_db
1145 .read()
1146 .contains_key(package_id)
1147 {
1148 self.runtime_packages_loaded_from_db
1153 .write()
1154 .insert(*package_id, v.clone());
1155 }
1156 })
1157 }
1158 }
1159}
1160
1161impl ParentSync for TemporaryStore<'_> {
1162 fn get_latest_parent_entry_ref_deprecated(&self, _object_id: ObjectID) -> Option<ObjectRef> {
1163 unreachable!("Never called in newer protocol versions")
1164 }
1165}