1use std::{
6 cell::RefCell,
7 collections::{BTreeMap, BTreeSet, HashSet},
8 rc::Rc,
9};
10
11#[cfg(not(target_arch = "wasm32"))]
12use iota_metrics::monitored_scope;
13use iota_protocol_config::ProtocolConfig;
14use iota_sdk_types::{
15 Address, ChangedObject, ExecutionStatus, GasCostSummary, IdOperation, ObjectId, ObjectIn,
16 ObjectOut, ObjectReference, Owner, TransactionDigest, TransactionEffects, TransactionEvents,
17 Version,
18};
19use iota_types::{
20 auth_context::AuthContext,
21 base_types::VersionDigest,
22 committee::EpochId,
23 deny_list_v1::check_coin_deny_list_v1_during_execution,
24 effects::TransactionEffectsExt,
25 error::{ExecutionError, IotaResult},
26 execution::{
27 DynamicallyLoadedObjectMetadata, ExecutionResults, ExecutionResultsV1, SharedInput,
28 },
29 execution_config_utils::to_binary_config,
30 inner_temporary_store::InnerTemporaryStore,
31 iota_system_state::{AdvanceEpochParams, get_iota_system_state_wrapper},
32 layout_resolver::LayoutResolver,
33 object::Object,
34 storage::{
35 BackingPackageStore, BackingStore, ChildObjectResolver, DenyListResult, PackageObject,
36 Storage,
37 },
38 transaction::InputObjects,
39};
40use parking_lot::RwLock;
41
42use crate::gas_charger::GasCharger;
43
44#[cfg(target_arch = "wasm32")]
46fn monitored_scope(_name: &'static str) -> Option<()> {
47 None
48}
49
50pub struct TemporaryStore<'backing> {
51 store: &'backing dyn BackingStore,
57 tx_digest: TransactionDigest,
58 input_objects: BTreeMap<ObjectId, Object>,
59 lamport_timestamp: Version,
62 mutable_input_refs: BTreeMap<ObjectId, (VersionDigest, Owner)>, execution_results: ExecutionResultsV1,
64 loaded_runtime_objects: BTreeMap<ObjectId, DynamicallyLoadedObjectMetadata>,
67 wrapped_object_containers: BTreeMap<ObjectId, ObjectId>,
70 protocol_config: &'backing ProtocolConfig,
71
72 runtime_packages_loaded_from_db: RwLock<BTreeMap<ObjectId, PackageObject>>,
75
76 receiving_objects: Vec<ObjectReference>,
79
80 cur_epoch: EpochId,
83
84 loaded_per_epoch_config_objects: RwLock<BTreeSet<ObjectId>>,
88
89 auth_context: Option<Rc<RefCell<AuthContext>>>,
91}
92
93impl<'backing> TemporaryStore<'backing> {
94 pub fn new(
97 store: &'backing dyn BackingStore,
98 input_objects: InputObjects,
99 receiving_objects: Vec<ObjectReference>,
100 tx_digest: TransactionDigest,
101 protocol_config: &'backing ProtocolConfig,
102 cur_epoch: EpochId,
103 ) -> Self {
104 let mutable_input_refs = input_objects.mutable_inputs();
105 let lamport_timestamp = input_objects.lamport_timestamp(&receiving_objects);
106 let objects = input_objects.into_object_map();
107 #[cfg(debug_assertions)]
108 {
109 assert!(
111 objects
112 .keys()
113 .collect::<HashSet<_>>()
114 .intersection(
115 &receiving_objects
116 .iter()
117 .map(|oref| &oref.object_id)
118 .collect::<HashSet<_>>()
119 )
120 .next()
121 .is_none()
122 );
123 }
124 Self {
125 store,
126 tx_digest,
127 input_objects: objects,
128 lamport_timestamp,
129 mutable_input_refs,
130 execution_results: ExecutionResultsV1::default(),
131 protocol_config,
132 loaded_runtime_objects: BTreeMap::new(),
133 wrapped_object_containers: BTreeMap::new(),
134 runtime_packages_loaded_from_db: RwLock::new(BTreeMap::new()),
135 receiving_objects,
136 cur_epoch,
137 loaded_per_epoch_config_objects: RwLock::new(BTreeSet::new()),
138 auth_context: None,
139 }
140 }
141
142 pub fn objects(&self) -> &BTreeMap<ObjectId, Object> {
144 &self.input_objects
145 }
146
147 pub fn update_object_version_and_prev_tx(&mut self) {
148 self.execution_results.update_version_and_previous_tx(
149 self.lamport_timestamp,
150 self.tx_digest,
151 &self.input_objects,
152 );
153
154 #[cfg(debug_assertions)]
155 {
156 self.check_invariants();
157 }
158 }
159
160 pub fn into_inner(self) -> InnerTemporaryStore {
163 let results = self.execution_results;
164 InnerTemporaryStore {
165 input_objects: self.input_objects,
166 mutable_inputs: self.mutable_input_refs,
167 written: results.written_objects,
168 events: TransactionEvents(results.user_events),
169 loaded_runtime_objects: self.loaded_runtime_objects,
170 runtime_packages_loaded_from_db: self.runtime_packages_loaded_from_db.into_inner(),
171 lamport_version: self.lamport_timestamp,
172 binary_config: to_binary_config(self.protocol_config, None),
173 }
174 }
175
176 pub(crate) fn ensure_active_inputs_mutated(&mut self) {
181 let mut to_be_updated = vec![];
182 for id in self.mutable_input_refs.keys() {
183 if !self.execution_results.modified_objects.contains(id) {
184 to_be_updated.push(self.input_objects[id].clone());
188 }
189 }
190 for object in to_be_updated {
191 self.mutate_input_object(object.clone());
193 }
194 }
195
196 fn get_object_changes(&self) -> BTreeMap<ObjectId, ChangedObject> {
197 let results = &self.execution_results;
198 let all_ids = results
199 .created_object_ids
200 .iter()
201 .chain(&results.deleted_object_ids)
202 .chain(&results.modified_objects)
203 .chain(results.written_objects.keys())
204 .collect::<BTreeSet<_>>();
205 all_ids
206 .into_iter()
207 .map(|id| (*id, self.object_change_for_id(id)))
208 .collect()
209 }
210
211 fn object_change_for_id(&self, id: &ObjectId) -> ChangedObject {
214 let results = &self.execution_results;
215 let id_created = results.created_object_ids.contains(id);
216 let id_deleted = results.deleted_object_ids.contains(id);
217 debug_assert!(
218 !id_created || !id_deleted,
219 "Object ID can't be created and deleted at the same time."
220 );
221
222 let input_state = self
223 .get_object_modified_at(id)
224 .map_or(ObjectIn::Missing, |m| ObjectIn::Data {
225 version: m.version,
226 digest: m.digest,
227 owner: m.owner,
228 });
229 let output_state = results
230 .written_objects
231 .get(id)
232 .map_or(ObjectOut::Missing, |o| {
233 if o.is_package() {
234 ObjectOut::PackageWrite {
235 version: o.version(),
236 digest: o.digest(),
237 }
238 } else {
239 ObjectOut::ObjectWrite {
240 digest: o.digest(),
241 owner: o.owner,
242 }
243 }
244 });
245 let id_operation = if id_created {
246 IdOperation::Created
247 } else if id_deleted {
248 IdOperation::Deleted
249 } else {
250 IdOperation::None
251 };
252
253 ChangedObject {
254 object_id: *id,
255 input_state,
256 output_state,
257 id_operation,
258 }
259 }
260
261 pub fn into_effects(
262 mut self,
263 shared_object_refs: Vec<SharedInput>,
264 transaction_digest: &TransactionDigest,
265 mut transaction_dependencies: BTreeSet<TransactionDigest>,
266 gas_cost_summary: GasCostSummary,
267 status: ExecutionStatus,
268 gas_charger: &mut GasCharger,
269 epoch: EpochId,
270 ) -> (InnerTemporaryStore, TransactionEffects) {
271 self.update_object_version_and_prev_tx();
272
273 for receiving_object in &self.receiving_objects {
277 if let Some(obj_meta) = self.loaded_runtime_objects.get(&receiving_object.object_id) {
282 let loaded_via_receive = obj_meta.version == receiving_object.version
286 && obj_meta.digest == receiving_object.digest
287 && obj_meta.owner.is_address();
288 if loaded_via_receive {
289 transaction_dependencies.insert(obj_meta.previous_transaction);
290 }
291 }
292 }
293
294 let gas_coin = gas_charger.gas_coin();
299
300 let object_changes = self.get_object_changes();
301
302 let lamport_version = self.lamport_timestamp;
303 let loaded_per_epoch_config_objects = self.loaded_per_epoch_config_objects.read().clone();
306 let inner = self.into_inner();
307
308 let effects = TransactionEffects::new_from_execution_v1(
309 status,
310 epoch,
311 gas_cost_summary,
312 shared_object_refs,
314 loaded_per_epoch_config_objects,
315 *transaction_digest,
316 lamport_version,
317 object_changes,
318 gas_coin,
319 if inner.events.is_empty() {
320 None
321 } else {
322 Some(inner.events.digest())
323 },
324 transaction_dependencies.into_iter().collect(),
325 );
326
327 (inner, effects)
328 }
329
330 #[cfg(debug_assertions)]
332 fn check_invariants(&self) {
333 debug_assert!(
335 {
336 self.execution_results
337 .written_objects
338 .keys()
339 .all(|id| !self.execution_results.deleted_object_ids.contains(id))
340 },
341 "Object both written and deleted."
342 );
343
344 debug_assert!(
346 {
347 self.mutable_input_refs
348 .keys()
349 .all(|id| self.execution_results.modified_objects.contains(id))
350 },
351 "Mutable input not modified."
352 );
353
354 debug_assert!(
355 {
356 self.execution_results
357 .written_objects
358 .values()
359 .all(|obj| obj.previous_transaction == self.tx_digest)
360 },
361 "Object previous transaction not properly set",
362 );
363 }
364
365 pub fn mutate_input_object(&mut self, object: Object) {
368 let id = object.id();
369 debug_assert!(self.input_objects.contains_key(&id));
370 debug_assert!(!object.is_immutable());
371 self.execution_results.modified_objects.insert(id);
372 self.execution_results.written_objects.insert(id, object);
373 }
374
375 pub fn mutate_child_object(&mut self, old_object: Object, new_object: Object) {
380 let id = new_object.id();
381 let old_ref = old_object.object_ref();
382 debug_assert_eq!(old_ref.object_id, id);
383 self.loaded_runtime_objects.insert(
384 id,
385 DynamicallyLoadedObjectMetadata {
386 version: old_ref.version,
387 digest: old_ref.digest,
388 owner: old_object.owner,
389 storage_rebate: old_object.storage_rebate,
390 previous_transaction: old_object.previous_transaction,
391 },
392 );
393 self.execution_results.modified_objects.insert(id);
394 self.execution_results
395 .written_objects
396 .insert(id, new_object);
397 }
398
399 pub fn upgrade_system_package(&mut self, package: Object) {
403 let id = package.id();
404 assert!(package.is_package() && id.is_system_package());
405 self.execution_results.modified_objects.insert(id);
406 self.execution_results.written_objects.insert(id, package);
407 }
408
409 pub fn create_object(&mut self, object: Object) {
412 debug_assert!(
418 object.is_immutable() || object.version() == Version::MIN_VALID_INCL,
419 "Created mutable objects should not have a version set",
420 );
421 let id = object.id();
422 self.execution_results.created_object_ids.insert(id);
423 self.execution_results.written_objects.insert(id, object);
424 }
425
426 pub fn delete_input_object(&mut self, id: &ObjectId) {
429 debug_assert!(!self.execution_results.written_objects.contains_key(id));
431 debug_assert!(self.input_objects.contains_key(id));
432 self.execution_results.modified_objects.insert(*id);
433 self.execution_results.deleted_object_ids.insert(*id);
434 }
435
436 pub fn drop_writes(&mut self) {
437 self.execution_results.drop_writes();
438 }
439
440 pub fn read_object(&self, id: &ObjectId) -> Option<&Object> {
441 debug_assert!(!self.execution_results.deleted_object_ids.contains(id));
443 self.execution_results
444 .written_objects
445 .get(id)
446 .or_else(|| self.input_objects.get(id))
447 }
448
449 pub fn save_loaded_runtime_objects(
450 &mut self,
451 loaded_runtime_objects: BTreeMap<ObjectId, DynamicallyLoadedObjectMetadata>,
452 ) {
453 #[cfg(debug_assertions)]
454 {
455 for (id, v1) in &loaded_runtime_objects {
456 if let Some(v2) = self.loaded_runtime_objects.get(id) {
457 assert_eq!(v1, v2);
458 }
459 }
460 for (id, v1) in &self.loaded_runtime_objects {
461 if let Some(v2) = loaded_runtime_objects.get(id) {
462 assert_eq!(v1, v2);
463 }
464 }
465 }
466 self.loaded_runtime_objects.extend(loaded_runtime_objects);
470 }
471
472 pub fn save_wrapped_object_containers(
473 &mut self,
474 wrapped_object_containers: BTreeMap<ObjectId, ObjectId>,
475 ) {
476 #[cfg(debug_assertions)]
477 {
478 for (id, container1) in &wrapped_object_containers {
479 if let Some(container2) = self.wrapped_object_containers.get(id) {
480 assert_eq!(container1, container2);
481 }
482 }
483 for (id, container1) in &self.wrapped_object_containers {
484 if let Some(container2) = wrapped_object_containers.get(id) {
485 assert_eq!(container1, container2);
486 }
487 }
488 }
489 self.wrapped_object_containers
493 .extend(wrapped_object_containers);
494 }
495
496 pub fn estimate_effects_size_upperbound(&self) -> usize {
497 TransactionEffects::estimate_size_upperbound_v1(
498 self.execution_results.written_objects.len(),
499 self.execution_results.modified_objects.len(),
500 self.input_objects.len(),
501 )
502 }
503
504 pub fn written_objects_size(&self) -> usize {
505 self.execution_results
506 .written_objects
507 .values()
508 .fold(0, |sum, obj| sum + obj.object_size_for_gas_metering())
509 }
510
511 pub fn conserve_unmetered_storage_rebate(&mut self, unmetered_storage_rebate: u64) {
516 if unmetered_storage_rebate == 0 {
517 return;
522 }
523 tracing::debug!(
524 "Amount of unmetered storage rebate from system tx: {:?}",
525 unmetered_storage_rebate
526 );
527 let mut system_state_wrapper = self
528 .read_object(&ObjectId::SYSTEM_STATE)
529 .expect("0x5 object must be mutated in system tx with unmetered storage rebate")
530 .clone();
531 assert_eq!(system_state_wrapper.storage_rebate, 0);
534 system_state_wrapper.storage_rebate = unmetered_storage_rebate;
535 self.mutate_input_object(system_state_wrapper);
536 }
537
538 fn get_object_modified_at(
545 &self,
546 object_id: &ObjectId,
547 ) -> Option<DynamicallyLoadedObjectMetadata> {
548 if self.execution_results.modified_objects.contains(object_id) {
549 Some(
550 self.mutable_input_refs
551 .get(object_id)
552 .map(
553 |((version, digest), owner)| DynamicallyLoadedObjectMetadata {
554 version: *version,
555 digest: *digest,
556 owner: *owner,
557 storage_rebate: self.input_objects[object_id].storage_rebate,
559 previous_transaction: self.input_objects[object_id]
560 .previous_transaction,
561 },
562 )
563 .or_else(|| self.loaded_runtime_objects.get(object_id).cloned())
564 .unwrap_or_else(|| {
565 debug_assert!(object_id.is_system_package());
566 let package_obj =
567 self.store.get_package_object(object_id).unwrap().unwrap();
568 let obj = package_obj.object();
569 DynamicallyLoadedObjectMetadata {
570 version: obj.version(),
571 digest: obj.digest(),
572 owner: obj.owner,
573 storage_rebate: obj.storage_rebate,
574 previous_transaction: obj.previous_transaction,
575 }
576 }),
577 )
578 } else {
579 None
580 }
581 }
582}
583
584impl TemporaryStore<'_> {
585 pub fn check_ownership_invariants(
588 &self,
589 sender: &Address,
590 gas_charger: &mut GasCharger,
591 mutable_inputs: &HashSet<ObjectId>,
592 is_epoch_change: bool,
593 ) -> IotaResult<()> {
594 let gas_objs: HashSet<&ObjectId> = gas_charger
595 .gas_coins()
596 .iter()
597 .map(|g| &g.object_id)
598 .collect();
599 let mut authenticated_for_mutation: HashSet<_> = self
601 .input_objects
602 .iter()
603 .filter_map(|(id, obj)| {
604 if gas_objs.contains(id) {
605 return None;
610 }
611 match &obj.owner {
612 Owner::Address(a) => {
613 assert!(sender == a, "Input object not owned by sender");
614 Some(id)
615 }
616 Owner::Shared(_) => Some(id),
617 Owner::Immutable => {
618 None
629 }
630 Owner::Object(_parent) => {
631 unreachable!("Input objects must be address owned, shared, or immutable")
632 }
633 _ => {
634 unimplemented!("a new Owner enum variant was added and needs to be handled")
635 }
636 }
637 })
638 .filter(|id| {
639 mutable_inputs.contains(id)
642 })
643 .copied()
644 .collect();
645
646 let mut objects_to_authenticate = self
648 .execution_results
649 .modified_objects
650 .iter()
651 .filter(|id| !gas_objs.contains(id))
652 .copied()
653 .collect::<Vec<_>>();
654 while let Some(to_authenticate) = objects_to_authenticate.pop() {
656 if authenticated_for_mutation.contains(&to_authenticate) {
657 continue;
659 }
660 let wrapped_parent = self.wrapped_object_containers.get(&to_authenticate);
661 let parent = if let Some(container_id) = wrapped_parent {
662 *container_id
665 } else {
666 let Some(old_obj) = self.store.try_get_object(&to_authenticate)? else {
667 panic!(
668 "
669 Failed to load object {to_authenticate:?}. \n\
670 If it cannot be loaded, \
671 we would expect it to be in the wrapped object map: {:?}",
672 self.wrapped_object_containers
673 )
674 };
675 match &old_obj.owner {
676 Owner::Object(parent) => *parent,
677 Owner::Address(parent) => {
678 ObjectId::from(*parent)
682 }
683 owner @ Owner::Shared(_) => panic!(
684 "Unauthenticated root at {to_authenticate:?} with owner {owner:?}\n\
685 Potentially covering objects in: {authenticated_for_mutation:#?}",
686 ),
687 Owner::Immutable => {
688 assert!(
689 is_epoch_change,
690 "Immutable objects cannot be written, except for \
691 IOTA Framework/Move stdlib upgrades at epoch change boundaries"
692 );
693 assert!(
697 to_authenticate.is_system_package(),
698 "Only system packages can be upgraded"
699 );
700 continue;
701 }
702 _ => {
703 unimplemented!("a new Owner enum variant was added and needs to be handled")
704 }
705 }
706 };
707 authenticated_for_mutation.insert(to_authenticate);
709 objects_to_authenticate.push(parent);
710 }
711 Ok(())
712 }
713
714 pub fn store_auth_context(&mut self, auth_context: Rc<RefCell<AuthContext>>) {
715 self.auth_context = Some(auth_context);
716 }
717}
718
719impl TemporaryStore<'_> {
720 pub(crate) fn collect_storage_and_rebate(&mut self, gas_charger: &mut GasCharger) {
728 let old_storage_rebates: Vec<_> = self
731 .execution_results
732 .written_objects
733 .keys()
734 .map(|object_id| {
735 self.get_object_modified_at(object_id)
736 .map(|metadata| metadata.storage_rebate)
737 .unwrap_or_default()
738 })
739 .collect();
740 for (object, old_storage_rebate) in self
741 .execution_results
742 .written_objects
743 .values_mut()
744 .zip(old_storage_rebates)
745 {
746 let new_object_size = object.object_size_for_gas_metering();
748 let new_storage_rebate = gas_charger.track_storage_mutation(
750 object.id(),
751 new_object_size,
752 old_storage_rebate,
753 );
754 object.storage_rebate = new_storage_rebate;
755 }
756
757 self.collect_rebate(gas_charger);
758 }
759
760 pub(crate) fn collect_rebate(&self, gas_charger: &mut GasCharger) {
761 for object_id in &self.execution_results.modified_objects {
762 if self
763 .execution_results
764 .written_objects
765 .contains_key(object_id)
766 {
767 continue;
768 }
769 let storage_rebate = self
771 .get_object_modified_at(object_id)
772 .unwrap()
774 .storage_rebate;
775 gas_charger.track_storage_mutation(*object_id, 0, storage_rebate);
776 }
777 }
778
779 pub fn check_execution_results_consistency(&self) -> Result<(), ExecutionError> {
780 assert_invariant!(
781 self.execution_results
782 .created_object_ids
783 .iter()
784 .all(|id| !self.execution_results.deleted_object_ids.contains(id)
785 && !self.execution_results.modified_objects.contains(id)),
786 "Created object IDs cannot also be deleted or modified"
787 );
788 assert_invariant!(
789 self.execution_results.modified_objects.iter().all(|id| {
790 self.mutable_input_refs.contains_key(id)
791 || self.loaded_runtime_objects.contains_key(id)
792 || id.is_system_package()
793 }),
794 "A modified object must be either a mutable input, a loaded child object, or a system package"
795 );
796 Ok(())
797 }
798
799 pub fn check_move_authenticator_results_consistency(&self) -> Result<(), ExecutionError> {
800 assert_invariant!(
801 self.execution_results.created_object_ids.is_empty(),
802 "Objects cannot be created during authenticator execution"
803 );
804 assert_invariant!(
805 self.execution_results.written_objects.is_empty(),
806 "Objects cannot be written during authenticator execution"
807 );
808 assert_invariant!(
809 self.execution_results.modified_objects.is_empty(),
810 "Objects cannot be modified during authenticator execution"
811 );
812 assert_invariant!(
813 self.execution_results.deleted_object_ids.is_empty(),
814 "Objects cannot be deleted during authenticator execution"
815 );
816 Ok(())
817 }
818}
819impl TemporaryStore<'_> {
824 pub fn advance_epoch_safe_mode(
825 &mut self,
826 params: &AdvanceEpochParams,
827 protocol_config: &ProtocolConfig,
828 ) {
829 let wrapper = get_iota_system_state_wrapper(self.store.as_object_store())
830 .expect("System state wrapper object must exist");
831 let (old_object, new_object) =
832 wrapper.advance_epoch_safe_mode(params, self.store.as_object_store(), protocol_config);
833 self.mutate_child_object(old_object, new_object);
834 }
835}
836
837type ModifiedObjectInfo<'a> = (
838 ObjectId,
839 Option<DynamicallyLoadedObjectMetadata>,
841 Option<&'a Object>,
842);
843
844impl TemporaryStore<'_> {
845 fn get_input_iota(
846 &self,
847 id: &ObjectId,
848 expected_version: Version,
849 layout_resolver: &mut impl LayoutResolver,
850 ) -> Result<u64, ExecutionError> {
851 if let Some(obj) = self.input_objects.get(id) {
852 if obj.version() != expected_version {
855 invariant_violation!(
856 "Version mismatching when resolving input object to check conservation--\
857 expected {}, got {}",
858 expected_version,
859 obj.version(),
860 );
861 }
862 obj.get_total_iota(layout_resolver).map_err(|e| {
863 make_invariant_violation!(
864 "Failed looking up input IOTA in IOTA conservation checking for input with \
865 type {:?}: {e:#?}",
866 obj.struct_tag(),
867 )
868 })
869 } else {
870 let Ok(Some(obj)) = self.store.try_get_object_by_key(id, expected_version) else {
872 invariant_violation!(
873 "Failed looking up dynamic field {id} in IOTA conservation checking"
874 );
875 };
876 obj.get_total_iota(layout_resolver).map_err(|e| {
877 make_invariant_violation!(
878 "Failed looking up input IOTA in IOTA conservation checking for type \
879 {:?}: {e:#?}",
880 obj.struct_tag(),
881 )
882 })
883 }
884 }
885
886 fn get_modified_objects(&self) -> Vec<ModifiedObjectInfo<'_>> {
893 self.execution_results
894 .modified_objects
895 .iter()
896 .map(|id| {
897 let metadata = self.get_object_modified_at(id);
898 let output = self.execution_results.written_objects.get(id);
899 (*id, metadata, output)
900 })
901 .chain(
902 self.execution_results
903 .written_objects
904 .iter()
905 .filter_map(|(id, object)| {
906 if self.execution_results.modified_objects.contains(id) {
907 None
908 } else {
909 Some((*id, None, Some(object)))
910 }
911 }),
912 )
913 .collect()
914 }
915
916 pub fn check_iota_conserved(&self, gas_summary: &GasCostSummary) -> Result<(), ExecutionError> {
932 let mut total_input_rebate = 0;
934 let mut total_output_rebate = 0;
936 for (_, input, output) in self.get_modified_objects() {
937 if let Some(input) = input {
938 total_input_rebate += input.storage_rebate;
939 }
940 if let Some(object) = output {
941 total_output_rebate += object.storage_rebate;
942 }
943 }
944
945 if gas_summary.storage_cost == 0 {
946 if total_input_rebate
958 != total_output_rebate
959 + gas_summary.storage_rebate
960 + gas_summary.non_refundable_storage_fee
961 {
962 return Err(ExecutionError::invariant_violation(format!(
963 "IOTA conservation failed -- no storage charges in gas summary \
964 and total storage input rebate {total_input_rebate} not equal \
965 to total storage output rebate {total_output_rebate}",
966 )));
967 }
968 } else {
969 if total_input_rebate
972 != gas_summary.storage_rebate + gas_summary.non_refundable_storage_fee
973 {
974 return Err(ExecutionError::invariant_violation(format!(
975 "IOTA conservation failed -- {} IOTA in storage rebate field of input objects, \
976 {} IOTA in tx storage rebate or tx non-refundable storage rebate",
977 total_input_rebate, gas_summary.non_refundable_storage_fee,
978 )));
979 }
980
981 if gas_summary.storage_cost != total_output_rebate {
984 return Err(ExecutionError::invariant_violation(format!(
985 "IOTA conservation failed -- {} IOTA charged for storage, \
986 {} IOTA in storage rebate field of output objects",
987 gas_summary.storage_cost, total_output_rebate
988 )));
989 }
990 }
991 Ok(())
992 }
993
994 pub fn check_iota_conserved_expensive(
1008 &self,
1009 gas_summary: &GasCostSummary,
1010 advance_epoch_gas_summary: Option<(u64, u64)>,
1011 layout_resolver: &mut impl LayoutResolver,
1012 ) -> Result<(), ExecutionError> {
1013 let mut total_input_iota = 0;
1016 let mut total_output_iota = 0;
1019 for (id, input, output) in self.get_modified_objects() {
1020 if let Some(input) = input {
1021 total_input_iota += self.get_input_iota(&id, input.version, layout_resolver)?;
1022 }
1023 if let Some(object) = output {
1024 total_output_iota += object.get_total_iota(layout_resolver).map_err(|e| {
1025 make_invariant_violation!(
1026 "Failed looking up output IOTA in IOTA conservation checking for \
1027 mutated type {:?}: {e:#?}",
1028 object.struct_tag(),
1029 )
1030 })?;
1031 }
1032 }
1033 total_output_iota += gas_summary.computation_cost + gas_summary.non_refundable_storage_fee;
1039 if let Some((epoch_fees, epoch_rebates)) = advance_epoch_gas_summary {
1040 total_input_iota += epoch_fees;
1041 total_output_iota += epoch_rebates;
1042 }
1043 if total_input_iota != total_output_iota {
1044 return Err(ExecutionError::invariant_violation(format!(
1045 "IOTA conservation failed: input={total_input_iota}, output={total_output_iota}, \
1046 this transaction either mints or burns IOTA",
1047 )));
1048 }
1049 Ok(())
1050 }
1051}
1052
1053impl ChildObjectResolver for TemporaryStore<'_> {
1054 fn read_child_object(
1055 &self,
1056 parent: &ObjectId,
1057 child: &ObjectId,
1058 child_version_upper_bound: Version,
1059 ) -> IotaResult<Option<Object>> {
1060 let obj_opt = self.execution_results.written_objects.get(child);
1061 if obj_opt.is_some() {
1062 Ok(obj_opt.cloned())
1063 } else {
1064 let _scope = monitored_scope("Execution::read_child_object");
1065 self.store
1066 .read_child_object(parent, child, child_version_upper_bound)
1067 }
1068 }
1069
1070 fn get_object_received_at_version(
1071 &self,
1072 owner: &ObjectId,
1073 receiving_object_id: &ObjectId,
1074 receive_object_at_version: Version,
1075 epoch_id: EpochId,
1076 ) -> IotaResult<Option<Object>> {
1077 debug_assert!(
1081 !self
1082 .execution_results
1083 .written_objects
1084 .contains_key(receiving_object_id)
1085 );
1086 debug_assert!(
1087 !self
1088 .execution_results
1089 .deleted_object_ids
1090 .contains(receiving_object_id)
1091 );
1092 self.store.get_object_received_at_version(
1093 owner,
1094 receiving_object_id,
1095 receive_object_at_version,
1096 epoch_id,
1097 )
1098 }
1099}
1100
1101impl Storage for TemporaryStore<'_> {
1102 fn reset(&mut self) {
1103 self.drop_writes();
1104 }
1105
1106 fn read_object(&self, id: &ObjectId) -> Option<&Object> {
1107 TemporaryStore::read_object(self, id)
1108 }
1109
1110 fn record_execution_results(&mut self, results: ExecutionResults) {
1112 let ExecutionResults::V1(results) = results;
1113
1114 self.execution_results.merge_results(results);
1117 }
1118
1119 fn save_loaded_runtime_objects(
1120 &mut self,
1121 loaded_runtime_objects: BTreeMap<ObjectId, DynamicallyLoadedObjectMetadata>,
1122 ) {
1123 TemporaryStore::save_loaded_runtime_objects(self, loaded_runtime_objects)
1124 }
1125
1126 fn save_wrapped_object_containers(
1127 &mut self,
1128 wrapped_object_containers: BTreeMap<ObjectId, ObjectId>,
1129 ) {
1130 TemporaryStore::save_wrapped_object_containers(self, wrapped_object_containers)
1131 }
1132
1133 fn check_coin_deny_list(&self, written_objects: &BTreeMap<ObjectId, Object>) -> DenyListResult {
1134 let result = check_coin_deny_list_v1_during_execution(
1135 written_objects,
1136 self.cur_epoch,
1137 self.store.as_object_store(),
1138 );
1139 if result.num_non_gas_coin_owners > 0
1143 && !self.input_objects.contains_key(&ObjectId::DENY_LIST)
1144 {
1145 self.loaded_per_epoch_config_objects
1146 .write()
1147 .insert(ObjectId::DENY_LIST);
1148 }
1149 result
1150 }
1151
1152 fn read_auth_context(&self) -> Option<Rc<RefCell<AuthContext>>> {
1153 self.auth_context.clone()
1154 }
1155}
1156
1157impl BackingPackageStore for TemporaryStore<'_> {
1158 fn get_package_object(&self, package_id: &ObjectId) -> IotaResult<Option<PackageObject>> {
1159 if let Some(obj) = self.execution_results.written_objects.get(package_id) {
1167 Ok(Some(PackageObject::new(obj.clone())))
1168 } else {
1169 self.store.get_package_object(package_id).inspect(|obj| {
1170 if let Some(v) = obj {
1172 if !self
1173 .runtime_packages_loaded_from_db
1174 .read()
1175 .contains_key(package_id)
1176 {
1177 self.runtime_packages_loaded_from_db
1182 .write()
1183 .insert(*package_id, v.clone());
1184 }
1185 }
1186 })
1187 }
1188 }
1189}