1use std::{
6 cell::RefCell,
7 collections::{BTreeMap, BTreeSet, HashSet},
8 rc::Rc,
9};
10
11use iota_metrics::monitored_scope;
12use iota_protocol_config::ProtocolConfig;
13use iota_types::{
14 IOTA_DENY_LIST_OBJECT_ID, IOTA_SYSTEM_STATE_OBJECT_ID,
15 auth_context::AuthContext,
16 base_types::{
17 IotaAddress, ObjectID, ObjectRef, SequenceNumber, TransactionDigest, VersionDigest,
18 },
19 committee::EpochId,
20 deny_list_v1::check_coin_deny_list_v1_during_execution,
21 effects::{EffectsObjectChange, TransactionEffects, TransactionEvents},
22 error::{ExecutionError, IotaError, IotaResult},
23 execution::{
24 DynamicallyLoadedObjectMetadata, ExecutionResults, ExecutionResultsV1, SharedInput,
25 },
26 execution_config_utils::to_binary_config,
27 execution_status::ExecutionStatus,
28 fp_bail,
29 gas::GasCostSummary,
30 inner_temporary_store::InnerTemporaryStore,
31 iota_system_state::{AdvanceEpochParams, get_iota_system_state_wrapper},
32 is_system_package,
33 layout_resolver::LayoutResolver,
34 object::{Data, Object, Owner},
35 storage::{
36 BackingPackageStore, BackingStore, ChildObjectResolver, DenyListResult, PackageObject,
37 Storage,
38 },
39 transaction::InputObjects,
40};
41use move_core_types::{
42 account_address::AccountAddress, language_storage::StructTag, resolver::ResourceResolver,
43};
44use parking_lot::RwLock;
45
46use crate::gas_charger::GasCharger;
47
48pub struct TemporaryStore<'backing> {
49 store: &'backing dyn BackingStore,
55 tx_digest: TransactionDigest,
56 input_objects: BTreeMap<ObjectID, Object>,
57 lamport_timestamp: SequenceNumber,
60 mutable_input_refs: BTreeMap<ObjectID, (VersionDigest, Owner)>, execution_results: ExecutionResultsV1,
62 loaded_runtime_objects: BTreeMap<ObjectID, DynamicallyLoadedObjectMetadata>,
65 wrapped_object_containers: BTreeMap<ObjectID, ObjectID>,
68 protocol_config: &'backing ProtocolConfig,
69
70 runtime_packages_loaded_from_db: RwLock<BTreeMap<ObjectID, PackageObject>>,
73
74 receiving_objects: Vec<ObjectRef>,
77
78 cur_epoch: EpochId,
81
82 loaded_per_epoch_config_objects: RwLock<BTreeSet<ObjectID>>,
86
87 auth_context: Option<Rc<RefCell<AuthContext>>>,
89}
90
91impl<'backing> TemporaryStore<'backing> {
92 pub fn new(
95 store: &'backing dyn BackingStore,
96 input_objects: InputObjects,
97 receiving_objects: Vec<ObjectRef>,
98 tx_digest: TransactionDigest,
99 protocol_config: &'backing ProtocolConfig,
100 cur_epoch: EpochId,
101 ) -> Self {
102 let mutable_input_refs = input_objects.mutable_inputs();
103 let lamport_timestamp = input_objects.lamport_timestamp(&receiving_objects);
104 let objects = input_objects.into_object_map();
105 #[cfg(debug_assertions)]
106 {
107 assert!(
109 objects
110 .keys()
111 .collect::<HashSet<_>>()
112 .intersection(
113 &receiving_objects
114 .iter()
115 .map(|oref| &oref.0)
116 .collect::<HashSet<_>>()
117 )
118 .next()
119 .is_none()
120 );
121 }
122 Self {
123 store,
124 tx_digest,
125 input_objects: objects,
126 lamport_timestamp,
127 mutable_input_refs,
128 execution_results: ExecutionResultsV1::default(),
129 protocol_config,
130 loaded_runtime_objects: BTreeMap::new(),
131 wrapped_object_containers: BTreeMap::new(),
132 runtime_packages_loaded_from_db: RwLock::new(BTreeMap::new()),
133 receiving_objects,
134 cur_epoch,
135 loaded_per_epoch_config_objects: RwLock::new(BTreeSet::new()),
136 auth_context: None,
137 }
138 }
139
140 pub fn objects(&self) -> &BTreeMap<ObjectID, Object> {
142 &self.input_objects
143 }
144
145 pub fn update_object_version_and_prev_tx(&mut self) {
146 self.execution_results.update_version_and_previous_tx(
147 self.lamport_timestamp,
148 self.tx_digest,
149 &self.input_objects,
150 );
151
152 #[cfg(debug_assertions)]
153 {
154 self.check_invariants();
155 }
156 }
157
158 pub fn into_inner(self) -> InnerTemporaryStore {
161 let results = self.execution_results;
162 InnerTemporaryStore {
163 input_objects: self.input_objects,
164 mutable_inputs: self.mutable_input_refs,
165 written: results.written_objects,
166 events: TransactionEvents {
167 data: results.user_events,
168 },
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),
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, EffectsObjectChange> {
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| {
208 (
209 *id,
210 EffectsObjectChange::new(
211 self.get_object_modified_at(id)
212 .map(|metadata| ((metadata.version, metadata.digest), metadata.owner)),
213 results.written_objects.get(id),
214 results.created_object_ids.contains(id),
215 results.deleted_object_ids.contains(id),
216 ),
217 )
218 })
219 .collect()
220 }
221
222 pub fn into_effects(
223 mut self,
224 shared_object_refs: Vec<SharedInput>,
225 transaction_digest: &TransactionDigest,
226 mut transaction_dependencies: BTreeSet<TransactionDigest>,
227 gas_cost_summary: GasCostSummary,
228 status: ExecutionStatus,
229 gas_charger: &mut GasCharger,
230 epoch: EpochId,
231 ) -> (InnerTemporaryStore, TransactionEffects) {
232 self.update_object_version_and_prev_tx();
233
234 for (id, expected_version, expected_digest) in &self.receiving_objects {
238 if let Some(obj_meta) = self.loaded_runtime_objects.get(id) {
243 let loaded_via_receive = obj_meta.version == *expected_version
247 && obj_meta.digest == *expected_digest
248 && obj_meta.owner.is_address_owned();
249 if loaded_via_receive {
250 transaction_dependencies.insert(obj_meta.previous_transaction);
251 }
252 }
253 }
254
255 let gas_coin = gas_charger.gas_coin();
260
261 let object_changes = self.get_object_changes();
262
263 let lamport_version = self.lamport_timestamp;
264 let loaded_per_epoch_config_objects = self.loaded_per_epoch_config_objects.read().clone();
267 let inner = self.into_inner();
268
269 let effects = TransactionEffects::new_from_execution_v1(
270 status,
271 epoch,
272 gas_cost_summary,
273 shared_object_refs,
275 loaded_per_epoch_config_objects,
276 *transaction_digest,
277 lamport_version,
278 object_changes,
279 gas_coin,
280 if inner.events.data.is_empty() {
281 None
282 } else {
283 Some(inner.events.digest())
284 },
285 transaction_dependencies.into_iter().collect(),
286 );
287
288 (inner, effects)
289 }
290
291 #[cfg(debug_assertions)]
293 fn check_invariants(&self) {
294 debug_assert!(
296 {
297 self.execution_results
298 .written_objects
299 .keys()
300 .all(|id| !self.execution_results.deleted_object_ids.contains(id))
301 },
302 "Object both written and deleted."
303 );
304
305 debug_assert!(
307 {
308 self.mutable_input_refs
309 .keys()
310 .all(|id| self.execution_results.modified_objects.contains(id))
311 },
312 "Mutable input not modified."
313 );
314
315 debug_assert!(
316 {
317 self.execution_results
318 .written_objects
319 .values()
320 .all(|obj| obj.previous_transaction == self.tx_digest)
321 },
322 "Object previous transaction not properly set",
323 );
324 }
325
326 pub fn mutate_input_object(&mut self, object: Object) {
329 let id = object.id();
330 debug_assert!(self.input_objects.contains_key(&id));
331 debug_assert!(!object.is_immutable());
332 self.execution_results.modified_objects.insert(id);
333 self.execution_results.written_objects.insert(id, object);
334 }
335
336 pub fn mutate_child_object(&mut self, old_object: Object, new_object: Object) {
341 let id = new_object.id();
342 let old_ref = old_object.compute_object_reference();
343 debug_assert_eq!(old_ref.0, id);
344 self.loaded_runtime_objects.insert(
345 id,
346 DynamicallyLoadedObjectMetadata {
347 version: old_ref.1,
348 digest: old_ref.2,
349 owner: old_object.owner,
350 storage_rebate: old_object.storage_rebate,
351 previous_transaction: old_object.previous_transaction,
352 },
353 );
354 self.execution_results.modified_objects.insert(id);
355 self.execution_results
356 .written_objects
357 .insert(id, new_object);
358 }
359
360 pub fn upgrade_system_package(&mut self, package: Object) {
364 let id = package.id();
365 assert!(package.is_package() && is_system_package(id));
366 self.execution_results.modified_objects.insert(id);
367 self.execution_results.written_objects.insert(id, package);
368 }
369
370 pub fn create_object(&mut self, object: Object) {
373 debug_assert!(
379 object.is_immutable() || object.version() == SequenceNumber::MIN_VALID_INCL,
380 "Created mutable objects should not have a version set",
381 );
382 let id = object.id();
383 self.execution_results.created_object_ids.insert(id);
384 self.execution_results.written_objects.insert(id, object);
385 }
386
387 pub fn delete_input_object(&mut self, id: &ObjectID) {
390 debug_assert!(!self.execution_results.written_objects.contains_key(id));
392 debug_assert!(self.input_objects.contains_key(id));
393 self.execution_results.modified_objects.insert(*id);
394 self.execution_results.deleted_object_ids.insert(*id);
395 }
396
397 pub fn drop_writes(&mut self) {
398 self.execution_results.drop_writes();
399 }
400
401 pub fn read_object(&self, id: &ObjectID) -> Option<&Object> {
402 debug_assert!(!self.execution_results.deleted_object_ids.contains(id));
404 self.execution_results
405 .written_objects
406 .get(id)
407 .or_else(|| self.input_objects.get(id))
408 }
409
410 pub fn save_loaded_runtime_objects(
411 &mut self,
412 loaded_runtime_objects: BTreeMap<ObjectID, DynamicallyLoadedObjectMetadata>,
413 ) {
414 #[cfg(debug_assertions)]
415 {
416 for (id, v1) in &loaded_runtime_objects {
417 if let Some(v2) = self.loaded_runtime_objects.get(id) {
418 assert_eq!(v1, v2);
419 }
420 }
421 for (id, v1) in &self.loaded_runtime_objects {
422 if let Some(v2) = loaded_runtime_objects.get(id) {
423 assert_eq!(v1, v2);
424 }
425 }
426 }
427 self.loaded_runtime_objects.extend(loaded_runtime_objects);
431 }
432
433 pub fn save_wrapped_object_containers(
434 &mut self,
435 wrapped_object_containers: BTreeMap<ObjectID, ObjectID>,
436 ) {
437 #[cfg(debug_assertions)]
438 {
439 for (id, container1) in &wrapped_object_containers {
440 if let Some(container2) = self.wrapped_object_containers.get(id) {
441 assert_eq!(container1, container2);
442 }
443 }
444 for (id, container1) in &self.wrapped_object_containers {
445 if let Some(container2) = wrapped_object_containers.get(id) {
446 assert_eq!(container1, container2);
447 }
448 }
449 }
450 self.wrapped_object_containers
454 .extend(wrapped_object_containers);
455 }
456
457 pub fn estimate_effects_size_upperbound(&self) -> usize {
458 TransactionEffects::estimate_effects_size_upperbound_v1(
459 self.execution_results.written_objects.len(),
460 self.execution_results.modified_objects.len(),
461 self.input_objects.len(),
462 )
463 }
464
465 pub fn written_objects_size(&self) -> usize {
466 self.execution_results
467 .written_objects
468 .values()
469 .fold(0, |sum, obj| sum + obj.object_size_for_gas_metering())
470 }
471
472 pub fn conserve_unmetered_storage_rebate(&mut self, unmetered_storage_rebate: u64) {
477 if unmetered_storage_rebate == 0 {
478 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(&IOTA_SYSTEM_STATE_OBJECT_ID)
490 .expect("0x5 object must be mutated 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.mutate_input_object(system_state_wrapper);
497 }
498
499 fn get_object_modified_at(
506 &self,
507 object_id: &ObjectID,
508 ) -> Option<DynamicallyLoadedObjectMetadata> {
509 if self.execution_results.modified_objects.contains(object_id) {
510 Some(
511 self.mutable_input_refs
512 .get(object_id)
513 .map(
514 |((version, digest), owner)| DynamicallyLoadedObjectMetadata {
515 version: *version,
516 digest: *digest,
517 owner: *owner,
518 storage_rebate: self.input_objects[object_id].storage_rebate,
520 previous_transaction: self.input_objects[object_id]
521 .previous_transaction,
522 },
523 )
524 .or_else(|| self.loaded_runtime_objects.get(object_id).cloned())
525 .unwrap_or_else(|| {
526 debug_assert!(is_system_package(*object_id));
527 let package_obj =
528 self.store.get_package_object(object_id).unwrap().unwrap();
529 let obj = package_obj.object();
530 DynamicallyLoadedObjectMetadata {
531 version: obj.version(),
532 digest: obj.digest(),
533 owner: obj.owner,
534 storage_rebate: obj.storage_rebate,
535 previous_transaction: obj.previous_transaction,
536 }
537 }),
538 )
539 } else {
540 None
541 }
542 }
543}
544
545impl TemporaryStore<'_> {
546 pub fn check_ownership_invariants(
549 &self,
550 sender: &IotaAddress,
551 gas_charger: &mut GasCharger,
552 mutable_inputs: &HashSet<ObjectID>,
553 is_epoch_change: bool,
554 ) -> IotaResult<()> {
555 let gas_objs: HashSet<&ObjectID> = gas_charger.gas_coins().iter().map(|g| &g.0).collect();
556 let mut authenticated_for_mutation: HashSet<_> = self
558 .input_objects
559 .iter()
560 .filter_map(|(id, obj)| {
561 if gas_objs.contains(id) {
562 return None;
567 }
568 match &obj.owner {
569 Owner::AddressOwner(a) => {
570 assert!(sender == a, "Input object not owned by sender");
571 Some(id)
572 }
573 Owner::Shared { .. } => Some(id),
574 Owner::Immutable => {
575 None
586 }
587 Owner::ObjectOwner(_parent) => {
588 unreachable!("Input objects must be address owned, shared, or immutable")
589 }
590 }
591 })
592 .filter(|id| {
593 mutable_inputs.contains(id)
596 })
597 .copied()
598 .collect();
599
600 let mut objects_to_authenticate = self
602 .execution_results
603 .modified_objects
604 .iter()
605 .filter(|id| !gas_objs.contains(id))
606 .copied()
607 .collect::<Vec<_>>();
608 while let Some(to_authenticate) = objects_to_authenticate.pop() {
610 if authenticated_for_mutation.contains(&to_authenticate) {
611 continue;
613 }
614 let wrapped_parent = self.wrapped_object_containers.get(&to_authenticate);
615 let parent = if let Some(container_id) = wrapped_parent {
616 *container_id
619 } else {
620 let Some(old_obj) = self.store.try_get_object(&to_authenticate)? else {
621 panic!(
622 "
623 Failed to load object {to_authenticate:?}. \n\
624 If it cannot be loaded, \
625 we would expect it to be in the wrapped object map: {:?}",
626 &self.wrapped_object_containers
627 )
628 };
629 match &old_obj.owner {
630 Owner::ObjectOwner(parent) => ObjectID::from(*parent),
631 Owner::AddressOwner(parent) => {
632 ObjectID::from(*parent)
636 }
637 owner @ Owner::Shared { .. } => panic!(
638 "Unauthenticated root at {to_authenticate:?} with owner {owner:?}\n\
639 Potentially covering objects in: {authenticated_for_mutation:#?}",
640 ),
641 Owner::Immutable => {
642 assert!(
643 is_epoch_change,
644 "Immutable objects cannot be written, except for \
645 IOTA Framework/Move stdlib upgrades at epoch change boundaries"
646 );
647 assert!(
651 is_system_package(to_authenticate),
652 "Only system packages can be upgraded"
653 );
654 continue;
655 }
656 }
657 };
658 authenticated_for_mutation.insert(to_authenticate);
660 objects_to_authenticate.push(parent);
661 }
662 Ok(())
663 }
664
665 pub fn store_auth_context(&mut self, auth_context: Rc<RefCell<AuthContext>>) {
666 self.auth_context = Some(auth_context);
667 }
668}
669
670impl TemporaryStore<'_> {
671 pub(crate) fn collect_storage_and_rebate(&mut self, gas_charger: &mut GasCharger) {
679 let old_storage_rebates: Vec<_> = self
682 .execution_results
683 .written_objects
684 .keys()
685 .map(|object_id| {
686 self.get_object_modified_at(object_id)
687 .map(|metadata| metadata.storage_rebate)
688 .unwrap_or_default()
689 })
690 .collect();
691 for (object, old_storage_rebate) in self
692 .execution_results
693 .written_objects
694 .values_mut()
695 .zip(old_storage_rebates)
696 {
697 let new_object_size = object.object_size_for_gas_metering();
699 let new_storage_rebate = gas_charger.track_storage_mutation(
701 object.id(),
702 new_object_size,
703 old_storage_rebate,
704 );
705 object.storage_rebate = new_storage_rebate;
706 }
707
708 self.collect_rebate(gas_charger);
709 }
710
711 pub(crate) fn collect_rebate(&self, gas_charger: &mut GasCharger) {
712 for object_id in &self.execution_results.modified_objects {
713 if self
714 .execution_results
715 .written_objects
716 .contains_key(object_id)
717 {
718 continue;
719 }
720 let storage_rebate = self
722 .get_object_modified_at(object_id)
723 .unwrap()
725 .storage_rebate;
726 gas_charger.track_storage_mutation(*object_id, 0, storage_rebate);
727 }
728 }
729
730 pub fn check_execution_results_consistency(&self) -> Result<(), ExecutionError> {
731 assert_invariant!(
732 self.execution_results
733 .created_object_ids
734 .iter()
735 .all(|id| !self.execution_results.deleted_object_ids.contains(id)
736 && !self.execution_results.modified_objects.contains(id)),
737 "Created object IDs cannot also be deleted or modified"
738 );
739 assert_invariant!(
740 self.execution_results.modified_objects.iter().all(|id| {
741 self.mutable_input_refs.contains_key(id)
742 || self.loaded_runtime_objects.contains_key(id)
743 || is_system_package(*id)
744 }),
745 "A modified object must be either a mutable input, a loaded child object, or a system package"
746 );
747 Ok(())
748 }
749
750 pub fn check_move_authenticator_results_consistency(&self) -> Result<(), ExecutionError> {
751 assert_invariant!(
752 self.execution_results.created_object_ids.is_empty(),
753 "Objects cannot be created during authenticator execution"
754 );
755 assert_invariant!(
756 self.execution_results.written_objects.is_empty(),
757 "Objects cannot be written during authenticator execution"
758 );
759 assert_invariant!(
760 self.execution_results.modified_objects.is_empty(),
761 "Objects cannot be modified during authenticator execution"
762 );
763 assert_invariant!(
764 self.execution_results.deleted_object_ids.is_empty(),
765 "Objects cannot be deleted during authenticator execution"
766 );
767 Ok(())
768 }
769}
770impl TemporaryStore<'_> {
775 pub fn advance_epoch_safe_mode(
776 &mut self,
777 params: &AdvanceEpochParams,
778 protocol_config: &ProtocolConfig,
779 ) {
780 let wrapper = get_iota_system_state_wrapper(self.store.as_object_store())
781 .expect("System state wrapper object must exist");
782 let (old_object, new_object) =
783 wrapper.advance_epoch_safe_mode(params, self.store.as_object_store(), protocol_config);
784 self.mutate_child_object(old_object, new_object);
785 }
786}
787
788type ModifiedObjectInfo<'a> = (
789 ObjectID,
790 Option<DynamicallyLoadedObjectMetadata>,
792 Option<&'a Object>,
793);
794
795impl TemporaryStore<'_> {
796 fn get_input_iota(
797 &self,
798 id: &ObjectID,
799 expected_version: SequenceNumber,
800 layout_resolver: &mut impl LayoutResolver,
801 ) -> Result<u64, ExecutionError> {
802 if let Some(obj) = self.input_objects.get(id) {
803 if obj.version() != expected_version {
806 invariant_violation!(
807 "Version mismatching when resolving input object to check conservation--\
808 expected {}, got {}",
809 expected_version,
810 obj.version(),
811 );
812 }
813 obj.get_total_iota(layout_resolver).map_err(|e| {
814 make_invariant_violation!(
815 "Failed looking up input IOTA in IOTA conservation checking for input with \
816 type {:?}: {e:#?}",
817 obj.struct_tag(),
818 )
819 })
820 } else {
821 let Ok(Some(obj)) = self.store.try_get_object_by_key(id, expected_version) else {
823 invariant_violation!(
824 "Failed looking up dynamic field {id} in IOTA conservation checking"
825 );
826 };
827 obj.get_total_iota(layout_resolver).map_err(|e| {
828 make_invariant_violation!(
829 "Failed looking up input IOTA in IOTA conservation checking for type \
830 {:?}: {e:#?}",
831 obj.struct_tag(),
832 )
833 })
834 }
835 }
836
837 fn get_modified_objects(&self) -> Vec<ModifiedObjectInfo<'_>> {
844 self.execution_results
845 .modified_objects
846 .iter()
847 .map(|id| {
848 let metadata = self.get_object_modified_at(id);
849 let output = self.execution_results.written_objects.get(id);
850 (*id, metadata, output)
851 })
852 .chain(
853 self.execution_results
854 .written_objects
855 .iter()
856 .filter_map(|(id, object)| {
857 if self.execution_results.modified_objects.contains(id) {
858 None
859 } else {
860 Some((*id, None, Some(object)))
861 }
862 }),
863 )
864 .collect()
865 }
866
867 pub fn check_iota_conserved(&self, gas_summary: &GasCostSummary) -> Result<(), ExecutionError> {
883 let mut total_input_rebate = 0;
885 let mut total_output_rebate = 0;
887 for (_, input, output) in self.get_modified_objects() {
888 if let Some(input) = input {
889 total_input_rebate += input.storage_rebate;
890 }
891 if let Some(object) = output {
892 total_output_rebate += object.storage_rebate;
893 }
894 }
895
896 if gas_summary.storage_cost == 0 {
897 if total_input_rebate
909 != total_output_rebate
910 + gas_summary.storage_rebate
911 + gas_summary.non_refundable_storage_fee
912 {
913 return Err(ExecutionError::invariant_violation(format!(
914 "IOTA conservation failed -- no storage charges in gas summary \
915 and total storage input rebate {total_input_rebate} not equal \
916 to total storage output rebate {total_output_rebate}",
917 )));
918 }
919 } else {
920 if total_input_rebate
923 != gas_summary.storage_rebate + gas_summary.non_refundable_storage_fee
924 {
925 return Err(ExecutionError::invariant_violation(format!(
926 "IOTA conservation failed -- {} IOTA in storage rebate field of input objects, \
927 {} IOTA in tx storage rebate or tx non-refundable storage rebate",
928 total_input_rebate, gas_summary.non_refundable_storage_fee,
929 )));
930 }
931
932 if gas_summary.storage_cost != total_output_rebate {
935 return Err(ExecutionError::invariant_violation(format!(
936 "IOTA conservation failed -- {} IOTA charged for storage, \
937 {} IOTA in storage rebate field of output objects",
938 gas_summary.storage_cost, total_output_rebate
939 )));
940 }
941 }
942 Ok(())
943 }
944
945 pub fn check_iota_conserved_expensive(
959 &self,
960 gas_summary: &GasCostSummary,
961 advance_epoch_gas_summary: Option<(u64, u64)>,
962 layout_resolver: &mut impl LayoutResolver,
963 ) -> Result<(), ExecutionError> {
964 let mut total_input_iota = 0;
967 let mut total_output_iota = 0;
970 for (id, input, output) in self.get_modified_objects() {
971 if let Some(input) = input {
972 total_input_iota += self.get_input_iota(&id, input.version, layout_resolver)?;
973 }
974 if let Some(object) = output {
975 total_output_iota += object.get_total_iota(layout_resolver).map_err(|e| {
976 make_invariant_violation!(
977 "Failed looking up output IOTA in IOTA conservation checking for \
978 mutated type {:?}: {e:#?}",
979 object.struct_tag(),
980 )
981 })?;
982 }
983 }
984 total_output_iota += gas_summary.computation_cost + gas_summary.non_refundable_storage_fee;
990 if let Some((epoch_fees, epoch_rebates)) = advance_epoch_gas_summary {
991 total_input_iota += epoch_fees;
992 total_output_iota += epoch_rebates;
993 }
994 if total_input_iota != total_output_iota {
995 return Err(ExecutionError::invariant_violation(format!(
996 "IOTA conservation failed: input={total_input_iota}, output={total_output_iota}, \
997 this transaction either mints or burns IOTA",
998 )));
999 }
1000 Ok(())
1001 }
1002}
1003
1004impl ChildObjectResolver for TemporaryStore<'_> {
1005 fn read_child_object(
1006 &self,
1007 parent: &ObjectID,
1008 child: &ObjectID,
1009 child_version_upper_bound: SequenceNumber,
1010 ) -> IotaResult<Option<Object>> {
1011 let obj_opt = self.execution_results.written_objects.get(child);
1012 if obj_opt.is_some() {
1013 Ok(obj_opt.cloned())
1014 } else {
1015 let _scope = monitored_scope("Execution::read_child_object");
1016 self.store
1017 .read_child_object(parent, child, child_version_upper_bound)
1018 }
1019 }
1020
1021 fn get_object_received_at_version(
1022 &self,
1023 owner: &ObjectID,
1024 receiving_object_id: &ObjectID,
1025 receive_object_at_version: SequenceNumber,
1026 epoch_id: EpochId,
1027 ) -> IotaResult<Option<Object>> {
1028 debug_assert!(
1032 !self
1033 .execution_results
1034 .written_objects
1035 .contains_key(receiving_object_id)
1036 );
1037 debug_assert!(
1038 !self
1039 .execution_results
1040 .deleted_object_ids
1041 .contains(receiving_object_id)
1042 );
1043 self.store.get_object_received_at_version(
1044 owner,
1045 receiving_object_id,
1046 receive_object_at_version,
1047 epoch_id,
1048 )
1049 }
1050}
1051
1052impl Storage for TemporaryStore<'_> {
1053 fn reset(&mut self) {
1054 self.drop_writes();
1055 }
1056
1057 fn read_object(&self, id: &ObjectID) -> Option<&Object> {
1058 TemporaryStore::read_object(self, id)
1059 }
1060
1061 fn record_execution_results(&mut self, results: ExecutionResults) {
1063 let ExecutionResults::V1(results) = results;
1064
1065 self.execution_results.merge_results(results);
1068 }
1069
1070 fn save_loaded_runtime_objects(
1071 &mut self,
1072 loaded_runtime_objects: BTreeMap<ObjectID, DynamicallyLoadedObjectMetadata>,
1073 ) {
1074 TemporaryStore::save_loaded_runtime_objects(self, loaded_runtime_objects)
1075 }
1076
1077 fn save_wrapped_object_containers(
1078 &mut self,
1079 wrapped_object_containers: BTreeMap<ObjectID, ObjectID>,
1080 ) {
1081 TemporaryStore::save_wrapped_object_containers(self, wrapped_object_containers)
1082 }
1083
1084 fn check_coin_deny_list(&self, written_objects: &BTreeMap<ObjectID, Object>) -> DenyListResult {
1085 let result = check_coin_deny_list_v1_during_execution(
1086 written_objects,
1087 self.cur_epoch,
1088 self.store.as_object_store(),
1089 );
1090 if result.num_non_gas_coin_owners > 0
1094 && !self.input_objects.contains_key(&IOTA_DENY_LIST_OBJECT_ID)
1095 {
1096 self.loaded_per_epoch_config_objects
1097 .write()
1098 .insert(IOTA_DENY_LIST_OBJECT_ID);
1099 }
1100 result
1101 }
1102
1103 fn read_auth_context(&self) -> Option<Rc<RefCell<AuthContext>>> {
1104 self.auth_context.clone()
1105 }
1106}
1107
1108impl BackingPackageStore for TemporaryStore<'_> {
1109 fn get_package_object(&self, package_id: &ObjectID) -> IotaResult<Option<PackageObject>> {
1110 if let Some(obj) = self.execution_results.written_objects.get(package_id) {
1118 Ok(Some(PackageObject::new(obj.clone())))
1119 } else {
1120 self.store.get_package_object(package_id).inspect(|obj| {
1121 if let Some(v) = obj {
1123 if !self
1124 .runtime_packages_loaded_from_db
1125 .read()
1126 .contains_key(package_id)
1127 {
1128 self.runtime_packages_loaded_from_db
1133 .write()
1134 .insert(*package_id, v.clone());
1135 }
1136 }
1137 })
1138 }
1139 }
1140}
1141
1142impl ResourceResolver for TemporaryStore<'_> {
1143 type Error = IotaError;
1144
1145 fn get_resource(
1146 &self,
1147 address: &AccountAddress,
1148 struct_tag: &StructTag,
1149 ) -> Result<Option<Vec<u8>>, Self::Error> {
1150 let object = match self.read_object(&ObjectID::from(*address)) {
1151 Some(x) => x,
1152 None => match self.read_object(&ObjectID::from(*address)) {
1153 None => return Ok(None),
1154 Some(x) => {
1155 if !x.is_immutable() {
1156 fp_bail!(IotaError::ExecutionInvariantViolation);
1157 }
1158 x
1159 }
1160 },
1161 };
1162
1163 match &object.data {
1164 Data::Move(m) => {
1165 assert!(
1166 m.is_type(struct_tag),
1167 "Invariant violation: ill-typed object in storage \
1168 or bad object request from caller"
1169 );
1170 Ok(Some(m.contents().to_vec()))
1171 }
1172 other => unimplemented!(
1173 "Bad object lookup: expected Move object, but got {:?}",
1174 other
1175 ),
1176 }
1177 }
1178}