1use std::collections::{BTreeMap, BTreeSet, HashSet};
6
7use iota_metrics::monitored_scope;
8use iota_protocol_config::ProtocolConfig;
9use iota_types::{
10 IOTA_DENY_LIST_OBJECT_ID, IOTA_SYSTEM_STATE_OBJECT_ID,
11 base_types::{
12 IotaAddress, ObjectID, ObjectRef, SequenceNumber, TransactionDigest, VersionDigest,
13 },
14 committee::EpochId,
15 deny_list_v1::check_coin_deny_list_v1_during_execution,
16 effects::{EffectsObjectChange, TransactionEffects, TransactionEvents},
17 error::{ExecutionError, IotaError, IotaResult},
18 execution::{
19 DynamicallyLoadedObjectMetadata, ExecutionResults, ExecutionResultsV1, SharedInput,
20 },
21 execution_config_utils::to_binary_config,
22 execution_status::ExecutionStatus,
23 fp_bail,
24 gas::GasCostSummary,
25 inner_temporary_store::InnerTemporaryStore,
26 iota_system_state::{AdvanceEpochParams, get_iota_system_state_wrapper},
27 is_system_package,
28 layout_resolver::LayoutResolver,
29 object::{Data, Object, Owner},
30 storage::{
31 BackingPackageStore, BackingStore, ChildObjectResolver, DenyListResult, PackageObject,
32 Storage,
33 },
34 transaction::InputObjects,
35};
36use move_core_types::{
37 account_address::AccountAddress, language_storage::StructTag, resolver::ResourceResolver,
38};
39use parking_lot::RwLock;
40
41use crate::gas_charger::GasCharger;
42
43pub struct TemporaryStore<'backing> {
44 store: &'backing dyn BackingStore,
50 tx_digest: TransactionDigest,
51 input_objects: BTreeMap<ObjectID, Object>,
52 lamport_timestamp: SequenceNumber,
55 mutable_input_refs: BTreeMap<ObjectID, (VersionDigest, Owner)>, execution_results: ExecutionResultsV1,
57 loaded_runtime_objects: BTreeMap<ObjectID, DynamicallyLoadedObjectMetadata>,
60 wrapped_object_containers: BTreeMap<ObjectID, ObjectID>,
63 protocol_config: &'backing ProtocolConfig,
64
65 runtime_packages_loaded_from_db: RwLock<BTreeMap<ObjectID, PackageObject>>,
68
69 receiving_objects: Vec<ObjectRef>,
72
73 cur_epoch: EpochId,
76
77 loaded_per_epoch_config_objects: RwLock<BTreeSet<ObjectID>>,
81}
82
83impl<'backing> TemporaryStore<'backing> {
84 pub fn new(
87 store: &'backing dyn BackingStore,
88 input_objects: InputObjects,
89 receiving_objects: Vec<ObjectRef>,
90 tx_digest: TransactionDigest,
91 protocol_config: &'backing ProtocolConfig,
92 cur_epoch: EpochId,
93 ) -> Self {
94 let mutable_input_refs = input_objects.mutable_inputs();
95 let lamport_timestamp = input_objects.lamport_timestamp(&receiving_objects);
96 let objects = input_objects.into_object_map();
97 #[cfg(debug_assertions)]
98 {
99 assert!(
101 objects
102 .keys()
103 .collect::<HashSet<_>>()
104 .intersection(
105 &receiving_objects
106 .iter()
107 .map(|oref| &oref.0)
108 .collect::<HashSet<_>>()
109 )
110 .next()
111 .is_none()
112 );
113 }
114 Self {
115 store,
116 tx_digest,
117 input_objects: objects,
118 lamport_timestamp,
119 mutable_input_refs,
120 execution_results: ExecutionResultsV1::default(),
121 protocol_config,
122 loaded_runtime_objects: BTreeMap::new(),
123 wrapped_object_containers: BTreeMap::new(),
124 runtime_packages_loaded_from_db: RwLock::new(BTreeMap::new()),
125 receiving_objects,
126 cur_epoch,
127 loaded_per_epoch_config_objects: RwLock::new(BTreeSet::new()),
128 }
129 }
130
131 pub fn objects(&self) -> &BTreeMap<ObjectID, Object> {
133 &self.input_objects
134 }
135
136 pub fn update_object_version_and_prev_tx(&mut self) {
137 self.execution_results.update_version_and_previous_tx(
138 self.lamport_timestamp,
139 self.tx_digest,
140 &self.input_objects,
141 );
142
143 #[cfg(debug_assertions)]
144 {
145 self.check_invariants();
146 }
147 }
148
149 pub fn into_inner(self) -> InnerTemporaryStore {
152 let results = self.execution_results;
153 InnerTemporaryStore {
154 input_objects: self.input_objects,
155 mutable_inputs: self.mutable_input_refs,
156 written: results.written_objects,
157 events: TransactionEvents {
158 data: results.user_events,
159 },
160 loaded_runtime_objects: self.loaded_runtime_objects,
161 runtime_packages_loaded_from_db: self.runtime_packages_loaded_from_db.into_inner(),
162 lamport_version: self.lamport_timestamp,
163 binary_config: to_binary_config(self.protocol_config),
164 }
165 }
166
167 pub(crate) fn ensure_active_inputs_mutated(&mut self) {
172 let mut to_be_updated = vec![];
173 for id in self.mutable_input_refs.keys() {
174 if !self.execution_results.modified_objects.contains(id) {
175 to_be_updated.push(self.input_objects[id].clone());
179 }
180 }
181 for object in to_be_updated {
182 self.mutate_input_object(object.clone());
184 }
185 }
186
187 fn get_object_changes(&self) -> BTreeMap<ObjectID, EffectsObjectChange> {
188 let results = &self.execution_results;
189 let all_ids = results
190 .created_object_ids
191 .iter()
192 .chain(&results.deleted_object_ids)
193 .chain(&results.modified_objects)
194 .chain(results.written_objects.keys())
195 .collect::<BTreeSet<_>>();
196 all_ids
197 .into_iter()
198 .map(|id| {
199 (
200 *id,
201 EffectsObjectChange::new(
202 self.get_object_modified_at(id)
203 .map(|metadata| ((metadata.version, metadata.digest), metadata.owner)),
204 results.written_objects.get(id),
205 results.created_object_ids.contains(id),
206 results.deleted_object_ids.contains(id),
207 ),
208 )
209 })
210 .collect()
211 }
212
213 pub fn into_effects(
214 mut self,
215 shared_object_refs: Vec<SharedInput>,
216 transaction_digest: &TransactionDigest,
217 mut transaction_dependencies: BTreeSet<TransactionDigest>,
218 gas_cost_summary: GasCostSummary,
219 status: ExecutionStatus,
220 gas_charger: &mut GasCharger,
221 epoch: EpochId,
222 ) -> (InnerTemporaryStore, TransactionEffects) {
223 self.update_object_version_and_prev_tx();
224
225 for (id, expected_version, expected_digest) in &self.receiving_objects {
229 if let Some(obj_meta) = self.loaded_runtime_objects.get(id) {
234 let loaded_via_receive = obj_meta.version == *expected_version
238 && obj_meta.digest == *expected_digest
239 && obj_meta.owner.is_address_owned();
240 if loaded_via_receive {
241 transaction_dependencies.insert(obj_meta.previous_transaction);
242 }
243 }
244 }
245
246 let gas_coin = gas_charger.gas_coin();
251
252 let object_changes = self.get_object_changes();
253
254 let lamport_version = self.lamport_timestamp;
255 let loaded_per_epoch_config_objects = self.loaded_per_epoch_config_objects.read().clone();
258 let inner = self.into_inner();
259
260 let effects = TransactionEffects::new_from_execution_v1(
261 status,
262 epoch,
263 gas_cost_summary,
264 shared_object_refs,
266 loaded_per_epoch_config_objects,
267 *transaction_digest,
268 lamport_version,
269 object_changes,
270 gas_coin,
271 if inner.events.data.is_empty() {
272 None
273 } else {
274 Some(inner.events.digest())
275 },
276 transaction_dependencies.into_iter().collect(),
277 );
278
279 (inner, effects)
280 }
281
282 #[cfg(debug_assertions)]
284 fn check_invariants(&self) {
285 debug_assert!(
287 {
288 self.execution_results
289 .written_objects
290 .keys()
291 .all(|id| !self.execution_results.deleted_object_ids.contains(id))
292 },
293 "Object both written and deleted."
294 );
295
296 debug_assert!(
298 {
299 self.mutable_input_refs
300 .keys()
301 .all(|id| self.execution_results.modified_objects.contains(id))
302 },
303 "Mutable input not modified."
304 );
305
306 debug_assert!(
307 {
308 self.execution_results
309 .written_objects
310 .values()
311 .all(|obj| obj.previous_transaction == self.tx_digest)
312 },
313 "Object previous transaction not properly set",
314 );
315 }
316
317 pub fn mutate_input_object(&mut self, object: Object) {
320 let id = object.id();
321 debug_assert!(self.input_objects.contains_key(&id));
322 debug_assert!(!object.is_immutable());
323 self.execution_results.modified_objects.insert(id);
324 self.execution_results.written_objects.insert(id, object);
325 }
326
327 pub fn mutate_child_object(&mut self, old_object: Object, new_object: Object) {
332 let id = new_object.id();
333 let old_ref = old_object.compute_object_reference();
334 debug_assert_eq!(old_ref.0, id);
335 self.loaded_runtime_objects.insert(
336 id,
337 DynamicallyLoadedObjectMetadata {
338 version: old_ref.1,
339 digest: old_ref.2,
340 owner: old_object.owner,
341 storage_rebate: old_object.storage_rebate,
342 previous_transaction: old_object.previous_transaction,
343 },
344 );
345 self.execution_results.modified_objects.insert(id);
346 self.execution_results
347 .written_objects
348 .insert(id, new_object);
349 }
350
351 pub fn upgrade_system_package(&mut self, package: Object) {
355 let id = package.id();
356 assert!(package.is_package() && is_system_package(id));
357 self.execution_results.modified_objects.insert(id);
358 self.execution_results.written_objects.insert(id, package);
359 }
360
361 pub fn create_object(&mut self, object: Object) {
364 debug_assert!(
370 object.is_immutable() || object.version() == SequenceNumber::MIN,
371 "Created mutable objects should not have a version set",
372 );
373 let id = object.id();
374 self.execution_results.created_object_ids.insert(id);
375 self.execution_results.written_objects.insert(id, object);
376 }
377
378 pub fn delete_input_object(&mut self, id: &ObjectID) {
381 debug_assert!(!self.execution_results.written_objects.contains_key(id));
383 debug_assert!(self.input_objects.contains_key(id));
384 self.execution_results.modified_objects.insert(*id);
385 self.execution_results.deleted_object_ids.insert(*id);
386 }
387
388 pub fn drop_writes(&mut self) {
389 self.execution_results.drop_writes();
390 }
391
392 pub fn read_object(&self, id: &ObjectID) -> Option<&Object> {
393 debug_assert!(!self.execution_results.deleted_object_ids.contains(id));
395 self.execution_results
396 .written_objects
397 .get(id)
398 .or_else(|| self.input_objects.get(id))
399 }
400
401 pub fn save_loaded_runtime_objects(
402 &mut self,
403 loaded_runtime_objects: BTreeMap<ObjectID, DynamicallyLoadedObjectMetadata>,
404 ) {
405 #[cfg(debug_assertions)]
406 {
407 for (id, v1) in &loaded_runtime_objects {
408 if let Some(v2) = self.loaded_runtime_objects.get(id) {
409 assert_eq!(v1, v2);
410 }
411 }
412 for (id, v1) in &self.loaded_runtime_objects {
413 if let Some(v2) = loaded_runtime_objects.get(id) {
414 assert_eq!(v1, v2);
415 }
416 }
417 }
418 self.loaded_runtime_objects.extend(loaded_runtime_objects);
422 }
423
424 pub fn save_wrapped_object_containers(
425 &mut self,
426 wrapped_object_containers: BTreeMap<ObjectID, ObjectID>,
427 ) {
428 #[cfg(debug_assertions)]
429 {
430 for (id, container1) in &wrapped_object_containers {
431 if let Some(container2) = self.wrapped_object_containers.get(id) {
432 assert_eq!(container1, container2);
433 }
434 }
435 for (id, container1) in &self.wrapped_object_containers {
436 if let Some(container2) = wrapped_object_containers.get(id) {
437 assert_eq!(container1, container2);
438 }
439 }
440 }
441 self.wrapped_object_containers
445 .extend(wrapped_object_containers);
446 }
447
448 pub fn estimate_effects_size_upperbound(&self) -> usize {
449 TransactionEffects::estimate_effects_size_upperbound_v1(
450 self.execution_results.written_objects.len(),
451 self.execution_results.modified_objects.len(),
452 self.input_objects.len(),
453 )
454 }
455
456 pub fn written_objects_size(&self) -> usize {
457 self.execution_results
458 .written_objects
459 .values()
460 .fold(0, |sum, obj| sum + obj.object_size_for_gas_metering())
461 }
462
463 pub fn conserve_unmetered_storage_rebate(&mut self, unmetered_storage_rebate: u64) {
468 if unmetered_storage_rebate == 0 {
469 return;
474 }
475 tracing::debug!(
476 "Amount of unmetered storage rebate from system tx: {:?}",
477 unmetered_storage_rebate
478 );
479 let mut system_state_wrapper = self
480 .read_object(&IOTA_SYSTEM_STATE_OBJECT_ID)
481 .expect("0x5 object must be mutated in system tx with unmetered storage rebate")
482 .clone();
483 assert_eq!(system_state_wrapper.storage_rebate, 0);
486 system_state_wrapper.storage_rebate = unmetered_storage_rebate;
487 self.mutate_input_object(system_state_wrapper);
488 }
489
490 fn get_object_modified_at(
497 &self,
498 object_id: &ObjectID,
499 ) -> Option<DynamicallyLoadedObjectMetadata> {
500 if self.execution_results.modified_objects.contains(object_id) {
501 Some(
502 self.mutable_input_refs
503 .get(object_id)
504 .map(
505 |((version, digest), owner)| DynamicallyLoadedObjectMetadata {
506 version: *version,
507 digest: *digest,
508 owner: *owner,
509 storage_rebate: self.input_objects[object_id].storage_rebate,
511 previous_transaction: self.input_objects[object_id]
512 .previous_transaction,
513 },
514 )
515 .or_else(|| self.loaded_runtime_objects.get(object_id).cloned())
516 .unwrap_or_else(|| {
517 debug_assert!(is_system_package(*object_id));
518 let package_obj =
519 self.store.get_package_object(object_id).unwrap().unwrap();
520 let obj = package_obj.object();
521 DynamicallyLoadedObjectMetadata {
522 version: obj.version(),
523 digest: obj.digest(),
524 owner: obj.owner,
525 storage_rebate: obj.storage_rebate,
526 previous_transaction: obj.previous_transaction,
527 }
528 }),
529 )
530 } else {
531 None
532 }
533 }
534}
535
536impl TemporaryStore<'_> {
537 pub fn check_ownership_invariants(
540 &self,
541 sender: &IotaAddress,
542 gas_charger: &mut GasCharger,
543 mutable_inputs: &HashSet<ObjectID>,
544 is_epoch_change: bool,
545 ) -> IotaResult<()> {
546 let gas_objs: HashSet<&ObjectID> = gas_charger.gas_coins().iter().map(|g| &g.0).collect();
547 let mut authenticated_for_mutation: HashSet<_> = self
549 .input_objects
550 .iter()
551 .filter_map(|(id, obj)| {
552 if gas_objs.contains(id) {
553 return None;
558 }
559 match &obj.owner {
560 Owner::AddressOwner(a) => {
561 assert!(sender == a, "Input object not owned by sender");
562 Some(id)
563 }
564 Owner::Shared { .. } => Some(id),
565 Owner::Immutable => {
566 None
577 }
578 Owner::ObjectOwner(_parent) => {
579 unreachable!("Input objects must be address owned, shared, or immutable")
580 }
581 }
582 })
583 .filter(|id| {
584 mutable_inputs.contains(id)
587 })
588 .copied()
589 .collect();
590
591 let mut objects_to_authenticate = self
593 .execution_results
594 .modified_objects
595 .iter()
596 .filter(|id| !gas_objs.contains(id))
597 .copied()
598 .collect::<Vec<_>>();
599 while let Some(to_authenticate) = objects_to_authenticate.pop() {
601 if authenticated_for_mutation.contains(&to_authenticate) {
602 continue;
604 }
605 let wrapped_parent = self.wrapped_object_containers.get(&to_authenticate);
606 let parent = if let Some(container_id) = wrapped_parent {
607 *container_id
610 } else {
611 let Some(old_obj) = self.store.get_object(&to_authenticate)? else {
612 panic!(
613 "
614 Failed to load object {to_authenticate:?}. \n\
615 If it cannot be loaded, \
616 we would expect it to be in the wrapped object map: {:?}",
617 &self.wrapped_object_containers
618 )
619 };
620 match &old_obj.owner {
621 Owner::ObjectOwner(parent) => ObjectID::from(*parent),
622 Owner::AddressOwner(parent) => {
623 ObjectID::from(*parent)
627 }
628 owner @ Owner::Shared { .. } => panic!(
629 "Unauthenticated root at {to_authenticate:?} with owner {owner:?}\n\
630 Potentially covering objects in: {authenticated_for_mutation:#?}",
631 ),
632 Owner::Immutable => {
633 assert!(
634 is_epoch_change,
635 "Immutable objects cannot be written, except for \
636 IOTA Framework/Move stdlib upgrades at epoch change boundaries"
637 );
638 assert!(
642 is_system_package(to_authenticate),
643 "Only system packages can be upgraded"
644 );
645 continue;
646 }
647 }
648 };
649 authenticated_for_mutation.insert(to_authenticate);
651 objects_to_authenticate.push(parent);
652 }
653 Ok(())
654 }
655}
656
657impl TemporaryStore<'_> {
658 pub(crate) fn collect_storage_and_rebate(&mut self, gas_charger: &mut GasCharger) {
666 let old_storage_rebates: Vec<_> = self
669 .execution_results
670 .written_objects
671 .keys()
672 .map(|object_id| {
673 self.get_object_modified_at(object_id)
674 .map(|metadata| metadata.storage_rebate)
675 .unwrap_or_default()
676 })
677 .collect();
678 for (object, old_storage_rebate) in self
679 .execution_results
680 .written_objects
681 .values_mut()
682 .zip(old_storage_rebates)
683 {
684 let new_object_size = object.object_size_for_gas_metering();
686 let new_storage_rebate = gas_charger.track_storage_mutation(
688 object.id(),
689 new_object_size,
690 old_storage_rebate,
691 );
692 object.storage_rebate = new_storage_rebate;
693 }
694
695 self.collect_rebate(gas_charger);
696 }
697
698 pub(crate) fn collect_rebate(&self, gas_charger: &mut GasCharger) {
699 for object_id in &self.execution_results.modified_objects {
700 if self
701 .execution_results
702 .written_objects
703 .contains_key(object_id)
704 {
705 continue;
706 }
707 let storage_rebate = self
709 .get_object_modified_at(object_id)
710 .unwrap()
712 .storage_rebate;
713 gas_charger.track_storage_mutation(*object_id, 0, storage_rebate);
714 }
715 }
716
717 pub fn check_execution_results_consistency(&self) -> Result<(), ExecutionError> {
718 assert_invariant!(
719 self.execution_results
720 .created_object_ids
721 .iter()
722 .all(|id| !self.execution_results.deleted_object_ids.contains(id)
723 && !self.execution_results.modified_objects.contains(id)),
724 "Created object IDs cannot also be deleted or modified"
725 );
726 assert_invariant!(
727 self.execution_results.modified_objects.iter().all(|id| {
728 self.mutable_input_refs.contains_key(id)
729 || self.loaded_runtime_objects.contains_key(id)
730 || is_system_package(*id)
731 }),
732 "A modified object must be either a mutable input, a loaded child object, or a system package"
733 );
734 Ok(())
735 }
736}
737impl TemporaryStore<'_> {
742 pub fn advance_epoch_safe_mode(
743 &mut self,
744 params: &AdvanceEpochParams,
745 protocol_config: &ProtocolConfig,
746 ) {
747 let wrapper = get_iota_system_state_wrapper(self.store.as_object_store())
748 .expect("System state wrapper object must exist");
749 let (old_object, new_object) =
750 wrapper.advance_epoch_safe_mode(params, self.store.as_object_store(), protocol_config);
751 self.mutate_child_object(old_object, new_object);
752 }
753}
754
755type ModifiedObjectInfo<'a> = (
756 ObjectID,
757 Option<DynamicallyLoadedObjectMetadata>,
759 Option<&'a Object>,
760);
761
762impl TemporaryStore<'_> {
763 fn get_input_iota(
764 &self,
765 id: &ObjectID,
766 expected_version: SequenceNumber,
767 layout_resolver: &mut impl LayoutResolver,
768 ) -> Result<u64, ExecutionError> {
769 if let Some(obj) = self.input_objects.get(id) {
770 if obj.version() != expected_version {
773 invariant_violation!(
774 "Version mismatching when resolving input object to check conservation--\
775 expected {}, got {}",
776 expected_version,
777 obj.version(),
778 );
779 }
780 obj.get_total_iota(layout_resolver).map_err(|e| {
781 make_invariant_violation!(
782 "Failed looking up input IOTA in IOTA conservation checking for input with \
783 type {:?}: {e:#?}",
784 obj.struct_tag(),
785 )
786 })
787 } else {
788 let Ok(Some(obj)) = self.store.get_object_by_key(id, expected_version) else {
790 invariant_violation!(
791 "Failed looking up dynamic field {id} in IOTA conservation checking"
792 );
793 };
794 obj.get_total_iota(layout_resolver).map_err(|e| {
795 make_invariant_violation!(
796 "Failed looking up input IOTA in IOTA conservation checking for type \
797 {:?}: {e:#?}",
798 obj.struct_tag(),
799 )
800 })
801 }
802 }
803
804 fn get_modified_objects(&self) -> Vec<ModifiedObjectInfo<'_>> {
811 self.execution_results
812 .modified_objects
813 .iter()
814 .map(|id| {
815 let metadata = self.get_object_modified_at(id);
816 let output = self.execution_results.written_objects.get(id);
817 (*id, metadata, output)
818 })
819 .chain(
820 self.execution_results
821 .written_objects
822 .iter()
823 .filter_map(|(id, object)| {
824 if self.execution_results.modified_objects.contains(id) {
825 None
826 } else {
827 Some((*id, None, Some(object)))
828 }
829 }),
830 )
831 .collect()
832 }
833
834 pub fn check_iota_conserved(&self, gas_summary: &GasCostSummary) -> Result<(), ExecutionError> {
850 let mut total_input_rebate = 0;
852 let mut total_output_rebate = 0;
854 for (_, input, output) in self.get_modified_objects() {
855 if let Some(input) = input {
856 total_input_rebate += input.storage_rebate;
857 }
858 if let Some(object) = output {
859 total_output_rebate += object.storage_rebate;
860 }
861 }
862
863 if gas_summary.storage_cost == 0 {
864 if total_input_rebate
876 != total_output_rebate
877 + gas_summary.storage_rebate
878 + gas_summary.non_refundable_storage_fee
879 {
880 return Err(ExecutionError::invariant_violation(format!(
881 "IOTA conservation failed -- no storage charges in gas summary \
882 and total storage input rebate {} not equal \
883 to total storage output rebate {}",
884 total_input_rebate, total_output_rebate,
885 )));
886 }
887 } else {
888 if total_input_rebate
891 != gas_summary.storage_rebate + gas_summary.non_refundable_storage_fee
892 {
893 return Err(ExecutionError::invariant_violation(format!(
894 "IOTA conservation failed -- {} IOTA in storage rebate field of input objects, \
895 {} IOTA in tx storage rebate or tx non-refundable storage rebate",
896 total_input_rebate, gas_summary.non_refundable_storage_fee,
897 )));
898 }
899
900 if gas_summary.storage_cost != total_output_rebate {
903 return Err(ExecutionError::invariant_violation(format!(
904 "IOTA conservation failed -- {} IOTA charged for storage, \
905 {} IOTA in storage rebate field of output objects",
906 gas_summary.storage_cost, total_output_rebate
907 )));
908 }
909 }
910 Ok(())
911 }
912
913 pub fn check_iota_conserved_expensive(
927 &self,
928 gas_summary: &GasCostSummary,
929 advance_epoch_gas_summary: Option<(u64, u64)>,
930 layout_resolver: &mut impl LayoutResolver,
931 ) -> Result<(), ExecutionError> {
932 let mut total_input_iota = 0;
935 let mut total_output_iota = 0;
938 for (id, input, output) in self.get_modified_objects() {
939 if let Some(input) = input {
940 total_input_iota += self.get_input_iota(&id, input.version, layout_resolver)?;
941 }
942 if let Some(object) = output {
943 total_output_iota += object.get_total_iota(layout_resolver).map_err(|e| {
944 make_invariant_violation!(
945 "Failed looking up output IOTA in IOTA conservation checking for \
946 mutated type {:?}: {e:#?}",
947 object.struct_tag(),
948 )
949 })?;
950 }
951 }
952 total_output_iota += gas_summary.computation_cost + gas_summary.non_refundable_storage_fee;
958 if let Some((epoch_fees, epoch_rebates)) = advance_epoch_gas_summary {
959 total_input_iota += epoch_fees;
960 total_output_iota += epoch_rebates;
961 }
962 if total_input_iota != total_output_iota {
963 return Err(ExecutionError::invariant_violation(format!(
964 "IOTA conservation failed: input={}, output={}, \
965 this transaction either mints or burns IOTA",
966 total_input_iota, total_output_iota,
967 )));
968 }
969 Ok(())
970 }
971}
972
973impl ChildObjectResolver for TemporaryStore<'_> {
974 fn read_child_object(
975 &self,
976 parent: &ObjectID,
977 child: &ObjectID,
978 child_version_upper_bound: SequenceNumber,
979 ) -> IotaResult<Option<Object>> {
980 let obj_opt = self.execution_results.written_objects.get(child);
981 if obj_opt.is_some() {
982 Ok(obj_opt.cloned())
983 } else {
984 let _scope = monitored_scope("Execution::read_child_object");
985 self.store
986 .read_child_object(parent, child, child_version_upper_bound)
987 }
988 }
989
990 fn get_object_received_at_version(
991 &self,
992 owner: &ObjectID,
993 receiving_object_id: &ObjectID,
994 receive_object_at_version: SequenceNumber,
995 epoch_id: EpochId,
996 ) -> IotaResult<Option<Object>> {
997 debug_assert!(
1001 !self
1002 .execution_results
1003 .written_objects
1004 .contains_key(receiving_object_id)
1005 );
1006 debug_assert!(
1007 !self
1008 .execution_results
1009 .deleted_object_ids
1010 .contains(receiving_object_id)
1011 );
1012 self.store.get_object_received_at_version(
1013 owner,
1014 receiving_object_id,
1015 receive_object_at_version,
1016 epoch_id,
1017 )
1018 }
1019}
1020
1021impl Storage for TemporaryStore<'_> {
1022 fn reset(&mut self) {
1023 self.drop_writes();
1024 }
1025
1026 fn read_object(&self, id: &ObjectID) -> Option<&Object> {
1027 TemporaryStore::read_object(self, id)
1028 }
1029
1030 fn record_execution_results(&mut self, results: ExecutionResults) {
1032 let ExecutionResults::V1(results) = results;
1033
1034 self.execution_results.merge_results(results);
1037 }
1038
1039 fn save_loaded_runtime_objects(
1040 &mut self,
1041 loaded_runtime_objects: BTreeMap<ObjectID, DynamicallyLoadedObjectMetadata>,
1042 ) {
1043 TemporaryStore::save_loaded_runtime_objects(self, loaded_runtime_objects)
1044 }
1045
1046 fn save_wrapped_object_containers(
1047 &mut self,
1048 wrapped_object_containers: BTreeMap<ObjectID, ObjectID>,
1049 ) {
1050 TemporaryStore::save_wrapped_object_containers(self, wrapped_object_containers)
1051 }
1052
1053 fn check_coin_deny_list(&self, written_objects: &BTreeMap<ObjectID, Object>) -> DenyListResult {
1054 let result = check_coin_deny_list_v1_during_execution(
1055 written_objects,
1056 self.cur_epoch,
1057 self.store.as_object_store(),
1058 );
1059 if result.num_non_gas_coin_owners > 0
1063 && !self.input_objects.contains_key(&IOTA_DENY_LIST_OBJECT_ID)
1064 {
1065 self.loaded_per_epoch_config_objects
1066 .write()
1067 .insert(IOTA_DENY_LIST_OBJECT_ID);
1068 }
1069 result
1070 }
1071}
1072
1073impl BackingPackageStore for TemporaryStore<'_> {
1074 fn get_package_object(&self, package_id: &ObjectID) -> IotaResult<Option<PackageObject>> {
1075 if let Some(obj) = self.execution_results.written_objects.get(package_id) {
1083 Ok(Some(PackageObject::new(obj.clone())))
1084 } else {
1085 self.store.get_package_object(package_id).inspect(|obj| {
1086 if let Some(v) = obj {
1088 if !self
1089 .runtime_packages_loaded_from_db
1090 .read()
1091 .contains_key(package_id)
1092 {
1093 self.runtime_packages_loaded_from_db
1098 .write()
1099 .insert(*package_id, v.clone());
1100 }
1101 }
1102 })
1103 }
1104 }
1105}
1106
1107impl ResourceResolver for TemporaryStore<'_> {
1108 type Error = IotaError;
1109
1110 fn get_resource(
1111 &self,
1112 address: &AccountAddress,
1113 struct_tag: &StructTag,
1114 ) -> Result<Option<Vec<u8>>, Self::Error> {
1115 let object = match self.read_object(&ObjectID::from(*address)) {
1116 Some(x) => x,
1117 None => match self.read_object(&ObjectID::from(*address)) {
1118 None => return Ok(None),
1119 Some(x) => {
1120 if !x.is_immutable() {
1121 fp_bail!(IotaError::ExecutionInvariantViolation);
1122 }
1123 x
1124 }
1125 },
1126 };
1127
1128 match &object.data {
1129 Data::Move(m) => {
1130 assert!(
1131 m.is_type(struct_tag),
1132 "Invariant violation: ill-typed object in storage \
1133 or bad object request from caller"
1134 );
1135 Ok(Some(m.contents().to_vec()))
1136 }
1137 other => unimplemented!(
1138 "Bad object lookup: expected Move object, but got {:?}",
1139 other
1140 ),
1141 }
1142 }
1143}