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::accumulator_root::AccumulatorObjId;
11use sui_types::base_types::VersionDigest;
12use sui_types::committee::EpochId;
13use sui_types::deny_list_v2::check_coin_deny_list_v2_during_execution;
14use sui_types::effects::{
15 AccumulatorOperation, AccumulatorValue, AccumulatorWriteV1, TransactionEffects,
16 TransactionEvents,
17};
18use sui_types::execution::{
19 DynamicallyLoadedObjectMetadata, ExecutionResults, ExecutionResultsV2, SharedInput,
20};
21use sui_types::execution_status::{ExecutionErrorKind, ExecutionStatus};
22use sui_types::inner_temporary_store::InnerTemporaryStore;
23use sui_types::layout_resolver::LayoutResolver;
24use sui_types::object::Data;
25use sui_types::storage::{BackingStore, DenyListResult, PackageObject};
26use sui_types::sui_system_state::{AdvanceEpochParams, get_sui_system_state_wrapper};
27use sui_types::{
28 SUI_DENY_LIST_OBJECT_ID,
29 base_types::{ObjectID, ObjectRef, SequenceNumber, SuiAddress, TransactionDigest},
30 effects::EffectsObjectChange,
31 error::{ExecutionError, SuiResult},
32 gas::GasCostSummary,
33 object::Object,
34 object::Owner,
35 storage::{BackingPackageStore, ChildObjectResolver, ParentSync, Storage},
36 transaction::InputObjects,
37};
38use sui_types::{SUI_SYSTEM_STATE_OBJECT_ID, TypeTag, is_system_package};
39
40pub struct TemporaryStore<'backing> {
41 store: &'backing dyn BackingStore,
47 tx_digest: TransactionDigest,
48 input_objects: BTreeMap<ObjectID, Object>,
49
50 non_exclusive_input_original_versions: BTreeMap<ObjectID, Object>,
53
54 stream_ended_consensus_objects: BTreeMap<ObjectID, SequenceNumber >,
55 lamport_timestamp: SequenceNumber,
57 mutable_input_refs: BTreeMap<ObjectID, (VersionDigest, Owner)>,
60 execution_results: ExecutionResultsV2,
61 loaded_runtime_objects: BTreeMap<ObjectID, DynamicallyLoadedObjectMetadata>,
63 wrapped_object_containers: BTreeMap<ObjectID, ObjectID>,
65 protocol_config: &'backing ProtocolConfig,
66
67 runtime_packages_loaded_from_db: RwLock<BTreeMap<ObjectID, PackageObject>>,
70
71 receiving_objects: Vec<ObjectRef>,
74
75 generated_runtime_ids: BTreeSet<ObjectID>,
79
80 cur_epoch: EpochId,
83
84 loaded_per_epoch_config_objects: RwLock<BTreeSet<ObjectID>>,
87}
88
89impl<'backing> TemporaryStore<'backing> {
90 pub fn new(
93 store: &'backing dyn BackingStore,
94 input_objects: InputObjects,
95 receiving_objects: Vec<ObjectRef>,
96 tx_digest: TransactionDigest,
97 protocol_config: &'backing ProtocolConfig,
98 cur_epoch: EpochId,
99 ) -> Self {
100 let mutable_input_refs = input_objects.exclusive_mutable_inputs();
101 let non_exclusive_input_original_versions = input_objects.non_exclusive_input_objects();
102
103 let lamport_timestamp = input_objects.lamport_timestamp(&receiving_objects);
104 let stream_ended_consensus_objects = input_objects.consensus_stream_ended_objects();
105 let objects = input_objects.into_object_map();
106 #[cfg(debug_assertions)]
107 {
108 assert!(
110 objects
111 .keys()
112 .collect::<HashSet<_>>()
113 .intersection(
114 &receiving_objects
115 .iter()
116 .map(|oref| &oref.0)
117 .collect::<HashSet<_>>()
118 )
119 .next()
120 .is_none()
121 );
122 }
123 Self {
124 store,
125 tx_digest,
126 input_objects: objects,
127 non_exclusive_input_original_versions,
128 stream_ended_consensus_objects,
129 lamport_timestamp,
130 mutable_input_refs,
131 execution_results: ExecutionResultsV2::default(),
132 protocol_config,
133 loaded_runtime_objects: BTreeMap::new(),
134 wrapped_object_containers: BTreeMap::new(),
135 runtime_packages_loaded_from_db: RwLock::new(BTreeMap::new()),
136 receiving_objects,
137 generated_runtime_ids: BTreeSet::new(),
138 cur_epoch,
139 loaded_per_epoch_config_objects: RwLock::new(BTreeSet::new()),
140 }
141 }
142
143 pub fn objects(&self) -> &BTreeMap<ObjectID, Object> {
145 &self.input_objects
146 }
147
148 pub fn update_object_version_and_prev_tx(&mut self) {
149 self.execution_results.update_version_and_previous_tx(
150 self.lamport_timestamp,
151 self.tx_digest,
152 &self.input_objects,
153 self.protocol_config.reshare_at_same_initial_version(),
154 );
155
156 #[cfg(debug_assertions)]
157 {
158 self.check_invariants();
159 }
160 }
161
162 fn calculate_accumulator_running_max_withdraws(&self) -> BTreeMap<AccumulatorObjId, u128> {
163 let mut running_net_withdraws: BTreeMap<AccumulatorObjId, i128> = BTreeMap::new();
164 let mut running_max_withdraws: BTreeMap<AccumulatorObjId, u128> = BTreeMap::new();
165 for event in &self.execution_results.accumulator_events {
166 match &event.write.value {
167 AccumulatorValue::Integer(amount) => match event.write.operation {
168 AccumulatorOperation::Split => {
169 let entry = running_net_withdraws
170 .entry(event.accumulator_obj)
171 .or_default();
172 *entry += *amount as i128;
173 if *entry > 0 {
174 let max_entry = running_max_withdraws
175 .entry(event.accumulator_obj)
176 .or_default();
177 *max_entry = (*max_entry).max(*entry as u128);
178 }
179 }
180 AccumulatorOperation::Merge => {
181 let entry = running_net_withdraws
182 .entry(event.accumulator_obj)
183 .or_default();
184 *entry -= *amount as i128;
185 }
186 },
187 AccumulatorValue::IntegerTuple(_, _) | AccumulatorValue::EventDigest(_) => {}
188 }
189 }
190 running_max_withdraws
191 }
192
193 fn merge_accumulator_events(&mut self) {
195 self.execution_results.accumulator_events = self
196 .execution_results
197 .accumulator_events
198 .iter()
199 .fold(
200 BTreeMap::<AccumulatorObjId, Vec<AccumulatorWriteV1>>::new(),
201 |mut map, event| {
202 map.entry(event.accumulator_obj)
203 .or_default()
204 .push(event.write.clone());
205 map
206 },
207 )
208 .into_iter()
209 .map(|(obj_id, writes)| {
210 AccumulatorEvent::new(obj_id, AccumulatorWriteV1::merge(writes))
211 })
212 .collect();
213 }
214
215 pub fn into_inner(
217 self,
218 accumulator_running_max_withdraws: BTreeMap<AccumulatorObjId, u128>,
219 ) -> InnerTemporaryStore {
220 let results = self.execution_results;
221 InnerTemporaryStore {
222 input_objects: self.input_objects,
223 stream_ended_consensus_objects: self.stream_ended_consensus_objects,
224 mutable_inputs: self.mutable_input_refs,
225 written: results.written_objects,
226 events: TransactionEvents {
227 data: results.user_events,
228 },
229 accumulator_events: results.accumulator_events,
230 loaded_runtime_objects: self.loaded_runtime_objects,
231 runtime_packages_loaded_from_db: self.runtime_packages_loaded_from_db.into_inner(),
232 lamport_version: self.lamport_timestamp,
233 binary_config: self.protocol_config.binary_config(None),
234 accumulator_running_max_withdraws,
235 }
236 }
237
238 pub(crate) fn ensure_active_inputs_mutated(&mut self) {
242 let mut to_be_updated = vec![];
243 for id in self.mutable_input_refs.keys() {
245 if !self.execution_results.modified_objects.contains(id) {
246 to_be_updated.push(self.input_objects[id].clone());
250 }
251 }
252 for object in to_be_updated {
253 self.mutate_input_object(object.clone());
255 }
256 }
257
258 fn get_object_changes(&self) -> BTreeMap<ObjectID, EffectsObjectChange> {
259 let results = &self.execution_results;
260 let all_ids = results
261 .created_object_ids
262 .iter()
263 .chain(&results.deleted_object_ids)
264 .chain(&results.modified_objects)
265 .chain(results.written_objects.keys())
266 .collect::<BTreeSet<_>>();
267 all_ids
268 .into_iter()
269 .map(|id| {
270 (
271 *id,
272 EffectsObjectChange::new(
273 self.get_object_modified_at(id)
274 .map(|metadata| ((metadata.version, metadata.digest), metadata.owner)),
275 results.written_objects.get(id),
276 results.created_object_ids.contains(id),
277 results.deleted_object_ids.contains(id),
278 ),
279 )
280 })
281 .chain(results.accumulator_events.iter().cloned().map(
282 |AccumulatorEvent {
283 accumulator_obj,
284 write,
285 }| {
286 (
287 *accumulator_obj.inner(),
288 EffectsObjectChange::new_from_accumulator_write(write),
289 )
290 },
291 ))
292 .collect()
293 }
294
295 pub fn into_effects(
296 mut self,
297 shared_object_refs: Vec<SharedInput>,
298 transaction_digest: &TransactionDigest,
299 mut transaction_dependencies: BTreeSet<TransactionDigest>,
300 gas_cost_summary: GasCostSummary,
301 status: ExecutionStatus,
302 gas_charger: &mut GasCharger,
303 epoch: EpochId,
304 ) -> (InnerTemporaryStore, TransactionEffects) {
305 self.update_object_version_and_prev_tx();
306 let accumulator_running_max_withdraws = self.calculate_accumulator_running_max_withdraws();
308 self.merge_accumulator_events();
309
310 for (id, expected_version, expected_digest) in &self.receiving_objects {
313 if let Some(obj_meta) = self.loaded_runtime_objects.get(id) {
317 let loaded_via_receive = obj_meta.version == *expected_version
321 && obj_meta.digest == *expected_digest
322 && obj_meta.owner.is_address_owned();
323 if loaded_via_receive {
324 transaction_dependencies.insert(obj_meta.previous_transaction);
325 }
326 }
327 }
328
329 assert!(self.protocol_config.enable_effects_v2());
330
331 let gas_coin = gas_charger.gas_coin();
336
337 let object_changes = self.get_object_changes();
338
339 let lamport_version = self.lamport_timestamp;
340 let loaded_per_epoch_config_objects = self.loaded_per_epoch_config_objects.read().clone();
342 let inner = self.into_inner(accumulator_running_max_withdraws);
343
344 let effects = TransactionEffects::new_from_execution_v2(
345 status,
346 epoch,
347 gas_cost_summary,
348 shared_object_refs,
350 loaded_per_epoch_config_objects,
351 *transaction_digest,
352 lamport_version,
353 object_changes,
354 gas_coin,
355 if inner.events.data.is_empty() {
356 None
357 } else {
358 Some(inner.events.digest())
359 },
360 transaction_dependencies.into_iter().collect(),
361 );
362
363 (inner, effects)
364 }
365
366 #[cfg(debug_assertions)]
368 fn check_invariants(&self) {
369 debug_assert!(
371 {
372 self.execution_results
373 .written_objects
374 .keys()
375 .all(|id| !self.execution_results.deleted_object_ids.contains(id))
376 },
377 "Object both written and deleted."
378 );
379
380 debug_assert!(
382 {
383 self.mutable_input_refs
384 .keys()
385 .all(|id| self.execution_results.modified_objects.contains(id))
386 },
387 "Mutable input not modified."
388 );
389
390 debug_assert!(
391 {
392 self.execution_results
393 .written_objects
394 .values()
395 .all(|obj| obj.previous_transaction == self.tx_digest)
396 },
397 "Object previous transaction not properly set",
398 );
399 }
400
401 pub fn mutate_input_object(&mut self, object: Object) {
403 let id = object.id();
404 debug_assert!(self.input_objects.contains_key(&id));
405 debug_assert!(!object.is_immutable());
406 self.execution_results.modified_objects.insert(id);
407 self.execution_results.written_objects.insert(id, object);
408 }
409
410 pub fn mutate_child_object(&mut self, old_object: Object, new_object: Object) {
414 let id = new_object.id();
415 let old_ref = old_object.compute_object_reference();
416 debug_assert_eq!(old_ref.0, id);
417 self.loaded_runtime_objects.insert(
418 id,
419 DynamicallyLoadedObjectMetadata {
420 version: old_ref.1,
421 digest: old_ref.2,
422 owner: old_object.owner.clone(),
423 storage_rebate: old_object.storage_rebate,
424 previous_transaction: old_object.previous_transaction,
425 },
426 );
427 self.execution_results.modified_objects.insert(id);
428 self.execution_results
429 .written_objects
430 .insert(id, new_object);
431 }
432
433 pub fn upgrade_system_package(&mut self, package: Object) {
437 let id = package.id();
438 assert!(package.is_package() && is_system_package(id));
439 self.execution_results.modified_objects.insert(id);
440 self.execution_results.written_objects.insert(id, package);
441 }
442
443 pub fn create_object(&mut self, object: Object) {
445 debug_assert!(
450 object.is_immutable() || object.version() == SequenceNumber::MIN,
451 "Created mutable objects should not have a version set",
452 );
453 let id = object.id();
454 self.execution_results.created_object_ids.insert(id);
455 self.execution_results.written_objects.insert(id, object);
456 }
457
458 pub fn delete_input_object(&mut self, id: &ObjectID) {
460 debug_assert!(!self.execution_results.written_objects.contains_key(id));
462 debug_assert!(self.input_objects.contains_key(id));
463 self.execution_results.modified_objects.insert(*id);
464 self.execution_results.deleted_object_ids.insert(*id);
465 }
466
467 pub fn drop_writes(&mut self) {
468 self.execution_results.drop_writes();
469 }
470
471 pub fn read_object(&self, id: &ObjectID) -> Option<&Object> {
472 debug_assert!(!self.execution_results.deleted_object_ids.contains(id));
474 self.execution_results
475 .written_objects
476 .get(id)
477 .or_else(|| self.input_objects.get(id))
478 }
479
480 pub fn save_loaded_runtime_objects(
481 &mut self,
482 loaded_runtime_objects: BTreeMap<ObjectID, DynamicallyLoadedObjectMetadata>,
483 ) {
484 #[cfg(debug_assertions)]
485 {
486 for (id, v1) in &loaded_runtime_objects {
487 if let Some(v2) = self.loaded_runtime_objects.get(id) {
488 assert_eq!(v1, v2);
489 }
490 }
491 for (id, v1) in &self.loaded_runtime_objects {
492 if let Some(v2) = loaded_runtime_objects.get(id) {
493 assert_eq!(v1, v2);
494 }
495 }
496 }
497 self.loaded_runtime_objects.extend(loaded_runtime_objects);
500 }
501
502 pub fn save_wrapped_object_containers(
503 &mut self,
504 wrapped_object_containers: BTreeMap<ObjectID, ObjectID>,
505 ) {
506 #[cfg(debug_assertions)]
507 {
508 for (id, container1) in &wrapped_object_containers {
509 if let Some(container2) = self.wrapped_object_containers.get(id) {
510 assert_eq!(container1, container2);
511 }
512 }
513 for (id, container1) in &self.wrapped_object_containers {
514 if let Some(container2) = wrapped_object_containers.get(id) {
515 assert_eq!(container1, container2);
516 }
517 }
518 }
519 self.wrapped_object_containers
522 .extend(wrapped_object_containers);
523 }
524
525 pub fn save_generated_object_ids(&mut self, generated_ids: BTreeSet<ObjectID>) {
526 #[cfg(debug_assertions)]
527 {
528 for id in &self.generated_runtime_ids {
529 assert!(!generated_ids.contains(id))
530 }
531 for id in &generated_ids {
532 assert!(!self.generated_runtime_ids.contains(id));
533 }
534 }
535 self.generated_runtime_ids.extend(generated_ids);
536 }
537
538 pub fn estimate_effects_size_upperbound(&self) -> usize {
539 TransactionEffects::estimate_effects_size_upperbound_v2(
540 self.execution_results.written_objects.len(),
541 self.execution_results.modified_objects.len(),
542 self.input_objects.len(),
543 )
544 }
545
546 pub fn written_objects_size(&self) -> usize {
547 self.execution_results
548 .written_objects
549 .values()
550 .fold(0, |sum, obj| sum + obj.object_size_for_gas_metering())
551 }
552
553 pub fn conserve_unmetered_storage_rebate(&mut self, unmetered_storage_rebate: u64) {
558 if unmetered_storage_rebate == 0 {
559 return;
563 }
564 tracing::debug!(
565 "Amount of unmetered storage rebate from system tx: {:?}",
566 unmetered_storage_rebate
567 );
568 let mut system_state_wrapper = self
569 .read_object(&SUI_SYSTEM_STATE_OBJECT_ID)
570 .expect("0x5 object must be mutated in system tx with unmetered storage rebate")
571 .clone();
572 assert_eq!(system_state_wrapper.storage_rebate, 0);
575 system_state_wrapper.storage_rebate = unmetered_storage_rebate;
576 self.mutate_input_object(system_state_wrapper);
577 }
578
579 pub fn add_accumulator_event(&mut self, event: AccumulatorEvent) {
581 self.execution_results.accumulator_events.push(event);
582 }
583
584 fn get_object_modified_at(
590 &self,
591 object_id: &ObjectID,
592 ) -> Option<DynamicallyLoadedObjectMetadata> {
593 if self.execution_results.modified_objects.contains(object_id) {
594 Some(
595 self.mutable_input_refs
596 .get(object_id)
597 .map(
598 |((version, digest), owner)| DynamicallyLoadedObjectMetadata {
599 version: *version,
600 digest: *digest,
601 owner: owner.clone(),
602 storage_rebate: self.input_objects[object_id].storage_rebate,
604 previous_transaction: self.input_objects[object_id]
605 .previous_transaction,
606 },
607 )
608 .or_else(|| self.loaded_runtime_objects.get(object_id).cloned())
609 .unwrap_or_else(|| {
610 debug_assert!(is_system_package(*object_id));
611 let package_obj =
612 self.store.get_package_object(object_id).unwrap().unwrap();
613 let obj = package_obj.object();
614 DynamicallyLoadedObjectMetadata {
615 version: obj.version(),
616 digest: obj.digest(),
617 owner: obj.owner.clone(),
618 storage_rebate: obj.storage_rebate,
619 previous_transaction: obj.previous_transaction,
620 }
621 }),
622 )
623 } else {
624 None
625 }
626 }
627}
628
629impl TemporaryStore<'_> {
630 pub fn check_ownership_invariants(
633 &self,
634 sender: &SuiAddress,
635 sponsor: &Option<SuiAddress>,
636 gas_charger: &mut GasCharger,
637 mutable_inputs: &HashSet<ObjectID>,
638 is_epoch_change: bool,
639 ) -> SuiResult<()> {
640 let gas_objs: HashSet<&ObjectID> = gas_charger.gas_coins().map(|g| &g.0).collect();
641 let gas_owner = sponsor.as_ref().unwrap_or(sender);
642
643 let mut authenticated_for_mutation: HashSet<_> = self
645 .input_objects
646 .iter()
647 .filter_map(|(id, obj)| {
648 match &obj.owner {
649 Owner::AddressOwner(a) => {
650 if gas_objs.contains(id) {
651 assert!(
653 a == gas_owner,
654 "Gas object must be owned by sender or sponsor"
655 );
656 } else {
657 assert!(sender == a, "Input object must be owned by sender");
658 }
659 Some(id)
660 }
661 Owner::Shared { .. } | Owner::ConsensusAddressOwner { .. } => Some(id),
662 Owner::Immutable => {
663 None
674 }
675 Owner::ObjectOwner(_parent) => {
676 unreachable!(
677 "Input objects must be address owned, shared, consensus, or immutable"
678 )
679 }
680 }
681 })
682 .filter(|id| {
683 mutable_inputs.contains(id)
686 })
687 .copied()
688 .chain(self.generated_runtime_ids.iter().copied())
691 .collect();
692
693 authenticated_for_mutation.insert((*sender).into());
695 if let Some(sponsor) = sponsor {
696 authenticated_for_mutation.insert((*sponsor).into());
697 }
698
699 let mut objects_to_authenticate = self
701 .execution_results
702 .modified_objects
703 .iter()
704 .copied()
705 .collect::<Vec<_>>();
706
707 while let Some(to_authenticate) = objects_to_authenticate.pop() {
708 if authenticated_for_mutation.contains(&to_authenticate) {
709 continue;
711 }
712
713 let parent = if let Some(container_id) =
714 self.wrapped_object_containers.get(&to_authenticate)
715 {
716 *container_id
718 } else {
719 let Some(old_obj) = self.store.get_object(&to_authenticate) else {
722 panic!(
723 "Failed to load object {to_authenticate:?}.\n \
724 If it cannot be loaded, we would expect it to be in the wrapped object map: {:#?}",
725 &self.wrapped_object_containers
726 )
727 };
728
729 match &old_obj.owner {
730 Owner::ObjectOwner(parent) => ObjectID::from(*parent),
733 Owner::AddressOwner(parent)
738 | Owner::ConsensusAddressOwner { owner: parent, .. } => {
739 ObjectID::from(*parent)
743 }
744 owner @ Owner::Shared { .. } => {
747 panic!(
748 "Unauthenticated root at {to_authenticate:?} with owner {owner:?}\n\
749 Potentially covering objects in: {authenticated_for_mutation:#?}"
750 );
751 }
752 Owner::Immutable => {
753 assert!(
754 is_epoch_change,
755 "Immutable objects cannot be written, except for \
756 Sui Framework/Move stdlib upgrades at epoch change boundaries"
757 );
758 assert!(
762 is_system_package(to_authenticate),
763 "Only system packages can be upgraded"
764 );
765 continue;
766 }
767 }
768 };
769
770 authenticated_for_mutation.insert(to_authenticate);
772 objects_to_authenticate.push(parent);
773 }
774 Ok(())
775 }
776}
777
778impl TemporaryStore<'_> {
779 pub(crate) fn collect_storage_and_rebate(&mut self, gas_charger: &mut GasCharger) {
786 let old_storage_rebates: Vec<_> = self
788 .execution_results
789 .written_objects
790 .keys()
791 .map(|object_id| {
792 self.get_object_modified_at(object_id)
793 .map(|metadata| metadata.storage_rebate)
794 .unwrap_or_default()
795 })
796 .collect();
797 for (object, old_storage_rebate) in self
798 .execution_results
799 .written_objects
800 .values_mut()
801 .zip(old_storage_rebates)
802 {
803 let new_object_size = object.object_size_for_gas_metering();
805 let new_storage_rebate = gas_charger.track_storage_mutation(
807 object.id(),
808 new_object_size,
809 old_storage_rebate,
810 );
811 object.storage_rebate = new_storage_rebate;
812 }
813
814 self.collect_rebate(gas_charger);
815 }
816
817 pub(crate) fn collect_rebate(&self, gas_charger: &mut GasCharger) {
818 for object_id in &self.execution_results.modified_objects {
819 if self
820 .execution_results
821 .written_objects
822 .contains_key(object_id)
823 {
824 continue;
825 }
826 let storage_rebate = self
828 .get_object_modified_at(object_id)
829 .unwrap()
831 .storage_rebate;
832 gas_charger.track_storage_mutation(*object_id, 0, storage_rebate);
833 }
834 }
835
836 pub fn check_execution_results_consistency(&self) -> Result<(), ExecutionError> {
837 assert_invariant!(
838 self.execution_results
839 .created_object_ids
840 .iter()
841 .all(|id| !self.execution_results.deleted_object_ids.contains(id)
842 && !self.execution_results.modified_objects.contains(id)),
843 "Created object IDs cannot also be deleted or modified"
844 );
845 assert_invariant!(
846 self.execution_results.modified_objects.iter().all(|id| {
847 self.mutable_input_refs.contains_key(id)
848 || self.loaded_runtime_objects.contains_key(id)
849 || is_system_package(*id)
850 }),
851 "A modified object must be either a mutable input, a loaded child object, or a system package"
852 );
853 Ok(())
854 }
855}
856impl TemporaryStore<'_> {
861 pub fn advance_epoch_safe_mode(
862 &mut self,
863 params: &AdvanceEpochParams,
864 protocol_config: &ProtocolConfig,
865 ) {
866 let wrapper = get_sui_system_state_wrapper(self.store.as_object_store())
867 .expect("System state wrapper object must exist");
868 let (old_object, new_object) =
869 wrapper.advance_epoch_safe_mode(params, self.store.as_object_store(), protocol_config);
870 self.mutate_child_object(old_object, new_object);
871 }
872}
873
874type ModifiedObjectInfo<'a> = (
875 ObjectID,
876 Option<DynamicallyLoadedObjectMetadata>,
878 Option<&'a Object>,
879);
880
881impl TemporaryStore<'_> {
882 fn get_input_sui(
883 &self,
884 id: &ObjectID,
885 expected_version: SequenceNumber,
886 layout_resolver: &mut impl LayoutResolver,
887 ) -> Result<u64, ExecutionError> {
888 if let Some(obj) = self.input_objects.get(id) {
889 if obj.version() != expected_version {
891 invariant_violation!(
892 "Version mismatching when resolving input object to check conservation--\
893 expected {}, got {}",
894 expected_version,
895 obj.version(),
896 );
897 }
898 obj.get_total_sui(layout_resolver).map_err(|e| {
899 make_invariant_violation!(
900 "Failed looking up input SUI in SUI conservation checking for input with \
901 type {:?}: {e:#?}",
902 obj.struct_tag(),
903 )
904 })
905 } else {
906 let Some(obj) = self.store.get_object_by_key(id, expected_version) else {
908 invariant_violation!(
909 "Failed looking up dynamic field {id} in SUI conservation checking"
910 );
911 };
912 obj.get_total_sui(layout_resolver).map_err(|e| {
913 make_invariant_violation!(
914 "Failed looking up input SUI in SUI conservation checking for type \
915 {:?}: {e:#?}",
916 obj.struct_tag(),
917 )
918 })
919 }
920 }
921
922 fn get_modified_objects(&self) -> Vec<ModifiedObjectInfo<'_>> {
927 self.execution_results
928 .modified_objects
929 .iter()
930 .map(|id| {
931 let metadata = self.get_object_modified_at(id);
932 let output = self.execution_results.written_objects.get(id);
933 (*id, metadata, output)
934 })
935 .chain(
936 self.execution_results
937 .written_objects
938 .iter()
939 .filter_map(|(id, object)| {
940 if self.execution_results.modified_objects.contains(id) {
941 None
942 } else {
943 Some((*id, None, Some(object)))
944 }
945 }),
946 )
947 .collect()
948 }
949
950 pub fn check_sui_conserved(
964 &self,
965 simple_conservation_checks: bool,
966 gas_summary: &GasCostSummary,
967 ) -> Result<(), ExecutionError> {
968 if !simple_conservation_checks {
969 return Ok(());
970 }
971 let mut total_input_rebate = 0;
973 let mut total_output_rebate = 0;
975 for (_, input, output) in self.get_modified_objects() {
976 if let Some(input) = input {
977 total_input_rebate += input.storage_rebate;
978 }
979 if let Some(object) = output {
980 total_output_rebate += object.storage_rebate;
981 }
982 }
983
984 if gas_summary.storage_cost == 0 {
985 if total_input_rebate
997 != total_output_rebate
998 + gas_summary.storage_rebate
999 + gas_summary.non_refundable_storage_fee
1000 {
1001 return Err(ExecutionError::invariant_violation(format!(
1002 "SUI conservation failed -- no storage charges in gas summary \
1003 and total storage input rebate {} not equal \
1004 to total storage output rebate {}",
1005 total_input_rebate, total_output_rebate,
1006 )));
1007 }
1008 } else {
1009 if total_input_rebate
1012 != gas_summary.storage_rebate + gas_summary.non_refundable_storage_fee
1013 {
1014 return Err(ExecutionError::invariant_violation(format!(
1015 "SUI conservation failed -- {} SUI in storage rebate field of input objects, \
1016 {} SUI in tx storage rebate or tx non-refundable storage rebate",
1017 total_input_rebate, gas_summary.non_refundable_storage_fee,
1018 )));
1019 }
1020
1021 if gas_summary.storage_cost != total_output_rebate {
1024 return Err(ExecutionError::invariant_violation(format!(
1025 "SUI conservation failed -- {} SUI charged for storage, \
1026 {} SUI in storage rebate field of output objects",
1027 gas_summary.storage_cost, total_output_rebate
1028 )));
1029 }
1030 }
1031 Ok(())
1032 }
1033
1034 pub fn check_sui_conserved_expensive(
1047 &self,
1048 gas_summary: &GasCostSummary,
1049 advance_epoch_gas_summary: Option<(u64, u64)>,
1050 layout_resolver: &mut impl LayoutResolver,
1051 ) -> Result<(), ExecutionError> {
1052 let mut total_input_sui = 0;
1054 let mut total_output_sui = 0;
1056
1057 total_input_sui += self.execution_results.settlement_input_sui;
1061 total_output_sui += self.execution_results.settlement_output_sui;
1062
1063 for (id, input, output) in self.get_modified_objects() {
1064 if let Some(input) = input {
1065 total_input_sui += self.get_input_sui(&id, input.version, layout_resolver)?;
1066 }
1067 if let Some(object) = output {
1068 total_output_sui += object.get_total_sui(layout_resolver).map_err(|e| {
1069 make_invariant_violation!(
1070 "Failed looking up output SUI in SUI conservation checking for \
1071 mutated type {:?}: {e:#?}",
1072 object.struct_tag(),
1073 )
1074 })?;
1075 }
1076 }
1077
1078 for event in &self.execution_results.accumulator_events {
1079 let (input, output) = event.total_sui_in_event();
1080 total_input_sui += input;
1081 total_output_sui += output;
1082 }
1083
1084 total_output_sui += gas_summary.computation_cost + gas_summary.non_refundable_storage_fee;
1089 if let Some((epoch_fees, epoch_rebates)) = advance_epoch_gas_summary {
1090 total_input_sui += epoch_fees;
1091 total_output_sui += epoch_rebates;
1092 }
1093 if total_input_sui != total_output_sui {
1094 return Err(ExecutionError::invariant_violation(format!(
1095 "SUI conservation failed: input={}, output={}, \
1096 this transaction either mints or burns SUI",
1097 total_input_sui, total_output_sui,
1098 )));
1099 }
1100 Ok(())
1101 }
1102}
1103
1104impl ChildObjectResolver for TemporaryStore<'_> {
1105 fn read_child_object(
1106 &self,
1107 parent: &ObjectID,
1108 child: &ObjectID,
1109 child_version_upper_bound: SequenceNumber,
1110 ) -> SuiResult<Option<Object>> {
1111 let obj_opt = self.execution_results.written_objects.get(child);
1112 if obj_opt.is_some() {
1113 Ok(obj_opt.cloned())
1114 } else {
1115 let _scope = monitored_scope("Execution::read_child_object");
1116 self.store
1117 .read_child_object(parent, child, child_version_upper_bound)
1118 }
1119 }
1120
1121 fn get_object_received_at_version(
1122 &self,
1123 owner: &ObjectID,
1124 receiving_object_id: &ObjectID,
1125 receive_object_at_version: SequenceNumber,
1126 epoch_id: EpochId,
1127 ) -> SuiResult<Option<Object>> {
1128 debug_assert!(
1131 !self
1132 .execution_results
1133 .written_objects
1134 .contains_key(receiving_object_id)
1135 );
1136 debug_assert!(
1137 !self
1138 .execution_results
1139 .deleted_object_ids
1140 .contains(receiving_object_id)
1141 );
1142 self.store.get_object_received_at_version(
1143 owner,
1144 receiving_object_id,
1145 receive_object_at_version,
1146 epoch_id,
1147 )
1148 }
1149}
1150
1151fn was_object_mutated(object: &Object, original: &Object) -> bool {
1154 let data_equal = match (&object.data, &original.data) {
1155 (Data::Move(a), Data::Move(b)) => a.contents_and_type_equal(b),
1156 (Data::Package(a), Data::Package(b)) => a == b,
1159 _ => false,
1160 };
1161
1162 let owner_equal = match (&object.owner, &original.owner) {
1163 (Owner::Shared { .. }, Owner::Shared { .. }) => true,
1167 (
1168 Owner::ConsensusAddressOwner { owner: a, .. },
1169 Owner::ConsensusAddressOwner { owner: b, .. },
1170 ) => a == b,
1171 (Owner::AddressOwner(a), Owner::AddressOwner(b)) => a == b,
1172 (Owner::Immutable, Owner::Immutable) => true,
1173 (Owner::ObjectOwner(a), Owner::ObjectOwner(b)) => a == b,
1174
1175 (Owner::AddressOwner(_), _)
1178 | (Owner::Immutable, _)
1179 | (Owner::ObjectOwner(_), _)
1180 | (Owner::Shared { .. }, _)
1181 | (Owner::ConsensusAddressOwner { .. }, _) => false,
1182 };
1183
1184 !data_equal || !owner_equal
1185}
1186
1187impl Storage for TemporaryStore<'_> {
1188 fn reset(&mut self) {
1189 self.drop_writes();
1190 }
1191
1192 fn read_object(&self, id: &ObjectID) -> Option<&Object> {
1193 TemporaryStore::read_object(self, id)
1194 }
1195
1196 fn record_execution_results(
1198 &mut self,
1199 results: ExecutionResults,
1200 ) -> Result<(), ExecutionError> {
1201 let ExecutionResults::V2(mut results) = results else {
1202 panic!("ExecutionResults::V2 expected in sui-execution v1 and above");
1203 };
1204
1205 let mut to_remove = Vec::new();
1207 for (id, original) in &self.non_exclusive_input_original_versions {
1208 if results
1210 .written_objects
1211 .get(id)
1212 .map(|obj| was_object_mutated(obj, original))
1213 .unwrap_or(true)
1214 {
1215 return Err(ExecutionError::new_with_source(
1216 ExecutionErrorKind::NonExclusiveWriteInputObjectModified { id: *id },
1217 "Non-exclusive write input object has been modified or deleted",
1218 ));
1219 }
1220 to_remove.push(*id);
1221 }
1222
1223 for id in to_remove {
1224 results.written_objects.remove(&id);
1225 results.modified_objects.remove(&id);
1226 }
1227
1228 self.execution_results
1231 .merge_results(
1232 results, false, false,
1233 )
1234 .unwrap();
1235
1236 Ok(())
1237 }
1238
1239 fn save_loaded_runtime_objects(
1240 &mut self,
1241 loaded_runtime_objects: BTreeMap<ObjectID, DynamicallyLoadedObjectMetadata>,
1242 ) {
1243 TemporaryStore::save_loaded_runtime_objects(self, loaded_runtime_objects)
1244 }
1245
1246 fn save_wrapped_object_containers(
1247 &mut self,
1248 wrapped_object_containers: BTreeMap<ObjectID, ObjectID>,
1249 ) {
1250 TemporaryStore::save_wrapped_object_containers(self, wrapped_object_containers)
1251 }
1252
1253 fn check_coin_deny_list(
1254 &self,
1255 receiving_funds_type_and_owners: BTreeMap<TypeTag, BTreeSet<SuiAddress>>,
1256 ) -> DenyListResult {
1257 let result = check_coin_deny_list_v2_during_execution(
1258 receiving_funds_type_and_owners,
1259 self.cur_epoch,
1260 self.store.as_object_store(),
1261 );
1262 if result.num_non_gas_coin_owners > 0
1265 && !self.input_objects.contains_key(&SUI_DENY_LIST_OBJECT_ID)
1266 {
1267 self.loaded_per_epoch_config_objects
1268 .write()
1269 .insert(SUI_DENY_LIST_OBJECT_ID);
1270 }
1271 result
1272 }
1273
1274 fn record_generated_object_ids(&mut self, generated_ids: BTreeSet<ObjectID>) {
1275 TemporaryStore::save_generated_object_ids(self, generated_ids)
1276 }
1277}
1278
1279impl BackingPackageStore for TemporaryStore<'_> {
1280 fn get_package_object(&self, package_id: &ObjectID) -> SuiResult<Option<PackageObject>> {
1281 if let Some(obj) = self.execution_results.written_objects.get(package_id) {
1288 Ok(Some(PackageObject::new(obj.clone())))
1289 } else {
1290 self.store.get_package_object(package_id).inspect(|obj| {
1291 if let Some(v) = obj
1293 && !self
1294 .runtime_packages_loaded_from_db
1295 .read()
1296 .contains_key(package_id)
1297 {
1298 self.runtime_packages_loaded_from_db
1303 .write()
1304 .insert(*package_id, v.clone());
1305 }
1306 })
1307 }
1308 }
1309}
1310
1311impl ParentSync for TemporaryStore<'_> {
1312 fn get_latest_parent_entry_ref_deprecated(&self, _object_id: ObjectID) -> Option<ObjectRef> {
1313 unreachable!("Never called in newer protocol versions")
1314 }
1315}