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