1use crate::gas_charger::GasCharger;
5use parking_lot::RwLock;
6use std::collections::{BTreeMap, BTreeSet, HashSet};
7use sui_protocol_config::ProtocolConfig;
8use sui_types::committee::EpochId;
9use sui_types::effects::{TransactionEffects, TransactionEvents};
10use sui_types::execution::{DynamicallyLoadedObjectMetadata, ExecutionResults, SharedInput};
11use sui_types::execution_status::ExecutionStatus;
12use sui_types::inner_temporary_store::InnerTemporaryStore;
13use sui_types::layout_resolver::LayoutResolver;
14use sui_types::storage::{BackingStore, DeleteKindWithOldVersion, DenyListResult, PackageObject};
15use sui_types::sui_system_state::{get_sui_system_state_wrapper, AdvanceEpochParams};
16use sui_types::{
17 base_types::{
18 ObjectDigest, ObjectID, ObjectRef, SequenceNumber, SuiAddress, TransactionDigest,
19 VersionDigest,
20 },
21 error::{ExecutionError, SuiResult},
22 event::Event,
23 gas::GasCostSummary,
24 object::Owner,
25 object::{Data, Object},
26 storage::{
27 BackingPackageStore, ChildObjectResolver, ObjectChange, ParentSync, Storage, WriteKind,
28 },
29 transaction::InputObjects,
30 TypeTag,
31};
32use sui_types::{is_system_package, SUI_SYSTEM_STATE_OBJECT_ID};
33
34pub struct TemporaryStore<'backing> {
35 store: &'backing dyn BackingStore,
41 tx_digest: TransactionDigest,
42 input_objects: BTreeMap<ObjectID, Object>,
43 deleted_consensus_objects: BTreeMap<ObjectID, SequenceNumber>,
44 lamport_timestamp: SequenceNumber,
46 mutable_input_refs: BTreeMap<ObjectID, (VersionDigest, Owner)>, written: BTreeMap<ObjectID, (Object, WriteKind)>, deleted: BTreeMap<ObjectID, DeleteKindWithOldVersion>,
53 loaded_child_objects: BTreeMap<ObjectID, DynamicallyLoadedObjectMetadata>,
56 events: Vec<Event>,
58 protocol_config: ProtocolConfig,
59
60 runtime_packages_loaded_from_db: RwLock<BTreeMap<ObjectID, PackageObject>>,
63}
64
65impl<'backing> TemporaryStore<'backing> {
66 pub fn new(
69 store: &'backing dyn BackingStore,
70 input_objects: InputObjects,
71 tx_digest: TransactionDigest,
72 protocol_config: &ProtocolConfig,
73 ) -> Self {
74 let mutable_input_refs = input_objects.exclusive_mutable_inputs();
75 let lamport_timestamp = input_objects.lamport_timestamp(&[]);
76 let deleted_consensus_objects = input_objects.consensus_stream_ended_objects();
77 let objects = input_objects.into_object_map();
78
79 Self {
80 store,
81 tx_digest,
82 input_objects: objects,
83 deleted_consensus_objects,
84 lamport_timestamp,
85 mutable_input_refs,
86 written: BTreeMap::new(),
87 deleted: BTreeMap::new(),
88 events: Vec::new(),
89 protocol_config: protocol_config.clone(),
90 loaded_child_objects: BTreeMap::new(),
91 runtime_packages_loaded_from_db: RwLock::new(BTreeMap::new()),
92 }
93 }
94
95 pub fn objects(&self) -> &BTreeMap<ObjectID, Object> {
97 &self.input_objects
98 }
99
100 pub fn update_object_version_and_prev_tx(&mut self) {
101 #[cfg(debug_assertions)]
102 {
103 self.check_invariants();
104 }
105
106 for (id, (obj, kind)) in self.written.iter_mut() {
107 match &mut obj.data {
109 Data::Move(obj) => {
110 obj.increment_version_to(self.lamport_timestamp);
112 }
113
114 Data::Package(pkg) => {
115 if *kind == WriteKind::Mutate {
119 pkg.increment_version();
120 }
121 }
122 }
123
124 if let Owner::Shared {
128 initial_shared_version,
129 } = &mut obj.owner
130 {
131 if *kind == WriteKind::Create {
132 assert_eq!(
133 *initial_shared_version,
134 SequenceNumber::new(),
135 "Initial version should be blank before this point for {id:?}",
136 );
137 *initial_shared_version = self.lamport_timestamp;
138 }
139 }
140 }
141 }
142
143 pub fn into_inner(self) -> InnerTemporaryStore {
145 InnerTemporaryStore {
146 input_objects: self.input_objects,
147 stream_ended_consensus_objects: self.deleted_consensus_objects,
148 mutable_inputs: self.mutable_input_refs,
149 written: self
150 .written
151 .into_iter()
152 .map(|(id, (obj, _))| (id, obj))
153 .collect(),
154 events: TransactionEvents { data: self.events },
155 accumulator_events: vec![],
157 loaded_runtime_objects: self.loaded_child_objects,
158 runtime_packages_loaded_from_db: self.runtime_packages_loaded_from_db.into_inner(),
159 lamport_version: self.lamport_timestamp,
160 binary_config: self.protocol_config.binary_config(None),
161 accumulator_running_max_withdraws: BTreeMap::new(),
162 }
163 }
164
165 fn ensure_active_inputs_mutated(&mut self) {
169 let mut to_be_updated = vec![];
170 for id in self.mutable_input_refs.keys() {
171 if !self.written.contains_key(id) && !self.deleted.contains_key(id) {
172 to_be_updated.push(self.input_objects[id].clone());
176 }
177 }
178 for object in to_be_updated {
179 self.write_object(object.clone(), WriteKind::Mutate);
181 }
182 }
183
184 pub fn to_effects(
185 mut self,
186 shared_object_refs: Vec<SharedInput>,
187 transaction_digest: &TransactionDigest,
188 transaction_dependencies: Vec<TransactionDigest>,
189 gas_cost_summary: GasCostSummary,
190 status: ExecutionStatus,
191 gas_charger: &mut GasCharger,
192 epoch: EpochId,
193 ) -> (InnerTemporaryStore, TransactionEffects) {
194 let mut modified_at_versions = vec![];
195
196 self.written.iter_mut().for_each(|(id, (obj, kind))| {
198 if *kind == WriteKind::Mutate {
199 modified_at_versions.push((*id, obj.version()))
200 }
201 });
202
203 self.deleted.iter_mut().for_each(|(id, kind)| {
204 if let Some(version) = kind.old_version() {
205 modified_at_versions.push((*id, version));
206 }
207 });
208
209 self.update_object_version_and_prev_tx();
210
211 let mut deleted = vec![];
212 let mut wrapped = vec![];
213 let mut unwrapped_then_deleted = vec![];
214 for (id, kind) in &self.deleted {
215 match kind {
216 DeleteKindWithOldVersion::Normal(_) => deleted.push((
217 *id,
218 self.lamport_timestamp,
219 ObjectDigest::OBJECT_DIGEST_DELETED,
220 )),
221 DeleteKindWithOldVersion::UnwrapThenDelete
222 | DeleteKindWithOldVersion::UnwrapThenDeleteDEPRECATED(_) => unwrapped_then_deleted
223 .push((
224 *id,
225 self.lamport_timestamp,
226 ObjectDigest::OBJECT_DIGEST_DELETED,
227 )),
228 DeleteKindWithOldVersion::Wrap(_) => wrapped.push((
229 *id,
230 self.lamport_timestamp,
231 ObjectDigest::OBJECT_DIGEST_WRAPPED,
232 )),
233 }
234 }
235
236 let updated_gas_object_info = if let Some(coin_id) = gas_charger.gas_coin() {
241 let (object, _kind) = &self.written[&coin_id];
242 (object.compute_object_reference(), object.owner.clone())
243 } else {
244 (
245 (ObjectID::ZERO, SequenceNumber::default(), ObjectDigest::MIN),
246 Owner::AddressOwner(SuiAddress::default()),
247 )
248 };
249
250 let mut mutated = vec![];
251 let mut created = vec![];
252 let mut unwrapped = vec![];
253 for (object, kind) in self.written.values() {
254 let object_ref = object.compute_object_reference();
255 let owner = object.owner.clone();
256 match kind {
257 WriteKind::Mutate => mutated.push((object_ref, owner)),
258 WriteKind::Create => created.push((object_ref, owner)),
259 WriteKind::Unwrap => unwrapped.push((object_ref, owner)),
260 }
261 }
262
263 let inner = self.into_inner();
264
265 let shared_object_refs = shared_object_refs
266 .into_iter()
267 .map(|shared_input| match shared_input {
268 SharedInput::Existing(oref) => oref,
269 SharedInput::ConsensusStreamEnded(_) => {
270 unreachable!("Shared object deletion not supported in effects v1")
271 }
272 SharedInput::Cancelled(_) => {
273 unreachable!("Per object congestion control not supported in effects v1.")
274 }
275 })
276 .collect();
277
278 let effects = TransactionEffects::new_from_execution_v1(
279 status,
280 epoch,
281 gas_cost_summary,
282 modified_at_versions,
283 shared_object_refs,
284 *transaction_digest,
285 created,
286 mutated,
287 unwrapped,
288 deleted,
289 unwrapped_then_deleted,
290 wrapped,
291 updated_gas_object_info,
292 if inner.events.data.is_empty() {
293 None
294 } else {
295 Some(inner.events.digest())
296 },
297 transaction_dependencies,
298 );
299 (inner, effects)
300 }
301
302 #[cfg(debug_assertions)]
304 fn check_invariants(&self) {
305 debug_assert!(
307 {
308 let mut used = HashSet::new();
309 self.written.iter().all(|(elt, _)| used.insert(elt));
310 self.deleted.iter().all(move |elt| used.insert(elt.0))
311 },
312 "Object both written and deleted."
313 );
314
315 debug_assert!(
317 {
318 let mut used = HashSet::new();
319 self.written.iter().all(|(elt, _)| used.insert(elt));
320 self.deleted.iter().all(|elt| used.insert(elt.0));
321
322 self.mutable_input_refs.keys().all(|elt| !used.insert(elt))
323 },
324 "Mutable input neither written nor deleted."
325 );
326
327 debug_assert!(
328 {
329 self.written
330 .iter()
331 .all(|(_, (obj, _))| obj.previous_transaction == self.tx_digest)
332 },
333 "Object previous transaction not properly set",
334 );
335
336 if self.protocol_config.simplified_unwrap_then_delete() {
337 debug_assert!(self.deleted.iter().all(|(_, kind)| {
338 !matches!(
339 kind,
340 DeleteKindWithOldVersion::UnwrapThenDeleteDEPRECATED(_)
341 )
342 }));
343 } else {
344 debug_assert!(self
345 .deleted
346 .iter()
347 .all(|(_, kind)| { !matches!(kind, DeleteKindWithOldVersion::UnwrapThenDelete) }));
348 }
349 }
350
351 pub fn write_object(&mut self, mut object: Object, kind: WriteKind) {
356 debug_assert!(!self.deleted.contains_key(&object.id()));
358 #[cfg(test)] if let Some(existing_object) = self.read_object(&object.id()) {
361 if existing_object.is_immutable() {
362 panic!("Internal invariant violation: Mutating a read-only object.")
365 }
366 }
367
368 debug_assert!(
373 kind != WriteKind::Create
374 || object.is_immutable()
375 || object.version() == SequenceNumber::MIN,
376 "Created mutable objects should not have a version set",
377 );
378
379 object.previous_transaction = self.tx_digest;
382 self.written.insert(object.id(), (object, kind));
383 }
384
385 pub fn delete_object(&mut self, id: &ObjectID, kind: DeleteKindWithOldVersion) {
386 debug_assert!(!self.written.contains_key(id));
388
389 #[cfg(debug_assertions)]
392 if let Some(object) = self.read_object(id) {
393 if object.is_immutable() {
394 let digest = self.tx_digest;
399 panic!("Internal invariant violation in tx {digest}: Deleting immutable object {id}, delete kind {kind:?}")
400 }
401 }
402
403 self.deleted.insert(*id, kind);
406 }
407
408 pub fn drop_writes(&mut self) {
409 self.written.clear();
410 self.deleted.clear();
411 self.events.clear();
412 }
413
414 pub fn log_event(&mut self, event: Event) {
415 self.events.push(event)
416 }
417
418 pub fn read_object(&self, id: &ObjectID) -> Option<&Object> {
419 debug_assert!(!self.deleted.contains_key(id));
421 self.written
422 .get(id)
423 .map(|(obj, _kind)| obj)
424 .or_else(|| self.input_objects.get(id))
425 }
426
427 pub fn apply_object_changes(&mut self, changes: BTreeMap<ObjectID, ObjectChange>) {
428 for (id, change) in changes {
429 match change {
430 ObjectChange::Write(new_value, kind) => self.write_object(new_value, kind),
431 ObjectChange::Delete(kind) => self.delete_object(&id, kind),
432 }
433 }
434 }
435
436 pub fn save_loaded_runtime_objects(
437 &mut self,
438 loaded_runtime_objects: BTreeMap<ObjectID, DynamicallyLoadedObjectMetadata>,
439 ) {
440 #[cfg(debug_assertions)]
441 {
442 for (id, v1) in &loaded_runtime_objects {
443 if let Some(v2) = self.loaded_child_objects.get(id) {
444 assert_eq!(v1, v2);
445 }
446 }
447 for (id, v1) in &self.loaded_child_objects {
448 if let Some(v2) = loaded_runtime_objects.get(id) {
449 assert_eq!(v1, v2);
450 }
451 }
452 }
453 self.loaded_child_objects.extend(loaded_runtime_objects);
456 }
457
458 pub fn estimate_effects_size_upperbound(&self) -> usize {
459 TransactionEffects::estimate_effects_size_upperbound_v1(
461 self.written.len(),
462 self.mutable_input_refs.len(),
463 self.deleted.len(),
464 self.input_objects.len(),
465 )
466 }
467
468 pub fn written_objects_size(&self) -> usize {
469 self.written
470 .iter()
471 .fold(0, |sum, obj| sum + obj.1 .0.object_size_for_gas_metering())
472 }
473
474 pub fn conserve_unmetered_storage_rebate(&mut self, unmetered_storage_rebate: u64) {
477 if unmetered_storage_rebate == 0 {
478 return;
482 }
483 tracing::debug!(
484 "Amount of unmetered storage rebate from system tx: {:?}",
485 unmetered_storage_rebate
486 );
487 let mut system_state_wrapper = self
488 .read_object(&SUI_SYSTEM_STATE_OBJECT_ID)
489 .expect("0x5 object must be muated in system tx with unmetered storage rebate")
490 .clone();
491 assert_eq!(system_state_wrapper.storage_rebate, 0);
494 system_state_wrapper.storage_rebate = unmetered_storage_rebate;
495 self.write_object(system_state_wrapper, WriteKind::Mutate);
496 }
497}
498
499impl TemporaryStore<'_> {
500 fn get_objects_to_authenticate(
502 &self,
503 sender: &SuiAddress,
504 gas_charger: &mut GasCharger,
505 is_epoch_change: bool,
506 ) -> SuiResult<(Vec<ObjectID>, HashSet<ObjectID>)> {
507 let gas_objs: HashSet<&ObjectID> = gas_charger.gas_coins().iter().map(|g| &g.0).collect();
508 let mut objs_to_authenticate = Vec::new();
509 let mut authenticated_objs = HashSet::new();
510 for (id, obj) in &self.input_objects {
511 if gas_objs.contains(id) {
512 continue;
517 }
518 match &obj.owner {
519 Owner::AddressOwner(a) => {
520 assert!(sender == a, "Input object not owned by sender");
521 authenticated_objs.insert(*id);
522 }
523 Owner::Shared { .. } => {
524 authenticated_objs.insert(*id);
525 }
526 Owner::Immutable => {
527 }
538 Owner::ObjectOwner(_parent) => {
539 unreachable!("Input objects must be address owned, shared, or immutable")
540 }
541 Owner::ConsensusAddressOwner { .. } => {
542 unimplemented!(
543 "ConsensusAddressOwner does not exist for this execution version"
544 )
545 }
546 Owner::Party { .. } => {
547 unimplemented!("Party does not exist for this execution version")
548 }
549 }
550 }
551
552 for (id, (_new_obj, kind)) in &self.written {
553 if authenticated_objs.contains(id) || gas_objs.contains(id) {
554 continue;
555 }
556 match kind {
557 WriteKind::Mutate => {
558 let old_obj = self.store.get_object(id).unwrap_or_else(|| {
561 panic!("Mutated object must exist in the store: ID = {:?}", id)
562 });
563 match &old_obj.owner {
564 Owner::ObjectOwner(_parent) => {
565 objs_to_authenticate.push(*id);
566 }
567 Owner::AddressOwner(_) | Owner::Shared { .. } => {
568 unreachable!("Should already be in authenticated_objs")
569 }
570 Owner::Immutable => {
571 assert!(is_epoch_change, "Immutable objects cannot be written, except for Sui Framework/Move stdlib upgrades at epoch change boundaries");
572 assert!(
575 is_system_package(*id),
576 "Only system packages can be upgraded"
577 );
578 }
579 Owner::ConsensusAddressOwner { .. } => {
580 unimplemented!(
581 "ConsensusAddressOwner does not exist for this execution version"
582 )
583 }
584 Owner::Party { .. } => {
585 unimplemented!("Party does not exist for this execution version")
586 }
587 }
588 }
589 WriteKind::Create | WriteKind::Unwrap => {
590 }
593 }
594 }
595
596 for (id, kind) in &self.deleted {
597 if authenticated_objs.contains(id) || gas_objs.contains(id) {
598 continue;
599 }
600 match kind {
601 DeleteKindWithOldVersion::Normal(_) | DeleteKindWithOldVersion::Wrap(_) => {
602 let old_obj = self.store.get_object(id).unwrap();
604 match &old_obj.owner {
605 Owner::ObjectOwner(_) => {
606 objs_to_authenticate.push(*id);
607 }
608 Owner::AddressOwner(_) | Owner::Shared { .. } => {
609 unreachable!("Should already be in authenticated_objs")
610 }
611 Owner::Immutable => unreachable!("Immutable objects cannot be deleted"),
612 Owner::ConsensusAddressOwner { .. } => {
613 unimplemented!(
614 "ConsensusAddressOwner does not exist for this execution version"
615 )
616 }
617 Owner::Party { .. } => {
618 unimplemented!("Party does not exist for this execution version")
619 }
620 }
621 }
622 DeleteKindWithOldVersion::UnwrapThenDelete
623 | DeleteKindWithOldVersion::UnwrapThenDeleteDEPRECATED(_) => {
624 }
627 }
628 }
629 Ok((objs_to_authenticate, authenticated_objs))
630 }
631
632 pub fn check_ownership_invariants(
634 &self,
635 sender: &SuiAddress,
636 gas_charger: &mut GasCharger,
637 is_epoch_change: bool,
638 ) -> SuiResult<()> {
639 let (mut objects_to_authenticate, mut authenticated_objects) =
640 self.get_objects_to_authenticate(sender, gas_charger, is_epoch_change)?;
641
642 let mut covered = BTreeMap::new();
644 while let Some(to_authenticate) = objects_to_authenticate.pop() {
645 let Some(old_obj) = self.store.get_object(&to_authenticate) else {
646 continue;
649 };
650 let parent = match &old_obj.owner {
651 Owner::ObjectOwner(parent) => ObjectID::from(*parent),
652 owner => panic!(
653 "Unauthenticated root at {to_authenticate:?} with owner {owner:?}\n\
654 Potentially covering objects in: {covered:#?}",
655 ),
656 };
657
658 if authenticated_objects.contains(&parent) {
659 authenticated_objects.insert(to_authenticate);
660 } else if !covered.contains_key(&parent) {
661 objects_to_authenticate.push(parent);
662 }
663
664 covered.insert(to_authenticate, parent);
665 }
666 Ok(())
667 }
668}
669
670impl TemporaryStore<'_> {
671 fn get_input_storage_rebate(&self, id: &ObjectID, expected_version: SequenceNumber) -> u64 {
673 if let Some(old_obj) = self.input_objects.get(id) {
675 old_obj.storage_rebate
676 } else if let Some(metadata) = self.loaded_child_objects.get(id) {
677 debug_assert_eq!(metadata.version, expected_version);
678 metadata.storage_rebate
679 } else if let Some(obj) = self.store.get_object_by_key(id, expected_version) {
680 debug_assert!(obj.is_package());
683 obj.storage_rebate
684 } else {
685 panic!(
687 "Looking up storage rebate of mutated object {:?} should not fail",
688 id
689 )
690 }
691 }
692
693 pub(crate) fn ensure_gas_and_input_mutated(&mut self, gas_charger: &mut GasCharger) {
694 if let Some(gas_object_id) = gas_charger.gas_coin() {
695 let gas_object = self
696 .read_object(&gas_object_id)
697 .expect("We constructed the object map so it should always have the gas object id")
698 .clone();
699 self.written
700 .entry(gas_object_id)
701 .or_insert_with(|| (gas_object, WriteKind::Mutate));
702 }
703 self.ensure_active_inputs_mutated();
704 }
705
706 pub(crate) fn collect_storage_and_rebate(&mut self, gas_charger: &mut GasCharger) {
713 let mut objects_to_update = vec![];
714 for (object_id, (object, write_kind)) in &mut self.written {
715 let old_storage_rebate = match write_kind {
717 WriteKind::Create | WriteKind::Unwrap => 0,
718 WriteKind::Mutate => {
719 if let Some(old_obj) = self.input_objects.get(object_id) {
720 old_obj.storage_rebate
721 } else {
722 let expected_version = object.version();
724 if let Some(old_obj) =
725 self.store.get_object_by_key(object_id, expected_version)
726 {
727 old_obj.storage_rebate
728 } else {
729 panic!("Looking up storage rebate of mutated object should not fail");
731 }
732 }
733 }
734 };
735 let new_object_size = object.object_size_for_gas_metering();
737 let new_storage_rebate =
739 gas_charger.track_storage_mutation(*object_id, new_object_size, old_storage_rebate);
740 object.storage_rebate = new_storage_rebate;
741 if !object.is_immutable() {
742 objects_to_update.push((object.clone(), *write_kind));
743 }
744 }
745
746 self.collect_rebate(gas_charger);
747
748 for (object, write_kind) in objects_to_update {
751 self.write_object(object, write_kind);
752 }
753 }
754
755 pub(crate) fn collect_rebate(&self, gas_charger: &mut GasCharger) {
756 for (object_id, kind) in &self.deleted {
757 match kind {
758 DeleteKindWithOldVersion::Wrap(version)
759 | DeleteKindWithOldVersion::Normal(version) => {
760 let storage_rebate = self.get_input_storage_rebate(object_id, *version);
762 gas_charger.track_storage_mutation(*object_id, 0, storage_rebate);
763 }
764 DeleteKindWithOldVersion::UnwrapThenDelete
765 | DeleteKindWithOldVersion::UnwrapThenDeleteDEPRECATED(_) => {
766 }
768 }
769 }
770 }
771}
772impl TemporaryStore<'_> {
777 pub fn advance_epoch_safe_mode(
778 &mut self,
779 params: &AdvanceEpochParams,
780 protocol_config: &ProtocolConfig,
781 ) {
782 let wrapper = get_sui_system_state_wrapper(self.store.as_object_store())
783 .expect("System state wrapper object must exist");
784 let (new_object, _) =
785 wrapper.advance_epoch_safe_mode(params, self.store.as_object_store(), protocol_config);
786 self.write_object(new_object, WriteKind::Mutate);
787 }
788}
789
790type ModifiedObjectInfo<'a> = (ObjectID, Option<(SequenceNumber, u64)>, Option<&'a Object>);
791
792impl TemporaryStore<'_> {
793 fn get_input_sui(
794 &self,
795 id: &ObjectID,
796 expected_version: SequenceNumber,
797 layout_resolver: &mut impl LayoutResolver,
798 ) -> Result<u64, ExecutionError> {
799 if let Some(obj) = self.input_objects.get(id) {
800 if obj.version() != expected_version {
802 invariant_violation!(
803 "Version mismatching when resolving input object to check conservation--\
804 expected {}, got {}",
805 expected_version,
806 obj.version(),
807 );
808 }
809 obj.get_total_sui(layout_resolver).map_err(|e| {
810 make_invariant_violation!(
811 "Failed looking up input SUI in SUI conservation checking for input with \
812 type {:?}: {e:#?}",
813 obj.struct_tag(),
814 )
815 })
816 } else {
817 let Some(obj) = self.store.get_object_by_key(id, expected_version) else {
819 invariant_violation!(
820 "Failed looking up dynamic field {id} in SUI conservation checking"
821 );
822 };
823 obj.get_total_sui(layout_resolver).map_err(|e| {
824 make_invariant_violation!(
825 "Failed looking up input SUI in SUI conservation checking for type \
826 {:?}: {e:#?}",
827 obj.struct_tag(),
828 )
829 })
830 }
831 }
832
833 fn get_modified_objects(&self) -> Vec<ModifiedObjectInfo<'_>> {
838 self.written
839 .iter()
840 .map(|(id, (object, kind))| match kind {
841 WriteKind::Mutate => {
842 let version = object.version();
844 let storage_rebate = self.get_input_storage_rebate(id, version);
845 (*id, Some((object.version(), storage_rebate)), Some(object))
846 }
847 WriteKind::Create | WriteKind::Unwrap => (*id, None, Some(object)),
848 })
849 .chain(self.deleted.iter().filter_map(|(id, kind)| match kind {
850 DeleteKindWithOldVersion::Normal(version)
851 | DeleteKindWithOldVersion::Wrap(version) => {
852 let storage_rebate = self.get_input_storage_rebate(id, *version);
853 Some((*id, Some((*version, storage_rebate)), None))
854 }
855 DeleteKindWithOldVersion::UnwrapThenDelete
856 | DeleteKindWithOldVersion::UnwrapThenDeleteDEPRECATED(_) => None,
857 }))
858 .collect()
859 }
860
861 pub fn check_sui_conserved(
884 &self,
885 gas_summary: &GasCostSummary,
886 advance_epoch_gas_summary: Option<(u64, u64)>,
887 layout_resolver: &mut impl LayoutResolver,
888 do_expensive_checks: bool,
889 ) -> Result<(), ExecutionError> {
890 let mut total_input_sui = 0;
892 let mut total_output_sui = 0;
894 let mut total_input_rebate = 0;
896 let mut total_output_rebate = 0;
898 for (id, input, output) in self.get_modified_objects() {
899 if let Some((version, storage_rebate)) = input {
900 total_input_rebate += storage_rebate;
901 if do_expensive_checks {
902 total_input_sui += self.get_input_sui(&id, version, layout_resolver)?;
903 }
904 }
905 if let Some(object) = output {
906 total_output_rebate += object.storage_rebate;
907 if do_expensive_checks {
908 total_output_sui += object.get_total_sui(layout_resolver).map_err(|e| {
909 make_invariant_violation!(
910 "Failed looking up output SUI in SUI conservation checking for \
911 mutated type {:?}: {e:#?}",
912 object.struct_tag(),
913 )
914 })?;
915 }
916 }
917 }
918 if do_expensive_checks {
919 total_output_sui +=
923 gas_summary.computation_cost + gas_summary.non_refundable_storage_fee;
924 if let Some((epoch_fees, epoch_rebates)) = advance_epoch_gas_summary {
925 total_input_sui += epoch_fees;
926 total_output_sui += epoch_rebates;
927 }
928 if total_input_sui != total_output_sui {
929 return Err(ExecutionError::invariant_violation(
930 format!("SUI conservation failed: input={}, output={}, this transaction either mints or burns SUI",
931 total_input_sui,
932 total_output_sui))
933 );
934 }
935 }
936
937 if total_input_rebate != gas_summary.storage_rebate + gas_summary.non_refundable_storage_fee
940 {
941 }
948
949 if gas_summary.storage_cost != total_output_rebate {
951 }
958 Ok(())
959 }
960}
961
962impl ChildObjectResolver for TemporaryStore<'_> {
963 fn read_child_object(
964 &self,
965 parent: &ObjectID,
966 child: &ObjectID,
967 child_version_upper_bound: SequenceNumber,
968 ) -> SuiResult<Option<Object>> {
969 debug_assert!(!self.deleted.contains_key(child));
971 let obj_opt = self.written.get(child).map(|(obj, _kind)| obj);
972 if obj_opt.is_some() {
973 Ok(obj_opt.cloned())
974 } else {
975 self.store
976 .read_child_object(parent, child, child_version_upper_bound)
977 }
978 }
979
980 fn get_object_received_at_version(
981 &self,
982 owner: &ObjectID,
983 receiving_object_id: &ObjectID,
984 receive_object_at_version: SequenceNumber,
985 epoch_id: EpochId,
986 ) -> SuiResult<Option<Object>> {
987 debug_assert!(!self.deleted.contains_key(receiving_object_id));
990 debug_assert!(!self.written.contains_key(receiving_object_id));
991 self.store.get_object_received_at_version(
992 owner,
993 receiving_object_id,
994 receive_object_at_version,
995 epoch_id,
996 )
997 }
998}
999
1000impl Storage for TemporaryStore<'_> {
1001 fn reset(&mut self) {
1002 TemporaryStore::drop_writes(self);
1003 }
1004
1005 fn read_object(&self, id: &ObjectID) -> Option<&Object> {
1006 TemporaryStore::read_object(self, id)
1007 }
1008
1009 fn record_execution_results(
1010 &mut self,
1011 results: ExecutionResults,
1012 ) -> Result<(), ExecutionError> {
1013 let ExecutionResults::V1(results) = results else {
1014 panic!("ExecutionResults::V1 expected in sui-execution v0");
1015 };
1016 TemporaryStore::apply_object_changes(self, results.object_changes);
1017 for event in results.user_events {
1018 TemporaryStore::log_event(self, event);
1019 }
1020 Ok(())
1021 }
1022
1023 fn save_loaded_runtime_objects(
1024 &mut self,
1025 loaded_runtime_objects: BTreeMap<ObjectID, DynamicallyLoadedObjectMetadata>,
1026 ) {
1027 TemporaryStore::save_loaded_runtime_objects(self, loaded_runtime_objects)
1028 }
1029
1030 fn save_wrapped_object_containers(
1031 &mut self,
1032 _wrapped_object_containers: BTreeMap<ObjectID, ObjectID>,
1033 ) {
1034 unreachable!("Unused in v0")
1035 }
1036
1037 fn check_coin_deny_list(
1038 &self,
1039 _receiving_funds_type_and_owners: BTreeMap<TypeTag, BTreeSet<SuiAddress>>,
1040 ) -> DenyListResult {
1041 unreachable!("Coin denylist v2 is not supported in sui-execution v0");
1042 }
1043
1044 fn record_generated_object_ids(&mut self, _generated_ids: BTreeSet<ObjectID>) {
1045 unreachable!(
1046 "Generated object IDs are not recorded in ExecutionResults in sui-execution v0"
1047 );
1048 }
1049}
1050
1051impl BackingPackageStore for TemporaryStore<'_> {
1052 fn get_package_object(&self, package_id: &ObjectID) -> SuiResult<Option<PackageObject>> {
1053 if let Some((obj, _)) = self.written.get(package_id) {
1054 Ok(Some(PackageObject::new(obj.clone())))
1055 } else {
1056 self.store.get_package_object(package_id).inspect(|obj| {
1057 if let Some(v) = obj {
1059 if !self
1060 .runtime_packages_loaded_from_db
1061 .read()
1062 .contains_key(package_id)
1063 {
1064 self.runtime_packages_loaded_from_db
1067 .write()
1068 .insert(*package_id, v.clone());
1069 }
1070 }
1071 })
1072 }
1073 }
1074}
1075
1076impl ParentSync for TemporaryStore<'_> {
1077 fn get_latest_parent_entry_ref_deprecated(&self, object_id: ObjectID) -> Option<ObjectRef> {
1078 self.store.get_latest_parent_entry_ref_deprecated(object_id)
1079 }
1080}