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