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