Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Move] Added support for object vectors to Sui JSON #4526

Merged
merged 3 commits into from
Sep 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 23 additions & 8 deletions crates/sui-core/src/gateway_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1151,6 +1151,21 @@ where
}))
}

async fn get_object_arg(
&self,
id: ObjectID,
objects: &mut BTreeMap<ObjectID, Object>,
) -> Result<ObjectArg, anyhow::Error> {
let obj = self.get_object_internal(&id).await?;
let arg = if obj.is_shared() {
ObjectArg::SharedObject(id)
} else {
ObjectArg::ImmOrOwnedObject(obj.compute_object_reference())
};
objects.insert(id, obj);
Ok(arg)
}

async fn create_move_call_transaction_kind(
&self,
params: MoveCallParams,
Expand Down Expand Up @@ -1181,16 +1196,16 @@ where
for json_arg in json_args {
args.push(match json_arg {
SuiJsonCallArg::Object(id) => {
let obj = self.get_object_internal(&id).await?;
let arg = if obj.is_shared() {
CallArg::Object(ObjectArg::SharedObject(id))
} else {
CallArg::Object(ObjectArg::ImmOrOwnedObject(obj.compute_object_reference()))
};
objects.insert(id, obj);
arg
CallArg::Object(self.get_object_arg(id, &mut objects).await?)
}
SuiJsonCallArg::Pure(bytes) => CallArg::Pure(bytes),
SuiJsonCallArg::ObjVec(v) => {
let mut object_ids = vec![];
for id in v {
object_ids.push(self.get_object_arg(id, &mut objects).await?);
}
CallArg::ObjVec(object_ids)
}
})
}

Expand Down
47 changes: 40 additions & 7 deletions crates/sui-json/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ pub enum SuiJsonCallArg {
Object(ObjectID),
// pure value, bcs encoded
Pure(Vec<u8>),
// a vector of objects
ObjVec(Vec<ObjectID>),
}

#[derive(Eq, PartialEq, Clone, Deserialize, Serialize, JsonSchema)]
Expand Down Expand Up @@ -286,9 +288,9 @@ fn make_prim_move_type_layout(param: &SignatureToken) -> Result<MoveTypeLayout,
})
}

