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 }
547 }
548
549 for (id, (_new_obj, kind)) in &self.written {
550 if authenticated_objs.contains(id) || gas_objs.contains(id) {
551 continue;
552 }
553 match kind {
554 WriteKind::Mutate => {
555 let old_obj = self.store.get_object(id).unwrap_or_else(|| {
558 panic!("Mutated object must exist in the store: ID = {:?}", id)
559 });
560 match &old_obj.owner {
561 Owner::ObjectOwner(_parent) => {
562 objs_to_authenticate.push(*id);
563 }
564 Owner::AddressOwner(_) | Owner::Shared { .. } => {
565 unreachable!("Should already be in authenticated_objs")
566 }
567 Owner::Immutable => {
568 assert!(is_epoch_change, "Immutable objects cannot be written, except for Sui Framework/Move stdlib upgrades at epoch change boundaries");
569 assert!(
572 is_system_package(*id),
573 "Only system packages can be upgraded"
574 );
575 }
576 Owner::ConsensusAddressOwner { .. } => {
577 unimplemented!(
578 "ConsensusAddressOwner does not exist for this execution version"
579 )
580 }
581 }
582 }
583 WriteKind::Create | WriteKind::Unwrap => {
584 }
587 }
588 }
589
590 for (id, kind) in &self.deleted {
591 if authenticated_objs.contains(id) || gas_objs.contains(id) {
592 continue;
593 }
594 match kind {
595 DeleteKindWithOldVersion::Normal(_) | DeleteKindWithOldVersion::Wrap(_) => {
596 let old_obj = self.store.get_object(id).unwrap();
598 match &old_obj.owner {
599 Owner::ObjectOwner(_) => {
600 objs_to_authenticate.push(*id);
601 }
602 Owner::AddressOwner(_) | Owner::Shared { .. } => {
603 unreachable!("Should already be in authenticated_objs")
604 }
605 Owner::Immutable => unreachable!("Immutable objects cannot be deleted"),
606 Owner::ConsensusAddressOwner { .. } => {
607 unimplemented!(
608 "ConsensusAddressOwner does not exist for this execution version"
609 )
610 }
611 }
612 }
613 DeleteKindWithOldVersion::UnwrapThenDelete
614 | DeleteKindWithOldVersion::UnwrapThenDeleteDEPRECATED(_) => {
615 }
618 }
619 }
620 Ok((objs_to_authenticate, authenticated_objs))
621 }
622
623 pub fn check_ownership_invariants(
625 &self,
626 sender: &SuiAddress,
627 gas_charger: &mut GasCharger,
628 is_epoch_change: bool,
629 ) -> SuiResult<()> {
630 let (mut objects_to_authenticate, mut authenticated_objects) =
631 self.get_objects_to_authenticate(sender, gas_charger, is_epoch_change)?;
632
633 let mut covered = BTreeMap::new();
635 while let Some(to_authenticate) = objects_to_authenticate.pop() {
636 let Some(old_obj) = self.store.get_object(&to_authenticate) else {
637 continue;
640 };
641 let parent = match &old_obj.owner {
642 Owner::ObjectOwner(parent) => ObjectID::from(*parent),
643 owner => panic!(
644 "Unauthenticated root at {to_authenticate:?} with owner {owner:?}\n\
645 Potentially covering objects in: {covered:#?}",
646 ),
647 };
648
649 if authenticated_objects.contains(&parent) {
650 authenticated_objects.insert(to_authenticate);
651 } else if !covered.contains_key(&parent) {
652 objects_to_authenticate.push(parent);
653 }
654
655 covered.insert(to_authenticate, parent);
656 }
657 Ok(())
658 }
659}
660
661impl TemporaryStore<'_> {
662 fn get_input_storage_rebate(&self, id: &ObjectID, expected_version: SequenceNumber) -> u64 {
664 if let Some(old_obj) = self.input_objects.get(id) {
666 old_obj.storage_rebate
667 } else if let Some(metadata) = self.loaded_child_objects.get(id) {
668 debug_assert_eq!(metadata.version, expected_version);
669 metadata.storage_rebate
670 } else if let Some(obj) = self.store.get_object_by_key(id, expected_version) {
671 debug_assert!(obj.is_package());
674 obj.storage_rebate
675 } else {
676 panic!(
678 "Looking up storage rebate of mutated object {:?} should not fail",
679 id
680 )
681 }
682 }
683
684 pub(crate) fn ensure_gas_and_input_mutated(&mut self, gas_charger: &mut GasCharger) {
685 if let Some(gas_object_id) = gas_charger.gas_coin() {
686 let gas_object = self
687 .read_object(&gas_object_id)
688 .expect("We constructed the object map so it should always have the gas object id")
689 .clone();
690 self.written
691 .entry(gas_object_id)
692 .or_insert_with(|| (gas_object, WriteKind::Mutate));
693 }
694 self.ensure_active_inputs_mutated();
695 }
696
697 pub(crate) fn collect_storage_and_rebate(&mut self, gas_charger: &mut GasCharger) {
704 let mut objects_to_update = vec![];
705 for (object_id, (object, write_kind)) in &mut self.written {
706 let old_storage_rebate = match write_kind {
708 WriteKind::Create | WriteKind::Unwrap => 0,
709 WriteKind::Mutate => {
710 if let Some(old_obj) = self.input_objects.get(object_id) {
711 old_obj.storage_rebate
712 } else {
713 let expected_version = object.version();
715 if let Some(old_obj) =
716 self.store.get_object_by_key(object_id, expected_version)
717 {
718 old_obj.storage_rebate
719 } else {
720 panic!("Looking up storage rebate of mutated object should not fail");
722 }
723 }
724 }
725 };
726 let new_object_size = object.object_size_for_gas_metering();
728 let new_storage_rebate =
730 gas_charger.track_storage_mutation(*object_id, new_object_size, old_storage_rebate);
731 object.storage_rebate = new_storage_rebate;
732 if !object.is_immutable() {
733 objects_to_update.push((object.clone(), *write_kind));
734 }
735 }
736
737 self.collect_rebate(gas_charger);
738
739 for (object, write_kind) in objects_to_update {
742 self.write_object(object, write_kind);
743 }
744 }
745
746 pub(crate) fn collect_rebate(&self, gas_charger: &mut GasCharger) {
747 for (object_id, kind) in &self.deleted {
748 match kind {
749 DeleteKindWithOldVersion::Wrap(version)
750 | DeleteKindWithOldVersion::Normal(version) => {
751 let storage_rebate = self.get_input_storage_rebate(object_id, *version);
753 gas_charger.track_storage_mutation(*object_id, 0, storage_rebate);
754 }
755 DeleteKindWithOldVersion::UnwrapThenDelete
756 | DeleteKindWithOldVersion::UnwrapThenDeleteDEPRECATED(_) => {
757 }
759 }
760 }
761 }
762}
763impl TemporaryStore<'_> {
768 pub fn advance_epoch_safe_mode(
769 &mut self,
770 params: &AdvanceEpochParams,
771 protocol_config: &ProtocolConfig,
772 ) {
773 let wrapper = get_sui_system_state_wrapper(self.store.as_object_store())
774 .expect("System state wrapper object must exist");
775 let (new_object, _) =
776 wrapper.advance_epoch_safe_mode(params, self.store.as_object_store(), protocol_config);
777 self.write_object(new_object, WriteKind::Mutate);
778 }
779}
780
781type ModifiedObjectInfo<'a> = (ObjectID, Option<(SequenceNumber, u64)>, Option<&'a Object>);
782
783impl TemporaryStore<'_> {
784 fn get_input_sui(
785 &self,
786 id: &ObjectID,
787 expected_version: SequenceNumber,
788 layout_resolver: &mut impl LayoutResolver,
789 ) -> Result<u64, ExecutionError> {
790 if let Some(obj) = self.input_objects.get(id) {
791 if obj.version() != expected_version {
793 invariant_violation!(
794 "Version mismatching when resolving input object to check conservation--\
795 expected {}, got {}",
796 expected_version,
797 obj.version(),
798 );
799 }
800 obj.get_total_sui(layout_resolver).map_err(|e| {
801 make_invariant_violation!(
802 "Failed looking up input SUI in SUI conservation checking for input with \
803 type {:?}: {e:#?}",
804 obj.struct_tag(),
805 )
806 })
807 } else {
808 let Some(obj) = self.store.get_object_by_key(id, expected_version) else {
810 invariant_violation!(
811 "Failed looking up dynamic field {id} in SUI conservation checking"
812 );
813 };
814 obj.get_total_sui(layout_resolver).map_err(|e| {
815 make_invariant_violation!(
816 "Failed looking up input SUI in SUI conservation checking for type \
817 {:?}: {e:#?}",
818 obj.struct_tag(),
819 )
820 })
821 }
822 }
823
824 fn get_modified_objects(&self) -> Vec<ModifiedObjectInfo<'_>> {
829 self.written
830 .iter()
831 .map(|(id, (object, kind))| match kind {
832 WriteKind::Mutate => {
833 let version = object.version();
835 let storage_rebate = self.get_input_storage_rebate(id, version);
836 (*id, Some((object.version(), storage_rebate)), Some(object))
837 }
838 WriteKind::Create | WriteKind::Unwrap => (*id, None, Some(object)),
839 })
840 .chain(self.deleted.iter().filter_map(|(id, kind)| match kind {
841 DeleteKindWithOldVersion::Normal(version)
842 | DeleteKindWithOldVersion::Wrap(version) => {
843 let storage_rebate = self.get_input_storage_rebate(id, *version);
844 Some((*id, Some((*version, storage_rebate)), None))
845 }
846 DeleteKindWithOldVersion::UnwrapThenDelete
847 | DeleteKindWithOldVersion::UnwrapThenDeleteDEPRECATED(_) => None,
848 }))
849 .collect()
850 }
851
852 pub fn check_sui_conserved(
875 &self,
876 gas_summary: &GasCostSummary,
877 advance_epoch_gas_summary: Option<(u64, u64)>,
878 layout_resolver: &mut impl LayoutResolver,
879 do_expensive_checks: bool,
880 ) -> Result<(), ExecutionError> {
881 let mut total_input_sui = 0;
883 let mut total_output_sui = 0;
885 let mut total_input_rebate = 0;
887 let mut total_output_rebate = 0;
889 for (id, input, output) in self.get_modified_objects() {
890 if let Some((version, storage_rebate)) = input {
891 total_input_rebate += storage_rebate;
892 if do_expensive_checks {
893 total_input_sui += self.get_input_sui(&id, version, layout_resolver)?;
894 }
895 }
896 if let Some(object) = output {
897 total_output_rebate += object.storage_rebate;
898 if do_expensive_checks {
899 total_output_sui += object.get_total_sui(layout_resolver).map_err(|e| {
900 make_invariant_violation!(
901 "Failed looking up output SUI in SUI conservation checking for \
902 mutated type {:?}: {e:#?}",
903 object.struct_tag(),
904 )
905 })?;
906 }
907 }
908 }
909 if do_expensive_checks {
910 total_output_sui +=
914 gas_summary.computation_cost + gas_summary.non_refundable_storage_fee;
915 if let Some((epoch_fees, epoch_rebates)) = advance_epoch_gas_summary {
916 total_input_sui += epoch_fees;
917 total_output_sui += epoch_rebates;
918 }
919 if total_input_sui != total_output_sui {
920 return Err(ExecutionError::invariant_violation(
921 format!("SUI conservation failed: input={}, output={}, this transaction either mints or burns SUI",
922 total_input_sui,
923 total_output_sui))
924 );
925 }
926 }
927
928 if total_input_rebate != gas_summary.storage_rebate + gas_summary.non_refundable_storage_fee
931 {
932 }
939
940 if gas_summary.storage_cost != total_output_rebate {
942 }
949 Ok(())
950 }
951}
952
953impl ChildObjectResolver for TemporaryStore<'_> {
954 fn read_child_object(
955 &self,
956 parent: &ObjectID,
957 child: &ObjectID,
958 child_version_upper_bound: SequenceNumber,
959 ) -> SuiResult<Option<Object>> {
960 debug_assert!(!self.deleted.contains_key(child));
962 let obj_opt = self.written.get(child).map(|(obj, _kind)| obj);
963 if obj_opt.is_some() {
964 Ok(obj_opt.cloned())
965 } else {
966 self.store
967 .read_child_object(parent, child, child_version_upper_bound)
968 }
969 }
970
971 fn get_object_received_at_version(
972 &self,
973 owner: &ObjectID,
974 receiving_object_id: &ObjectID,
975 receive_object_at_version: SequenceNumber,
976 epoch_id: EpochId,
977 ) -> SuiResult<Option<Object>> {
978 debug_assert!(!self.deleted.contains_key(receiving_object_id));
981 debug_assert!(!self.written.contains_key(receiving_object_id));
982 self.store.get_object_received_at_version(
983 owner,
984 receiving_object_id,
985 receive_object_at_version,
986 epoch_id,
987 )
988 }
989}
990
991impl Storage for TemporaryStore<'_> {
992 fn reset(&mut self) {
993 TemporaryStore::drop_writes(self);
994 }
995
996 fn read_object(&self, id: &ObjectID) -> Option<&Object> {
997 TemporaryStore::read_object(self, id)
998 }
999
1000 fn record_execution_results(
1001 &mut self,
1002 results: ExecutionResults,
1003 ) -> Result<(), ExecutionError> {
1004 let ExecutionResults::V1(results) = results else {
1005 panic!("ExecutionResults::V1 expected in sui-execution v0");
1006 };
1007 TemporaryStore::apply_object_changes(self, results.object_changes);
1008 for event in results.user_events {
1009 TemporaryStore::log_event(self, event);
1010 }
1011 Ok(())
1012 }
1013
1014 fn save_loaded_runtime_objects(
1015 &mut self,
1016 loaded_runtime_objects: BTreeMap<ObjectID, DynamicallyLoadedObjectMetadata>,
1017 ) {
1018 TemporaryStore::save_loaded_runtime_objects(self, loaded_runtime_objects)
1019 }
1020
1021 fn save_wrapped_object_containers(
1022 &mut self,
1023 _wrapped_object_containers: BTreeMap<ObjectID, ObjectID>,
1024 ) {
1025 unreachable!("Unused in v0")
1026 }
1027
1028 fn check_coin_deny_list(
1029 &self,
1030 _receiving_funds_type_and_owners: BTreeMap<TypeTag, BTreeSet<SuiAddress>>,
1031 ) -> DenyListResult {
1032 unreachable!("Coin denylist v2 is not supported in sui-execution v0");
1033 }
1034
1035 fn record_generated_object_ids(&mut self, _generated_ids: BTreeSet<ObjectID>) {
1036 unreachable!(
1037 "Generated object IDs are not recorded in ExecutionResults in sui-execution v0"
1038 );
1039 }
1040}
1041
1042impl BackingPackageStore for TemporaryStore<'_> {
1043 fn get_package_object(&self, package_id: &ObjectID) -> SuiResult<Option<PackageObject>> {
1044 if let Some((obj, _)) = self.written.get(package_id) {
1045 Ok(Some(PackageObject::new(obj.clone())))
1046 } else {
1047 self.store.get_package_object(package_id).inspect(|obj| {
1048 if let Some(v) = obj {
1050 if !self
1051 .runtime_packages_loaded_from_db
1052 .read()
1053 .contains_key(package_id)
1054 {
1055 self.runtime_packages_loaded_from_db
1058 .write()
1059 .insert(*package_id, v.clone());
1060 }
1061 }
1062 })
1063 }
1064 }
1065}
1066
1067impl ParentSync for TemporaryStore<'_> {
1068 fn get_latest_parent_entry_ref_deprecated(&self, object_id: ObjectID) -> Option<ObjectRef> {
1069 self.store.get_latest_parent_entry_ref_deprecated(object_id)
1070 }
1071}