multiversx_chain_scenario_format/value_interpreter/
reconstructor.rs1use num_bigint::BigUint;
2
3use crate::{
4 reconstruct_trait::ReconstructorContext,
5 serde_raw::ValueSubTree,
6 value_interpreter::functions::{
7 SC_ADDRESS_NUM_LEADING_ZEROS, SC_ADDRESS_RESERVED_PREFIX_LENGTH,
8 },
9};
10pub enum ExprReconstructorHint {
11 NoHint,
13
14 UnsignedNumberHint,
16
17 AddressHint,
19
20 StrHint,
22
23 CodeHint,
25}
26
27const MAX_BYTES_INTERPRETED_AS_NUMBER: usize = 15;
28
29const SC_ADDRESS_LENGTH: usize = 32;
30const SC_CODE_LENGTH: usize = 20;
31
32pub fn reconstruct(
33 value: &[u8],
34 hint: &ExprReconstructorHint,
35 _context: &ReconstructorContext,
36) -> ValueSubTree {
37 let str: String = match hint {
38 ExprReconstructorHint::UnsignedNumberHint => BigUint::from_bytes_be(value).to_string(),
39 ExprReconstructorHint::StrHint => format!("str:{}", String::from_utf8_lossy(value)),
40 ExprReconstructorHint::AddressHint => address_pretty(value),
41 ExprReconstructorHint::CodeHint => code_pretty(value),
42 _ => unknown_byte_array_pretty(value),
43 };
44 ValueSubTree::Str(str)
45}
46
47pub fn reconstruct_from_biguint(value: BigUint, context: &ReconstructorContext) -> ValueSubTree {
48 reconstruct(
49 &value.to_bytes_be(),
50 &ExprReconstructorHint::UnsignedNumberHint,
51 context,
52 )
53}
54
55pub fn reconstruct_from_u64(value: u64, context: &ReconstructorContext) -> ValueSubTree {
56 reconstruct(
57 &BigUint::from(value).to_bytes_be(),
58 &ExprReconstructorHint::UnsignedNumberHint,
59 context,
60 )
61}
62
63pub fn reconstruction_list(
64 values: &[&[u8]],
65 hint: &ExprReconstructorHint,
66 context: &ReconstructorContext,
67) -> ValueSubTree {
68 let mut strings: Vec<ValueSubTree> = Vec::new();
69 for value in values.iter() {
70 strings.push(reconstruct(value, hint, context));
71 }
72 ValueSubTree::List(strings)
73}
74
75fn unknown_byte_array_pretty(bytes: &[u8]) -> String {
76 if bytes.is_empty() {
77 return String::new();
78 }
79
80 if can_interpret_as_string(bytes) {
82 return format!(
83 "0x{} (str:{})",
84 hex::encode(bytes),
85 String::from_utf8_lossy(bytes)
86 );
87 }
88
89 if bytes.len() < MAX_BYTES_INTERPRETED_AS_NUMBER {
91 let as_uint = BigUint::from_bytes_be(bytes).to_string();
92 return format!("0x{} ({})", hex::encode(bytes), as_uint);
93 }
94
95 format!(
97 "0x{} (str:{:?})",
98 hex::encode(bytes),
99 String::from_utf8_lossy(bytes).to_string(),
100 )
101}
102
103fn address_pretty(value: &[u8]) -> String {
104 if value.len() != 32 {
105 return unknown_byte_array_pretty(value);
106 }
107
108 if value[..SC_ADDRESS_NUM_LEADING_ZEROS] == [0; 8] {
110 if value[SC_ADDRESS_LENGTH - 1] == b'_' {
111 let address_str =
112 String::from_utf8_lossy(&value[SC_ADDRESS_RESERVED_PREFIX_LENGTH..]).to_string();
113 return format!("sc:{}", address_str.trim_end_matches('_').to_owned());
114 } else {
115 let address_str = String::from_utf8_lossy(
118 &value[SC_ADDRESS_RESERVED_PREFIX_LENGTH..SC_ADDRESS_LENGTH - 1],
119 )
120 .to_string();
121 let shard_id = value[SC_ADDRESS_LENGTH - 1];
122 return format!(
123 "sc:{}#{:x}",
124 address_str.trim_end_matches('_').to_owned(),
125 shard_id
126 );
127 }
128 }
129
130 if value[SC_ADDRESS_LENGTH - 1] == b'_' {
132 let address_str = String::from_utf8_lossy(value).to_string();
133 format!("address:{}", address_str.trim_end_matches('_').to_owned())
134 } else {
135 let mut address_str = String::from_utf8_lossy(&value[..SC_ADDRESS_LENGTH - 1]).to_string();
136 address_str = address_str.trim_end_matches('_').to_string();
137 let shard_id = value[SC_ADDRESS_LENGTH - 1];
138 let address_expr = format!("address:{address_str}#{shard_id:02x}");
139 if !can_interpret_as_string(&[value[SC_ADDRESS_LENGTH - 1]]) {
140 return format!("0x{} ({})", hex::encode(value), address_expr);
141 }
142 address_expr
143 }
144}
145
146fn can_interpret_as_string(bytes: &[u8]) -> bool {
147 if bytes.is_empty() {
148 return false;
149 }
150 !bytes.iter().any(|&b| !(32..=126).contains(&b))
151}
152
153fn code_pretty(bytes: &[u8]) -> String {
154 if bytes.is_empty() {
155 return String::new();
156 }
157 let encoded = hex::encode(bytes);
158
159 if encoded.len() > SC_CODE_LENGTH {
160 return format!("0x{}...", &encoded[..SC_CODE_LENGTH]);
161 }
162
163 format!("0x{encoded}")
164}