iota_json/
lib.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use std::{
6    collections::{BTreeMap, VecDeque},
7    fmt::{self, Debug, Formatter},
8    str::FromStr,
9};
10
11use anyhow::{anyhow, bail};
12use fastcrypto::encoding::{Encoding, Hex};
13use iota_types::{
14    MOVE_STDLIB_ADDRESS,
15    base_types::{
16        IotaAddress, ObjectID, RESOLVED_ASCII_STR, RESOLVED_STD_OPTION, RESOLVED_UTF8_STR,
17        STD_ASCII_MODULE_NAME, STD_ASCII_STRUCT_NAME, STD_OPTION_MODULE_NAME,
18        STD_OPTION_STRUCT_NAME, STD_UTF8_MODULE_NAME, STD_UTF8_STRUCT_NAME, TxContext,
19        TxContextKind, is_primitive_type_tag, move_ascii_str_layout, move_utf8_str_layout,
20    },
21    id::{self, ID, RESOLVED_IOTA_ID},
22    move_package::MovePackage,
23    object::bounded_visitor::BoundedVisitor,
24    transfer::RESOLVED_RECEIVING_STRUCT,
25};
26use move_binary_format::{
27    CompiledModule, binary_config::BinaryConfig, file_format::SignatureToken,
28};
29use move_bytecode_utils::resolve_struct;
30pub use move_core_types::annotated_value::MoveTypeLayout;
31use move_core_types::{
32    annotated_value::{MoveFieldLayout, MoveStruct, MoveValue, MoveVariant},
33    identifier::Identifier,
34    language_storage::{StructTag, TypeTag},
35    runtime_value as R,
36    u256::U256,
37};
38use schemars::JsonSchema;
39use serde::{Deserialize, Serialize};
40use serde_json::{Number, Value as JsonValue, json};
41
42const HEX_PREFIX: &str = "0x";
43
44#[cfg(test)]
45mod tests;
46
47/// A list of error categories encountered when parsing numbers.
48#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
49pub enum IotaJsonValueErrorKind {
50    /// JSON value must be of specific types.
51    ValueTypeNotAllowed,
52
53    /// JSON arrays must be homogeneous.
54    ArrayNotHomogeneous,
55}
56
57#[derive(Debug)]
58pub struct IotaJsonValueError {
59    kind: IotaJsonValueErrorKind,
60    val: JsonValue,
61}
62
63impl IotaJsonValueError {
64    pub fn new(val: &JsonValue, kind: IotaJsonValueErrorKind) -> Self {
65        Self {
66            kind,
67            val: val.clone(),
68        }
69    }
70}
71
72impl std::error::Error for IotaJsonValueError {}
73
74impl fmt::Display for IotaJsonValueError {
75    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76        let err_str = match self.kind {
77            IotaJsonValueErrorKind::ValueTypeNotAllowed => {
78                format!("JSON value type {} not allowed.", self.val)
79            }
80            IotaJsonValueErrorKind::ArrayNotHomogeneous => {
81                format!("Array not homogeneous. Mismatched value: {}.", self.val)
82            }
83        };
84        write!(f, "{err_str}")
85    }
86}
87
88// Intermediate type to hold resolved args
89#[derive(Eq, PartialEq, Debug)]
90pub enum ResolvedCallArg {
91    Object(ObjectID),
92    Pure(Vec<u8>),
93    ObjVec(Vec<ObjectID>),
94}
95
96#[derive(Eq, PartialEq, Clone, Deserialize, Serialize, JsonSchema)]
97pub struct IotaJsonValue(JsonValue);
98impl IotaJsonValue {
99    pub fn new(json_value: JsonValue) -> Result<IotaJsonValue, anyhow::Error> {
100        Self::check_value(&json_value)?;
101        Ok(Self(json_value))
102    }
103
104    fn check_value(json_value: &JsonValue) -> Result<(), anyhow::Error> {
105        match json_value {
106            // No checks needed for Bool and String
107            JsonValue::Bool(_) | JsonValue::String(_) => (),
108            JsonValue::Number(n) => {
109                // Must be castable to u64
110                if !n.is_u64() {
111                    return Err(anyhow!(
112                        "{n} not allowed. Number must be unsigned integer of at most u32"
113                    ));
114                }
115            }
116            // Must be homogeneous
117            JsonValue::Array(a) => {
118                // Fail if not homogeneous
119                check_valid_homogeneous(&JsonValue::Array(a.to_vec()))?
120            }
121            JsonValue::Object(v) => {
122                for (_, value) in v {
123                    Self::check_value(value)?;
124                }
125            }
126            JsonValue::Null => bail!("Null not allowed."),
127        };
128        Ok(())
129    }
130
131    pub fn from_object_id(id: ObjectID) -> IotaJsonValue {
132        Self(JsonValue::String(id.to_hex_uncompressed()))
133    }
134
135    pub fn to_bcs_bytes(&self, ty: &MoveTypeLayout) -> Result<Vec<u8>, anyhow::Error> {
136        let move_value = Self::to_move_value(&self.0, ty)?;
137        R::MoveValue::simple_serialize(&move_value)
138            .ok_or_else(|| anyhow!("Unable to serialize {:?}. Expected {}", move_value, ty))
139    }
140
141    pub fn from_bcs_bytes(
142        layout: Option<&MoveTypeLayout>,
143        bytes: &[u8],
144    ) -> Result<Self, anyhow::Error> {
145        let json = if let Some(layout) = layout {
146            // Try to convert Vec<u8> inputs into string
147            fn try_parse_string(layout: &MoveTypeLayout, bytes: &[u8]) -> Option<String> {
148                if let MoveTypeLayout::Vector(t) = layout {
149                    if let MoveTypeLayout::U8 = **t {
150                        return bcs::from_bytes::<String>(bytes).ok();
151                    }
152                }
153                None
154            }
155            if let Some(s) = try_parse_string(layout, bytes) {
156                json!(s)
157            } else {
158                let result = BoundedVisitor::deserialize_value(bytes, layout).map_or_else(
159                    |_| {
160                        // fallback to array[u8] if fail to convert to json.
161                        JsonValue::Array(
162                            bytes
163                                .iter()
164                                .map(|b| JsonValue::Number(Number::from(*b)))
165                                .collect(),
166                        )
167                    },
168                    |move_value| {
169                        move_value_to_json(&move_value).unwrap_or_else(|| {
170                            // fallback to array[u8] if fail to convert to json.
171                            JsonValue::Array(
172                                bytes
173                                    .iter()
174                                    .map(|b| JsonValue::Number(Number::from(*b)))
175                                    .collect(),
176                            )
177                        })
178                    },
179                );
180                result
181            }
182        } else {
183            json!(bytes)
184        };
185        IotaJsonValue::new(json)
186    }
187
188    pub fn to_json_value(&self) -> JsonValue {
189        self.0.clone()
190    }
191
192    pub fn to_iota_address(&self) -> anyhow::Result<IotaAddress> {
193        json_value_to_iota_address(&self.0)
194    }
195
196    fn handle_inner_struct_layout(
197        inner_vec: &[MoveFieldLayout],
198        val: &JsonValue,
199        ty: &MoveTypeLayout,
200        s: &String,
201    ) -> Result<R::MoveValue, anyhow::Error> {
202        // delegate MoveValue construction to the case when JsonValue::String and
203        // MoveTypeLayout::Vector are handled to get an address (with 0x string
204        // prefix) or a vector of u8s (no prefix)
205        debug_assert!(matches!(val, JsonValue::String(_)));
206
207        if inner_vec.len() != 1 {
208            bail!(
209                "Cannot convert string arg {s} to {ty} which is expected \
210                 to be a struct with one field"
211            );
212        }
213
214        match &inner_vec[0].layout {
215            MoveTypeLayout::Vector(inner) => match **inner {
216                MoveTypeLayout::U8 => Ok(R::MoveValue::Struct(R::MoveStruct(vec![
217                    Self::to_move_value(val, &inner_vec[0].layout.clone())?,
218                ]))),
219                MoveTypeLayout::Address => Ok(R::MoveValue::Struct(R::MoveStruct(vec![
220                    Self::to_move_value(val, &MoveTypeLayout::Address)?,
221                ]))),
222                _ => bail!(
223                    "Cannot convert string arg {s} to {ty} \
224                             which is expected to be a struct \
225                             with one field of address or u8 vector type"
226                ),
227            },
228            MoveTypeLayout::Struct(struct_layout) if struct_layout.type_ == ID::type_() => {
229                Ok(R::MoveValue::Struct(R::MoveStruct(vec![
230                    Self::to_move_value(val, &inner_vec[0].layout.clone())?,
231                ])))
232            }
233            _ => bail!(
234                "Cannot convert string arg {s} to {ty} which is expected \
235                 to be a struct with one field of a vector type"
236            ),
237        }
238    }
239
240    pub fn to_move_value(
241        val: &JsonValue,
242        ty: &MoveTypeLayout,
243    ) -> Result<R::MoveValue, anyhow::Error> {
244        Ok(match (val, ty) {
245            // Bool to Bool is simple
246            (JsonValue::Bool(b), MoveTypeLayout::Bool) => R::MoveValue::Bool(*b),
247
248            // In constructor, we have already checked that the JSON number is unsigned int of at
249            // most U32
250            (JsonValue::Number(n), MoveTypeLayout::U8) => match n.as_u64() {
251                Some(x) => R::MoveValue::U8(u8::try_from(x)?),
252                None => return Err(anyhow!("{} is not a valid number. Only u8 allowed.", n)),
253            },
254            (JsonValue::Number(n), MoveTypeLayout::U16) => match n.as_u64() {
255                Some(x) => R::MoveValue::U16(u16::try_from(x)?),
256                None => return Err(anyhow!("{} is not a valid number. Only u16 allowed.", n)),
257            },
258            (JsonValue::Number(n), MoveTypeLayout::U32) => match n.as_u64() {
259                Some(x) => R::MoveValue::U32(u32::try_from(x)?),
260                None => return Err(anyhow!("{} is not a valid number. Only u32 allowed.", n)),
261            },
262
263            // u8, u16, u32, u64, u128, u256 can be encoded as String
264            (JsonValue::String(s), MoveTypeLayout::U8) => {
265                R::MoveValue::U8(u8::try_from(convert_string_to_u256(s.as_str())?)?)
266            }
267            (JsonValue::String(s), MoveTypeLayout::U16) => {
268                R::MoveValue::U16(u16::try_from(convert_string_to_u256(s.as_str())?)?)
269            }
270            (JsonValue::String(s), MoveTypeLayout::U32) => {
271                R::MoveValue::U32(u32::try_from(convert_string_to_u256(s.as_str())?)?)
272            }
273            (JsonValue::String(s), MoveTypeLayout::U64) => {
274                R::MoveValue::U64(u64::try_from(convert_string_to_u256(s.as_str())?)?)
275            }
276            (JsonValue::String(s), MoveTypeLayout::U128) => {
277                R::MoveValue::U128(u128::try_from(convert_string_to_u256(s.as_str())?)?)
278            }
279            (JsonValue::String(s), MoveTypeLayout::U256) => {
280                R::MoveValue::U256(convert_string_to_u256(s.as_str())?)
281            }
282            // For ascii and utf8 strings
283            (JsonValue::String(s), MoveTypeLayout::Struct(struct_layout))
284                if is_move_string_type(&struct_layout.type_) =>
285            {
286                R::MoveValue::Vector(s.as_bytes().iter().copied().map(R::MoveValue::U8).collect())
287            }
288            // For ID
289            (JsonValue::String(s), MoveTypeLayout::Struct(struct_layout))
290                if struct_layout.type_ == ID::type_() =>
291            {
292                if struct_layout.fields.len() != 1 {
293                    bail!(
294                        "Cannot convert string arg {s} to {} which is expected to be a struct with one field",
295                        struct_layout.type_
296                    );
297                };
298                let addr = IotaAddress::from_str(s)?;
299                R::MoveValue::Address(addr.into())
300            }
301            (JsonValue::Object(o), MoveTypeLayout::Struct(struct_layout)) => {
302                let mut field_values = vec![];
303                for layout in struct_layout.fields.iter() {
304                    let field = o
305                        .get(layout.name.as_str())
306                        .ok_or_else(|| anyhow!("Missing field {} for struct {ty}", layout.name))?;
307                    field_values.push(Self::to_move_value(field, &layout.layout)?);
308                }
309                R::MoveValue::Struct(R::MoveStruct(field_values))
310            }
311            // Unnest fields
312            (value, MoveTypeLayout::Struct(struct_layout)) if struct_layout.fields.len() == 1 => {
313                Self::to_move_value(value, &struct_layout.fields[0].layout)?
314            }
315            (JsonValue::String(s), MoveTypeLayout::Vector(t)) => {
316                match &**t {
317                    MoveTypeLayout::U8 => {
318                        // We can encode U8 Vector as string in 2 ways
319                        // 1. If it starts with 0x, we treat it as hex strings, where each pair is a
320                        //    byte
321                        // 2. If it does not start with 0x, we treat each character as an ASCII
322                        //    encoded byte
323                        // We have to support both for the convenience of the user. This is because
324                        // sometime we need Strings as arg Other times we need vec of hex bytes for
325                        // address. Issue is both Address and Strings are represented as Vec<u8> in
326                        // Move call
327                        let vec = if s.starts_with(HEX_PREFIX) {
328                            // If starts with 0x, treat as hex vector
329                            Hex::decode(s).map_err(|e| anyhow!(e))?
330                        } else {
331                            // Else raw bytes
332                            s.as_bytes().to_vec()
333                        };
334                        R::MoveValue::Vector(vec.iter().copied().map(R::MoveValue::U8).collect())
335                    }
336                    MoveTypeLayout::Struct(struct_layout) => {
337                        Self::handle_inner_struct_layout(&struct_layout.fields, val, ty, s)?
338                    }
339                    _ => bail!("Cannot convert string arg {s} to {ty}"),
340                }
341            }
342
343            // We have already checked that the array is homogeneous in the constructor
344            (JsonValue::Array(a), MoveTypeLayout::Vector(inner)) => {
345                // Recursively build an IntermediateValue array
346                R::MoveValue::Vector(
347                    a.iter()
348                        .map(|i| Self::to_move_value(i, inner))
349                        .collect::<Result<Vec<_>, _>>()?,
350                )
351            }
352
353            (v, MoveTypeLayout::Address) => {
354                let addr = json_value_to_iota_address(v)?;
355                R::MoveValue::Address(addr.into())
356            }
357
358            _ => bail!("Unexpected arg {val:?} for expected type {ty:?}"),
359        })
360    }
361}
362
363impl Debug for IotaJsonValue {
364    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
365        write!(f, "{}", self.0)
366    }
367}
368
369fn json_value_to_iota_address(value: &JsonValue) -> anyhow::Result<IotaAddress> {
370    match value {
371        JsonValue::String(s) => {
372            let s = s.trim().to_lowercase();
373            if !s.starts_with(HEX_PREFIX) {
374                bail!("Address hex string must start with 0x.",);
375            }
376            Ok(IotaAddress::from_str(&s)?)
377        }
378        JsonValue::Array(bytes) => {
379            fn value_to_byte_array(v: &Vec<JsonValue>) -> Option<Vec<u8>> {
380                let mut bytes = vec![];
381                for b in v {
382                    let b = b.as_u64()?;
383                    if b <= u8::MAX as u64 {
384                        bytes.push(b as u8);
385                    } else {
386                        return None;
387                    }
388                }
389                Some(bytes)
390            }
391            let bytes = value_to_byte_array(bytes)
392                .ok_or_else(|| anyhow!("Invalid input: Cannot parse input into IotaAddress."))?;
393            Ok(IotaAddress::try_from(bytes)?)
394        }
395        v => bail!("Unexpected arg {v} for expected type address"),
396    }
397}
398
399fn move_value_to_json(move_value: &MoveValue) -> Option<JsonValue> {
400    Some(match move_value {
401        MoveValue::Vector(values) => JsonValue::Array(
402            values
403                .iter()
404                .map(move_value_to_json)
405                .collect::<Option<_>>()?,
406        ),
407        MoveValue::Bool(v) => json!(v),
408        MoveValue::Signer(v) | MoveValue::Address(v) => json!(IotaAddress::from(*v).to_string()),
409        MoveValue::U8(v) => json!(v),
410        MoveValue::U64(v) => json!(v.to_string()),
411        MoveValue::U128(v) => json!(v.to_string()),
412        MoveValue::U16(v) => json!(v),
413        MoveValue::U32(v) => json!(v),
414        MoveValue::U256(v) => json!(v.to_string()),
415        MoveValue::Struct(move_struct) => match move_struct {
416            MoveStruct { fields, type_ } if is_move_string_type(type_) => {
417                // ascii::string and utf8::string has a single bytes field.
418                let (_, v) = fields.first()?;
419                let string: String = bcs::from_bytes(&v.simple_serialize()?).ok()?;
420                json!(string)
421            }
422            MoveStruct { fields, type_ } if is_move_option_type(type_) => {
423                // option has a single vec field.
424                let (_, v) = fields.first()?;
425                if let MoveValue::Vector(v) = v {
426                    JsonValue::Array(v.iter().filter_map(move_value_to_json).collect::<Vec<_>>())
427                } else {
428                    return None;
429                }
430            }
431            MoveStruct { fields, type_ } if type_ == &ID::type_() => {
432                // option has a single vec field.
433                let (_, v) = fields.first()?;
434                if let MoveValue::Address(address) = v {
435                    json!(IotaAddress::from(*address))
436                } else {
437                    return None;
438                }
439            }
440            // We only care about values here, assuming struct type information is known at the
441            // client side.
442            MoveStruct { fields, .. } => {
443                let fields = fields
444                    .iter()
445                    .map(|(key, value)| (key, move_value_to_json(value)))
446                    .collect::<BTreeMap<_, _>>();
447                json!(fields)
448            }
449        },
450        // Don't return the type assuming type information is known at the client side.
451        MoveValue::Variant(MoveVariant {
452            type_: _,
453            tag: _,
454            variant_name,
455            fields,
456        }) => {
457            let fields = fields
458                .iter()
459                .map(|(key, value)| (key, move_value_to_json(value)))
460                .collect::<BTreeMap<_, _>>();
461            json!({
462                "variant": variant_name.to_string(),
463                "fields": fields,
464            })
465        }
466    })
467}
468
469fn is_move_string_type(tag: &StructTag) -> bool {
470    (tag.address == MOVE_STDLIB_ADDRESS
471        && tag.module.as_ident_str() == STD_UTF8_MODULE_NAME
472        && tag.name.as_ident_str() == STD_UTF8_STRUCT_NAME)
473        || (tag.address == MOVE_STDLIB_ADDRESS
474            && tag.module.as_ident_str() == STD_ASCII_MODULE_NAME
475            && tag.name.as_ident_str() == STD_ASCII_STRUCT_NAME)
476}
477fn is_move_option_type(tag: &StructTag) -> bool {
478    tag.address == MOVE_STDLIB_ADDRESS
479        && tag.module.as_ident_str() == STD_OPTION_MODULE_NAME
480        && tag.name.as_ident_str() == STD_OPTION_STRUCT_NAME
481}
482
483impl FromStr for IotaJsonValue {
484    type Err = anyhow::Error;
485    fn from_str(s: &str) -> Result<Self, anyhow::Error> {
486        fn try_escape_array(s: &str) -> JsonValue {
487            let s = s.trim();
488            if s.starts_with('[') && s.ends_with(']') {
489                if let Some(s) = s.strip_prefix('[').and_then(|s| s.strip_suffix(']')) {
490                    return JsonValue::Array(s.split(',').map(try_escape_array).collect());
491                }
492            }
493            json!(s)
494        }
495        // if serde_json fails, the failure usually cause by missing quote escapes, try
496        // parse array manually.
497        IotaJsonValue::new(serde_json::from_str(s).unwrap_or_else(|_| try_escape_array(s)))
498    }
499}
500
501#[derive(Eq, PartialEq, Debug, Clone, Hash)]
502enum ValidJsonType {
503    Bool,
504    Number,
505    String,
506    Array,
507    // Matches any type
508    Any,
509}
510
511/// Check via BFS
512/// The invariant is that all types at a given level must be the same or be
513/// empty, and all must be valid
514pub fn check_valid_homogeneous(val: &JsonValue) -> Result<(), IotaJsonValueError> {
515    let mut deq: VecDeque<&JsonValue> = VecDeque::new();
516    deq.push_back(val);
517    check_valid_homogeneous_rec(&mut deq)
518}
519
520/// Check via BFS
521/// The invariant is that all types at a given level must be the same or be
522/// empty
523fn check_valid_homogeneous_rec(
524    curr_q: &mut VecDeque<&JsonValue>,
525) -> Result<(), IotaJsonValueError> {
526    if curr_q.is_empty() {
527        // Nothing to do
528        return Ok(());
529    }
530    // Queue for the next level
531    let mut next_q = VecDeque::new();
532    // The types at this level must be the same
533    let mut level_type = ValidJsonType::Any;
534
535    // Process all in this queue/level
536    while let Some(v) = curr_q.pop_front() {
537        let curr = match v {
538            JsonValue::Bool(_) => ValidJsonType::Bool,
539            JsonValue::Number(x) if x.is_u64() => ValidJsonType::Number,
540            JsonValue::String(_) => ValidJsonType::String,
541            JsonValue::Array(w) => {
542                // Add to the next level
543                w.iter().for_each(|t| next_q.push_back(t));
544                ValidJsonType::Array
545            }
546            // Not valid
547            _ => {
548                return Err(IotaJsonValueError::new(
549                    v,
550                    IotaJsonValueErrorKind::ValueTypeNotAllowed,
551                ));
552            }
553        };
554
555        if level_type == ValidJsonType::Any {
556            // Update the level with the first found type
557            level_type = curr;
558        } else if level_type != curr {
559            // Mismatch in the level
560            return Err(IotaJsonValueError::new(
561                v,
562                IotaJsonValueErrorKind::ArrayNotHomogeneous,
563            ));
564        }
565    }
566    // Process the next level
567    check_valid_homogeneous_rec(&mut next_q)
568}
569
570/// Checks if a give SignatureToken represents a primitive type and, if so,
571/// returns MoveTypeLayout for this type (if available). The reason we need to
572/// return both information about whether a SignatureToken represents a
573/// primitive and an Option representing MoveTypeLayout is that there
574/// can be signature tokens that represent primitives but that do not have
575/// corresponding MoveTypeLayout (e.g., SignatureToken::DatatypeInstantiation).
576pub fn primitive_type(
577    view: &CompiledModule,
578    type_args: &[TypeTag],
579    param: &SignatureToken,
580) -> Option<MoveTypeLayout> {
581    Some(match param {
582        SignatureToken::Bool => MoveTypeLayout::Bool,
583        SignatureToken::U8 => MoveTypeLayout::U8,
584        SignatureToken::U16 => MoveTypeLayout::U16,
585        SignatureToken::U32 => MoveTypeLayout::U32,
586        SignatureToken::U64 => MoveTypeLayout::U64,
587        SignatureToken::U128 => MoveTypeLayout::U128,
588        SignatureToken::U256 => MoveTypeLayout::U256,
589        SignatureToken::Address => MoveTypeLayout::Address,
590        SignatureToken::Vector(inner) => {
591            MoveTypeLayout::Vector(Box::new(primitive_type(view, type_args, inner)?))
592        }
593        SignatureToken::Datatype(struct_handle_idx) => {
594            let resolved_struct = resolve_struct(view, *struct_handle_idx);
595            if resolved_struct == RESOLVED_ASCII_STR {
596                MoveTypeLayout::Struct(Box::new(move_ascii_str_layout()))
597            } else if resolved_struct == RESOLVED_UTF8_STR {
598                // both structs structs representing strings have one field - a vector of type
599                // u8
600                MoveTypeLayout::Struct(Box::new(move_utf8_str_layout()))
601            } else if resolved_struct == RESOLVED_IOTA_ID {
602                MoveTypeLayout::Struct(Box::new(id::ID::layout()))
603            } else {
604                return None;
605            }
606        }
607        SignatureToken::DatatypeInstantiation(struct_inst) => {
608            let (idx, targs) = &**struct_inst;
609            let resolved_struct = resolve_struct(view, *idx);
610            // is option of a primitive
611            if resolved_struct == RESOLVED_STD_OPTION && targs.len() == 1 {
612                // there is no MoveLayout for this so the type is not a primitive.
613                MoveTypeLayout::Vector(Box::new(primitive_type(view, type_args, &targs[0])?))
614            } else {
615                return None;
616            }
617        }
618        SignatureToken::TypeParameter(idx) => {
619            layout_of_primitive_typetag(type_args.get(*idx as usize)?)?
620        }
621        SignatureToken::Signer
622        | SignatureToken::Reference(_)
623        | SignatureToken::MutableReference(_) => return None,
624    })
625}
626
627fn layout_of_primitive_typetag(tag: &TypeTag) -> Option<MoveTypeLayout> {
628    use MoveTypeLayout as MTL;
629    if !is_primitive_type_tag(tag) {
630        return None;
631    }
632
633    Some(match tag {
634        TypeTag::Bool => MTL::Bool,
635        TypeTag::U8 => MTL::U8,
636        TypeTag::U16 => MTL::U16,
637        TypeTag::U32 => MTL::U32,
638        TypeTag::U64 => MTL::U64,
639        TypeTag::U128 => MTL::U128,
640        TypeTag::U256 => MTL::U256,
641        TypeTag::Address => MTL::Address,
642        TypeTag::Signer => return None,
643        TypeTag::Vector(tag) => MTL::Vector(Box::new(layout_of_primitive_typetag(tag)?)),
644        TypeTag::Struct(stag) => {
645            let StructTag {
646                address,
647                module,
648                name,
649                type_params: type_args,
650            } = &**stag;
651            let resolved_struct = (address, module.as_ident_str(), name.as_ident_str());
652            // is id or..
653            if resolved_struct == RESOLVED_IOTA_ID {
654                MTL::Struct(Box::new(id::ID::layout()))
655            } else if resolved_struct == RESOLVED_ASCII_STR {
656                MTL::Struct(Box::new(move_ascii_str_layout()))
657            } else if resolved_struct == RESOLVED_UTF8_STR {
658                MTL::Struct(Box::new(move_utf8_str_layout()))
659            } else if resolved_struct == RESOLVED_STD_OPTION // is option of a primitive
660                && type_args.len() == 1
661                && is_primitive_type_tag(&type_args[0])
662            {
663                MTL::Vector(Box::new(
664                    layout_of_primitive_typetag(&type_args[0]).unwrap(),
665                ))
666            } else {
667                return None;
668            }
669        }
670    })
671}
672
673fn resolve_object_arg(idx: usize, arg: &JsonValue) -> Result<ObjectID, anyhow::Error> {
674    // Every elem has to be a string convertible to a ObjectID
675    match arg {
676        JsonValue::String(s) => {
677            let s = s.trim().to_lowercase();
678            if !s.starts_with(HEX_PREFIX) {
679                bail!("ObjectID hex string must start with 0x.",);
680            }
681            Ok(ObjectID::from_hex_literal(&s)?)
682        }
683        _ => bail!(
684            "Unable to parse arg {:?} as ObjectID at pos {}. Expected {:?}-byte hex string \
685                prefixed with 0x.",
686            arg,
687            idx,
688            ObjectID::LENGTH,
689        ),
690    }
691}
692
693fn resolve_object_vec_arg(idx: usize, arg: &IotaJsonValue) -> Result<Vec<ObjectID>, anyhow::Error> {
694    // Every elem has to be a string convertible to a ObjectID
695    match arg.to_json_value() {
696        JsonValue::Array(a) => {
697            let mut object_ids = vec![];
698            for id in a {
699                object_ids.push(resolve_object_arg(idx, &id)?);
700            }
701            Ok(object_ids)
702        }
703        JsonValue::String(s) if s.starts_with('[') && s.ends_with(']') => {
704            // Due to how escaping of square bracket works, we may be dealing with a JSON
705            // string representing a JSON array rather than with the array
706            // itself ("[0x42,0x7]" rather than [0x42,0x7]).
707            let mut object_ids = vec![];
708            for tok in s[1..s.len() - 1].split(',') {
709                let id = JsonValue::String(tok.to_string());
710                object_ids.push(resolve_object_arg(idx, &id)?);
711            }
712            Ok(object_ids)
713        }
714        _ => bail!(
715            "Unable to parse arg {:?} as vector of ObjectIDs at pos {}. \
716             Expected a vector of {:?}-byte hex strings prefixed with 0x.\n\
717             Consider escaping your curly braces with a backslash (as in \\[0x42,0x7\\]) \
718             or enclosing the whole vector in single quotes (as in '[0x42,0x7]')",
719            arg.to_json_value(),
720            idx,
721            ObjectID::LENGTH,
722        ),
723    }
724}
725
726fn resolve_call_arg(
727    view: &CompiledModule,
728    type_args: &[TypeTag],
729    idx: usize,
730    arg: &IotaJsonValue,
731    param: &SignatureToken,
732) -> Result<ResolvedCallArg, anyhow::Error> {
733    if let Some(layout) = primitive_type(view, type_args, param) {
734        return Ok(ResolvedCallArg::Pure(arg.to_bcs_bytes(&layout).map_err(
735            |e| {
736                anyhow!(
737                    "Could not serialize argument of type {:?} at {} into {}. Got error: {:?}",
738                    param,
739                    idx,
740                    layout,
741                    e
742                )
743            },
744        )?));
745    }
746
747    // in terms of non-primitives we only currently support objects and "flat"
748    // (depth == 1) vectors of objects (but not, for example, vectors of
749    // references)
750    match param {
751        SignatureToken::Datatype(_)
752        | SignatureToken::DatatypeInstantiation(_)
753        | SignatureToken::TypeParameter(_)
754        | SignatureToken::Reference(_)
755        | SignatureToken::MutableReference(_) => Ok(ResolvedCallArg::Object(resolve_object_arg(
756            idx,
757            &arg.to_json_value(),
758        )?)),
759        SignatureToken::Vector(inner) => match &**inner {
760            SignatureToken::Datatype(_) | SignatureToken::DatatypeInstantiation(_) => {
761                Ok(ResolvedCallArg::ObjVec(resolve_object_vec_arg(idx, arg)?))
762            }
763            _ => {
764                bail!(
765                    "Unexpected non-primitive vector arg {:?} at {} with value {:?}",
766                    param,
767                    idx,
768                    arg
769                );
770            }
771        },
772        _ => bail!(
773            "Unexpected non-primitive arg {:?} at {} with value {:?}",
774            param,
775            idx,
776            arg
777        ),
778    }
779}
780
781pub fn is_receiving_argument(view: &CompiledModule, arg_type: &SignatureToken) -> bool {
782    use SignatureToken as ST;
783
784    // Progress down into references to determine if the underlying type is a
785    // receiving type or not.
786    let mut token = arg_type;
787    while let ST::Reference(inner) | ST::MutableReference(inner) = token {
788        token = inner;
789    }
790
791    matches!(
792        token,
793        ST::DatatypeInstantiation(inst) if resolve_struct(view, inst.0) == RESOLVED_RECEIVING_STRUCT && inst.1.len() == 1
794    )
795}
796
797fn resolve_call_args(
798    view: &CompiledModule,
799    type_args: &[TypeTag],
800    json_args: &[IotaJsonValue],
801    parameter_types: &[SignatureToken],
802) -> Result<Vec<ResolvedCallArg>, anyhow::Error> {
803    json_args
804        .iter()
805        .zip(parameter_types)
806        .enumerate()
807        .map(|(idx, (arg, param))| resolve_call_arg(view, type_args, idx, arg, param))
808        .collect()
809}
810
811/// Resolve the JSON args of a function into the expected formats to make them
812/// usable by Move call This is because we have special types which we need to
813/// specify in other formats
814pub fn resolve_move_function_args(
815    package: &MovePackage,
816    module_ident: Identifier,
817    function: Identifier,
818    type_args: &[TypeTag],
819    combined_args_json: Vec<IotaJsonValue>,
820) -> Result<Vec<(ResolvedCallArg, SignatureToken)>, anyhow::Error> {
821    // Extract the expected function signature
822    let module = package.deserialize_module(&module_ident, &BinaryConfig::standard())?;
823    let function_str = function.as_ident_str();
824    let fdef = module
825        .function_defs
826        .iter()
827        .find(|fdef| {
828            module.identifier_at(module.function_handle_at(fdef.function).name) == function_str
829        })
830        .ok_or_else(|| {
831            anyhow!(
832                "Could not resolve function {} in module {}",
833                function,
834                module_ident
835            )
836        })?;
837    let function_signature = module.function_handle_at(fdef.function);
838    let parameters = &module.signature_at(function_signature.parameters).0;
839
840    // Lengths have to match, less one, due to TxContext
841    let expected_len = match parameters.last() {
842        Some(param) if TxContext::kind(&module, param) != TxContextKind::None => {
843            parameters.len() - 1
844        }
845        _ => parameters.len(),
846    };
847    if combined_args_json.len() != expected_len {
848        bail!(
849            "Expected {} args, found {}",
850            expected_len,
851            combined_args_json.len()
852        );
853    }
854    // Check that the args are valid and convert to the correct format
855    let call_args = resolve_call_args(&module, type_args, &combined_args_json, parameters)?;
856    let tupled_call_args = call_args
857        .into_iter()
858        .zip(parameters.iter())
859        .map(|(arg, expected_type)| (arg, expected_type.clone()))
860        .collect::<Vec<_>>();
861    Ok(tupled_call_args)
862}
863
864fn convert_string_to_u256(s: &str) -> Result<U256, anyhow::Error> {
865    // Try as normal number
866    if let Ok(v) = s.parse::<U256>() {
867        return Ok(v);
868    }
869
870    // Check prefix
871    // For now only Hex supported
872    // TODO: add support for bin and octal?
873
874    let s = s.trim().to_lowercase();
875    if !s.starts_with(HEX_PREFIX) {
876        bail!("Unable to convert {s} to unsigned int.",);
877    }
878    U256::from_str_radix(s.trim_start_matches(HEX_PREFIX), 16).map_err(|e| e.into())
879}
880
881#[macro_export]
882macro_rules! call_args {
883        ($($value:expr),*) => {
884        Ok::<_, anyhow::Error>(vec![$(iota_json::call_arg!($value)?,)*])
885    };
886    }
887
888#[macro_export]
889macro_rules! call_arg {
890    ($value:expr) => {{
891        use iota_json::IotaJsonValue;
892        trait IotaJsonArg {
893            fn to_iota_json(&self) -> anyhow::Result<IotaJsonValue>;
894        }
895        // TODO: anyway to condense this?
896        impl IotaJsonArg for &str {
897            fn to_iota_json(&self) -> anyhow::Result<IotaJsonValue> {
898                IotaJsonValue::from_str(self)
899            }
900        }
901        impl IotaJsonArg for String {
902            fn to_iota_json(&self) -> anyhow::Result<IotaJsonValue> {
903                IotaJsonValue::from_str(&self)
904            }
905        }
906        impl IotaJsonArg for iota_types::base_types::ObjectID {
907            fn to_iota_json(&self) -> anyhow::Result<IotaJsonValue> {
908                IotaJsonValue::from_str(&self.to_string())
909            }
910        }
911        impl IotaJsonArg for iota_types::base_types::IotaAddress {
912            fn to_iota_json(&self) -> anyhow::Result<IotaJsonValue> {
913                IotaJsonValue::from_str(&self.to_string())
914            }
915        }
916        impl IotaJsonArg for u64 {
917            fn to_iota_json(&self) -> anyhow::Result<IotaJsonValue> {
918                IotaJsonValue::from_bcs_bytes(
919                    Some(&iota_json::MoveTypeLayout::U64),
920                    &bcs::to_bytes(self)?,
921                )
922            }
923        }
924        impl IotaJsonArg for Vec<u8> {
925            fn to_iota_json(&self) -> anyhow::Result<IotaJsonValue> {
926                IotaJsonValue::from_bcs_bytes(None, &self)
927            }
928        }
929        impl IotaJsonArg for &[u8] {
930            fn to_iota_json(&self) -> anyhow::Result<IotaJsonValue> {
931                IotaJsonValue::from_bcs_bytes(None, self)
932            }
933        }
934        $value.to_iota_json()
935    }};
936}
937
938#[macro_export]
939macro_rules! type_args {
940    ($($value:expr), *) => {{
941        use iota_json_rpc_types::IotaTypeTag;
942        use iota_types::TypeTag;
943        trait IotaJsonTypeArg {
944            fn to_iota_json(&self) -> anyhow::Result<IotaTypeTag>;
945        }
946        impl <T: core::fmt::Display> IotaJsonTypeArg for T {
947            fn to_iota_json(&self) -> anyhow::Result<IotaTypeTag> {
948                Ok(iota_types::parse_iota_type_tag(&self.to_string())?.into())
949            }
950        }
951        Ok::<_, anyhow::Error>(vec![$($value.to_iota_json()?,)*])
952    }};
953    }