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{table}\n")?;
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{table}\n")?;
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: {function} \n │ Module: {module}\n │ Package: {package}"
218 )?;
219
220 if !type_arguments.is_empty() {
221 write!(f, "\n │ Type Arguments: \n │ ")?;
222 write_sep(f, type_arguments, "\n │ ")?;
223 }
224 if !arguments.is_empty() {
225 write!(f, "\n │ Arguments: \n │ ")?;
226 write_sep(f, arguments.iter().map(Pretty), "\n │ ")?;
227 }
228
229 write!(f, "\n └")
230 }
231}
232
233impl Display for Pretty<'_, Argument> {
234 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
235 let Pretty(argument) = self;
236
237 let output = match argument {
238 Argument::GasCoin => "GasCoin".to_string(),
239 Argument::Input(i) => format!("Input {i}"),
240 Argument::Result(i) => format!("Result {i}"),
241 Argument::NestedResult(j, k) => format!("Result {j}: {k}"),
242 };
243 write!(f, "{output}")
244 }
245}
246impl Display for Pretty<'_, ResolvedResults> {
247 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
248 let Pretty(ResolvedResults {
249 mutable_reference_outputs,
250 return_values,
251 }) = self;
252
253 let len_m_ref = mutable_reference_outputs.len();
254 let len_ret_vals = return_values.len();
255
256 if len_ret_vals > 0 {
257 write!(f, "\n Return Values:\n ──────────────")?;
258 }
259
260 for (i, value) in return_values.iter().enumerate() {
261 write!(f, "\n • Result {i:<2} ")?;
262 write!(f, "\n{value:#}\n")?;
263 }
264
265 if len_m_ref > 0 {
266 write!(
267 f,
268 "\n Mutable Reference Outputs:\n ──────────────────────────"
269 )?;
270 }
271
272 for (arg, value) in mutable_reference_outputs {
273 write!(f, "\n • {arg} ")?;
274 write!(f, "\n{value:#}\n")?;
275 }
276
277 if len_ret_vals == 0 && len_m_ref == 0 {
278 write!(f, "\n No return values")?;
279 }
280
281 Ok(())
282 }
283}
284
285impl Display for Pretty<'_, TypeTag> {
286 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
287 let Pretty(type_tag) = self;
288 match type_tag {
289 TypeTag::Vector(v) => {
290 write!(f, "Vector of {}", Pretty(&**v))
291 }
292 TypeTag::Struct(s) => {
293 write!(f, "{}::{}", s.module, s.name)
294 }
295 _ => {
296 write!(f, "{type_tag}")
297 }
298 }
299 }
300}
301
302fn resolve_to_layout(
303 type_tag: &TypeTag,
304 executor: &Arc<dyn Executor + Send + Sync>,
305 store_factory: &LocalExec,
306) -> MoveTypeLayout {
307 match type_tag {
308 TypeTag::Vector(inner) => {
309 MoveTypeLayout::Vector(Box::from(resolve_to_layout(inner, executor, store_factory)))
310 }
311 TypeTag::Struct(inner) => {
312 let mut layout_resolver = executor.type_layout_resolver(Box::new(store_factory));
313 layout_resolver
314 .get_annotated_layout(inner)
315 .unwrap()
316 .into_layout()
317 }
318 TypeTag::Bool => MoveTypeLayout::Bool,
319 TypeTag::U8 => MoveTypeLayout::U8,
320 TypeTag::U64 => MoveTypeLayout::U64,
321 TypeTag::U128 => MoveTypeLayout::U128,
322 TypeTag::Address => MoveTypeLayout::Address,
323 TypeTag::Signer => MoveTypeLayout::Signer,
324 TypeTag::U16 => MoveTypeLayout::U16,
325 TypeTag::U32 => MoveTypeLayout::U32,
326 TypeTag::U256 => MoveTypeLayout::U256,
327 }
328}
329
330fn resolve_value(
331 bytes: &[u8],
332 type_tag: &TypeTag,
333 executor: &Arc<dyn Executor + Send + Sync>,
334 store_factory: &LocalExec,
335) -> anyhow::Result<MoveValue> {
336 let layout = resolve_to_layout(type_tag, executor, store_factory);
337 BoundedVisitor::deserialize_value(bytes, &layout)
338}
339
340pub fn transform_command_results_to_annotated(
341 executor: &Arc<dyn Executor + Send + Sync>,
342 store_factory: &LocalExec,
343 results: Vec<ExecutionResult>,
344) -> anyhow::Result<Vec<ResolvedResults>> {
345 let mut output = Vec::new();
346 for (m_refs, return_vals) in results.iter() {
347 let mut m_refs_out = Vec::new();
348 let mut return_vals_out = Vec::new();
349 for (arg, bytes, tag) in m_refs {
350 m_refs_out.push((*arg, resolve_value(bytes, tag, executor, store_factory)?));
351 }
352 for (bytes, tag) in return_vals {
353 return_vals_out.push(resolve_value(bytes, tag, executor, store_factory)?);
354 }
355 output.push(ResolvedResults {
356 mutable_reference_outputs: m_refs_out,
357 return_values: return_vals_out,
358 });
359 }
360 Ok(output)
361}