1use std::{
6 fmt::{Display, Formatter},
7 sync::Arc,
8};
9
10use iota_execution::Executor;
11use iota_types::{
12 execution::ExecutionResult,
13 object::bounded_visitor::BoundedVisitor,
14 transaction::{
15 Argument, CallArg, CallArg::Pure, Command, ObjectArg, ProgrammableMoveCall,
16 ProgrammableTransaction, write_sep,
17 },
18};
19use move_core_types::{
20 annotated_value::{MoveTypeLayout, MoveValue},
21 language_storage::TypeTag,
22};
23use tabled::{
24 builder::Builder as TableBuilder,
25 settings::{Panel as TablePanel, Style as TableStyle, style::HorizontalLine},
26};
27
28use crate::{displays::Pretty, replay::LocalExec};
29
30pub struct FullPTB {
31 pub ptb: ProgrammableTransaction,
32 pub results: Vec<ResolvedResults>,
33}
34
35pub struct ResolvedResults {
36 pub mutable_reference_outputs: Vec<(Argument, MoveValue)>,
37 pub return_values: Vec<MoveValue>,
38}
39
40impl Display for Pretty<'_, FullPTB> {
44 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
45 let Pretty(full_ptb) = self;
46 let FullPTB { ptb, results } = full_ptb;
47
48 let ProgrammableTransaction { inputs, commands } = ptb;
49
50 if !inputs.is_empty() {
52 let mut builder = TableBuilder::default();
53 for (i, input) in inputs.iter().enumerate() {
54 match input {
55 Pure(v) => {
56 if v.len() <= 16 {
57 builder.push_record(vec![format!("{i:<3} Pure Arg {:?}", v)]);
58 } else {
59 builder.push_record(vec![format!(
60 "{i:<3} Pure Arg [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, ...]",
61 v[0],
62 v[1],
63 v[2],
64 v[3],
65 v[4],
66 v[5],
67 v[6],
68 v[7],
69 v[8],
70 v[9],
71 v[10],
72 v[11],
73 v[12],
74 v[13],
75 v[14],
76 )]);
77 }
78 }
79
80 CallArg::Object(ObjectArg::ImmOrOwnedObject(o)) => {
81 builder.push_record(vec![format!("{i:<3} Imm/Owned Object ID: {}", o.0)]);
82 }
83 CallArg::Object(ObjectArg::SharedObject { id, .. }) => {
84 builder.push_record(vec![format!("{i:<3} Shared Object ID: {}", id)]);
85 }
86 CallArg::Object(ObjectArg::Receiving(o)) => {
87 builder.push_record(vec![format!("{i:<3} Receiving Object ID: {}", o.0)]);
88 }
89 };
90 }
91
92 let mut table = builder.build();
93 table.with(TablePanel::header("Input Objects"));
94 table.with(TableStyle::rounded().horizontals([HorizontalLine::new(
95 1,
96 TableStyle::modern().get_horizontal(),
97 )]));
98 write!(f, "\n{}\n", table)?;
99 } else {
100 write!(f, "\n No input objects for this transaction")?;
101 }
102
103 if !results.is_empty() {
105 write!(f, "\n\n")?;
106 }
107 for (i, result) in results.iter().enumerate() {
108 if i == results.len() - 1 {
109 write!(
110 f,
111 "╭───────────────────╮\n│ Command {i:<2} Output │\n╰───────────────────╯{}\n\n\n",
112 Pretty(result)
113 )?
114 } else {
115 write!(
116 f,
117 "╭───────────────────╮\n│ Command {i:<2} Output │\n╰───────────────────╯{}\n",
118 Pretty(result)
119 )?
120 }
121 }
122
123 let mut builder = TableBuilder::default();
125 if !commands.is_empty() {
126 for (i, c) in commands.iter().enumerate() {
127 if i == commands.len() - 1 {
128 builder.push_record(vec![format!("{i:<2} {}", Pretty(c))]);
129 } else {
130 builder.push_record(vec![format!("{i:<2} {}\n", Pretty(c))]);
131 }
132 }
133
134 let mut table = builder.build();
135 table.with(TablePanel::header("Commands"));
136 table.with(TableStyle::rounded().horizontals([HorizontalLine::new(
137 1,
138 TableStyle::modern().get_horizontal(),
139 )]));
140 write!(f, "\n{}\n", table)?;
141 } else {
142 write!(f, "\n No commands for this transaction")?;
143 }
144
145 Ok(())
146 }
147}
148
149impl Display for Pretty<'_, Command> {
150 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
151 let Pretty(command) = self;
152 match command {
153 Command::MoveCall(p) => {
154 write!(f, "{}", Pretty(&**p))
155 }
156 Command::MakeMoveVec(ty_opt, elems) => {
157 write!(f, "MakeMoveVec:\n ┌")?;
158 if let Some(ty) = ty_opt {
159 write!(f, "\n │ Type Tag: {ty}")?;
160 }
161 write!(f, "\n │ Arguments:\n │ ")?;
162 write_sep(f, elems.iter().map(Pretty), "\n │ ")?;
163 write!(f, "\n └")
164 }
165 Command::TransferObjects(objs, addr) => {
166 write!(f, "TransferObjects:\n ┌\n │ Arguments: \n │ ")?;
167 write_sep(f, objs.iter().map(Pretty), "\n │ ")?;
168 write!(f, "\n │ Address: {}\n └", Pretty(addr))
169 }
170 Command::SplitCoins(coin, amounts) => {
171 write!(
172 f,
173 "SplitCoins:\n ┌\n │ Coin: {}\n │ Amounts: \n │ ",
174 Pretty(coin)
175 )?;
176 write_sep(f, amounts.iter().map(Pretty), "\n │ ")?;
177 write!(f, "\n └")
178 }
179 Command::MergeCoins(target, coins) => {
180 write!(
181 f,
182 "MergeCoins:\n ┌\n │ Target: {}\n │ Coins: \n │ ",
183 Pretty(target)
184 )?;
185 write_sep(f, coins.iter().map(Pretty), "\n │ ")?;
186 write!(f, "\n └")
187 }
188 Command::Publish(_bytes, deps) => {
189 write!(f, "Publish:\n ┌\n │ Dependencies: \n │ ")?;
190 write_sep(f, deps, "\n │ ")?;
191 write!(f, "\n └")
192 }
193 Command::Upgrade(_bytes, deps, current_package_id, ticket) => {
194 write!(f, "Upgrade:\n ┌\n │ Dependencies: \n │ ")?;
195 write_sep(f, deps, "\n │ ")?;
196 write!(f, "\n │ Current Package ID: {current_package_id}")?;
197 write!(f, "\n │ Ticket: {}", Pretty(ticket))?;
198 write!(f, "\n └")
199 }
200 }
201 }
202}
203
204impl Display for Pretty<'_, ProgrammableMoveCall> {
205 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
206 let Pretty(move_call) = self;
207 let ProgrammableMoveCall {
208 package,
209 module,
210 function,
211 type_arguments,
212 arguments,
213 } = move_call;
214
215 write!(
216 f,
217 "MoveCall:\n ┌\n │ Function: {} \n │ Module: {}\n │ Package: {}",
218 function, module, package
219 )?;
220
221 if !type_arguments.is_empty() {
222 write!(f, "\n │ Type Arguments: \n │ ")?;
223 write_sep(f, type_arguments, "\n │ ")?;
224 }
225 if !arguments.is_empty() {
226 write!(f, "\n │ Arguments: \n │ ")?;
227 write_sep(f, arguments.iter().map(Pretty), "\n │ ")?;
228 }
229
230 write!(f, "\n └")
231 }
232}
233
234impl Display for Pretty<'_, Argument> {
235 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
236 let Pretty(argument) = self;
237
238 let output = match argument {
239 Argument::GasCoin => "GasCoin".to_string(),
240 Argument::Input(i) => format!("Input {}", i),
241 Argument::Result(i) => format!("Result {}", i),
242 Argument::NestedResult(j, k) => format!("Result {}: {}", j, k),
243 };
244 write!(f, "{}", output)
245 }
246}
247impl Display for Pretty<'_, ResolvedResults> {
248 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
249 let Pretty(ResolvedResults {
250 mutable_reference_outputs,
251 return_values,
252 }) = self;
253
254 let len_m_ref = mutable_reference_outputs.len();
255 let len_ret_vals = return_values.len();
256
257 if len_ret_vals > 0 {
258 write!(f, "\n Return Values:\n ──────────────")?;
259 }
260
261 for (i, value) in return_values.iter().enumerate() {
262 write!(f, "\n • Result {i:<2} ")?;
263 write!(f, "\n{:#}\n", value)?;
264 }
265
266 if len_m_ref > 0 {
267 write!(
268 f,
269 "\n Mutable Reference Outputs:\n ──────────────────────────"
270 )?;
271 }
272
273 for (arg, value) in mutable_reference_outputs {
274 write!(f, "\n • {} ", arg)?;
275 write!(f, "\n{:#}\n", value)?;
276 }
277
278 if len_ret_vals == 0 && len_m_ref == 0 {
279 write!(f, "\n No return values")?;
280 }
281
282 Ok(())
283 }
284}
285
286impl Display for Pretty<'_, TypeTag> {
287 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
288 let Pretty(type_tag) = self;
289 match type_tag {
290 TypeTag::Vector(v) => {
291 write!(f, "Vector of {}", Pretty(&**v))
292 }
293 TypeTag::Struct(s) => {
294 write!(f, "{}::{}", s.module, s.name)
295 }
296 _ => {
297 write!(f, "{}", type_tag)
298 }
299 }
300 }
301}
302
303fn resolve_to_layout(
304 type_tag: &TypeTag,
305 executor: &Arc<dyn Executor + Send + Sync>,
306 store_factory: &LocalExec,
307) -> MoveTypeLayout {
308 match type_tag {
309 TypeTag::Vector(inner) => {
310 MoveTypeLayout::Vector(Box::from(resolve_to_layout(inner, executor, store_factory)))
311 }
312 TypeTag::Struct(inner) => {
313 let mut layout_resolver = executor.type_layout_resolver(Box::new(store_factory));
314 layout_resolver
315 .get_annotated_layout(inner)
316 .unwrap()
317 .into_layout()
318 }
319 TypeTag::Bool => MoveTypeLayout::Bool,
320 TypeTag::U8 => MoveTypeLayout::U8,
321 TypeTag::U64 => MoveTypeLayout::U64,
322 TypeTag::U128 => MoveTypeLayout::U128,
323 TypeTag::Address => MoveTypeLayout::Address,
324 TypeTag::Signer => MoveTypeLayout::Signer,
325 TypeTag::U16 => MoveTypeLayout::U16,
326 TypeTag::U32 => MoveTypeLayout::U32,
327 TypeTag::U256 => MoveTypeLayout::U256,
328 }
329}
330
331fn resolve_value(
332 bytes: &[u8],
333 type_tag: &TypeTag,
334 executor: &Arc<dyn Executor + Send + Sync>,
335 store_factory: &LocalExec,
336) -> anyhow::Result<MoveValue> {
337 let layout = resolve_to_layout(type_tag, executor, store_factory);
338 BoundedVisitor::deserialize_value(bytes, &layout)
339}
340
341pub fn transform_command_results_to_annotated(
342 executor: &Arc<dyn Executor + Send + Sync>,
343 store_factory: &LocalExec,
344 results: Vec<ExecutionResult>,
345) -> anyhow::Result<Vec<ResolvedResults>> {
346 let mut output = Vec::new();
347 for (m_refs, return_vals) in results.iter() {
348 let mut m_refs_out = Vec::new();
349 let mut return_vals_out = Vec::new();
350 for (arg, bytes, tag) in m_refs {
351 m_refs_out.push((*arg, resolve_value(bytes, tag, executor, store_factory)?));
352 }
353 for (bytes, tag) in return_vals {
354 return_vals_out.push(resolve_value(bytes, tag, executor, store_factory)?);
355 }
356 output.push(ResolvedResults {
357 mutable_reference_outputs: m_refs_out,
358 return_values: return_vals_out,
359 });
360 }
361 Ok(output)
362}