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 }
162 }
163
164 fn ensure_active_inputs_mutated(&mut self) {
168 let mut to_be_updated = vec![];
169 for id in self.mutable_input_refs.keys() {
170 if !self.written.contains_key(id) && !self.deleted.contains_key(id) {
171 to_be_updated.push(self.input_objects[id].clone());
175 }
176 }
177 for object in to_be_updated {
178 self.write_object(object.clone(), WriteKind::Mutate);
180 }
181 }
182
183 pub fn to_effects(
184 mut self,
185 shared_object_refs: Vec<SharedInput>,
186 transaction_digest: &TransactionDigest,
187 transaction_dependencies: Vec<TransactionDigest>,
188 gas_cost_summary: GasCostSummary,
189 status: ExecutionStatus,
190 gas_charger: &mut GasCharger,
191 epoch: EpochId,
192 ) -> (InnerTemporaryStore, TransactionEffects) {
193 let mut modified_at_versions = vec![];
194
195 self.written.iter_mut().for_each(|(id, (obj, kind))| {
197 if *kind == WriteKind::Mutate {
198 modified_at_versions.push((*id, obj.version()))
199 }
200 });
201
202 self.deleted.iter_mut().for_each(|(id, kind)| {
203 if let Some(version) = kind.old_version() {
204 modified_at_versions.push((*id, version));
205 }
206 });
207
208 self.update_object_version_and_prev_tx();
209
210 let mut deleted = vec![];
211 let mut wrapped = vec![];
212 let mut unwrapped_then_deleted = vec![];
213 for (id, kind) in &self.deleted {
214 match kind {
215 DeleteKindWithOldVersion::Normal(_) => deleted.push((
216 *id,
217 self.lamport_timestamp,
218 ObjectDigest::OBJECT_DIGEST_DELETED,
219 )),
220 DeleteKindWithOldVersion::UnwrapThenDelete
221 | DeleteKindWithOldVersion::UnwrapThenDeleteDEPRECATED(_) => unwrapped_then_deleted
222 .push((
223 *id,
224 self.lamport_timestamp,
225 ObjectDigest::OBJECT_DIGEST_DELETED,
226 )),
227 DeleteKindWithOldVersion::Wrap(_) => wrapped.push((
228 *id,
229 self.lamport_timestamp,
230 ObjectDigest::OBJECT_DIGEST_WRAPPED,
231 )),
232 }
233 }
234
235 let updated_gas_object_info = if let Some(coin_id) = gas_charger.gas_coin() {
240 let (object, _kind) = &self.written[&coin_id];
241 (object.compute_object_reference(), object.owner.clone())
242 } else {
243 (
244 (ObjectID::ZERO, SequenceNumber::default(), ObjectDigest::MIN),
245 Owner::AddressOwner(SuiAddress::default()),
246 )
247 };
248
249 let mut mutated = vec![];
250 let mut created = vec![];
251 let mut unwrapped = vec![];
252 for (object, kind) in self.written.values() {
253 let object_ref = object.compute_object_reference();
254 let owner = object.owner.clone();
255 match kind {
256 WriteKind::Mutate => mutated.push((object_ref, owner)),
257 WriteKind::Create => created.push((object_ref, owner)),
258 WriteKind::Unwrap => unwrapped.push((object_ref, owner)),
259 }
260 }
261
262 let inner = self.into_inner();
263
264 let shared_object_refs = shared_object_refs
265 .into_iter()
266 .map(|shared_input| match shared_input {
267 SharedInput::Existing(oref) => oref,
268 SharedInput::ConsensusStreamEnded(_) => {
269 unreachable!("Shared object deletion not supported in effects v1")
270 }
271 SharedInput::Cancelled(_) => {
272 unreachable!("Per object congestion control not supported in effects v1.")
273 }
274 })
275 .collect();
276
277 let effects = TransactionEffects::new_from_execution_v1(
278 status,
279 epoch,
280 gas_cost_summary,
281 modified_at_versions,
282 shared_object_refs,
283 *transaction_digest,
284 created,
285 mutated,
286 unwrapped,
287 deleted,
288 unwrapped_then_deleted,
289 wrapped,
290 updated_gas_object_info,
291 if inner.events.data.is_empty() {
292 None
293 } else {
294 Some(inner.events.digest())
295 },
296 transaction_dependencies,
297 );
298 (inner, effects)
299 }
300
301 #[cfg(debug_assertions)]
303 fn check_invariants(&self) {
304 debug_assert!(
306 {
307 let mut used = HashSet::new();
308 self.written.iter().all(|(elt, _)| used.insert(elt));
309 self.deleted.iter().all(move |elt| used.insert(elt.0))
310 },
311 "Object both written and deleted."
312 );
313
314 debug_assert!(
316 {
317 let mut used = HashSet::new();
318 self.written.iter().all(|(elt, _)| used.insert(elt));
319 self.deleted.iter().all(|elt| used.insert(elt.0));
320
321 self.mutable_input_refs.keys().all(|elt| !used.insert(elt))
322 },
323 "Mutable input neither written nor deleted."
324 );
325
326 debug_assert!(
327 {
328 self.written
329 .iter()
330 .all(|(_, (obj, _))| obj.previous_transaction == self.tx_digest)
331 },
332 "Object previous transaction not properly set",
333 );
334
335 if self.protocol_config.simplified_unwrap_then_delete() {
336 debug_assert!(self.deleted.iter().all(|(_, kind)| {
337 !matches!(
338 kind,
339 DeleteKindWithOldVersion::UnwrapThenDeleteDEPRECATED(_)
340 )
341 }));
342 } else {
343 debug_assert!(self
344 .deleted
345 .iter()
346 .all(|(_, kind)| { !matches!(kind, DeleteKindWithOldVersion::UnwrapThenDelete) }));
347 }
348 }
349
350 pub fn write_object(&mut self, mut object: Object, kind: WriteKind) {
355 debug_assert!(!self.deleted.contains_key(&object.id()));
357 #[cfg(test)] if let Some(existing_object) = self.read_object(&object.id()) {
360 if existing_object.is_immutable() {
361 panic!("Internal invariant violation: Mutating a read-only object.")
364 }
365 }
366
367 debug_assert!(
372 kind != WriteKind::Create
373 || object.is_immutable()
374 || object.version() == SequenceNumber::MIN,
375 "Created mutable objects should not have a version set",
376 );
377
378 object.previous_transaction = self.tx_digest;
381 self.written.insert(object.id(), (object, kind));
382 }
383
384 pub fn delete_object(&mut self, id: &ObjectID, kind: DeleteKindWithOldVersion) {
385 debug_assert!(!self.written.contains_key(id));
387
388 #[cfg(debug_assertions)]
391 if let Some(object) = self.read_object(id) {
392 if object.is_immutable() {
393 let digest = self.tx_digest;
398 panic!("Internal invariant violation in tx {digest}: Deleting immutable object {id}, delete kind {kind:?}")
399 }
400 }
401
402 self.deleted.insert(*id, kind);
405 }
406
407 pub fn drop_writes(&mut self) {
408 self.written.clear();
409 self.deleted.clear();
410 self.events.clear();
411 }
412
413 pub fn log_event(&mut self, event: Event) {
414 self.events.push(event)
415 }
416
417 pub fn read_object(&self, id: &ObjectID) -> Option<&Object> {
418 debug_assert!(!self.deleted.contains_key(id));
420 self.written
421 .get(id)
422 .map(|(obj, _kind)| obj)
423 .or_else(|| self.input_objects.get(id))
424 }
425
426 pub fn apply_object_changes(&mut self, changes: BTreeMap<ObjectID, ObjectChange>) {
427 for (id, change) in changes {
428 match change {
429 ObjectChange::Write(new_value, kind) => self.write_object(new_value, kind),
430 ObjectChange::Delete(kind) => self.delete_object(&id, kind),
431 }
432 }
433 }
434
435 pub fn save_loaded_runtime_objects(
436 &mut self,
437 loaded_runtime_objects: BTreeMap<ObjectID, DynamicallyLoadedObjectMetadata>,
438 ) {
439 #[cfg(debug_assertions)]
440 {
441 for (id, v1) in &loaded_runtime_objects {
442 if let Some(v2) = self.loaded_child_objects.get(id) {
443 assert_eq!(v1, v2);
444 }
445 }
446 for (id, v1) in &self.loaded_child_objects {
447 if let Some(v2) = loaded_runtime_objects.get(id) {
448 assert_eq!(v1, v2);
449 }
450 }
451 }
452 self.loaded_child_objects.extend(loaded_runtime_objects);
455 }
456
457 pub fn estimate_effects_size_upperbound(&self) -> usize {
458 TransactionEffects::estimate_effects_size_upperbound_v1(
460 self.written.len(),
461 self.mutable_input_refs.len(),
462 self.deleted.len(),
463 self.input_objects.len(),
464 )
465 }
466
467 pub fn written_objects_size(&self) -> usize {
468 self.written
469 .iter()
470 .fold(0, |sum, obj| sum + obj.1 .0.object_size_for_gas_metering())
471 }
472
473 pub fn conserve_unmetered_storage_rebate(&mut self, unmetered_storage_rebate: u64) {
476 if unmetered_storage_rebate == 0 {
477 return;
481 }
482 tracing::debug!(
483 "Amount of unmetered storage rebate from system tx: {:?}",
484 unmetered_storage_rebate
485 );
486 let mut system_state_wrapper = self
487 .read_object(&SUI_SYSTEM_STATE_OBJECT_ID)
488 .expect("0x5 object must be muated in system tx with unmetered storage rebate")
489 .clone();
490 assert_eq!(system_state_wrapper.storage_rebate, 0);
493 system_state_wrapper.storage_rebate = unmetered_storage_rebate;
494 self.write_object(system_state_wrapper, WriteKind::Mutate);
495 }
496}
497
498impl TemporaryStore<'_> {
499 fn get_objects_to_authenticate(
501 &self,
502 sender: &SuiAddress,
503 gas_charger: &mut GasCharger,
504 is_epoch_change: bool,
505 ) -> SuiResult<(Vec<ObjectID>, HashSet<ObjectID>)> {
506 let gas_objs: HashSet<&ObjectID> = gas_charger.gas_coins().iter().map(|g| &g.0).collect();
507 let mut objs_to_authenticate = Vec::new();
508 let mut authenticated_objs = HashSet::new();
509 for (id, obj) in &self.input_objects {
510 if gas_objs.contains(id) {
511 continue;
516 }
517 match &obj.owner {
518 Owner::AddressOwner(a) => {
519 assert!(sender == a, "Input object not owned by sender");
520 authenticated_objs.insert(*id);
521 }
522 Owner::Shared { .. } => {
523 authenticated_objs.insert(*id);
524 }
525 Owner::Immutable => {
526 }
537 Owner::ObjectOwner(_parent) => {
538 unreachable!("Input objects must be address owned, shared, or immutable")
539 }
540 Owner::ConsensusAddressOwner { .. } => {
541 unimplemented!(
542 "ConsensusAddressOwner does not exist for this execution version"
543 )
544 }
545 }
546 }
547
548 for (id, (_new_obj, kind)) in &self.written {
549 if authenticated_objs.contains(id) || gas_objs.contains(id) {
550 continue;
551 }
552 match kind {
553 WriteKind::Mutate => {
554 let old_obj = self.store.get_object(id).unwrap_or_else(|| {
557 panic!("Mutated object must exist in the store: ID = {:?}", id)
558 });
559 match &old_obj.owner {
560 Owner::ObjectOwner(_parent) => {
561 objs_to_authenticate.push(*id);
562 }
563 Owner::AddressOwner(_) | Owner::Shared { .. } => {
564 unreachable!("Should already be in authenticated_objs")
565 }
566 Owner::Immutable => {
567 assert!(is_epoch_change, "Immutable objects cannot be written, except for Sui Framework/Move stdlib upgrades at epoch change boundaries");
568 assert!(
571 is_system_package(*id),
572 "Only system packages can be upgraded"
573 );
574 }
575 Owner::ConsensusAddressOwner { .. } => {
576 unimplemented!(
577 "ConsensusAddressOwner does not exist for this execution version"
578 )
579 }
580 }
581 }
582 WriteKind::Create | WriteKind::Unwrap => {
583 }
586 }
587 }
588
589 for (id, kind) in &self.deleted {
590 if authenticated_objs.contains(id) || gas_objs.contains(id) {
591 continue;
592 }
593 match kind {
594 DeleteKindWithOldVersion::Normal(_) | DeleteKindWithOldVersion::Wrap(_) => {
595 let old_obj = self.store.get_object(id).unwrap();
597 match &old_obj.owner {
598 Owner::ObjectOwner(_) => {
599 objs_to_authenticate.push(*id);
600 }
601 Owner::AddressOwner(_) | Owner::Shared { .. } => {
602 unreachable!("Should already be in authenticated_objs")
603 }
604 Owner::Immutable => unreachable!("Immutable objects cannot be deleted"),
605 Owner::ConsensusAddressOwner { .. } => {
606 unimplemented!(
607 "ConsensusAddressOwner does not exist for this execution version"
608 )
609 }
610 }
611 }
612 DeleteKindWithOldVersion::UnwrapThenDelete
613 | DeleteKindWithOldVersion::UnwrapThenDeleteDEPRECATED(_) => {
614 }
617 }
618 }
619 Ok((objs_to_authenticate, authenticated_objs))
620 }
621
622 pub fn check_ownership_invariants(
624 &self,
625 sender: &SuiAddress,
626 gas_charger: &mut GasCharger,
627 is_epoch_change: bool,
628 ) -> SuiResult<()> {
629 let (mut objects_to_authenticate, mut authenticated_objects) =
630 self.get_objects_to_authenticate(sender, gas_charger, is_epoch_change)?;
631
632 let mut covered = BTreeMap::new();
634 while let Some(to_authenticate) = objects_to_authenticate.pop() {
635 let Some(old_obj) = self.store.get_object(&to_authenticate) else {
636 continue;
639 };
640 let parent = match &old_obj.owner {
641 Owner::ObjectOwner(parent) => ObjectID::from(*parent),
642 owner => panic!(
643 "Unauthenticated root at {to_authenticate:?} with owner {owner:?}\n\
644 Potentially covering objects in: {covered:#?}",
645 ),
646 };
647
648 if authenticated_objects.contains(&parent) {
649 authenticated_objects.insert(to_authenticate);
650 } else if !covered.contains_key(&parent) {
651 objects_to_authenticate.push(parent);
652 }
653
654 covered.insert(to_authenticate, parent);
655 }
656 Ok(())
657 }
658}
659
660impl TemporaryStore<'_> {
661 fn get_input_storage_rebate(&self, id: &ObjectID, expected_version: SequenceNumber) -> u64 {
663 if let Some(old_obj) = self.input_objects.get(id) {
665 old_obj.storage_rebate
666 } else if let Some(metadata) = self.loaded_child_objects.get(id) {
667 debug_assert_eq!(metadata.version, expected_version);
668 metadata.storage_rebate
669 } else if let Some(obj) = self.store.get_object_by_key(id, expected_version) {
670 debug_assert!(obj.is_package());
673 obj.storage_rebate
674 } else {
675 panic!(
677 "Looking up storage rebate of mutated object {:?} should not fail",
678 id
679 )
680 }
681 }
682
683 pub(crate) fn ensure_gas_and_input_mutated(&mut self, gas_charger: &mut GasCharger) {
684 if let Some(gas_object_id) = gas_charger.gas_coin() {
685 let gas_object = self
686 .read_object(&gas_object_id)
687 .expect("We constructed the object map so it should always have the gas object id")
688 .clone();
689 self.written
690 .entry(gas_object_id)
691 .or_insert_with(|| (gas_object, WriteKind::Mutate));
692 }
693 self.ensure_active_inputs_mutated();
694 }
695
696 pub(crate) fn collect_storage_and_rebate(&mut self, gas_charger: &mut GasCharger) {
703 let mut objects_to_update = vec![];
704 for (object_id, (object, write_kind)) in &mut self.written {
705 let old_storage_rebate = match write_kind {
707 WriteKind::Create | WriteKind::Unwrap => 0,
708 WriteKind::Mutate => {
709 if let Some(old_obj) = self.input_objects.get(object_id) {
710 old_obj.storage_rebate
711 } else {
712 let expected_version = object.version();
714 if let Some(old_obj) =
715 self.store.get_object_by_key(object_id, expected_version)
716 {
717 old_obj.storage_rebate
718 } else {
719 panic!("Looking up storage rebate of mutated object should not fail");
721 }
722 }
723 }
724 };
725 let new_object_size = object.object_size_for_gas_metering();
727 let new_storage_rebate =
729 gas_charger.track_storage_mutation(*object_id, new_object_size, old_storage_rebate);
730 object.storage_rebate = new_storage_rebate;
731 if !object.is_immutable() {
732 objects_to_update.push((object.clone(), *write_kind));
733 }
734 }
735
736 self.collect_rebate(gas_charger);
737
738 for (object, write_kind) in objects_to_update {
741 self.write_object(object, write_kind);
742 }
743 }
744
745 pub(crate) fn collect_rebate(&self, gas_charger: &mut GasCharger) {
746 for (object_id, kind) in &self.deleted {
747 match kind {
748 DeleteKindWithOldVersion::Wrap(version)
749 | DeleteKindWithOldVersion::Normal(version) => {
750 let storage_rebate = self.get_input_storage_rebate(object_id, *version);
752 gas_charger.track_storage_mutation(*object_id, 0, storage_rebate);
753 }
754 DeleteKindWithOldVersion::UnwrapThenDelete
755 | DeleteKindWithOldVersion::UnwrapThenDeleteDEPRECATED(_) => {
756 }
758 }
759 }
760 }
761}
762impl TemporaryStore<'_> {
767 pub fn advance_epoch_safe_mode(
768 &mut self,
769 params: &AdvanceEpochParams,
770 protocol_config: &ProtocolConfig,
771 ) {
772 let wrapper = get_sui_system_state_wrapper(self.store.as_object_store())
773 .expect("System state wrapper object must exist");
774 let (new_object, _) =
775 wrapper.advance_epoch_safe_mode(params, self.store.as_object_store(), protocol_config);
776 self.write_object(new_object, WriteKind::Mutate);
777 }
778}
779
780type ModifiedObjectInfo<'a> = (ObjectID, Option<(SequenceNumber, u64)>, Option<&'a Object>);
781
782impl TemporaryStore<'_> {
783 fn get_input_sui(
784 &self,
785 id: &ObjectID,
786 expected_version: SequenceNumber,
787 layout_resolver: &mut impl LayoutResolver,
788 ) -> Result<u64, ExecutionError> {
789 if let Some(obj) = self.input_objects.get(id) {
790 if obj.version() != expected_version {
792 invariant_violation!(
793 "Version mismatching when resolving input object to check conservation--\
794 expected {}, got {}",
795 expected_version,
796 obj.version(),
797 );
798 }
799 obj.get_total_sui(layout_resolver).map_err(|e| {
800 make_invariant_violation!(
801 "Failed looking up input SUI in SUI conservation checking for input with \
802 type {:?}: {e:#?}",
803 obj.struct_tag(),
804 )
805 })
806 } else {
807 let Some(obj) = self.store.get_object_by_key(id, expected_version) else {
809 invariant_violation!(
810 "Failed looking up dynamic field {id} in SUI conservation checking"
811 );
812 };
813 obj.get_total_sui(layout_resolver).map_err(|e| {
814 make_invariant_violation!(
815 "Failed looking up input SUI in SUI conservation checking for type \
816 {:?}: {e:#?}",
817 obj.struct_tag(),
818 )
819 })
820 }
821 }
822
823 fn get_modified_objects(&self) -> Vec<ModifiedObjectInfo<'_>> {
828 self.written
829 .iter()
830 .map(|(id, (object, kind))| match kind {
831 WriteKind::Mutate => {
832 let version = object.version();
834 let storage_rebate = self.get_input_storage_rebate(id, version);
835 (*id, Some((object.version(), storage_rebate)), Some(object))
836 }
837 WriteKind::Create | WriteKind::Unwrap => (*id, None, Some(object)),
838 })
839 .chain(self.deleted.iter().filter_map(|(id, kind)| match kind {
840 DeleteKindWithOldVersion::Normal(version)
841 | DeleteKindWithOldVersion::Wrap(version) => {
842 let storage_rebate = self.get_input_storage_rebate(id, *version);
843 Some((*id, Some((*version, storage_rebate)), None))
844 }
845 DeleteKindWithOldVersion::UnwrapThenDelete
846 | DeleteKindWithOldVersion::UnwrapThenDeleteDEPRECATED(_) => None,
847 }))
848 .collect()
849 }
850
851 pub fn check_sui_conserved(
874 &self,
875 gas_summary: &GasCostSummary,
876 advance_epoch_gas_summary: Option<(u64, u64)>,
877 layout_resolver: &mut impl LayoutResolver,
878 do_expensive_checks: bool,
879 ) -> Result<(), ExecutionError> {
880 let mut total_input_sui = 0;
882 let mut total_output_sui = 0;
884 let mut total_input_rebate = 0;
886 let mut total_output_rebate = 0;
888 for (id, input, output) in self.get_modified_objects() {
889 if let Some((version, storage_rebate)) = input {
890 total_input_rebate += storage_rebate;
891 if do_expensive_checks {
892 total_input_sui += self.get_input_sui(&id, version, layout_resolver)?;
893 }
894 }
895 if let Some(object) = output {
896 total_output_rebate += object.storage_rebate;
897 if do_expensive_checks {
898 total_output_sui += object.get_total_sui(layout_resolver).map_err(|e| {
899 make_invariant_violation!(
900 "Failed looking up output SUI in SUI conservation checking for \
901 mutated type {:?}: {e:#?}",
902 object.struct_tag(),
903 )
904 })?;
905 }
906 }
907 }
908 if do_expensive_checks {
909 total_output_sui +=
913 gas_summary.computation_cost + gas_summary.non_refundable_storage_fee;
914 if let Some((epoch_fees, epoch_rebates)) = advance_epoch_gas_summary {
915 total_input_sui += epoch_fees;
916 total_output_sui += epoch_rebates;
917 }
918 if total_input_sui != total_output_sui {
919 return Err(ExecutionError::invariant_violation(
920 format!("SUI conservation failed: input={}, output={}, this transaction either mints or burns SUI",
921 total_input_sui,
922 total_output_sui))
923 );
924 }
925 }
926
927 if total_input_rebate != gas_summary.storage_rebate + gas_summary.non_refundable_storage_fee
930 {
931 }
938
939 if gas_summary.storage_cost != total_output_rebate {
941 }
948 Ok(())
949 }
950}
951
952impl ChildObjectResolver for TemporaryStore<'_> {
953 fn read_child_object(
954 &self,
955 parent: &ObjectID,
956 child: &ObjectID,
957 child_version_upper_bound: SequenceNumber,
958 ) -> SuiResult<Option<Object>> {
959 debug_assert!(!self.deleted.contains_key(child));
961 let obj_opt = self.written.get(child).map(|(obj, _kind)| obj);
962 if obj_opt.is_some() {
963 Ok(obj_opt.cloned())
964 } else {
965 self.store
966 .read_child_object(parent, child, child_version_upper_bound)
967 }
968 }
969
970 fn get_object_received_at_version(
971 &self,
972 owner: &ObjectID,
973 receiving_object_id: &ObjectID,
974 receive_object_at_version: SequenceNumber,
975 epoch_id: EpochId,
976 ) -> SuiResult<Option<Object>> {
977 debug_assert!(!self.deleted.contains_key(receiving_object_id));
980 debug_assert!(!self.written.contains_key(receiving_object_id));
981 self.store.get_object_received_at_version(
982 owner,
983 receiving_object_id,
984 receive_object_at_version,
985 epoch_id,
986 )
987 }
988}
989
990impl Storage for TemporaryStore<'_> {
991 fn reset(&mut self) {
992 TemporaryStore::drop_writes(self);
993 }
994
995 fn read_object(&self, id: &ObjectID) -> Option<&Object> {
996 TemporaryStore::read_object(self, id)
997 }
998
999 fn record_execution_results(
1000 &mut self,
1001 results: ExecutionResults,
1002 ) -> Result<(), ExecutionError> {
1003 let ExecutionResults::V1(results) = results else {
1004 panic!("ExecutionResults::V1 expected in sui-execution v0");
1005 };
1006 TemporaryStore::apply_object_changes(self, results.object_changes);
1007 for event in results.user_events {
1008 TemporaryStore::log_event(self, event);
1009 }
1010 Ok(())
1011 }
1012
1013 fn save_loaded_runtime_objects(
1014 &mut self,
1015 loaded_runtime_objects: BTreeMap<ObjectID, DynamicallyLoadedObjectMetadata>,
1016 ) {
1017 TemporaryStore::save_loaded_runtime_objects(self, loaded_runtime_objects)
1018 }
1019
1020 fn save_wrapped_object_containers(
1021 &mut self,
1022 _wrapped_object_containers: BTreeMap<ObjectID, ObjectID>,
1023 ) {
1024 unreachable!("Unused in v0")
1025 }
1026
1027 fn check_coin_deny_list(
1028 &self,
1029 _receiving_funds_type_and_owners: BTreeMap<TypeTag, BTreeSet<SuiAddress>>,
1030 ) -> DenyListResult {
1031 unreachable!("Coin denylist v2 is not supported in sui-execution v0");
1032 }
1033
1034 fn record_generated_object_ids(&mut self, _generated_ids: BTreeSet<ObjectID>) {
1035 unreachable!(
1036 "Generated object IDs are not recorded in ExecutionResults in sui-execution v0"
1037 );
1038 }
1039}
1040
1041impl BackingPackageStore for TemporaryStore<'_> {
1042 fn get_package_object(&self, package_id: &ObjectID) -> SuiResult<Option<PackageObject>> {
1043 if let Some((obj, _)) = self.written.get(package_id) {
1044 Ok(Some(PackageObject::new(obj.clone())))
1045 } else {
1046 self.store.get_package_object(package_id).inspect(|obj| {
1047 if let Some(v) = obj {
1049 if !self
1050 .runtime_packages_loaded_from_db
1051 .read()
1052 .contains_key(package_id)
1053 {
1054 self.runtime_packages_loaded_from_db
1057 .write()
1058 .insert(*package_id, v.clone());
1059 }
1060 }
1061 })
1062 }
1063 }
1064}
1065
1066impl ParentSync for TemporaryStore<'_> {
1067 fn get_latest_parent_entry_ref_deprecated(&self, object_id: ObjectID) -> Option<ObjectRef> {
1068 self.store.get_latest_parent_entry_ref_deprecated(object_id)
1069 }
1070}