1use crate::{
10 execution_mode::ExecutionMode,
11 execution_value::{ObjectContents, ObjectValue, Value},
12 programmable_transactions::context::*,
13};
14use move_core_types::{
15 identifier::Identifier,
16 language_storage::{StructTag, TypeTag},
17};
18use move_trace_format::{
19 format::{Effect, MoveTraceBuilder, RefType, TraceEvent, TypeTagWithRefs},
20 value::{SerializableMoveValue, SimplifiedMoveStruct},
21};
22use move_vm_types::loaded_data::runtime_types::Type;
23use sui_types::{
24 base_types::ObjectID,
25 coin::Coin,
26 error::ExecutionError,
27 execution_status::ExecutionErrorKind,
28 object::bounded_visitor::BoundedVisitor,
29 ptb_trace::{
30 ExtMoveValue, ExtMoveValueInfo, ExternalEvent, PTBCommandInfo, PTBEvent, SummaryEvent,
31 },
32 transaction::Command,
33};
34use sui_verifier::INIT_FN_NAME;
35
36pub fn trace_move_call_start(trace_builder_opt: &mut Option<MoveTraceBuilder>) {
53 if let Some(trace_builder) = trace_builder_opt {
54 trace_builder.push_event(TraceEvent::External(
55 serde_json::value::to_raw_value(&PTBEvent::MoveCallStart).unwrap(),
56 ));
57 }
58}
59
60pub fn trace_move_call_end(trace_builder_opt: &mut Option<MoveTraceBuilder>) {
63 if let Some(trace_builder) = trace_builder_opt {
64 trace_builder.push_event(TraceEvent::External(
65 serde_json::value::to_raw_value(&PTBEvent::MoveCallEnd).unwrap(),
66 ));
67 }
68}
69
70pub fn trace_transfer(
73 context: &mut ExecutionContext<'_, '_, '_>,
74 trace_builder_opt: &mut Option<MoveTraceBuilder>,
75 obj_values: &[ObjectValue],
76) -> Result<(), ExecutionError> {
77 if let Some(trace_builder) = trace_builder_opt {
78 let mut to_transfer = vec![];
79 for (idx, v) in obj_values.iter().enumerate() {
80 let obj_info = move_value_info_from_obj_value(context, v)?;
81 to_transfer.push(ExtMoveValue::Single {
82 name: format!("obj{idx}"),
83 info: obj_info,
84 });
85 }
86 trace_builder.push_event(TraceEvent::External(
87 serde_json::value::to_raw_value(&PTBEvent::ExternalEvent(ExternalEvent {
88 description: "TransferObjects: obj0...objN => ()".to_string(),
89 name: "Transfer".to_string(),
90 values: to_transfer,
91 }))
92 .map_err(|e| make_invariant_violation!("Failed to serialize PTB trace event: {}", e))?,
93 ));
94 }
95 Ok(())
96}
97
98pub fn trace_ptb_summary<Mode: ExecutionMode>(
101 context: &mut ExecutionContext<'_, '_, '_>,
102 trace_builder_opt: &mut Option<MoveTraceBuilder>,
103 commands: &[Command],
104) -> Result<(), ExecutionError> {
105 if let Some(trace_builder) = trace_builder_opt {
106 let events = commands
107 .iter()
108 .map(|c| match c {
109 Command::MoveCall(move_call) => {
110 let pkg = move_call.package.to_string();
111 let module = move_call.module.clone();
112 let function = move_call.function.clone();
113 Ok(vec![PTBCommandInfo::MoveCall {
114 pkg,
115 module,
116 function,
117 }])
118 }
119 Command::TransferObjects(..) => Ok(vec![PTBCommandInfo::ExternalEvent(
120 "TransferObjects".to_string(),
121 )]),
122 Command::SplitCoins(..) => Ok(vec![PTBCommandInfo::ExternalEvent(
123 "SplitCoins".to_string(),
124 )]),
125 Command::MergeCoins(..) => Ok(vec![PTBCommandInfo::ExternalEvent(
126 "MergeCoins".to_string(),
127 )]),
128 Command::Publish(module_bytes, _) => {
129 let mut events = vec![];
130 events.push(PTBCommandInfo::ExternalEvent("Publish".to_string()));
131 let modules = context.deserialize_modules(module_bytes)?;
134 events.extend(modules.into_iter().find_map(|m| {
135 for fdef in &m.function_defs {
136 let fhandle = m.function_handle_at(fdef.function);
137 let fname = m.identifier_at(fhandle.name);
138 if fname == INIT_FN_NAME {
139 return Some(PTBCommandInfo::MoveCall {
140 pkg: m.address().to_string(),
141 module: m.name().to_string(),
142 function: INIT_FN_NAME.to_string(),
143 });
144 }
145 }
146 None
147 }));
148 Ok(events)
149 }
150 Command::MakeMoveVec(..) => Ok(vec![PTBCommandInfo::ExternalEvent(
151 "MakeMoveVec".to_string(),
152 )]),
153 Command::Upgrade(..) => {
154 Ok(vec![PTBCommandInfo::ExternalEvent("Upgrade".to_string())])
155 }
156 })
157 .collect::<Result<Vec<Vec<PTBCommandInfo>>, ExecutionError>>()?
158 .into_iter()
159 .flatten()
160 .collect();
161 trace_builder.push_event(TraceEvent::External(
162 serde_json::value::to_raw_value(&PTBEvent::Summary(SummaryEvent {
163 name: "PTBSummary".to_string(),
164 events,
165 }))
166 .map_err(|e| make_invariant_violation!("Failed to serialize PTB trace event: {}", e))?,
167 ));
168 }
169
170 Ok(())
171}
172
173pub fn trace_split_coins(
176 context: &mut ExecutionContext<'_, '_, '_>,
177 trace_builder_opt: &mut Option<MoveTraceBuilder>,
178 coin_type: &Type,
179 input_coin: &Coin,
180 split_coin_values: &[Value],
181) -> Result<(), ExecutionError> {
182 if let Some(trace_builder) = trace_builder_opt {
183 let type_tag_with_refs = trace_type_to_type_tag_with_refs(context, coin_type)?;
184 let mut split_coin_move_values = vec![];
185 for coin_val in split_coin_values {
186 let Value::Object(ObjectValue {
187 contents: ObjectContents::Coin(coin),
188 ..
189 }) = coin_val
190 else {
191 invariant_violation!("Expected result of split coins PTB command to be a coin");
192 };
193 split_coin_move_values.push(
194 coin_move_value_info(
195 type_tag_with_refs.clone(),
196 *coin.id.object_id(),
197 coin.balance.value(),
198 )?
199 .value,
200 );
201 }
202
203 let input = coin_move_value_info(
204 type_tag_with_refs.clone(),
205 *input_coin.id.object_id(),
206 input_coin.value(),
207 )?;
208 trace_builder.push_event(TraceEvent::External(
209 serde_json::value::to_raw_value(&PTBEvent::ExternalEvent(ExternalEvent {
210 description: "SplitCoins: input => result".to_string(),
211 name: "SplitCoins".to_string(),
212 values: vec![
213 ExtMoveValue::Single {
214 name: "input".to_string(),
215 info: input,
216 },
217 ExtMoveValue::Vector {
218 name: "result".to_string(),
219 type_: type_tag_with_refs.clone(),
220 value: split_coin_move_values,
221 },
222 ],
223 }))
224 .map_err(|e| make_invariant_violation!("Failed to serialize PTB trace event: {}", e))?,
225 ));
226 }
227 Ok(())
228}
229
230pub fn trace_merge_coins(
233 context: &mut ExecutionContext<'_, '_, '_>,
234 trace_builder_opt: &mut Option<MoveTraceBuilder>,
235 coin_type: &Type,
236 input_infos: &[(u64, ObjectID)],
237 target_coin: &Coin,
238) -> Result<(), ExecutionError> {
239 if let Some(trace_builder) = trace_builder_opt {
240 let type_tag_with_refs = trace_type_to_type_tag_with_refs(context, coin_type)?;
241 let mut input_coin_move_values = vec![];
242 let mut to_merge = 0;
243 for (balance, id) in input_infos {
244 input_coin_move_values.push(coin_move_value_info(
245 type_tag_with_refs.clone(),
246 *id,
247 *balance,
248 )?);
249 to_merge += balance;
250 }
251 let merge_target = coin_move_value_info(
252 type_tag_with_refs.clone(),
253 *target_coin.id.object_id(),
254 target_coin.value() - to_merge,
255 )?;
256 let mut values = vec![ExtMoveValue::Single {
257 name: "merge_target".to_string(),
258 info: merge_target,
259 }];
260 for (idx, input_value) in input_coin_move_values.into_iter().enumerate() {
261 values.push(ExtMoveValue::Single {
262 name: format!("coin{idx}"),
263 info: input_value,
264 });
265 }
266 let merge_result = coin_move_value_info(
267 type_tag_with_refs.clone(),
268 *target_coin.id.object_id(),
269 target_coin.value(),
270 )?;
271 values.push(ExtMoveValue::Single {
272 name: "merge_result".to_string(),
273 info: merge_result,
274 });
275 trace_builder.push_event(TraceEvent::External(
276 serde_json::value::to_raw_value(&PTBEvent::ExternalEvent(ExternalEvent {
277 description: "MergeCoins: merge_target, coin0...coinN => mergeresult".to_string(),
278 name: "MergeCoins".to_string(),
279 values,
280 }))
281 .map_err(|e| make_invariant_violation!("Failed to serialize PTB trace event: {}", e))?,
282 ));
283 }
284 Ok(())
285}
286
287pub fn trace_make_move_vec(
290 context: &mut ExecutionContext<'_, '_, '_>,
291 trace_builder_opt: &mut Option<MoveTraceBuilder>,
292 move_values: Vec<ExtMoveValueInfo>,
293 type_: &Type,
294) -> Result<(), ExecutionError> {
295 if let Some(trace_builder) = trace_builder_opt {
296 let type_tag_with_refs = trace_type_to_type_tag_with_refs(context, type_)?;
297 trace_builder.push_event(TraceEvent::External(
298 serde_json::value::to_raw_value(&PTBEvent::ExternalEvent(ExternalEvent {
299 description: "MakeMoveVec: vector".to_string(),
300 name: "MakeMoveVec".to_string(),
301 values: vec![ExtMoveValue::Vector {
302 name: "vector".to_string(),
303 type_: type_tag_with_refs,
304 value: move_values
305 .into_iter()
306 .map(|move_value| move_value.value)
307 .collect(),
308 }],
309 }))
310 .map_err(|e| make_invariant_violation!("Failed to serialize PTB trace event: {}", e))?,
311 ));
312 }
313 Ok(())
314}
315
316pub fn trace_publish_event(
319 trace_builder_opt: &mut Option<MoveTraceBuilder>,
320) -> Result<(), ExecutionError> {
321 if let Some(trace_builder) = trace_builder_opt {
322 trace_builder.push_event(TraceEvent::External(
323 serde_json::value::to_raw_value(&PTBEvent::ExternalEvent(ExternalEvent {
324 description: "Publish: ()".to_string(),
325 name: "Publish".to_string(),
326 values: vec![],
327 }))
328 .map_err(|e| make_invariant_violation!("Failed to serialize PTB trace event: {}", e))?,
329 ));
330 }
331 Ok(())
332}
333
334pub fn trace_upgrade_event(
337 trace_builder_opt: &mut Option<MoveTraceBuilder>,
338) -> Result<(), ExecutionError> {
339 if let Some(trace_builder) = trace_builder_opt {
340 trace_builder.push_event(TraceEvent::External(
341 serde_json::value::to_raw_value(&PTBEvent::ExternalEvent(ExternalEvent {
342 description: "Upgrade: ()".to_string(),
343 name: "Upgrade".to_string(),
344 values: vec![],
345 }))
346 .map_err(|e| make_invariant_violation!("Failed to serialize PTB trace event: {}", e))?,
347 ));
348 }
349 Ok(())
350}
351
352pub fn trace_execution_error(trace_builder_opt: &mut Option<MoveTraceBuilder>, msg: String) {
355 if let Some(trace_builder) = trace_builder_opt {
356 trace_builder.push_event(TraceEvent::Effect(Box::new(Effect::ExecutionError(msg))));
357 }
358}
359
360pub fn add_move_value_info_from_value(
364 context: &mut ExecutionContext<'_, '_, '_>,
365 trace_builder_opt: &mut Option<MoveTraceBuilder>,
366 move_values: &mut Vec<ExtMoveValueInfo>,
367 type_: &Type,
368 value: &Value,
369) -> Result<(), ExecutionError> {
370 if trace_builder_opt.is_some()
371 && let Some(move_value_info) = move_value_info_from_value(context, type_, value)?
372 {
373 move_values.push(move_value_info);
374 }
375 Ok(())
376}
377
378pub fn add_move_value_info_from_obj_value(
382 context: &mut ExecutionContext<'_, '_, '_>,
383 trace_builder_opt: &mut Option<MoveTraceBuilder>,
384 move_values: &mut Vec<ExtMoveValueInfo>,
385 obj_val: &ObjectValue,
386) -> Result<(), ExecutionError> {
387 if trace_builder_opt.is_some() {
388 let move_value_info = move_value_info_from_obj_value(context, obj_val)?;
389 move_values.push(move_value_info);
390 }
391 Ok(())
392}
393
394pub fn add_coin_obj_info(
398 trace_builder_opt: &mut Option<MoveTraceBuilder>,
399 coin_infos: &mut Vec<(u64, ObjectID)>,
400 balance: u64,
401 id: ObjectID,
402) {
403 if trace_builder_opt.is_some() {
404 coin_infos.push((balance, id));
405 }
406}
407
408fn move_value_info_from_raw_bytes(
410 context: &mut ExecutionContext<'_, '_, '_>,
411 type_: &Type,
412 bytes: &[u8],
413) -> Result<ExtMoveValueInfo, ExecutionError> {
414 let type_tag_with_refs = trace_type_to_type_tag_with_refs(context, type_)?;
415 let layout = context
416 .vm
417 .get_runtime()
418 .type_to_fully_annotated_layout(type_)
419 .map_err(|e| ExecutionError::new_with_source(ExecutionErrorKind::InvariantViolation, e))?;
420 let move_value = BoundedVisitor::deserialize_value(bytes, &layout)
421 .map_err(|e| ExecutionError::new_with_source(ExecutionErrorKind::InvariantViolation, e))?;
422 let serialized_move_value = SerializableMoveValue::from(move_value);
423 Ok(ExtMoveValueInfo {
424 type_: type_tag_with_refs,
425 value: serialized_move_value,
426 })
427}
428
429fn move_value_info_from_value(
431 context: &mut ExecutionContext<'_, '_, '_>,
432 type_: &Type,
433 value: &Value,
434) -> Result<Option<ExtMoveValueInfo>, ExecutionError> {
435 match value {
436 Value::Object(obj_val) => Ok(Some(move_value_info_from_obj_value(context, obj_val)?)),
437 Value::Raw(_, bytes) => Ok(Some(move_value_info_from_raw_bytes(context, type_, bytes)?)),
438 Value::Receiving(_, _, _) => Ok(None),
439 }
440}
441
442fn move_value_info_from_obj_value(
444 context: &mut ExecutionContext<'_, '_, '_>,
445 obj_val: &ObjectValue,
446) -> Result<ExtMoveValueInfo, ExecutionError> {
447 let type_tag_with_refs = trace_type_to_type_tag_with_refs(context, &obj_val.type_)?;
448 match &obj_val.contents {
449 ObjectContents::Coin(coin) => {
450 coin_move_value_info(type_tag_with_refs, *coin.id.object_id(), coin.value())
451 }
452 ObjectContents::Raw(bytes) => {
453 move_value_info_from_raw_bytes(context, &obj_val.type_, bytes)
454 }
455 }
456}
457
458fn coin_move_value_info(
460 type_tag_with_refs: TypeTagWithRefs,
461 object_id: ObjectID,
462 balance: u64,
463) -> Result<ExtMoveValueInfo, ExecutionError> {
464 let coin_type_tag = match type_tag_with_refs.type_.clone() {
465 TypeTag::Struct(tag) => tag,
466 _ => invariant_violation!("Expected a struct type tag when creating a Move coin value"),
467 };
468 let object_id = SerializableMoveValue::Address(object_id.into());
470 let object_id_struct_tag = StructTag {
471 address: coin_type_tag.address,
472 module: Identifier::new("object").unwrap(),
473 name: Identifier::new("ID").unwrap(),
474 type_params: vec![],
475 };
476 let object_id_struct = SimplifiedMoveStruct {
477 type_: object_id_struct_tag,
478 fields: vec![(Identifier::new("value").unwrap(), object_id)],
479 };
480 let serializable_object_id = SerializableMoveValue::Struct(object_id_struct);
481 let object_uid_struct_tag = StructTag {
483 address: coin_type_tag.address,
484 module: Identifier::new("object").unwrap(),
485 name: Identifier::new("UID").unwrap(),
486 type_params: vec![],
487 };
488 let object_uid_struct = SimplifiedMoveStruct {
489 type_: object_uid_struct_tag,
490 fields: vec![(Identifier::new("id").unwrap(), serializable_object_id)],
491 };
492 let serializable_object_uid = SerializableMoveValue::Struct(object_uid_struct);
493 let serializable_value = SerializableMoveValue::U64(balance);
495 let balance_struct_tag = StructTag {
496 address: coin_type_tag.address,
497 module: Identifier::new("balance").unwrap(),
498 name: Identifier::new("Balance").unwrap(),
499 type_params: coin_type_tag.type_params.clone(),
500 };
501 let balance_struct = SimplifiedMoveStruct {
502 type_: balance_struct_tag,
503 fields: vec![(Identifier::new("value").unwrap(), serializable_value)],
504 };
505 let serializable_balance = SerializableMoveValue::Struct(balance_struct);
506 let coin_obj = SimplifiedMoveStruct {
508 type_: *coin_type_tag,
509 fields: vec![
510 (Identifier::new("id").unwrap(), serializable_object_uid),
511 (Identifier::new("balance").unwrap(), serializable_balance),
512 ],
513 };
514 Ok(ExtMoveValueInfo {
515 type_: type_tag_with_refs,
516 value: SerializableMoveValue::Struct(coin_obj),
517 })
518}
519
520fn trace_type_to_type_tag_with_refs(
522 context: &mut ExecutionContext<'_, '_, '_>,
523 type_: &Type,
524) -> Result<TypeTagWithRefs, ExecutionError> {
525 let (deref_type, ref_type) = match type_ {
526 Type::Reference(t) => (t.as_ref(), Some(RefType::Imm)),
527 Type::MutableReference(t) => (t.as_ref(), Some(RefType::Mut)),
528 t => (t, None),
529 };
530 let type_ = context
531 .vm
532 .get_runtime()
533 .get_type_tag(deref_type)
534 .map_err(|e| ExecutionError::new_with_source(ExecutionErrorKind::InvariantViolation, e))?;
535 Ok(TypeTagWithRefs { type_, ref_type })
536}