@@ -199,9 +278,9 @@ Which type we coerce depends on the expected Move type. For example, if the Move
U8 vectors represented as UTF-8 (and ASCII) strings.
- √®ˆbo72 √∂†∆˚–œ∑π2ie : UTF-8
+ | "√®ˆbo72 √∂†∆˚–œ∑π2ie" : UTF-8
- abcdE738-2 _=? : ASCII
+ "abcdE738-2 _=?" : ASCII
|
diff --git a/sui/src/rest_server.rs b/sui/src/rest_server.rs
index 8ece53532c27a..2e6d3a84d7f01 100644
--- a/sui/src/rest_server.rs
+++ b/sui/src/rest_server.rs
@@ -42,7 +42,7 @@ use sui_types::move_package::resolve_and_type_check;
use sui_types::object::Object as SuiObject;
use sui_types::object::ObjectRead;
-const REST_SERVER_PORT: u16 = 5000;
+const REST_SERVER_PORT: u16 = 5001;
const REST_SERVER_ADDR_IPV4: Ipv4Addr = Ipv4Addr::new(127, 0, 0, 1);
#[path = "unit_tests/rest_server_tests.rs"]
diff --git a/sui/src/sui_json.rs b/sui/src/sui_json.rs
index 7bdb90fc092ef..8510cbc18fb22 100644
--- a/sui/src/sui_json.rs
+++ b/sui/src/sui_json.rs
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use anyhow::anyhow;
-use move_core_types::identifier::Identifier;
+use move_core_types::{account_address::AccountAddress, identifier::Identifier};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::collections::VecDeque;
@@ -15,7 +15,6 @@ use sui_types::{
// Alias the type names for clarity
use move_binary_format::normalized::{Function as MoveFunction, Type as NormalizedMoveType};
use serde_json::Value as JsonValue;
-use serde_value::Value as SerdeValue;
const HEX_PREFIX: &str = "0x";
@@ -23,6 +22,17 @@ const HEX_PREFIX: &str = "0x";
#[path = "unit_tests/sui_json.rs"]
mod base_types_tests;
+#[derive(Eq, PartialEq, Debug, Clone, Deserialize, Serialize)]
+pub enum IntermediateValue {
+ Bool(bool),
+ U8(u8),
+ U64(u64),
+ U128(u128),
+ Address(SuiAddress),
+ ObjectID(ObjectID),
+ Vector(Vec),
+}
+
#[derive(Eq, PartialEq, Debug, Clone, Deserialize, Serialize, JsonSchema)]
pub struct SuiJsonValue(JsonValue);
impl SuiJsonValue {
@@ -49,35 +59,33 @@ impl SuiJsonValue {
}
pub fn to_bcs_bytes(&self, typ: &NormalizedMoveType) -> Result, anyhow::Error> {
- let serde_val = Self::to_serde_value(&self.0, typ)?;
+ let intermediate_val = Self::to_intermediate_value(&self.0, typ)?;
fn inner_serialize(
- ser_val: SerdeValue,
+ inter_val: IntermediateValue,
ty: &NormalizedMoveType,
) -> Result, anyhow::Error> {
- let ret = match ty {
- NormalizedMoveType::Address => bcs::to_bytes(&ser_val)?[1..].to_vec(),
- NormalizedMoveType::Vector(t) => {
+ let ser = match (inter_val.clone(), ty) {
+ (IntermediateValue::Bool(b), NormalizedMoveType::Bool) => bcs::to_bytes(&b)?,
+ (IntermediateValue::U8(n), NormalizedMoveType::U8) => bcs::to_bytes(&n)?,
+ (IntermediateValue::U64(n), NormalizedMoveType::U64) => bcs::to_bytes(&n)?,
+ (IntermediateValue::U128(n), NormalizedMoveType::U128) => bcs::to_bytes(&n)?,
+
+ (IntermediateValue::Address(a), NormalizedMoveType::Address) => {
+ bcs::to_bytes(&AccountAddress::from(a))?
+ }
+
+ // Not currently used
+ // (IntermediateValue::ObjectID(a), NormalizedMoveType::Address) => {
+ // bcs::to_bytes(&AccountAddress::from(a))?
+ // }
+ (IntermediateValue::Vector(v), NormalizedMoveType::Vector(move_type)) => {
let mut inner_ser = vec![];
- // This must be an array. Checked in previous step
-
- let arr_len = match ser_val {
- SerdeValue::Seq(s) => {
- let l = s.len();
- for i in s {
- // Serialize each
- inner_ser.append(&mut inner_serialize(i, t)?);
- }
- l
- }
- SerdeValue::Bytes(b) => {
- let l = b.len();
-
- inner_ser.extend(b);
- l
- }
- _ => return Err(anyhow!("Unable to serialize {:?} as vector", ser_val)),
- };
+ let arr_len = v.len();
+ for i in v {
+ // Serialize each
+ inner_ser.append(&mut inner_serialize(i, move_type)?);
+ }
// The data is already serialized, so ideally we just append
// First serialize the types like they u8s
// We use this to create the ULEB128 length prefix
@@ -87,38 +95,55 @@ impl SuiJsonValue {
ser_container.truncate(ser_container.len() - arr_len);
// Append the actual data data
ser_container.append(&mut inner_ser);
+
ser_container
}
-
- _ => bcs::to_bytes(&ser_val)?,
+ _ => {
+ return Err(anyhow!(
+ "Unable to serialize {:?}. Expected {}",
+ inter_val,
+ ty
+ ))
+ }
};
- Ok(ret)
+ Ok(ser)
}
-
- inner_serialize(serde_val, typ)
+ inner_serialize(intermediate_val, typ)
}
pub fn to_json_value(&self) -> JsonValue {
self.0.clone()
}
- fn to_serde_value(
+ fn to_intermediate_value(
val: &JsonValue,
typ: &NormalizedMoveType,
- ) -> Result {
+ ) -> Result {
let new_serde_value = match (val, typ.clone()) {
// Bool to Bool is simple
- (JsonValue::Bool(b), NormalizedMoveType::Bool) => SerdeValue::Bool(*b),
+ (JsonValue::Bool(b), NormalizedMoveType::Bool) => IntermediateValue::Bool(*b),
- // In constructor, we have already checked that the number is unsigned int of at most U64
+ // In constructor, we have already checked that the JSON number is unsigned int of at most U64
// Hence it is okay to unwrap() numbers
(JsonValue::Number(n), NormalizedMoveType::U8) => {
- SerdeValue::U8(u8::try_from(n.as_u64().unwrap())?)
+ IntermediateValue::U8(u8::try_from(n.as_u64().unwrap())?)
+ }
+ (JsonValue::Number(n), NormalizedMoveType::U64) => {
+ IntermediateValue::U64(n.as_u64().unwrap())
}
- (JsonValue::Number(n), NormalizedMoveType::U64) => SerdeValue::U64(n.as_u64().unwrap()),
- // U128 Not allowed for now
- (_, NormalizedMoveType::U128) => unimplemented!("U128 not supported yet."),
+ // u8, u64, u128 can be encoded as String
+ (JsonValue::String(s), NormalizedMoveType::U8) => {
+ IntermediateValue::U8(u8::try_from(convert_string_to_u128(s.as_str())?)?)
+ }
+ (JsonValue::String(s), NormalizedMoveType::U64) => {
+ IntermediateValue::U64(u64::try_from(convert_string_to_u128(s.as_str())?)?)
+ }
+ (JsonValue::String(s), NormalizedMoveType::U128) => {
+ IntermediateValue::U128(convert_string_to_u128(s.as_str())?)
+ }
+
+ // U256 Not allowed for now
// We can encode U8 Vector as string in 2 ways
// 1. If it starts with 0x, we treat it as hex strings, where each pair is a byte
@@ -136,16 +161,16 @@ impl SuiJsonValue {
// Else raw bytes
s.as_bytes().to_vec()
};
- SerdeValue::Bytes(vec)
+ IntermediateValue::Vector(vec.iter().map(|q| IntermediateValue::U8(*q)).collect())
}
// We have already checked that the array is homogeneous in the constructor
(JsonValue::Array(a), NormalizedMoveType::Vector(t)) => {
- // Recursively build a SerdeValue array
- SerdeValue::Seq(
+ // Recursively build an IntermediateValue array
+ IntermediateValue::Vector(
a.iter()
- .map(|i| Self::to_serde_value(i, &t))
- .collect::, _>>()?,
+ .map(|i| Self::to_intermediate_value(i, &t))
+ .collect::, _>>()?,
)
}
@@ -155,7 +180,7 @@ impl SuiJsonValue {
return Err(anyhow!("Address hex string must start with 0x.",));
}
let r: SuiAddress = decode_bytes_hex(s.trim_start_matches(HEX_PREFIX))?;
- SerdeValue::Bytes(r.to_vec())
+ IntermediateValue::Address(r)
}
_ => return Err(anyhow!("Unexpected arg {} for expected type {}", val, typ)),
};
@@ -251,7 +276,9 @@ fn check_and_serialize_pure_args(
// Check that the args are what we expect or can be converted
// Then return the serialized bcs value
match curr.to_bcs_bytes(expected_pure_arg_type) {
- Ok(a) => pure_args_serialized.push(a),
+ Ok(a) => {
+ pure_args_serialized.push(a.clone());
+ }
Err(e) => return Err(anyhow!("Unable to parse arg at pos: {}, err: {:?}", idx, e)),
}
}
@@ -345,3 +372,20 @@ pub fn resolve_move_function_args(
Ok((obj_args, pure_args_serialized))
}
+
+fn convert_string_to_u128(s: &str) -> Result {
+ // Try as normal number
+ if let Ok(v) = s.parse::() {
+ return Ok(v);
+ }
+
+ // Check prefix
+ // For now only Hex supported
+ // TODO: add support for bin and octal?
+
+ let s = s.trim().to_lowercase();
+ if !s.starts_with(HEX_PREFIX) {
+ return Err(anyhow!("Unable to convert {s} to unsigned int.",));
+ }
+ u128::from_str_radix(s.trim_start_matches(HEX_PREFIX), 16).map_err(|e| e.into())
+}
diff --git a/sui/src/unit_tests/sui_json.rs b/sui/src/unit_tests/sui_json.rs
index 5d018146a6254..ac5fadb35245f 100644
--- a/sui/src/unit_tests/sui_json.rs
+++ b/sui/src/unit_tests/sui_json.rs
@@ -19,6 +19,9 @@ fn test_json_is_homogenous() {
let checks = vec![
(json!([1, 2, 3, true, 5, 6, 7]), false),
(json!([1, 2, 3, 4, 5, 6, 7]), true),
+ // Although we can encode numbers as strings, we do not allow mixing primitive
+ // numbers and string encoded numbers
+ (json!([1, 2, "4", 4, 5, 6, 7]), false),
(json!([1, 2, 3, 4, "", 6, 7]), false),
(json!([]), true),
(json!([[], 2, 3, 5, 6, 7]), false),
@@ -50,7 +53,7 @@ fn test_json_is_valid_sui_json() {
(json!([1, 2, 3, true, 5, 6, 7]), false),
// Homogenous
(json!([1, 2, 3, 4, 5, 6, 7]), true),
- // String not allowed
+ // String allowed
(json!("a string"), true),
// Float not allowed
(json!(1.3), false),
@@ -85,6 +88,7 @@ fn test_basic_args_linter_pure_args() {
let good_utf8_str = "enbeuf√12∫∆∂3456789hdπ˚ffwfof libgude ˚ø˙߃çß +_))@+";
let good_hex_val = "0x1234ABCD";
let bad_hex_val = "0x1234AB CD";
+ let u128_val = u64::MAX as u128 + 0xff;
let checks = vec![
// Expected Bool match
@@ -105,6 +109,65 @@ fn test_basic_args_linter_pure_args() {
Type::U8,
Some(bcs::to_bytes(&9u8).unwrap()),
),
+ // U8 value encoded as str
+ (
+ Value::from("89"),
+ Type::U8,
+ Some(bcs::to_bytes(&89u8).unwrap()),
+ ),
+ // U8 value encoded as str promoted to U64
+ (
+ Value::from("89"),
+ Type::U64,
+ Some(bcs::to_bytes(&89u64).unwrap()),
+ ),
+ // U64 value encoded as str
+ (
+ Value::from("890"),
+ Type::U64,
+ Some(bcs::to_bytes(&890u64).unwrap()),
+ ),
+ // U128 value encoded as str
+ (
+ Value::from(format!("{}", u128_val)),
+ Type::U128,
+ Some(bcs::to_bytes(&u128_val).unwrap()),
+ ),
+ // U8 value encoded as hex str
+ (
+ Value::from("0x12"),
+ Type::U8,
+ Some(bcs::to_bytes(&0x12u8).unwrap()),
+ ),
+ // U8 value encoded as hex str promoted to U64
+ (
+ Value::from("0x12"),
+ Type::U64,
+ Some(bcs::to_bytes(&0x12u64).unwrap()),
+ ),
+ // U64 value encoded as hex str
+ (
+ Value::from("0x890"),
+ Type::U64,
+ Some(bcs::to_bytes(&0x890u64).unwrap()),
+ ),
+ // U128 value encoded as hex str
+ (
+ Value::from(format!("0x{:02x}", u128_val)),
+ Type::U128,
+ Some(bcs::to_bytes(&u128_val).unwrap()),
+ ),
+ // Space not allowed
+ (Value::from(" 9"), Type::U8, None),
+ // Hex must start with 0x
+ (Value::from("AB"), Type::U8, None),
+ // Too large
+ (Value::from("123456789"), Type::U8, None),
+ // Too large
+ (Value::from("123456789123456789123456789123456789"), Type::U64, None),
+ // Too large
+ (Value::from("123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789"), Type::U128, None),
+
// U64 value greater than 255 cannot be used as U8
(Value::from(900u64), Type::U8, None),
// floats cannot be used as U8
|