1use crate::gas_charger::GasCharger;
5use parking_lot::RwLock;
6use std::collections::{BTreeMap, BTreeSet, HashSet};
7use sui_protocol_config::ProtocolConfig;
8use sui_types::base_types::VersionDigest;
9use sui_types::committee::EpochId;
10use sui_types::digests::ObjectDigest;
11use sui_types::effects::{TransactionEffects, TransactionEffectsV2, TransactionEvents};
12use sui_types::execution::{
13 DynamicallyLoadedObjectMetadata, ExecutionResults, ExecutionResultsV2, SharedInput,
14};
15use sui_types::execution_status::ExecutionStatus;
16use sui_types::inner_temporary_store::InnerTemporaryStore;
17use sui_types::layout_resolver::LayoutResolver;
18use sui_types::storage::{BackingStore, DenyListResult, PackageObject};
19use sui_types::sui_system_state::{get_sui_system_state_wrapper, AdvanceEpochParams};
20use sui_types::{
21 base_types::{ObjectID, ObjectRef, SequenceNumber, SuiAddress, TransactionDigest},
22 effects::EffectsObjectChange,
23 error::{ExecutionError, SuiResult},
24 gas::GasCostSummary,
25 object::Object,
26 object::Owner,
27 storage::{BackingPackageStore, ParentSync, RuntimeObjectResolver, Storage},
28 transaction::InputObjects,
29 TypeTag,
30};
31use sui_types::{is_system_package, SUI_SYSTEM_STATE_OBJECT_ID};
32
33pub struct TemporaryStore<'backing> {
34 store: &'backing dyn BackingStore,
40 tx_digest: TransactionDigest,
41 input_objects: BTreeMap<ObjectID, Object>,
42 deleted_consensus_objects: BTreeMap<ObjectID, SequenceNumber>,
43 lamport_timestamp: SequenceNumber,
45 mutable_input_refs: BTreeMap<ObjectID, (VersionDigest, Owner)>, execution_results: ExecutionResultsV2,
47 loaded_runtime_objects: BTreeMap<ObjectID, DynamicallyLoadedObjectMetadata>,
49 protocol_config: ProtocolConfig,
50
51 runtime_packages_loaded_from_db: RwLock<BTreeMap<ObjectID, PackageObject>>,
54
55 receiving_objects: Vec<ObjectRef>,
58}
59
60impl<'backing> TemporaryStore<'backing> {
61 pub fn new(
64 store: &'backing dyn BackingStore,
65 input_objects: InputObjects,
66 receiving_objects: Vec<ObjectRef>,
67 tx_digest: TransactionDigest,
68 protocol_config: &ProtocolConfig,
69 ) -> Self {
70 let mutable_input_refs = input_objects.exclusive_mutable_inputs();
71 let lamport_timestamp = input_objects.lamport_timestamp(&receiving_objects);
72 let deleted_consensus_objects = input_objects.consensus_stream_ended_objects();
73 let objects = input_objects.into_object_map();
74 Self {
75 store,
76 tx_digest,
77 input_objects: objects,
78 deleted_consensus_objects,
79 lamport_timestamp,
80 mutable_input_refs,
81 execution_results: ExecutionResultsV2::default(),
82 protocol_config: protocol_config.clone(),
83 loaded_runtime_objects: BTreeMap::new(),
84 runtime_packages_loaded_from_db: RwLock::new(BTreeMap::new()),
85 receiving_objects,
86 }
87 }
88
89 pub fn objects(&self) -> &BTreeMap<ObjectID, Object> {
91 &self.input_objects
92 }
93
94 pub fn update_object_version_and_prev_tx(&mut self) {
95 self.execution_results.update_version_and_previous_tx(
96 self.lamport_timestamp,
97 self.tx_digest,
98 &self.input_objects,
99 false,
100 );
101
102 #[cfg(debug_assertions)]
103 {
104 self.check_invariants();
105 }
106 }
107
108 pub fn into_inner(self) -> InnerTemporaryStore {
110 let results = self.execution_results;
111 InnerTemporaryStore {
112 input_objects: self.input_objects,
113 stream_ended_consensus_objects: self.deleted_consensus_objects,
114 mutable_inputs: self.mutable_input_refs,
115 written: results.written_objects,
116 events: TransactionEvents {
117 data: results.user_events,
118 },
119 accumulator_events: vec![],
121 loaded_runtime_objects: self.loaded_runtime_objects,
122 runtime_packages_loaded_from_db: self.runtime_packages_loaded_from_db.into_inner(),
123 lamport_version: self.lamport_timestamp,
124 binary_config: self.protocol_config.binary_config(None),
125 accumulator_running_max_withdraws: BTreeMap::new(),
126 }
127 }
128
129 pub(crate) fn ensure_active_inputs_mutated(&mut self) {
133 let mut to_be_updated = vec![];
134 for id in self.mutable_input_refs.keys() {
135 if !self.execution_results.modified_objects.contains(id) {
136 to_be_updated.push(self.input_objects[id].clone());
140 }
141 }
142 for object in to_be_updated {
143 self.mutate_input_object(object.clone());
145 }
146 }
147
148 fn get_object_changes(&self) -> BTreeMap<ObjectID, EffectsObjectChange> {
149 let results = &self.execution_results;
150 let all_ids = results
151 .created_object_ids
152 .iter()
153 .chain(&results.deleted_object_ids)
154 .chain(&results.modified_objects)
155 .chain(results.written_objects.keys())
156 .collect::<BTreeSet<_>>();
157 all_ids
158 .into_iter()
159 .map(|id| {
160 (
161 *id,
162 EffectsObjectChange::new(
163 self.get_object_modified_at(id)
164 .map(|metadata| ((metadata.version, metadata.digest), metadata.owner)),
165 results.written_objects.get(id),
166 results.created_object_ids.contains(id),
167 results.deleted_object_ids.contains(id),
168 ),
169 )
170 })
171 .collect()
172 }
173
174 pub fn into_effects(
175 mut self,
176 shared_object_refs: Vec<SharedInput>,
177 transaction_digest: &TransactionDigest,
178 mut transaction_dependencies: BTreeSet<TransactionDigest>,
179 gas_cost_summary: GasCostSummary,
180 status: ExecutionStatus,
181 gas_charger: &mut GasCharger,
182 epoch: EpochId,
183 ) -> (InnerTemporaryStore, TransactionEffects) {
184 self.update_object_version_and_prev_tx();
185
186 for (id, expected_version, expected_digest) in &self.receiving_objects {
189 if let Some(obj_meta) = self.loaded_runtime_objects.get(id) {
193 let loaded_via_receive = obj_meta.version == *expected_version
197 && obj_meta.digest == *expected_digest
198 && obj_meta.owner.is_address_owned();
199 if loaded_via_receive {
200 transaction_dependencies.insert(obj_meta.previous_transaction);
201 }
202 }
203 }
204
205 if self.protocol_config.enable_effects_v2() {
206 self.into_effects_v2(
207 shared_object_refs,
208 transaction_digest,
209 transaction_dependencies,
210 gas_cost_summary,
211 status,
212 gas_charger,
213 epoch,
214 )
215 } else {
216 let shared_object_refs = shared_object_refs
217 .into_iter()
218 .map(|shared_input| match shared_input {
219 SharedInput::Existing(oref) => oref,
220 SharedInput::ConsensusStreamEnded(_) => {
221 unreachable!("Shared object deletion not supported in effects v1")
222 }
223 SharedInput::Cancelled(_) => {
224 unreachable!("Per object congestion control not supported in effects v1.")
225 }
226 })
227 .collect();
228 self.into_effects_v1(
229 shared_object_refs,
230 transaction_digest,
231 transaction_dependencies,
232 gas_cost_summary,
233 status,
234 gas_charger,
235 epoch,
236 )
237 }
238 }
239
240 fn into_effects_v1(
241 self,
242 shared_object_refs: Vec<ObjectRef>,
243 transaction_digest: &TransactionDigest,
244 transaction_dependencies: BTreeSet<TransactionDigest>,
245 gas_cost_summary: GasCostSummary,
246 status: ExecutionStatus,
247 gas_charger: &mut GasCharger,
248 epoch: EpochId,
249 ) -> (InnerTemporaryStore, TransactionEffects) {
250 let updated_gas_object_info = if let Some(coin_id) = gas_charger.gas_coin() {
251 let object = &self.execution_results.written_objects[&coin_id];
252 (object.compute_object_reference(), object.owner.clone())
253 } else {
254 (
255 (ObjectID::ZERO, SequenceNumber::default(), ObjectDigest::MIN),
256 Owner::AddressOwner(SuiAddress::default()),
257 )
258 };
259 let lampot_version = self.lamport_timestamp;
260
261 let mut created = vec![];
262 let mut mutated = vec![];
263 let mut unwrapped = vec![];
264 let mut deleted = vec![];
265 let mut unwrapped_then_deleted = vec![];
266 let mut wrapped = vec![];
267 let mut modified_at_versions = vec![];
270 let mut deleted_at_versions = vec![];
271 self.execution_results
272 .written_objects
273 .iter()
274 .for_each(|(id, object)| {
275 let object_ref = object.compute_object_reference();
276 let owner = object.owner.clone();
277 if let Some(old_object_meta) = self.get_object_modified_at(id) {
278 modified_at_versions.push((*id, old_object_meta.version));
279 mutated.push((object_ref, owner));
280 } else if self.execution_results.created_object_ids.contains(id) {
281 created.push((object_ref, owner));
282 } else {
283 unwrapped.push((object_ref, owner));
284 }
285 });
286 self.execution_results
287 .modified_objects
288 .iter()
289 .filter(|id| !self.execution_results.written_objects.contains_key(id))
290 .for_each(|id| {
291 let old_object_meta = self.get_object_modified_at(id).unwrap();
292 deleted_at_versions.push((*id, old_object_meta.version));
293 if self.execution_results.deleted_object_ids.contains(id) {
294 deleted.push((*id, lampot_version, ObjectDigest::OBJECT_DIGEST_DELETED));
295 } else {
296 wrapped.push((*id, lampot_version, ObjectDigest::OBJECT_DIGEST_WRAPPED));
297 }
298 });
299 self.execution_results
300 .deleted_object_ids
301 .iter()
302 .filter(|id| !self.execution_results.modified_objects.contains(id))
303 .for_each(|id| {
304 unwrapped_then_deleted.push((
305 *id,
306 lampot_version,
307 ObjectDigest::OBJECT_DIGEST_DELETED,
308 ));
309 });
310 modified_at_versions.extend(deleted_at_versions);
311
312 let inner = self.into_inner();
313 let effects = TransactionEffects::new_from_execution_v1(
314 status,
315 epoch,
316 gas_cost_summary,
317 modified_at_versions,
318 shared_object_refs,
319 *transaction_digest,
320 created,
321 mutated,
322 unwrapped,
323 deleted,
324 unwrapped_then_deleted,
325 wrapped,
326 updated_gas_object_info,
327 if inner.events.data.is_empty() {
328 None
329 } else {
330 Some(inner.events.digest())
331 },
332 transaction_dependencies.into_iter().collect(),
333 );
334 (inner, effects)
335 }
336
337 fn into_effects_v2(
338 self,
339 shared_object_refs: Vec<SharedInput>,
340 transaction_digest: &TransactionDigest,
341 transaction_dependencies: BTreeSet<TransactionDigest>,
342 gas_cost_summary: GasCostSummary,
343 status: ExecutionStatus,
344 gas_charger: &mut GasCharger,
345 epoch: EpochId,
346 ) -> (InnerTemporaryStore, TransactionEffects) {
347 let gas_coin = gas_charger.gas_coin();
352
353 let object_changes = self.get_object_changes();
354
355 let lamport_version = self.lamport_timestamp;
356 let unchanged_consensus_objects = TransactionEffectsV2::compute_unchanged_consensus_objects(
357 shared_object_refs,
358 BTreeSet::new(),
359 &object_changes,
360 BTreeMap::new(),
361 );
362 let inner = self.into_inner();
363
364 let effects = TransactionEffects::new_from_execution_v2(
365 status,
366 epoch,
367 gas_cost_summary,
368 unchanged_consensus_objects,
369 *transaction_digest,
370 lamport_version,
371 object_changes,
372 gas_coin,
373 if inner.events.data.is_empty() {
374 None
375 } else {
376 Some(inner.events.digest())
377 },
378 transaction_dependencies.into_iter().collect(),
379 );
380
381 (inner, effects)
382 }
383
384 #[cfg(debug_assertions)]
386 fn check_invariants(&self) {
387 debug_assert!(
389 {
390 self.execution_results
391 .written_objects
392 .keys()
393 .all(|id| !self.execution_results.deleted_object_ids.contains(id))
394 },
395 "Object both written and deleted."
396 );
397
398 debug_assert!(
400 {
401 self.mutable_input_refs
402 .keys()
403 .all(|id| self.execution_results.modified_objects.contains(id))
404 },
405 "Mutable input not modified."
406 );
407
408 debug_assert!(
409 {
410 self.execution_results
411 .written_objects
412 .values()
413 .all(|obj| obj.previous_transaction == self.tx_digest)
414 },
415 "Object previous transaction not properly set",
416 );
417 }
418
419 pub fn mutate_input_object(&mut self, object: Object) {
421 let id = object.id();
422 self.execution_results.modified_objects.insert(id);
423 self.execution_results.written_objects.insert(id, object);
424 }
425
426 pub fn mutate_child_object(&mut self, old_object: Object, new_object: Object) {
430 let id = new_object.id();
431 let old_ref = old_object.compute_object_reference();
432 debug_assert_eq!(old_ref.0, id);
433 self.loaded_runtime_objects.insert(
434 id,
435 DynamicallyLoadedObjectMetadata {
436 version: old_ref.1,
437 digest: old_ref.2,
438 owner: old_object.owner.clone(),
439 storage_rebate: old_object.storage_rebate,
440 previous_transaction: old_object.previous_transaction,
441 },
442 );
443 self.execution_results.modified_objects.insert(id);
444 self.execution_results
445 .written_objects
446 .insert(id, new_object);
447 }
448
449 pub fn upgrade_system_package(&mut self, package: Object) {
455 let id = package.id();
456 assert!(package.is_package() && is_system_package(id));
457 self.execution_results.modified_objects.insert(id);
458 self.execution_results.written_objects.insert(id, package);
459 }
460
461 pub fn create_object(&mut self, object: Object) {
463 debug_assert!(
468 object.is_immutable() || object.version() == SequenceNumber::MIN,
469 "Created mutable objects should not have a version set",
470 );
471 let id = object.id();
472 self.execution_results.created_object_ids.insert(id);
473 self.execution_results.written_objects.insert(id, object);
474 }
475
476 pub fn delete_input_object(&mut self, id: &ObjectID) {
478 debug_assert!(!self.execution_results.written_objects.contains_key(id));
480 self.execution_results.modified_objects.insert(*id);
481 self.execution_results.deleted_object_ids.insert(*id);
482 }
483
484 pub fn drop_writes(&mut self) {
485 self.execution_results.drop_writes();
486 }
487
488 pub fn read_object(&self, id: &ObjectID) -> Option<&Object> {
489 debug_assert!(!self.execution_results.deleted_object_ids.contains(id));
491 self.execution_results
492 .written_objects
493 .get(id)
494 .or_else(|| self.input_objects.get(id))
495 }
496
497 pub fn save_loaded_runtime_objects(
498 &mut self,
499 loaded_runtime_objects: BTreeMap<ObjectID, DynamicallyLoadedObjectMetadata>,
500 ) {
501 #[cfg(debug_assertions)]
502 {
503 for (id, v1) in &loaded_runtime_objects {
504 if let Some(v2) = self.loaded_runtime_objects.get(id) {
505 assert_eq!(v1, v2);
506 }
507 }
508 for (id, v1) in &self.loaded_runtime_objects {
509 if let Some(v2) = loaded_runtime_objects.get(id) {
510 assert_eq!(v1, v2);
511 }
512 }
513 }
514 self.loaded_runtime_objects.extend(loaded_runtime_objects);
517 }
518
519 pub fn estimate_effects_size_upperbound(&self) -> usize {
520 if self.protocol_config.enable_effects_v2() {
521 TransactionEffects::estimate_effects_size_upperbound_v2(
522 self.execution_results.written_objects.len(),
523 self.execution_results.modified_objects.len(),
524 self.input_objects.len(),
525 )
526 } else {
527 let num_deletes = self.execution_results.deleted_object_ids.len()
528 + self
529 .execution_results
530 .modified_objects
531 .iter()
532 .filter(|id| {
533 !self.execution_results.written_objects.contains_key(id)
535 && !self.execution_results.deleted_object_ids.contains(id)
536 })
537 .count();
538 TransactionEffects::estimate_effects_size_upperbound_v1(
540 self.execution_results.written_objects.len(),
541 self.mutable_input_refs.len(),
542 num_deletes,
543 self.input_objects.len(),
544 )
545 }
546 }
547
548 pub fn written_objects_size(&self) -> usize {
549 self.execution_results
550 .written_objects
551 .values()
552 .fold(0, |sum, obj| sum + obj.object_size_for_gas_metering())
553 }
554
555 pub fn conserve_unmetered_storage_rebate(&mut self, unmetered_storage_rebate: u64) {
560 if unmetered_storage_rebate == 0 {
561 return;
565 }
566 tracing::debug!(
567 "Amount of unmetered storage rebate from system tx: {:?}",
568 unmetered_storage_rebate
569 );
570 let mut system_state_wrapper = self
571 .read_object(&SUI_SYSTEM_STATE_OBJECT_ID)
572 .expect("0x5 object must be muated in system tx with unmetered storage rebate")
573 .clone();
574 assert_eq!(system_state_wrapper.storage_rebate, 0);
577 system_state_wrapper.storage_rebate = unmetered_storage_rebate;
578 self.mutate_input_object(system_state_wrapper);
579 }
580
581 fn get_object_modified_at(
587 &self,
588 object_id: &ObjectID,
589 ) -> Option<DynamicallyLoadedObjectMetadata> {
590 if self.execution_results.modified_objects.contains(object_id) {
591 Some(
592 self.mutable_input_refs
593 .get(object_id)
594 .map(
595 |((version, digest), owner)| DynamicallyLoadedObjectMetadata {
596 version: *version,
597 digest: *digest,
598 owner: owner.clone(),
599 storage_rebate: self.input_objects[object_id].storage_rebate,
601 previous_transaction: self.input_objects[object_id]
602 .previous_transaction,
603 },
604 )
605 .or_else(|| self.loaded_runtime_objects.get(object_id).cloned())
606 .unwrap_or_else(|| {
607 debug_assert!(is_system_package(*object_id));
608 let obj = self.store.get_object(object_id).unwrap();
609 DynamicallyLoadedObjectMetadata {
610 version: obj.version(),
611 digest: obj.digest(),
612 owner: obj.owner.clone(),
613 storage_rebate: obj.storage_rebate,
614 previous_transaction: obj.previous_transaction,
615 }
616 }),
617 )
618 } else {
619 None
620 }
621 }
622}
623
624impl TemporaryStore<'_> {
625 fn get_objects_to_authenticate(
627 &self,
628 sender: &SuiAddress,
629 gas_charger: &mut GasCharger,
630 is_epoch_change: bool,
631 ) -> SuiResult<(Vec<ObjectID>, HashSet<ObjectID>)> {
632 let gas_objs: HashSet<&ObjectID> = gas_charger.gas_coins().iter().map(|g| &g.0).collect();
633 let mut objs_to_authenticate = Vec::new();
634 let mut authenticated_objs = HashSet::new();
635 for (id, obj) in &self.input_objects {
636 if gas_objs.contains(id) {
637 continue;
642 }
643 match &obj.owner {
644 Owner::AddressOwner(a) => {
645 assert!(sender == a, "Input object not owned by sender");
646 authenticated_objs.insert(*id);
647 }
648 Owner::Shared { .. } => {
649 authenticated_objs.insert(*id);
650 }
651 Owner::Immutable => {
652 }
663 Owner::ObjectOwner(_parent) => {
664 unreachable!("Input objects must be address owned, shared, or immutable")
665 }
666 Owner::ConsensusAddressOwner { .. } => {
667 unimplemented!(
668 "ConsensusAddressOwner does not exist for this execution version"
669 )
670 }
671 Owner::Party { .. } => {
672 unimplemented!("Party does not exist for this execution version")
673 }
674 }
675 }
676
677 for id in &self.execution_results.modified_objects {
678 if authenticated_objs.contains(id) || gas_objs.contains(id) {
679 continue;
680 }
681 let old_obj = self.store.get_object(id).unwrap_or_else(|| {
682 panic!("Modified object must exist in the store: ID = {:?}", id)
683 });
684 match &old_obj.owner {
685 Owner::ObjectOwner(_) | Owner::AddressOwner(_) => {
688 objs_to_authenticate.push(*id);
689 }
690 Owner::Shared { .. } => {
691 unreachable!("Should already be in authenticated_objs")
692 }
693 Owner::Immutable => {
694 assert!(is_epoch_change, "Immutable objects cannot be written, except for Sui Framework/Move stdlib upgrades at epoch change boundaries");
695 assert!(
698 is_system_package(*id),
699 "Only system packages can be upgraded"
700 );
701 }
702 Owner::ConsensusAddressOwner { .. } => {
703 unimplemented!(
704 "ConsensusAddressOwner does not exist for this execution version"
705 )
706 }
707 Owner::Party { .. } => {
708 unimplemented!("Party does not exist for this execution version")
709 }
710 }
711 }
712 Ok((objs_to_authenticate, authenticated_objs))
713 }
714
715 pub fn check_ownership_invariants(
717 &self,
718 sender: &SuiAddress,
719 gas_charger: &mut GasCharger,
720 is_epoch_change: bool,
721 ) -> SuiResult<()> {
722 let (mut objects_to_authenticate, mut authenticated_objects) =
723 self.get_objects_to_authenticate(sender, gas_charger, is_epoch_change)?;
724
725 let mut covered = BTreeMap::new();
727 while let Some(to_authenticate) = objects_to_authenticate.pop() {
728 let Some(old_obj) = self.store.get_object(&to_authenticate) else {
729 continue;
732 };
733 let parent = match &old_obj.owner {
734 Owner::ObjectOwner(parent) | Owner::AddressOwner(parent) => ObjectID::from(*parent),
735 owner => panic!(
736 "Unauthenticated root at {to_authenticate:?} with owner {owner:?}\n\
737 Potentially covering objects in: {covered:#?}",
738 ),
739 };
740
741 if authenticated_objects.contains(&parent) {
742 authenticated_objects.insert(to_authenticate);
743 } else if !covered.contains_key(&parent) {
744 objects_to_authenticate.push(parent);
745 }
746
747 covered.insert(to_authenticate, parent);
748 }
749 Ok(())
750 }
751}
752
753impl TemporaryStore<'_> {
754 pub(crate) fn collect_storage_and_rebate(&mut self, gas_charger: &mut GasCharger) {
761 let old_storage_rebates: Vec<_> = self
763 .execution_results
764 .written_objects
765 .keys()
766 .map(|object_id| {
767 self.get_object_modified_at(object_id)
768 .map(|metadata| metadata.storage_rebate)
769 .unwrap_or_default()
770 })
771 .collect();
772 for (object, old_storage_rebate) in self
773 .execution_results
774 .written_objects
775 .values_mut()
776 .zip(old_storage_rebates)
777 {
778 let new_object_size = object.object_size_for_gas_metering();
780 let new_storage_rebate = gas_charger.track_storage_mutation(
782 object.id(),
783 new_object_size,
784 old_storage_rebate,
785 );
786 object.storage_rebate = new_storage_rebate;
787 }
788
789 self.collect_rebate(gas_charger);
790 }
791
792 pub(crate) fn collect_rebate(&self, gas_charger: &mut GasCharger) {
793 for object_id in &self.execution_results.modified_objects {
794 if self
795 .execution_results
796 .written_objects
797 .contains_key(object_id)
798 {
799 continue;
800 }
801 let storage_rebate = self
803 .get_object_modified_at(object_id)
804 .unwrap()
806 .storage_rebate;
807 gas_charger.track_storage_mutation(*object_id, 0, storage_rebate);
808 }
809 }
810
811 pub fn check_execution_results_consistency(&self) -> Result<(), ExecutionError> {
812 assert_invariant!(
813 self.execution_results
814 .created_object_ids
815 .iter()
816 .all(|id| !self.execution_results.deleted_object_ids.contains(id)
817 && !self.execution_results.modified_objects.contains(id)),
818 "Created object IDs cannot also be deleted or modified"
819 );
820 assert_invariant!(
821 self.execution_results.modified_objects.iter().all(|id| {
822 self.mutable_input_refs.contains_key(id)
823 || self.loaded_runtime_objects.contains_key(id)
824 || is_system_package(*id)
825 }),
826 "A modified object must be either a mutable input, a loaded child object, or a system package"
827 );
828 Ok(())
829 }
830}
831impl TemporaryStore<'_> {
836 pub fn advance_epoch_safe_mode(
837 &mut self,
838 params: &AdvanceEpochParams,
839 protocol_config: &ProtocolConfig,
840 ) {
841 let wrapper = get_sui_system_state_wrapper(self.store)
842 .expect("System state wrapper object must exist");
843 let (old_object, new_object) =
844 wrapper.advance_epoch_safe_mode(params, self.store, protocol_config);
845 self.mutate_child_object(old_object, new_object);
846 }
847}
848
849type ModifiedObjectInfo<'a> = (
850 ObjectID,
851 Option<DynamicallyLoadedObjectMetadata>,
853 Option<&'a Object>,
854);
855
856impl TemporaryStore<'_> {
857 fn get_input_sui(
858 &self,
859 id: &ObjectID,
860 expected_version: SequenceNumber,
861 layout_resolver: &mut impl LayoutResolver,
862 ) -> Result<u64, ExecutionError> {
863 if let Some(obj) = self.input_objects.get(id) {
864 if obj.version() != expected_version {
866 invariant_violation!(
867 "Version mismatching when resolving input object to check conservation--\
868 expected {}, got {}",
869 expected_version,
870 obj.version(),
871 );
872 }
873 obj.get_total_sui(layout_resolver).map_err(|e| {
874 make_invariant_violation!(
875 "Failed looking up input SUI in SUI conservation checking for input with \
876 type {:?}: {e:#?}",
877 obj.struct_tag(),
878 )
879 })
880 } else {
881 let Some(obj) = self.store.get_object_by_key(id, expected_version) else {
883 invariant_violation!(
884 "Failed looking up dynamic field {id} in SUI conservation checking"
885 );
886 };
887 obj.get_total_sui(layout_resolver).map_err(|e| {
888 make_invariant_violation!(
889 "Failed looking up input SUI in SUI conservation checking for type \
890 {:?}: {e:#?}",
891 obj.struct_tag(),
892 )
893 })
894 }
895 }
896
897 fn get_modified_objects(&self) -> Vec<ModifiedObjectInfo<'_>> {
902 self.execution_results
903 .modified_objects
904 .iter()
905 .map(|id| {
906 let metadata = self.get_object_modified_at(id);
907 let output = self.execution_results.written_objects.get(id);
908 (*id, metadata, output)
909 })
910 .chain(
911 self.execution_results
912 .written_objects
913 .iter()
914 .filter_map(|(id, object)| {
915 if self.execution_results.modified_objects.contains(id) {
916 None
917 } else {
918 Some((*id, None, Some(object)))
919 }
920 }),
921 )
922 .collect()
923 }
924
925 pub fn check_sui_conserved(
939 &self,
940 simple_conservation_checks: bool,
941 gas_summary: &GasCostSummary,
942 ) -> Result<(), ExecutionError> {
943 if !simple_conservation_checks {
944 return Ok(());
945 }
946 let mut total_input_rebate = 0;
948 let mut total_output_rebate = 0;
950 for (_, input, output) in self.get_modified_objects() {
951 if let Some(input) = input {
952 total_input_rebate += input.storage_rebate;
953 }
954 if let Some(object) = output {
955 total_output_rebate += object.storage_rebate;
956 }
957 }
958
959 if gas_summary.storage_cost == 0 {
960 if total_input_rebate
972 != total_output_rebate
973 + gas_summary.storage_rebate
974 + gas_summary.non_refundable_storage_fee
975 {
976 return Err(ExecutionError::invariant_violation(format!(
977 "SUI conservation failed -- no storage charges in gas summary \
978 and total storage input rebate {} not equal \
979 to total storage output rebate {}",
980 total_input_rebate, total_output_rebate,
981 )));
982 }
983 } else {
984 if total_input_rebate
987 != gas_summary.storage_rebate + gas_summary.non_refundable_storage_fee
988 {
989 return Err(ExecutionError::invariant_violation(format!(
990 "SUI conservation failed -- {} SUI in storage rebate field of input objects, \
991 {} SUI in tx storage rebate or tx non-refundable storage rebate",
992 total_input_rebate, gas_summary.non_refundable_storage_fee,
993 )));
994 }
995
996 if gas_summary.storage_cost != total_output_rebate {
999 return Err(ExecutionError::invariant_violation(format!(
1000 "SUI conservation failed -- {} SUI charged for storage, \
1001 {} SUI in storage rebate field of output objects",
1002 gas_summary.storage_cost, total_output_rebate
1003 )));
1004 }
1005 }
1006 Ok(())
1007 }
1008
1009 pub fn check_sui_conserved_expensive(
1022 &self,
1023 gas_summary: &GasCostSummary,
1024 advance_epoch_gas_summary: Option<(u64, u64)>,
1025 layout_resolver: &mut impl LayoutResolver,
1026 ) -> Result<(), ExecutionError> {
1027 let mut total_input_sui = 0;
1029 let mut total_output_sui = 0;
1031 for (id, input, output) in self.get_modified_objects() {
1032 if let Some(input) = input {
1033 total_input_sui += self.get_input_sui(&id, input.version, layout_resolver)?;
1034 }
1035 if let Some(object) = output {
1036 total_output_sui += object.get_total_sui(layout_resolver).map_err(|e| {
1037 make_invariant_violation!(
1038 "Failed looking up output SUI in SUI conservation checking for \
1039 mutated type {:?}: {e:#?}",
1040 object.struct_tag(),
1041 )
1042 })?;
1043 }
1044 }
1045 total_output_sui += gas_summary.computation_cost + gas_summary.non_refundable_storage_fee;
1050 if let Some((epoch_fees, epoch_rebates)) = advance_epoch_gas_summary {
1051 total_input_sui += epoch_fees;
1052 total_output_sui += epoch_rebates;
1053 }
1054 if total_input_sui != total_output_sui {
1055 return Err(ExecutionError::invariant_violation(format!(
1056 "SUI conservation failed: input={}, output={}, \
1057 this transaction either mints or burns SUI",
1058 total_input_sui, total_output_sui,
1059 )));
1060 }
1061 Ok(())
1062 }
1063}
1064
1065impl RuntimeObjectResolver for TemporaryStore<'_> {
1066 fn read_child_object(
1067 &self,
1068 parent: &ObjectID,
1069 child: &ObjectID,
1070 child_version_upper_bound: SequenceNumber,
1071 ) -> SuiResult<Option<Object>> {
1072 let obj_opt = self.execution_results.written_objects.get(child);
1073 if obj_opt.is_some() {
1074 Ok(obj_opt.cloned())
1075 } else {
1076 self.store
1077 .read_child_object(parent, child, child_version_upper_bound)
1078 }
1079 }
1080
1081 fn get_object_received_at_version(
1082 &self,
1083 owner: &ObjectID,
1084 receiving_object_id: &ObjectID,
1085 receive_object_at_version: SequenceNumber,
1086 epoch_id: EpochId,
1087 ) -> SuiResult<Option<Object>> {
1088 debug_assert!(!self
1091 .execution_results
1092 .written_objects
1093 .contains_key(receiving_object_id));
1094 debug_assert!(!self
1095 .execution_results
1096 .deleted_object_ids
1097 .contains(receiving_object_id));
1098 self.store.get_object_received_at_version(
1099 owner,
1100 receiving_object_id,
1101 receive_object_at_version,
1102 epoch_id,
1103 )
1104 }
1105}
1106
1107impl Storage for TemporaryStore<'_> {
1108 fn reset(&mut self) {
1109 self.drop_writes();
1110 }
1111
1112 fn read_object(&self, id: &ObjectID) -> Option<&Object> {
1113 TemporaryStore::read_object(self, id)
1114 }
1115
1116 fn record_execution_results(
1118 &mut self,
1119 results: ExecutionResults,
1120 ) -> Result<(), ExecutionError> {
1121 let ExecutionResults::V2(results) = results else {
1122 panic!("ExecutionResults::V2 expected in sui-execution v1 and above");
1123 };
1124 self.execution_results
1127 .merge_results(
1128 results, false, true,
1129 )
1130 .unwrap();
1131 Ok(())
1132 }
1133
1134 fn save_loaded_runtime_objects(
1135 &mut self,
1136 loaded_runtime_objects: BTreeMap<ObjectID, DynamicallyLoadedObjectMetadata>,
1137 ) {
1138 TemporaryStore::save_loaded_runtime_objects(self, loaded_runtime_objects)
1139 }
1140
1141 fn save_wrapped_object_containers(
1142 &mut self,
1143 _wrapped_object_containers: BTreeMap<ObjectID, ObjectID>,
1144 ) {
1145 unreachable!("Unused in v1")
1146 }
1147
1148 fn check_coin_deny_list(
1149 &self,
1150 _receiving_funds_type_and_owners: BTreeMap<TypeTag, BTreeSet<SuiAddress>>,
1151 ) -> DenyListResult {
1152 unreachable!("Coin denylist v2 is not supported in sui-execution v1");
1153 }
1154
1155 fn record_generated_object_ids(&mut self, _generated_ids: BTreeSet<ObjectID>) {
1156 unreachable!(
1157 "Generated object IDs are not recorded in ExecutionResults in sui-execution v1"
1158 );
1159 }
1160}
1161
1162impl BackingPackageStore for TemporaryStore<'_> {
1163 fn get_package_object(&self, package_id: &ObjectID) -> SuiResult<Option<PackageObject>> {
1164 if let Some(obj) = self.read_object(package_id) {
1171 Ok(Some(PackageObject::new(obj.clone())))
1172 } else {
1173 self.store.get_package_object(package_id).inspect(|obj| {
1174 if let Some(v) = obj {
1176 if !self
1177 .runtime_packages_loaded_from_db
1178 .read()
1179 .contains_key(package_id)
1180 {
1181 self.runtime_packages_loaded_from_db
1184 .write()
1185 .insert(*package_id, v.clone());
1186 }
1187 }
1188 })
1189 }
1190 }
1191}
1192
1193impl ParentSync for TemporaryStore<'_> {
1194 fn get_latest_parent_entry_ref_deprecated(&self, _object_id: ObjectID) -> Option<ObjectRef> {
1195 unreachable!("Never called in newer protocol versions")
1196 }
1197}