Skip to content

Commit

Permalink
[api] decode Move ASCII::String type as JSON string
Browse files Browse the repository at this point in the history
  • Loading branch information
Xiao Li authored and bors-libra committed Dec 4, 2021
1 parent 83873cb commit c14c6fa
Show file tree
Hide file tree
Showing 5 changed files with 178 additions and 7 deletions.
6 changes: 5 additions & 1 deletion api/doc/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1406,7 +1406,7 @@ components:
* `vector<u64>{255, 255}` => `["255", "255"]`
* `vector<u8>{255, 255}` => `0xffff`
Move `struct` type value is serialized into `object` that looks like this:
Move `struct` type value is serialized into `object` that looks like this (except some Move stdlib types, see the following section):
```json
{
Expand All @@ -1419,6 +1419,10 @@ components:
For example:
`{ "created": "0xa550c18", "role_id": "0" }`
**Special serialization for Move stdlib types:**
* [0x1::ASCII::String](https://github.com/diem/diem/blob/main/language/move-stdlib/docs/ASCII.md) is serialized into `string`. For example, struct value `0x1::ASCII::String{bytes: b"hello world"}` is serialized as `"hello world"` in JSON.
example: "3344000000"
Event:
title: Event
Expand Down
1 change: 1 addition & 0 deletions api/src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ mod accounts_test;
mod events_test;
mod index_test;
mod invalid_post_request_test;
mod string_resource_test;
mod test_context;
mod transactions_test;

Expand Down
64 changes: 64 additions & 0 deletions api/src/tests/string_resource_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::tests::new_test_context;

use diem_api_types::Address;
use diem_crypto::ed25519::Ed25519PrivateKey;
use diem_sdk::types::LocalAccount;
use serde_json::json;

use std::convert::TryInto;

#[tokio::test]
async fn test_renders_move_acsii_string_into_utf8_string() {
let context = new_test_context();
let mut account = init_test_account();
let txn = context.create_parent_vasp(&account);
context.commit_block(&vec![txn]).await;

// module 0x87342d91af60c3a883a2812c9294c2f8::Message {
// use Std::ASCII;
// struct MessageHolder has key {
// message: ASCII::String,
// }
// public(script) fun set_message(account: signer, msg: vector<u8>) {
// let message = ASCII::string(msg);
// move_to(&account, MessageHolder {
// message,
// });
// }
// }
let module_code = "0xa11ceb0b0400000008010004020408030c0a05160b07213e085f200a7f060c85011500000101000208000105070000030001000106030200020c0a0200010801010a02074d6573736167650541534349490d4d657373616765486f6c6465720b7365745f6d657373616765076d65737361676506537472696e6706737472696e6787342d91af60c3a883a2812c9294c2f8000000000000000000000000000000010002010408010002000002080b0111010c020e000b0212002d000200";
context
.api_publish_module(&mut account, module_code.parse().unwrap())
.await;

context
.api_execute_script_function(
&mut account,
"Message::set_message",
json!([]),
json!([hex::encode(b"hello world")]),
)
.await;

let message = context
.api_get_account_resource(
&account,
format!(
"{}::Message::MessageHolder",
account.address().to_hex_literal()
),
)
.await;
assert_eq!("hello world", message["data"]["message"]);
}

fn init_test_account() -> LocalAccount {
let key_bytes =
hex::decode("a38ba78b1a0fbfc55e2c5dfdedf48d1172283d0f7c59fd64c02d811130a2f4b2").unwrap();
let private_key: Ed25519PrivateKey = (&key_bytes[..]).try_into().unwrap();
let address: Address = "87342d91af60c3a883a2812c9294c2f8".parse().unwrap();
LocalAccount::new(address.into(), private_key, 0)
}
86 changes: 83 additions & 3 deletions api/src/tests/test_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
use crate::{context::Context, index, tests::pretty};
use bytes::Bytes;
use diem_api_types::{
mime_types, TransactionOnChainData, X_DIEM_CHAIN_ID, X_DIEM_LEDGER_TIMESTAMP,
mime_types, HexEncodedBytes, TransactionOnChainData, X_DIEM_CHAIN_ID, X_DIEM_LEDGER_TIMESTAMP,
X_DIEM_LEDGER_VERSION,
};
use diem_config::config::{ApiConfig, JsonRpcConfig, RoleType};
use diem_crypto::hash::HashValue;
use diem_crypto::{hash::HashValue, SigningKey};
use diem_genesis_tool::validator_builder::{RootKeys, ValidatorBuilder};
use diem_global_constants::OWNER_ACCOUNT;
use diem_mempool::mocks::MockSharedMempool;
Expand Down Expand Up @@ -42,7 +42,7 @@ use mempool_notifications::MempoolNotificationSender;
use storage_interface::DbReaderWriter;

use rand::{Rng, SeedableRng};
use serde_json::Value;
use serde_json::{json, Value};
use std::{boxed::Box, collections::BTreeMap, sync::Arc, time::SystemTime};
use vm_validator::vm_validator::VMValidator;
use warp::http::header::CONTENT_TYPE;
Expand Down Expand Up @@ -254,6 +254,86 @@ impl TestContext {
.unwrap();
}

pub async fn api_get_account_resource(
&self,
account: &LocalAccount,
type_name: String,
) -> serde_json::Value {
let resources = self
.get(&format!(
"/accounts/{}/resources",
account.address().to_hex_literal()
))
.await;
let vals: Vec<serde_json::Value> = serde_json::from_value(resources).unwrap();
vals.into_iter().find(|v| v["type"] == type_name).unwrap()
}

pub async fn api_execute_script_function(
&self,
account: &mut LocalAccount,
func_id: &str,
type_args: serde_json::Value,
args: serde_json::Value,
) {
self.api_execute_txn(
account,
json!({
"type": "script_function_payload",
"function": format!("{}::{}", account.address().to_hex_literal(), func_id),
"type_arguments": type_args,
"arguments": args
}),
)
.await;
}

pub async fn api_publish_module(&self, account: &mut LocalAccount, code: HexEncodedBytes) {
self.api_execute_txn(
account,
json!({
"type": "module_bundle_payload",
"modules" : [
{"bytecode": code},
],
}),
)
.await;
}

pub async fn api_execute_txn(&self, account: &mut LocalAccount, payload: Value) {
let mut request = json!({
"sender": account.address(),
"sequence_number": account.sequence_number().to_string(),
"gas_unit_price": "0",
"max_gas_amount": "1000000",
"gas_currency_code": "XUS",
"expiration_timestamp_secs": "16373698888888",
"payload": payload,
});
let resp = self
.post("/transactions/signing_message", request.clone())
.await;

let signing_msg: HexEncodedBytes = resp["message"].as_str().unwrap().parse().unwrap();

let sig = account
.private_key()
.sign_arbitrary_message(signing_msg.inner());

request["signature"] = json!({
"type": "ed25519_signature",
"public_key": HexEncodedBytes::from(account.public_key().to_bytes().to_vec()),
"signature": HexEncodedBytes::from(sig.to_bytes().to_vec()),
});

self.expect_status_code(202)
.post("/transactions", request)
.await;
self.commit_mempool_txns(1).await;
*account.sequence_number_mut() += 1;
}

pub async fn get(&self, path: &str) -> Value {
self.execute(warp::test::request().method("GET").path(path))
.await
Expand Down
28 changes: 25 additions & 3 deletions api/types/src/move_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@

use crate::{Address, Bytecode};

use anyhow::format_err;
use diem_types::{event::EventKey, transaction::Module};
use anyhow::{bail, format_err};
use diem_types::{account_config::CORE_CODE_ADDRESS, event::EventKey, transaction::Module};
use move_binary_format::{
access::ModuleAccess,
file_format::{
Expand Down Expand Up @@ -269,12 +269,27 @@ pub enum MoveValue {
Vector(Vec<MoveValue>),
Bytes(HexEncodedBytes),
Struct(MoveStructValue),
String(String),
}

impl MoveValue {
pub fn json(&self) -> anyhow::Result<serde_json::Value> {
Ok(serde_json::to_value(self)?)
}

pub fn is_ascii_string(st: &StructTag) -> bool {
st.address == CORE_CODE_ADDRESS
&& st.name.to_string() == "String"
&& st.module.to_string() == "ASCII"
}

pub fn convert_ascii_string(v: AnnotatedMoveStruct) -> anyhow::Result<MoveValue> {
if let Some((_, AnnotatedMoveValue::Bytes(bytes))) = v.value.into_iter().next() {
Ok(MoveValue::String(String::from_utf8(bytes)?))
} else {
bail!("expect ASCII::String, but failed to decode struct value");
}
}
}

impl TryFrom<AnnotatedMoveValue> for MoveValue {
Expand All @@ -293,7 +308,13 @@ impl TryFrom<AnnotatedMoveValue> for MoveValue {
.collect::<anyhow::Result<_>>()?,
),
AnnotatedMoveValue::Bytes(v) => MoveValue::Bytes(HexEncodedBytes(v)),
AnnotatedMoveValue::Struct(v) => MoveValue::Struct(v.try_into()?),
AnnotatedMoveValue::Struct(v) => {
if MoveValue::is_ascii_string(&v.type_) {
MoveValue::convert_ascii_string(v)?
} else {
MoveValue::Struct(v.try_into()?)
}
}
})
}
}
Expand Down Expand Up @@ -322,6 +343,7 @@ impl Serialize for MoveValue {
MoveValue::Vector(v) => v.serialize(serializer),
MoveValue::Bytes(v) => v.serialize(serializer),
MoveValue::Struct(v) => v.serialize(serializer),
MoveValue::String(v) => v.serialize(serializer),
}
}
}
Expand Down

0 comments on commit c14c6fa

Please sign in to comment.