fn resolve_object_arg(idx: usize, arg: &SuiJsonValue) -> Result<ObjectID, anyhow::Error> {
fn resolve_object_arg(idx: usize, arg: &JsonValue) -> Result<ObjectID, anyhow::Error> {
// Every elem has to be a string convertible to a ObjectID
match arg.to_json_value() {
match arg {
JsonValue::String(s) => {
let s = s.trim().to_lowercase();
if !s.starts_with(HEX_PREFIX) {
Expand All @@ -297,11 +299,31 @@ fn resolve_object_arg(idx: usize, arg: &SuiJsonValue) -> Result<ObjectID, anyhow
Ok(ObjectID::from_hex_literal(&s)?)
}
_ => Err(anyhow!(
"Unable to parse arg {:?} as ObjectID at pos {}. Expected {:?} byte hex string \
"Unable to parse arg {:?} as ObjectID at pos {}. Expected {:?}-byte hex string \
prefixed with 0x.",
ObjectID::LENGTH,
arg,
idx,
ObjectID::LENGTH,
)),
}
}

fn resolve_object_vec_arg(idx: usize, arg: &SuiJsonValue) -> Result<Vec<ObjectID>, anyhow::Error> {
// Every elem has to be a string convertible to a ObjectID
match arg.to_json_value() {
JsonValue::Array(a) => {
let mut object_ids = vec![];
for id in a {
object_ids.push(resolve_object_arg(idx, &id)?);
}
Ok(object_ids)
}
_ => Err(anyhow!(
"Unable to parse arg {:?} as vector of ObjectIDs at pos {}. \
Expected a vector of {:?}-byte hex strings prefixed with 0x.",
arg.to_json_value(),
idx,
ObjectID::LENGTH,
)),
}
}
Expand All @@ -316,15 +338,26 @@ fn resolve_call_arg(
| SignatureToken::U8
| SignatureToken::U64
| SignatureToken::U128
| SignatureToken::Address
| SignatureToken::Vector(_) => SuiJsonCallArg::Pure(resolve_primtive_arg(arg, param)?),
| SignatureToken::Address => SuiJsonCallArg::Pure(resolve_primtive_arg(arg, param)?),

SignatureToken::Struct(_)
| SignatureToken::StructInstantiation(_, _)
| SignatureToken::TypeParameter(_)
| SignatureToken::Reference(_)
| SignatureToken::MutableReference(_) => {
SuiJsonCallArg::Object(resolve_object_arg(idx, arg)?)
SuiJsonCallArg::Object(resolve_object_arg(idx, &arg.to_json_value())?)
}

SignatureToken::Vector(inner) => {
match **inner {
// in terms of non-primitive vectors we only currently support vectors of objects
// (but not, for example, vectors of references), so vector content are either
// objects of we assume that they are primitive (and throw an error if not)
SignatureToken::Struct(_) => {
SuiJsonCallArg::ObjVec(resolve_object_vec_arg(idx, arg)?)
}
_ => SuiJsonCallArg::Pure(resolve_primtive_arg(arg, param)?),
}
}

SignatureToken::Signer => unreachable!(),
Expand Down
39 changes: 39 additions & 0 deletions crates/sui-json/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,45 @@ fn test_basic_args_linter_top_level() {
args[1],
SuiJsonCallArg::Pure(bcs::to_bytes(&AccountAddress::from(address)).unwrap())
);

// Test with object vector args
let path = Path::new(env!("CARGO_MANIFEST_DIR"))
.join("../sui-core/src/unit_tests/data/entry_point_vector");
let compiled_modules =
sui_framework::build_and_verify_package(&path, move_package::BuildConfig::default())
.unwrap();
let example_package = Object::new_package(compiled_modules, TransactionDigest::genesis());
let example_package = example_package.data.try_as_package().unwrap();

let module = Identifier::new("entry_point_vector").unwrap();
let function = Identifier::new("two_obj_vec_destroy").unwrap();

/*
Function signature:
public entry fun two_obj_vec_destroy(v: vector<Obj>, _: &mut TxContext)
*/
let object_id_raw1 = ObjectID::random();
let object_id_raw2 = ObjectID::random();
let object_id1 = json!(format!("0x{:02x}", object_id_raw1));
let object_id2 = json!(format!("0x{:02x}", object_id_raw2));

let args = vec![SuiJsonValue::new(Value::Array(vec![object_id1, object_id2])).unwrap()];

let args = resolve_move_function_args(example_package, module, function, args).unwrap();

assert!(matches!(args[0], SuiJsonCallArg::ObjVec { .. }));

if let SuiJsonCallArg::ObjVec(vec) = &args[0] {
assert!(vec.len() == 2);
assert_eq!(
vec[0],
ObjectID::from_hex_literal(&format!("0x{:02x}", object_id_raw1)).unwrap()
);
assert_eq!(
vec[1],
ObjectID::from_hex_literal(&format!("0x{:02x}", object_id_raw2)).unwrap()
);
}
}

#[test]
Expand Down
37 changes: 26 additions & 11 deletions crates/sui-sdk/src/transaction_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,23 @@ impl TransactionBuilder {
}))
}

async fn get_object_arg(
&self,
id: ObjectID,
objects: &mut BTreeMap<ObjectID, Object>,
) -> Result<ObjectArg, anyhow::Error> {
let response = self.0.get_object(id).await?;
let obj: Object = response.into_object()?.try_into()?;
let obj_ref = obj.compute_object_reference();
let owner = obj.owner;
objects.insert(id, obj);
Ok(if owner.is_shared() {
ObjectArg::SharedObject(id)
} else {
ObjectArg::ImmOrOwnedObject(obj_ref)
})
}

