iota_types/execution.rs
1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use std::collections::{BTreeMap, BTreeSet, HashSet};
6
7use iota_sdk_types::{Argument, Event, ObjectData, ObjectId, Owner, TypeTag};
8use once_cell::sync::Lazy;
9use serde::{Deserialize, Serialize};
10
11use crate::{
12 base_types::{ObjectRef, SequenceNumber},
13 digests::{ObjectDigest, TransactionDigest},
14 object::{MoveObjectExt, Object},
15 storage::BackingPackageStore,
16};
17
18/// A type containing all of the information needed to work with a deleted
19/// shared object in execution and when committing the execution effects of the
20/// transaction. This holds:
21/// 0. The object ID of the deleted shared object.
22/// 1. The version of the shared object.
23/// 2. Whether the object appeared as mutable (or owned) in the transaction, or
24/// as a read-only shared object.
25/// 3. The transaction digest of the previous transaction that used this shared
26/// object mutably or took it by value.
27pub type DeletedSharedObjectInfo = (ObjectId, SequenceNumber, bool, TransactionDigest);
28
29/// A sequence of information about deleted shared objects in the transaction's
30/// inputs.
31pub type DeletedSharedObjects = Vec<DeletedSharedObjectInfo>;
32
33#[derive(Clone, Debug, PartialEq, Eq)]
34pub enum SharedInput {
35 Existing(ObjectRef),
36 Deleted(DeletedSharedObjectInfo),
37 Cancelled((ObjectId, SequenceNumber)),
38}
39
40#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
41pub struct DynamicallyLoadedObjectMetadata {
42 pub version: SequenceNumber,
43 pub digest: ObjectDigest,
44 pub owner: Owner,
45 pub storage_rebate: u64,
46 pub previous_transaction: TransactionDigest,
47}
48
49/// View of the store necessary to produce the layouts of types.
50pub trait TypeLayoutStore: BackingPackageStore {}
51impl<T> TypeLayoutStore for T where T: BackingPackageStore {}
52
53#[derive(Debug)]
54pub enum ExecutionResults {
55 V1(ExecutionResultsV1),
56}
57
58/// Used by iota-execution v1 and above, to capture the execution results from
59/// Move. The results represent the primitive information that can then be used
60/// to construct both transaction effect V1.
61#[derive(Debug, Default)]
62pub struct ExecutionResultsV1 {
63 /// All objects written regardless of whether they were mutated, created, or
64 /// unwrapped.
65 pub written_objects: BTreeMap<ObjectId, Object>,
66 /// All objects that existed prior to this transaction, and are modified in
67 /// this transaction. This includes any type of modification, including
68 /// mutated, wrapped and deleted objects.
69 pub modified_objects: BTreeSet<ObjectId>,
70 /// All object IDs created in this transaction.
71 pub created_object_ids: BTreeSet<ObjectId>,
72 /// All object IDs deleted in this transaction.
73 /// No object ID should be in both created_object_ids and
74 /// deleted_object_ids.
75 pub deleted_object_ids: BTreeSet<ObjectId>,
76 /// All Move events emitted in this transaction.
77 pub user_events: Vec<Event>,
78}
79
80pub type ExecutionResult = (
81 // mutable_reference_outputs
82 Vec<(Argument, Vec<u8>, TypeTag)>,
83 // return_values
84 Vec<(Vec<u8>, TypeTag)>,
85);
86
87impl ExecutionResultsV1 {
88 pub fn drop_writes(&mut self) {
89 self.written_objects.clear();
90 self.modified_objects.clear();
91 self.created_object_ids.clear();
92 self.deleted_object_ids.clear();
93 self.user_events.clear();
94 }
95
96 pub fn merge_results(&mut self, new_results: Self) {
97 self.written_objects.extend(new_results.written_objects);
98 self.modified_objects.extend(new_results.modified_objects);
99 self.created_object_ids
100 .extend(new_results.created_object_ids);
101 self.deleted_object_ids
102 .extend(new_results.deleted_object_ids);
103 self.user_events.extend(new_results.user_events);
104 }
105
106 pub fn update_version_and_previous_tx(
107 &mut self,
108 lamport_version: SequenceNumber,
109 prev_tx: TransactionDigest,
110 input_objects: &BTreeMap<ObjectId, Object>,
111 ) {
112 for (id, obj) in self.written_objects.iter_mut() {
113 // TODO: We can now get rid of the following logic by passing in lamport version
114 // into the execution layer, and create new objects using the lamport version
115 // directly.
116
117 // Update the version for the written object.
118 match &mut obj.data {
119 ObjectData::Struct(obj) => {
120 // Move objects all get the transaction's lamport timestamp
121 obj.increment_version_to(lamport_version);
122 }
123
124 ObjectData::Package(pkg) => {
125 // Modified packages get their version incremented (this is a special case that
126 // only applies to system packages). All other packages can only be created,
127 // and they are left alone.
128 if self.modified_objects.contains(id) {
129 debug_assert!(id.is_system_package());
130 pkg.increment_version()
131 .expect("package version should never overflow");
132 }
133 }
134 }
135
136 // Record the version that the shared object was created at in its owner field.
137 // Note, this only works because shared objects must be created as
138 // shared (not created as owned in one transaction and later
139 // converted to shared in another).
140 if let Owner::Shared(initial_shared_version) = &mut obj.owner {
141 if self.created_object_ids.contains(id) {
142 assert_eq!(
143 *initial_shared_version,
144 SequenceNumber::default(),
145 "Initial version should be blank before this point for {id}",
146 );
147 *initial_shared_version = lamport_version;
148 }
149
150 // Update initial_shared_version for reshared objects
151 if let Some(previous_initial_shared_version) = input_objects
152 .get(id)
153 .and_then(|obj| obj.owner.as_opt_shared())
154 {
155 debug_assert!(!self.created_object_ids.contains(id));
156 debug_assert!(!self.deleted_object_ids.contains(id));
157 debug_assert!(
158 *initial_shared_version == SequenceNumber::default()
159 || *initial_shared_version == *previous_initial_shared_version
160 );
161
162 *initial_shared_version = *previous_initial_shared_version;
163 }
164 }
165
166 obj.previous_transaction = prev_tx;
167 }
168 }
169}
170
171/// If a transaction digest shows up in this list, when executing such
172/// transaction, we will always return `ExecutionError::CertificateDenied`
173/// without executing it (but still do gas smashing). Because this list is not
174/// gated by protocol version, there are a few important criteria for adding a
175/// digest to this list:
176/// 1. The certificate must be causing all validators to either panic or hang
177/// forever deterministically.
178/// 2. If we ever ship a fix to make it no longer panic or hang when executing
179/// such transaction, we must make sure the transaction is already in this
180/// list. Otherwise nodes running the newer version without these
181/// transactions in the list will generate forked result.
182///
183/// Below is a scenario of when we need to use this list:
184/// 1. We detect that a specific transaction is causing all validators to either
185/// panic or hang forever deterministically.
186/// 2. We push a CertificateDenyConfig to deny such transaction to all
187/// validators asap.
188/// 3. To make sure that all fullnodes are able to sync to the latest version,
189/// we need to add the transaction digest to this list as well asap, and ship
190/// this binary to all fullnodes, so that they can sync past this
191/// transaction.
192/// 4. We then can start fixing the issue, and ship the fix to all nodes.
193/// 5. Unfortunately, we can't remove the transaction digest from this list,
194/// because if we do so, any future node that sync from genesis will fork on
195/// this transaction. We may be able to remove it once we have stable
196/// snapshots and the binary has a minimum supported protocol version past
197/// the epoch.
198pub fn get_denied_certificates() -> &'static HashSet<TransactionDigest> {
199 static DENIED_CERTIFICATES: Lazy<HashSet<TransactionDigest>> = Lazy::new(|| HashSet::from([]));
200 Lazy::force(&DENIED_CERTIFICATES)
201}
202
203pub fn is_certificate_denied(
204 transaction_digest: &TransactionDigest,
205 certificate_deny_set: &HashSet<TransactionDigest>,
206) -> bool {
207 certificate_deny_set.contains(transaction_digest)
208 || get_denied_certificates().contains(transaction_digest)
209}