1use crate::{
5 MoveTypeTagTrait, SUI_SYSTEM_ADDRESS,
6 accumulator_event::AccumulatorEvent,
7 base_types::{ObjectID, ObjectRef, SequenceNumber},
8 digests::{ObjectDigest, TransactionDigest},
9 error::{ExecutionError, SuiError},
10 event::Event,
11 is_system_package,
12 object::{Data, Object, Owner},
13 storage::{BackingPackageStore, ObjectChange},
14 sui_system_state::SUI_SYSTEM_STATE_INNER_MODULE_NAME,
15 transaction::{Argument, Command, SharedObjectMutability},
16 type_input::TypeInput,
17};
18use move_core_types::{
19 ident_str,
20 identifier::IdentStr,
21 language_storage::{StructTag, TypeTag},
22};
23use serde::{Deserialize, Serialize};
24use std::collections::{BTreeMap, BTreeSet};
25use std::time::Duration;
26
27const EXECUTION_TIME_OBSERVATION_CHUNK_KEY_STRUCT: &IdentStr =
28 ident_str!("ExecutionTimeObservationChunkKey");
29
30pub type ConsensusStreamEndedInfo = (
39 ObjectID,
40 SequenceNumber,
41 SharedObjectMutability,
42 TransactionDigest,
43);
44
45pub type ConsensusStreamEndedObjects = Vec<ConsensusStreamEndedInfo>;
47
48#[derive(Clone, Debug, PartialEq, Eq)]
49pub enum SharedInput {
50 Existing(ObjectRef),
51 ConsensusStreamEnded(ConsensusStreamEndedInfo),
52 Cancelled((ObjectID, SequenceNumber)),
53}
54
55#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
56pub struct DynamicallyLoadedObjectMetadata {
57 pub version: SequenceNumber,
58 pub digest: ObjectDigest,
59 pub owner: Owner,
60 pub storage_rebate: u64,
61 pub previous_transaction: TransactionDigest,
62}
63
64pub trait TypeLayoutStore: BackingPackageStore {}
66impl<T> TypeLayoutStore for T where T: BackingPackageStore {}
67
68#[derive(Debug)]
69pub enum ExecutionResults {
70 V1(ExecutionResultsV1),
71 V2(ExecutionResultsV2),
72}
73
74#[derive(Debug)]
75pub struct ExecutionResultsV1 {
76 pub object_changes: BTreeMap<ObjectID, ObjectChange>,
77 pub user_events: Vec<Event>,
78}
79
80#[derive(Debug, Default)]
84pub struct ExecutionResultsV2 {
85 pub written_objects: BTreeMap<ObjectID, Object>,
87 pub modified_objects: BTreeSet<ObjectID>,
90 pub created_object_ids: BTreeSet<ObjectID>,
92 pub deleted_object_ids: BTreeSet<ObjectID>,
95 pub user_events: Vec<Event>,
97 pub accumulator_events: Vec<AccumulatorEvent>,
99
100 pub settlement_input_sui: u64,
106 pub settlement_output_sui: u64,
107}
108
109pub type ExecutionResult = (
110 Vec<(Argument, Vec<u8>, TypeTag)>,
111 Vec<(Vec<u8>, TypeTag)>,
112);
113
114impl ExecutionResultsV2 {
115 pub fn drop_writes(&mut self) {
116 self.written_objects.clear();
117 self.modified_objects.clear();
118 self.created_object_ids.clear();
119 self.deleted_object_ids.clear();
120 self.user_events.clear();
121 self.accumulator_events.clear();
122 }
123
124 pub fn merge_results(
130 &mut self,
131 new_results: Self,
132 consistent_merge: bool,
133 invariant_checks: bool,
134 ) -> Result<(), ExecutionError> {
135 if consistent_merge {
136 for id in &new_results.deleted_object_ids {
140 self.written_objects.remove(id);
141 self.created_object_ids.remove(id);
143 }
144 for id in new_results.written_objects.keys() {
147 self.deleted_object_ids.remove(id);
148 }
149 }
150
151 self.written_objects.extend(new_results.written_objects);
152 self.modified_objects.extend(new_results.modified_objects);
153 self.created_object_ids
154 .extend(new_results.created_object_ids);
155 self.deleted_object_ids
156 .extend(new_results.deleted_object_ids);
157
158 if invariant_checks {
159 assert_invariant!(
161 self.deleted_object_ids
162 .is_disjoint(&self.created_object_ids),
163 "Deleted object IDs should be disjoint with created object IDs"
164 );
165 assert_invariant!(
166 self.written_objects
167 .keys()
168 .all(|id| !self.deleted_object_ids.contains(id)),
169 "Deleted object IDs should be disjoint with written object IDs"
170 );
171 }
172 self.user_events.extend(new_results.user_events);
173 self.accumulator_events
174 .extend(new_results.accumulator_events);
175 self.settlement_input_sui += new_results.settlement_input_sui;
176 self.settlement_output_sui += new_results.settlement_output_sui;
177 Ok(())
178 }
179
180 pub fn update_version_and_previous_tx(
181 &mut self,
182 lamport_version: SequenceNumber,
183 prev_tx: TransactionDigest,
184 input_objects: &BTreeMap<ObjectID, Object>,
185 reshare_at_initial_version: bool,
186 ) {
187 for (id, obj) in self.written_objects.iter_mut() {
188 match &mut obj.data {
193 Data::Move(obj) => {
194 obj.increment_version_to(lamport_version);
196 }
197
198 Data::Package(pkg) => {
199 if self.modified_objects.contains(id) {
203 debug_assert!(is_system_package(*id));
204 pkg.increment_version();
205 }
206 }
207 }
208
209 if let Owner::Shared {
213 initial_shared_version,
214 } = &mut obj.owner
215 {
216 if self.created_object_ids.contains(id) {
217 assert_eq!(
218 *initial_shared_version,
219 SequenceNumber::new(),
220 "Initial version should be blank before this point for {id:?}",
221 );
222 *initial_shared_version = lamport_version;
223 }
224
225 if reshare_at_initial_version
227 && let Some(Owner::Shared {
228 initial_shared_version: previous_initial_shared_version,
229 }) = input_objects.get(id).map(|obj| &obj.owner)
230 {
231 debug_assert!(!self.created_object_ids.contains(id));
232 debug_assert!(!self.deleted_object_ids.contains(id));
233 debug_assert!(
234 *initial_shared_version == SequenceNumber::new()
235 || *initial_shared_version == *previous_initial_shared_version
236 );
237
238 *initial_shared_version = *previous_initial_shared_version;
239 }
240 }
241
242 if let Owner::ConsensusAddressOwner {
244 start_version,
245 owner,
246 } = &mut obj.owner
247 {
248 debug_assert!(!self.deleted_object_ids.contains(id));
249
250 if let Some(Owner::ConsensusAddressOwner {
251 start_version: previous_start_version,
252 owner: previous_owner,
253 }) = input_objects.get(id).map(|obj| &obj.owner)
254 {
255 if owner == previous_owner {
256 *start_version = *previous_start_version;
259 } else {
260 *start_version = lamport_version;
262 }
263 } else {
264 *start_version = lamport_version;
267 }
268 }
269
270 obj.previous_transaction = prev_tx;
271 }
272 }
273}
274
275#[derive(Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Clone, Serialize, Deserialize)]
276pub enum ExecutionTimeObservationKey {
277 MoveEntryPoint {
279 package: ObjectID,
281 module: String,
283 function: String,
285 type_arguments: Vec<TypeInput>,
288 },
289 TransferObjects,
290 SplitCoins,
291 MergeCoins,
292 Publish, MakeMoveVec,
294 Upgrade,
295}
296
297impl std::fmt::Display for ExecutionTimeObservationKey {
298 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
299 match self {
300 ExecutionTimeObservationKey::MoveEntryPoint {
301 module, function, ..
302 } => {
303 write!(f, "{}:{}", module, function)
304 }
305 ExecutionTimeObservationKey::TransferObjects => write!(f, "TransferObjects"),
306 ExecutionTimeObservationKey::SplitCoins => write!(f, "SplitCoins"),
307 ExecutionTimeObservationKey::MergeCoins => write!(f, "MergeCoins"),
308 ExecutionTimeObservationKey::Publish => write!(f, "Publish"),
309 ExecutionTimeObservationKey::MakeMoveVec => write!(f, "MakeMoveVec"),
310 ExecutionTimeObservationKey::Upgrade => write!(f, "Upgrade"),
311 }
312 }
313}
314
315impl ExecutionTimeObservationKey {
316 pub fn is_move_call(&self) -> bool {
317 matches!(self, ExecutionTimeObservationKey::MoveEntryPoint { .. })
318 }
319
320 pub fn from_command(command: &Command) -> Self {
321 match command {
322 Command::MoveCall(call) => ExecutionTimeObservationKey::MoveEntryPoint {
323 package: call.package,
324 module: call.module.clone(),
325 function: call.function.clone(),
326 type_arguments: vec![],
327 },
328 Command::TransferObjects(_, _) => ExecutionTimeObservationKey::TransferObjects,
329 Command::SplitCoins(_, _) => ExecutionTimeObservationKey::SplitCoins,
330 Command::MergeCoins(_, _) => ExecutionTimeObservationKey::MergeCoins,
331 Command::Publish(_, _) => ExecutionTimeObservationKey::Publish,
332 Command::MakeMoveVec(_, _) => ExecutionTimeObservationKey::MakeMoveVec,
333 Command::Upgrade(_, _, _, _) => ExecutionTimeObservationKey::Upgrade,
334 }
335 }
336
337 pub fn default_duration(&self) -> Duration {
338 match self {
339 ExecutionTimeObservationKey::MoveEntryPoint { .. } => Duration::from_millis(1),
340 ExecutionTimeObservationKey::TransferObjects => Duration::from_millis(1),
341 ExecutionTimeObservationKey::SplitCoins => Duration::from_millis(1),
342 ExecutionTimeObservationKey::MergeCoins => Duration::from_millis(1),
343 ExecutionTimeObservationKey::Publish => Duration::from_millis(3),
344 ExecutionTimeObservationKey::MakeMoveVec => Duration::from_millis(1),
345 ExecutionTimeObservationKey::Upgrade => Duration::from_millis(3),
346 }
347 }
348}
349
350#[derive(Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Clone, Serialize, Deserialize)]
351pub struct ExecutionTimeObservationChunkKey {
352 pub chunk_index: u64,
353}
354
355impl MoveTypeTagTrait for ExecutionTimeObservationChunkKey {
356 fn get_type_tag() -> TypeTag {
357 TypeTag::Struct(Box::new(StructTag {
358 address: SUI_SYSTEM_ADDRESS,
359 module: SUI_SYSTEM_STATE_INNER_MODULE_NAME.to_owned(),
360 name: EXECUTION_TIME_OBSERVATION_CHUNK_KEY_STRUCT.to_owned(),
361 type_params: vec![],
362 }))
363 }
364}
365
366#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
367pub enum ExecutionTiming {
368 Success(Duration),
369 Abort(Duration),
370}
371
372impl ExecutionTiming {
373 pub fn is_abort(&self) -> bool {
374 matches!(self, ExecutionTiming::Abort(_))
375 }
376
377 pub fn duration(&self) -> Duration {
378 match self {
379 ExecutionTiming::Success(duration) => *duration,
380 ExecutionTiming::Abort(duration) => *duration,
381 }
382 }
383}
384
385pub type ResultWithTimings<R, E> = Result<(R, Vec<ExecutionTiming>), (E, Vec<ExecutionTiming>)>;
386
387#[derive(Debug)]
389pub enum ExecutionOutput<T> {
390 Success(T),
392 EpochEnded,
395 Fatal(SuiError),
397 RetryLater,
401}
402
403impl<T> ExecutionOutput<T> {
404 pub fn unwrap(self) -> T {
407 match self {
408 ExecutionOutput::Success(value) => value,
409 ExecutionOutput::EpochEnded => {
410 panic!("called `ExecutionOutput::unwrap()` on `EpochEnded`")
411 }
412 ExecutionOutput::Fatal(e) => {
413 panic!("called `ExecutionOutput::unwrap()` on `Fatal`: {e}")
414 }
415 ExecutionOutput::RetryLater => {
416 panic!("called `ExecutionOutput::unwrap()` on `RetryLater`")
417 }
418 }
419 }
420
421 pub fn unwrap_err<S>(self) -> ExecutionOutput<S> {
423 match self {
424 Self::Success(_) => {
425 panic!("called `ExecutionOutput::unwrap_err()` on `Success`")
426 }
427 Self::EpochEnded => ExecutionOutput::EpochEnded,
428 Self::Fatal(e) => ExecutionOutput::Fatal(e),
429 Self::RetryLater => ExecutionOutput::RetryLater,
430 }
431 }
432}