async fn resolve_and_checks_json_args(
&self,
package_id: ObjectID,
Expand All @@ -193,19 +210,17 @@ impl TransactionBuilder {
let mut objects = BTreeMap::new();
for arg in json_args {
args.push(match arg {
SuiJsonCallArg::Object(o) => {
let response = self.0.get_object(o).await?;
let obj: Object = response.into_object()?.try_into()?;
let obj_ref = obj.compute_object_reference();
let owner = obj.owner;
objects.insert(o, obj);
if owner.is_shared() {
CallArg::Object(ObjectArg::SharedObject(o))
} else {
CallArg::Object(ObjectArg::ImmOrOwnedObject(obj_ref))
}
SuiJsonCallArg::Object(id) => {
CallArg::Object(self.get_object_arg(id, &mut objects).await?)
}
SuiJsonCallArg::Pure(p) => CallArg::Pure(p),
SuiJsonCallArg::ObjVec(v) => {
let mut object_ids = vec![];
for id in v {
object_ids.push(self.get_object_arg(id, &mut objects).await?);
}
CallArg::ObjVec(object_ids)
}
})
}
let compiled_module = package.deserialize_module(module)?;
Expand Down
10 changes: 10 additions & 0 deletions doc/src/build/cli-client.md
Original file line number Diff line number Diff line change
Expand Up @@ -959,6 +959,16 @@ Note the third argument to the `transfer` function representing
is a required argument for all functions callable from Sui and is
auto-injected by the platform at the point of a function call.

> **Important:** If you use a shell that interprets square brackets ([ ]) as special characters (such as the `zsh` shell), you must enclose the brackets in single quotes. For example, instead of `[7,42]` you must use `'[7,42]'`.
>
> Additionally, when you specify a vector of object IDs, you must enclose each ID in double quotes. For example,
> `'["0x471c8e241d0473c34753461529b70f9c4ed3151b","0x53b50e3020a01e1fd6acf832a871feee240183f0"]'`

To gain a deeper view into the object, include the
> `--json` flag in the `sui client` command to see the raw JSON representation
> of the object.
Comment on lines +967 to +969
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
To gain a deeper view into the object, include the
> `--json` flag in the `sui client` command to see the raw JSON representation
> of the object.
To view the raw JSON representation of the object, include the
> `--json` flag in the `sui client` command. For example,
`sui client --json`

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one is unrelated to the current PR and does not sound quite right. For the sake of expediency I suggest changing it in a separate PR if necessary



The output of the call command is a bit verbose, but the important
information that should be printed at the end indicates objects
changes as a result of the function call:
Expand Down
2 changes: 1 addition & 1 deletion doc/src/build/sui-json.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,6 @@ Additionally, Move supports U128 but JSON doesn't. As a result we allow encoding
| Address | 20 byte hex string prefixed with `0x` | `"0x2B1A39A1514E1D8A7CE45919CFEB4FEE70B4E011"` | `0x2B1A39`: string too short<br>`2B1A39A1514E1D8A7CE45919CFEB4FEE70B4E011`: missing `0x` prefix<br>`0xG2B1A39A1514E1D8A7CE45919CFEB4FEE70B4E01`: invalid hex char `G` |
| ObjectID | 16 byte hex string prefixed with `0x` | `"0x2B1A39A1514E1D8A7CE45919CFEB4FEE"` | Similar to above |
| Identifier | Typically used for module and function names. Encoded as one of the following:<ol><li>A String whose first character is a letter and the remaining characters are letters, digits or underscore.</li><li>A String whose first character is an underscore, and there is at least one further letter, digit or underscore</li></ol> | `"function"`,<br>`"_function"`,<br>`"some_name"`,<br>`"\___\_some_name"`,<br>`"Another"` | `"_"`: missing trailing underscore, digit or letter,<br>`"8name"`: cannot start with digit,<br>`".function"`: cannot start with period,<br>`" "`: cannot be empty space,<br>`"func name"`: cannot have spaces |
| Vector&lt;Move Type> | Homogeneous vector of aforementioned types including nested vectors | `[1,2,3,4]`: simple U8 vector<br>`[[3,600],[],[0,7,4]]`: nested U64 vector | `[1,2,3,false]`: not homogeneous JSON<br>`[1,2,null,4]`: invalid elements<br>`[1,2,"7"]`: although we allow encoding numbers as strings meaning this array can evaluate to `[1,2,7]`, the array is still ambiguous so it fails the homogeneity check. |
| Vector&lt;Move Type> | Homogeneous vector of aforementioned types including nested vectors of primitive types (only "flat" vectors of ObjectIDs are allowed) | `[1,2,3,4]`: simple U8 vector<br>`[[3,600],[],[0,7,4]]`: nested U64 vector `["0x2B1A39A1514E1D8A7CE45919CFEB4FEE", "0x2B1A39A1514E1D8A7CE45919CFEB4FEF"]`: ObjectID vector | `[1,2,3,false]`: not homogeneous JSON<br>`[1,2,null,4]`: invalid elements<br>`[1,2,"7"]`: although we allow encoding numbers as strings meaning this array can evaluate to `[1,2,7]`, the array is still ambiguous so it fails the homogeneity check. |
| Vector&lt;U8> | <em>For convenience, we allow:</em><br>U8 vectors represented as UTF-8 (and ASCII) strings. | `"√®ˆbo72 √∂†∆˚–œ∑π2ie"`: UTF-8<br>`"abcdE738-2 _=?"`: ASCII ||