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 Owner::Party { .. } => {
681 unimplemented!("Party does not exist for this execution version")
682 }
683 }
684 })
685 .filter(|id| {
686 mutable_inputs.contains(id)
689 })
690 .copied()
691 .chain(self.generated_runtime_ids.iter().copied())
694 .collect();
695
696 authenticated_for_mutation.insert((*sender).into());
698 if let Some(sponsor) = sponsor {
699 authenticated_for_mutation.insert((*sponsor).into());
700 }
701
702 let mut objects_to_authenticate = self
704 .execution_results
705 .modified_objects
706 .iter()
707 .copied()
708 .collect::<Vec<_>>();
709
710 while let Some(to_authenticate) = objects_to_authenticate.pop() {
711 if authenticated_for_mutation.contains(&to_authenticate) {
712 continue;
714 }
715
716 let parent = if let Some(container_id) =
717 self.wrapped_object_containers.get(&to_authenticate)
718 {
719 *container_id
721 } else {
722 let Some(old_obj) = self.store.get_object(&to_authenticate) else {
725 panic!(
726 "Failed to load object {to_authenticate:?}.\n \
727 If it cannot be loaded, we would expect it to be in the wrapped object map: {:#?}",
728 &self.wrapped_object_containers
729 )
730 };
731
732 match &old_obj.owner {
733 Owner::ObjectOwner(parent) => ObjectID::from(*parent),
736 Owner::AddressOwner(parent)
741 | Owner::ConsensusAddressOwner { owner: parent, .. } => {
742 ObjectID::from(*parent)
746 }
747 owner @ Owner::Shared { .. } => {
750 panic!(
751 "Unauthenticated root at {to_authenticate:?} with owner {owner:?}\n\
752 Potentially covering objects in: {authenticated_for_mutation:#?}"
753 );
754 }
755 Owner::Immutable => {
756 assert!(
757 is_epoch_change,
758 "Immutable objects cannot be written, except for \
759 Sui Framework/Move stdlib upgrades at epoch change boundaries"
760 );
761 assert!(
765 is_system_package(to_authenticate),
766 "Only system packages can be upgraded"
767 );
768 continue;
769 }
770 Owner::Party { .. } => {
771 unimplemented!("Party does not exist for this execution version")
772 }
773 }
774 };
775
776 authenticated_for_mutation.insert(to_authenticate);
778 objects_to_authenticate.push(parent);
779 }
780 Ok(())
781 }
782}
783
784impl TemporaryStore<'_> {
785 pub(crate) fn collect_storage_and_rebate(&mut self, gas_charger: &mut GasCharger) {
792 let old_storage_rebates: Vec<_> = self
794 .execution_results
795 .written_objects
796 .keys()
797 .map(|object_id| {
798 self.get_object_modified_at(object_id)
799 .map(|metadata| metadata.storage_rebate)
800 .unwrap_or_default()
801 })
802 .collect();
803 for (object, old_storage_rebate) in self
804 .execution_results
805 .written_objects
806 .values_mut()
807 .zip(old_storage_rebates)
808 {
809 let new_object_size = object.object_size_for_gas_metering();
811 let new_storage_rebate = gas_charger.track_storage_mutation(
813 object.id(),
814 new_object_size,
815 old_storage_rebate,
816 );
817 object.storage_rebate = new_storage_rebate;
818 }
819
820 self.collect_rebate(gas_charger);
821 }
822
823 pub(crate) fn collect_rebate(&self, gas_charger: &mut GasCharger) {
824 for object_id in &self.execution_results.modified_objects {
825 if self
826 .execution_results
827 .written_objects
828 .contains_key(object_id)
829 {
830 continue;
831 }
832 let storage_rebate = self
834 .get_object_modified_at(object_id)
835 .unwrap()
837 .storage_rebate;
838 gas_charger.track_storage_mutation(*object_id, 0, storage_rebate);
839 }
840 }
841
842 pub fn check_execution_results_consistency(&self) -> Result<(), ExecutionError> {
843 assert_invariant!(
844 self.execution_results
845 .created_object_ids
846 .iter()
847 .all(|id| !self.execution_results.deleted_object_ids.contains(id)
848 && !self.execution_results.modified_objects.contains(id)),
849 "Created object IDs cannot also be deleted or modified"
850 );
851 assert_invariant!(
852 self.execution_results.modified_objects.iter().all(|id| {
853 self.mutable_input_refs.contains_key(id)
854 || self.loaded_runtime_objects.contains_key(id)
855 || is_system_package(*id)
856 }),
857 "A modified object must be either a mutable input, a loaded child object, or a system package"
858 );
859 Ok(())
860 }
861}
862impl TemporaryStore<'_> {
867 pub fn advance_epoch_safe_mode(
868 &mut self,
869 params: &AdvanceEpochParams,
870 protocol_config: &ProtocolConfig,
871 ) {
872 let wrapper = get_sui_system_state_wrapper(self.store.as_object_store())
873 .expect("System state wrapper object must exist");
874 let (old_object, new_object) =
875 wrapper.advance_epoch_safe_mode(params, self.store.as_object_store(), protocol_config);
876 self.mutate_child_object(old_object, new_object);
877 }
878}
879
880type ModifiedObjectInfo<'a> = (
881 ObjectID,
882 Option<DynamicallyLoadedObjectMetadata>,
884 Option<&'a Object>,
885);
886
887impl TemporaryStore<'_> {
888 fn get_input_sui(
889 &self,
890 id: &ObjectID,
891 expected_version: SequenceNumber,
892 layout_resolver: &mut impl LayoutResolver,
893 ) -> Result<u64, ExecutionError> {
894 if let Some(obj) = self.input_objects.get(id) {
895 if obj.version() != expected_version {
897 invariant_violation!(
898 "Version mismatching when resolving input object to check conservation--\
899 expected {}, got {}",
900 expected_version,
901 obj.version(),
902 );
903 }
904 obj.get_total_sui(layout_resolver).map_err(|e| {
905 make_invariant_violation!(
906 "Failed looking up input SUI in SUI conservation checking for input with \
907 type {:?}: {e:#?}",
908 obj.struct_tag(),
909 )
910 })
911 } else {
912 let Some(obj) = self.store.get_object_by_key(id, expected_version) else {
914 invariant_violation!(
915 "Failed looking up dynamic field {id} in SUI conservation checking"
916 );
917 };
918 obj.get_total_sui(layout_resolver).map_err(|e| {
919 make_invariant_violation!(
920 "Failed looking up input SUI in SUI conservation checking for type \
921 {:?}: {e:#?}",
922 obj.struct_tag(),
923 )
924 })
925 }
926 }
927
928 fn get_modified_objects(&self) -> Vec<ModifiedObjectInfo<'_>> {
933 self.execution_results
934 .modified_objects
935 .iter()
936 .map(|id| {
937 let metadata = self.get_object_modified_at(id);
938 let output = self.execution_results.written_objects.get(id);
939 (*id, metadata, output)
940 })
941 .chain(
942 self.execution_results
943 .written_objects
944 .iter()
945 .filter_map(|(id, object)| {
946 if self.execution_results.modified_objects.contains(id) {
947 None
948 } else {
949 Some((*id, None, Some(object)))
950 }
951 }),
952 )
953 .collect()
954 }
955
956 pub fn check_sui_conserved(
970 &self,
971 simple_conservation_checks: bool,
972 gas_summary: &GasCostSummary,
973 ) -> Result<(), ExecutionError> {
974 if !simple_conservation_checks {
975 return Ok(());
976 }
977 let mut total_input_rebate = 0;
979 let mut total_output_rebate = 0;
981 for (_, input, output) in self.get_modified_objects() {
982 if let Some(input) = input {
983 total_input_rebate += input.storage_rebate;
984 }
985 if let Some(object) = output {
986 total_output_rebate += object.storage_rebate;
987 }
988 }
989
990 if gas_summary.storage_cost == 0 {
991 if total_input_rebate
1003 != total_output_rebate
1004 + gas_summary.storage_rebate
1005 + gas_summary.non_refundable_storage_fee
1006 {
1007 return Err(ExecutionError::invariant_violation(format!(
1008 "SUI conservation failed -- no storage charges in gas summary \
1009 and total storage input rebate {} not equal \
1010 to total storage output rebate {}",
1011 total_input_rebate, total_output_rebate,
1012 )));
1013 }
1014 } else {
1015 if total_input_rebate
1018 != gas_summary.storage_rebate + gas_summary.non_refundable_storage_fee
1019 {
1020 return Err(ExecutionError::invariant_violation(format!(
1021 "SUI conservation failed -- {} SUI in storage rebate field of input objects, \
1022 {} SUI in tx storage rebate or tx non-refundable storage rebate",
1023 total_input_rebate, gas_summary.non_refundable_storage_fee,
1024 )));
1025 }
1026
1027 if gas_summary.storage_cost != total_output_rebate {
1030 return Err(ExecutionError::invariant_violation(format!(
1031 "SUI conservation failed -- {} SUI charged for storage, \
1032 {} SUI in storage rebate field of output objects",
1033 gas_summary.storage_cost, total_output_rebate
1034 )));
1035 }
1036 }
1037 Ok(())
1038 }
1039
1040 pub fn check_sui_conserved_expensive(
1053 &self,
1054 gas_summary: &GasCostSummary,
1055 advance_epoch_gas_summary: Option<(u64, u64)>,
1056 layout_resolver: &mut impl LayoutResolver,
1057 ) -> Result<(), ExecutionError> {
1058 let mut total_input_sui = 0;
1060 let mut total_output_sui = 0;
1062
1063 total_input_sui += self.execution_results.settlement_input_sui;
1067 total_output_sui += self.execution_results.settlement_output_sui;
1068
1069 for (id, input, output) in self.get_modified_objects() {
1070 if let Some(input) = input {
1071 total_input_sui += self.get_input_sui(&id, input.version, layout_resolver)?;
1072 }
1073 if let Some(object) = output {
1074 total_output_sui += object.get_total_sui(layout_resolver).map_err(|e| {
1075 make_invariant_violation!(
1076 "Failed looking up output SUI in SUI conservation checking for \
1077 mutated type {:?}: {e:#?}",
1078 object.struct_tag(),
1079 )
1080 })?;
1081 }
1082 }
1083
1084 for event in &self.execution_results.accumulator_events {
1085 let (input, output) = event.total_sui_in_event();
1086 total_input_sui += input;
1087 total_output_sui += output;
1088 }
1089
1090 total_output_sui += gas_summary.computation_cost + gas_summary.non_refundable_storage_fee;
1095 if let Some((epoch_fees, epoch_rebates)) = advance_epoch_gas_summary {
1096 total_input_sui += epoch_fees;
1097 total_output_sui += epoch_rebates;
1098 }
1099 if total_input_sui != total_output_sui {
1100 return Err(ExecutionError::invariant_violation(format!(
1101 "SUI conservation failed: input={}, output={}, \
1102 this transaction either mints or burns SUI",
1103 total_input_sui, total_output_sui,
1104 )));
1105 }
1106 Ok(())
1107 }
1108}
1109
1110impl ChildObjectResolver for TemporaryStore<'_> {
1111 fn read_child_object(
1112 &self,
1113 parent: &ObjectID,
1114 child: &ObjectID,
1115 child_version_upper_bound: SequenceNumber,
1116 ) -> SuiResult<Option<Object>> {
1117 let obj_opt = self.execution_results.written_objects.get(child);
1118 if obj_opt.is_some() {
1119 Ok(obj_opt.cloned())
1120 } else {
1121 let _scope = monitored_scope("Execution::read_child_object");
1122 self.store
1123 .read_child_object(parent, child, child_version_upper_bound)
1124 }
1125 }
1126
1127 fn get_object_received_at_version(
1128 &self,
1129 owner: &ObjectID,
1130 receiving_object_id: &ObjectID,
1131 receive_object_at_version: SequenceNumber,
1132 epoch_id: EpochId,
1133 ) -> SuiResult<Option<Object>> {
1134 debug_assert!(
1137 !self
1138 .execution_results
1139 .written_objects
1140 .contains_key(receiving_object_id)
1141 );
1142 debug_assert!(
1143 !self
1144 .execution_results
1145 .deleted_object_ids
1146 .contains(receiving_object_id)
1147 );
1148 self.store.get_object_received_at_version(
1149 owner,
1150 receiving_object_id,
1151 receive_object_at_version,
1152 epoch_id,
1153 )
1154 }
1155}
1156
1157fn was_object_mutated(object: &Object, original: &Object) -> bool {
1160 let data_equal = match (&object.data, &original.data) {
1161 (Data::Move(a), Data::Move(b)) => a.contents_and_type_equal(b),
1162 (Data::Package(a), Data::Package(b)) => a == b,
1165 _ => false,
1166 };
1167
1168 let owner_equal = match (&object.owner, &original.owner) {
1169 (Owner::Shared { .. }, Owner::Shared { .. }) => true,
1173 (
1174 Owner::ConsensusAddressOwner { owner: a, .. },
1175 Owner::ConsensusAddressOwner { owner: b, .. },
1176 ) => a == b,
1177 (Owner::AddressOwner(a), Owner::AddressOwner(b)) => a == b,
1178 (Owner::Immutable, Owner::Immutable) => true,
1179 (Owner::ObjectOwner(a), Owner::ObjectOwner(b)) => a == b,
1180
1181 (Owner::AddressOwner(_), _)
1184 | (Owner::Immutable, _)
1185 | (Owner::ObjectOwner(_), _)
1186 | (Owner::Shared { .. }, _)
1187 | (Owner::ConsensusAddressOwner { .. }, _) => false,
1188 (Owner::Party { .. }, _) => {
1189 unimplemented!("Party does not exist for this execution version")
1190 }
1191 };
1192
1193 !data_equal || !owner_equal
1194}
1195
1196impl Storage for TemporaryStore<'_> {
1197 fn reset(&mut self) {
1198 self.drop_writes();
1199 }
1200
1201 fn read_object(&self, id: &ObjectID) -> Option<&Object> {
1202 TemporaryStore::read_object(self, id)
1203 }
1204
1205 fn record_execution_results(
1207 &mut self,
1208 results: ExecutionResults,
1209 ) -> Result<(), ExecutionError> {
1210 let ExecutionResults::V2(mut results) = results else {
1211 panic!("ExecutionResults::V2 expected in sui-execution v1 and above");
1212 };
1213
1214 let mut to_remove = Vec::new();
1216 for (id, original) in &self.non_exclusive_input_original_versions {
1217 if results
1219 .written_objects
1220 .get(id)
1221 .map(|obj| was_object_mutated(obj, original))
1222 .unwrap_or(true)
1223 {
1224 return Err(ExecutionError::new_with_source(
1225 ExecutionErrorKind::NonExclusiveWriteInputObjectModified { id: *id },
1226 "Non-exclusive write input object has been modified or deleted",
1227 ));
1228 }
1229 to_remove.push(*id);
1230 }
1231
1232 for id in to_remove {
1233 results.written_objects.remove(&id);
1234 results.modified_objects.remove(&id);
1235 }
1236
1237 self.execution_results
1240 .merge_results(
1241 results, false, false,
1242 )
1243 .unwrap();
1244
1245 Ok(())
1246 }
1247
1248 fn save_loaded_runtime_objects(
1249 &mut self,
1250 loaded_runtime_objects: BTreeMap<ObjectID, DynamicallyLoadedObjectMetadata>,
1251 ) {
1252 TemporaryStore::save_loaded_runtime_objects(self, loaded_runtime_objects)
1253 }
1254
1255 fn save_wrapped_object_containers(
1256 &mut self,
1257 wrapped_object_containers: BTreeMap<ObjectID, ObjectID>,
1258 ) {
1259 TemporaryStore::save_wrapped_object_containers(self, wrapped_object_containers)
1260 }
1261
1262 fn check_coin_deny_list(
1263 &self,
1264 receiving_funds_type_and_owners: BTreeMap<TypeTag, BTreeSet<SuiAddress>>,
1265 ) -> DenyListResult {
1266 let result = check_coin_deny_list_v2_during_execution(
1267 receiving_funds_type_and_owners,
1268 self.cur_epoch,
1269 self.store.as_object_store(),
1270 );
1271 if result.num_non_gas_coin_owners > 0
1274 && !self.input_objects.contains_key(&SUI_DENY_LIST_OBJECT_ID)
1275 {
1276 self.loaded_per_epoch_config_objects
1277 .write()
1278 .insert(SUI_DENY_LIST_OBJECT_ID);
1279 }
1280 result
1281 }
1282
1283 fn record_generated_object_ids(&mut self, generated_ids: BTreeSet<ObjectID>) {
1284 TemporaryStore::save_generated_object_ids(self, generated_ids)
1285 }
1286}
1287
1288impl BackingPackageStore for TemporaryStore<'_> {
1289 fn get_package_object(&self, package_id: &ObjectID) -> SuiResult<Option<PackageObject>> {
1290 if let Some(obj) = self.execution_results.written_objects.get(package_id) {
1297 Ok(Some(PackageObject::new(obj.clone())))
1298 } else {
1299 self.store.get_package_object(package_id).inspect(|obj| {
1300 if let Some(v) = obj
1302 && !self
1303 .runtime_packages_loaded_from_db
1304 .read()
1305 .contains_key(package_id)
1306 {
1307 self.runtime_packages_loaded_from_db
1312 .write()
1313 .insert(*package_id, v.clone());
1314 }
1315 })
1316 }
1317 }
1318}
1319
1320impl ParentSync for TemporaryStore<'_> {
1321 fn get_latest_parent_entry_ref_deprecated(&self, _object_id: ObjectID) -> Option<ObjectRef> {
1322 unreachable!("Never called in newer protocol versions")
1323 }
1324}