Skip to content

Commit

Permalink
Owen/moveos stdlib/cbor (rooch-network#1633)
Browse files Browse the repository at this point in the history
* feat: rebase from main

* feat: update cbor

* feat: cbor type cast

* feat: fix some error

* feat: update cbor

* feat: fix syntax error

* feat: test ok for cbor.move

* feat: test cbor to_map

* feat: rebase from main 2

* feat: rebase from main 3

* feat: test Option and u256

* feat: fmt code

* feat: lint code

* feat: fix cbor comments

* feat: fix test_gas_parameters fail
  • Loading branch information
yubing744 authored May 6, 2024
1 parent 5e0c120 commit 8ce00d7
Show file tree
Hide file tree
Showing 10 changed files with 1,092 additions and 1 deletion.
1 change: 1 addition & 0 deletions frameworks/moveos-stdlib/doc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ This is the reference documentation of the MoveOS standard library.
- [`0x2::bech32`](bech32.md#0x2_bech32)
- [`0x2::big_vector`](big_vector.md#0x2_big_vector)
- [`0x2::bls12381`](bls12381.md#0x2_bls12381)
- [`0x2::cbor`](cbor.md#0x2_cbor)
- [`0x2::copyable_any`](copyable_any.md#0x2_copyable_any)
- [`0x2::core_addresses`](core_addresses.md#0x2_core_addresses)
- [`0x2::display`](display.md#0x2_display)
Expand Down
87 changes: 87 additions & 0 deletions frameworks/moveos-stdlib/doc/cbor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@

<a name="0x2_cbor"></a>

# Module `0x2::cbor`



- [Constants](#@Constants_0)
- [Function `from_cbor`](#0x2_cbor_from_cbor)
- [Function `from_cbor_option`](#0x2_cbor_from_cbor_option)
- [Function `to_map`](#0x2_cbor_to_map)
- [Function `to_cbor`](#0x2_cbor_to_cbor)


<pre><code><b>use</b> <a href="">0x1::option</a>;
<b>use</b> <a href="">0x1::string</a>;
<b>use</b> <a href="simple_map.md#0x2_simple_map">0x2::simple_map</a>;
</code></pre>



<a name="@Constants_0"></a>

## Constants


<a name="0x2_cbor_ERROR_INVALID_CBOR_BYTES"></a>

Error if the CBOR bytes are invalid


<pre><code><b>const</b> <a href="cbor.md#0x2_cbor_ERROR_INVALID_CBOR_BYTES">ERROR_INVALID_CBOR_BYTES</a>: u64 = 1;
</code></pre>



<a name="0x2_cbor_from_cbor"></a>

## Function `from_cbor`

Function to deserialize a type T from CBOR bytes.


<pre><code>#[data_struct(#[T])]
<b>public</b> <b>fun</b> <a href="cbor.md#0x2_cbor_from_cbor">from_cbor</a>&lt;T: drop&gt;(bytes: <a href="">vector</a>&lt;u8&gt;): T
</code></pre>



<a name="0x2_cbor_from_cbor_option"></a>

## Function `from_cbor_option`

Function to deserialize a type T from CBOR bytes.
If the CBOR bytes are invalid, it will return None.


<pre><code>#[data_struct(#[T])]
<b>public</b> <b>fun</b> <a href="cbor.md#0x2_cbor_from_cbor_option">from_cbor_option</a>&lt;T: drop&gt;(bytes: <a href="">vector</a>&lt;u8&gt;): <a href="_Option">option::Option</a>&lt;T&gt;
</code></pre>



<a name="0x2_cbor_to_map"></a>

## Function `to_map`

Parse a cbor object bytes to a SimpleMap
If the cbor bytes is invalid, it will return an empty SimpleMap
If the field type is primitive type, it will be parsed to bytes, array or object will be parsed to cbor bytes


<pre><code><b>public</b> <b>fun</b> <a href="cbor.md#0x2_cbor_to_map">to_map</a>(bytes: <a href="">vector</a>&lt;u8&gt;): <a href="simple_map.md#0x2_simple_map_SimpleMap">simple_map::SimpleMap</a>&lt;<a href="_String">string::String</a>, <a href="">vector</a>&lt;u8&gt;&gt;
</code></pre>



<a name="0x2_cbor_to_cbor"></a>

## Function `to_cbor`

Serialize a value of type T to CBOR bytes.


<pre><code>#[data_struct(#[T])]
<b>public</b> <b>fun</b> <a href="cbor.md#0x2_cbor_to_cbor">to_cbor</a>&lt;T: drop&gt;(value: &T): <a href="">vector</a>&lt;u8&gt;
</code></pre>
238 changes: 238 additions & 0 deletions frameworks/moveos-stdlib/sources/cbor.move
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
// Copyright (c) RoochNetwork
// SPDX-License-Identifier: Apache-2.0

module moveos_std::cbor {
use std::string::String;
use std::option::{Self, Option};
use moveos_std::simple_map::{Self, SimpleMap};

/// Error if the CBOR bytes are invalid
const ERROR_INVALID_CBOR_BYTES: u64 = 1;

#[data_struct(T)]
/// Function to deserialize a type T from CBOR bytes.
public fun from_cbor<T: drop>(bytes: vector<u8>): T {
let opt_result = native_from_cbor(bytes);
assert!(option::is_some(&opt_result), ERROR_INVALID_CBOR_BYTES);
option::destroy_some(opt_result)
}

#[data_struct(T)]
/// Function to deserialize a type T from CBOR bytes.
/// If the CBOR bytes are invalid, it will return None.
public fun from_cbor_option<T: drop>(bytes: vector<u8>): Option<T> {
native_from_cbor(bytes)
}

/// Parse a cbor object bytes to a SimpleMap
/// If the cbor bytes is invalid, it will return an empty SimpleMap
/// If the field type is primitive type, it will be parsed to bytes, array or object will be parsed to cbor bytes
public fun to_map(bytes: vector<u8>): SimpleMap<String,vector<u8>>{
let opt_result = native_from_cbor<SimpleMap<String,vector<u8>>>(bytes);
if(option::is_none(&opt_result)){
option::destroy_none(opt_result);
return simple_map::create()
};

option::destroy_some(opt_result)
}

#[data_struct(T)]
/// Serialize a value of type T to CBOR bytes.
public fun to_cbor<T: drop>(value: &T): vector<u8> {
native_to_cbor(value)
}

native fun native_from_cbor<T>(bytes: vector<u8>): Option<T>;
native fun native_to_cbor<T>(value: &T): vector<u8>;

#[test_only]
use std::vector;

#[test_only]
#[data_struct]
struct Inner has copy, drop, store {
value: u64,
}

#[test_only]
#[data_struct]
struct Test has copy, drop, store {
bool_value: bool,
age: u8,
balance: u128,
sig: u256,
ascii_string: std::ascii::String,
utf8_string: std::string::String,
option_string: Option<std::string::String>,
inner: Inner,
null_value: Option<Inner>,
inner_option: Option<Inner>,
inner_array: vector<Inner>,
account: address,
bytes: vector<u8>,
}

#[test]
fun test_to_cbor() {
let test = Test {
bool_value: true,
age: 30u8,
balance: 170141183460469231731687303715884105728u128,
sig: 1701411834604692317316873037158841057281687303715884105728u256,
ascii_string: std::ascii::string(b"rooch.network"),
utf8_string: std::string::utf8(b"rooch.network"),
null_value: option::none(),
option_string: option::some(std::string::utf8(b"rooch.network")),
inner: Inner {
value: 100u64,
},
inner_option: option::some(Inner {
value: 102u64,
}),
inner_array: std::vector::singleton(Inner {
value: 101u64,
}),
account: @0x42,
bytes: vector<u8>[3u8, 2u8, 1u8, 0u8],
};

let cbor_bytes = to_cbor(&test);
assert!(cbor_bytes == x"ad6a626f6f6c5f76616c7565f563616765181e6762616c616e6365c2508000000000000000000000000000000063736967c258184563918244f400000000000000000000176a81ca357800006c61736369695f737472696e676d726f6f63682e6e6574776f726b6b757466385f737472696e676d726f6f63682e6e6574776f726b6d6f7074696f6e5f737472696e676d726f6f63682e6e6574776f726b65696e6e6572a16576616c756518646a6e756c6c5f76616c7565f66c696e6e65725f6f7074696f6ea16576616c756518666b696e6e65725f617272617981a16576616c75651865676163636f756e74582000000000000000000000000000000000000000000000000000000000000000426562797465734403020100", 1);
}

#[test]
fun test_from_cbor() {
let cbor_bytes = x"ad6a626f6f6c5f76616c7565f563616765181e6762616c616e6365c2508000000000000000000000000000000063736967c258184563918244f400000000000000000000176a81ca357800006c61736369695f737472696e676d726f6f63682e6e6574776f726b6b757466385f737472696e676d726f6f63682e6e6574776f726b6d6f7074696f6e5f737472696e676d726f6f63682e6e6574776f726b65696e6e6572a16576616c756518646a6e756c6c5f76616c7565f66c696e6e65725f6f7074696f6ea16576616c756518666b696e6e65725f617272617981a16576616c75651865676163636f756e74582000000000000000000000000000000000000000000000000000000000000000426562797465734403020100";
let obj = from_cbor<Test>(cbor_bytes);
assert!(obj.balance == 170141183460469231731687303715884105728u128, 1);
assert!(obj.age == 30u8, 2);
assert!(obj.inner.value == 100u64, 3);

// check bytes
assert!(vector::length(&obj.bytes) == 4, 4);
assert!(vector::borrow(&obj.bytes, 0) == &3u8, 5);
assert!(vector::borrow(&obj.bytes, 1) == &2u8, 6);
assert!(vector::borrow(&obj.bytes, 2) == &1u8, 7);
assert!(vector::borrow(&obj.bytes, 3) == &0u8, 8);

// check inner array
assert!(vector::length(&obj.inner_array) == 1, 9);
assert!(vector::borrow(&obj.inner_array, 0).value == 101u64, 10);

// check account
assert!(obj.account == @0x42, 11);

// check ascii string
assert!(obj.ascii_string == std::ascii::string(b"rooch.network"), 12);

// check utf8 string
assert!(obj.utf8_string == std::string::utf8(b"rooch.network"), 13);

// check bool
assert!(obj.bool_value, 14);

// check null
assert!(option::is_none<Inner>(&obj.null_value), 15);

// check inner_option
assert!(option::is_some(&obj.inner_option), 16);
assert!(option::borrow(&obj.inner_option).value == 102u64, 17);

// check u256
assert!(obj.sig == 1701411834604692317316873037158841057281687303715884105728u256, 18);

// check option string
assert!(option::is_some(&obj.option_string), 19);
assert!(option::borrow(&obj.option_string) == &std::string::utf8(b"rooch.network"), 20);
}

#[test]
fun test_to_map(){
let cbor_bytes = x"ad6a626f6f6c5f76616c7565f563616765181e6762616c616e6365c2508000000000000000000000000000000063736967c258184563918244f400000000000000000000176a81ca357800006c61736369695f737472696e676d726f6f63682e6e6574776f726b6b757466385f737472696e676d726f6f63682e6e6574776f726b6d6f7074696f6e5f737472696e676d726f6f63682e6e6574776f726b65696e6e6572a16576616c756518646a6e756c6c5f76616c7565f66c696e6e65725f6f7074696f6ea16576616c756518666b696e6e65725f617272617981a16576616c75651865676163636f756e74582000000000000000000000000000000000000000000000000000000000000000426562797465734403020100";
let map = to_map(cbor_bytes);

// check u128
let balance_bytes = simple_map::borrow(&map, &std::string::utf8(b"balance"));
let balance = from_cbor<u128>(*balance_bytes);
assert!(balance == 170141183460469231731687303715884105728u128, 1);

// check ascii string
let ascii_string_bytes = simple_map::borrow(&map, &std::string::utf8(b"ascii_string"));
let ascii_string = from_cbor<std::ascii::String>(*ascii_string_bytes);
assert!(ascii_string == std::ascii::string(b"rooch.network"), 2);

// check utf8 string
let utf8_string_bytes = simple_map::borrow(&map, &std::string::utf8(b"utf8_string"));
let utf8_string = from_cbor<std::string::String>(*utf8_string_bytes);
assert!(utf8_string == std::string::utf8(b"rooch.network"), 3);

// check u8
let age_bytes = simple_map::borrow(&map, &std::string::utf8(b"age"));
let age = from_cbor<u8>(*age_bytes);
assert!(age == 30u8, 4);

// check bool
let bool_value_bytes = simple_map::borrow(&map, &std::string::utf8(b"bool_value"));
let bool_value = from_cbor<bool>(*bool_value_bytes);
assert!(bool_value, 5);

// check null
let null_value_bytes = simple_map::borrow(&map, &std::string::utf8(b"null_value"));
let null_value = from_cbor<Option<Inner>>(*null_value_bytes);
assert!(option::is_none<Inner>(&null_value), 6);

// check address
let account_bytes = simple_map::borrow(&map, &std::string::utf8(b"account"));
let account = moveos_std::address::from_bytes(*account_bytes);
assert!(account == @0x42, 7);

// check inner struct
let inner_bytes = simple_map::borrow(&map, &std::string::utf8(b"inner"));
let inner = from_cbor<Inner>(*inner_bytes);
assert!(inner.value == 100u64, 8);

// check bytes
let bytes = simple_map::borrow(&map, &std::string::utf8(b"bytes"));
assert!(vector::length(bytes) == 4, 9);
assert!(vector::borrow(bytes, 0) == &3u8, 10);
assert!(vector::borrow(bytes, 1) == &2u8, 11);
assert!(vector::borrow(bytes, 2) == &1u8, 12);
assert!(vector::borrow(bytes, 3) == &0u8, 13);

// check inner array
let inner_array_bytes = simple_map::borrow(&map, &std::string::utf8(b"inner_array"));
let inner_array = from_cbor<vector<Inner>>(*inner_array_bytes);
assert!(vector::length(&inner_array) == 1, 14);
assert!(vector::borrow(&inner_array, 0).value == 101u64, 15);

// check u256
let sig_bytes = simple_map::borrow(&map, &std::string::utf8(b"sig"));
let sig = from_cbor<u256>(*sig_bytes);
assert!(sig == 1701411834604692317316873037158841057281687303715884105728u256, 16);

// check option string
let option_string_bytes = simple_map::borrow(&map, &std::string::utf8(b"option_string"));
std::debug::print(option_string_bytes);
let option_string = from_cbor<Option<std::string::String>>(*option_string_bytes);
assert!(option::is_some(&option_string), 17);
assert!(option::borrow(&option_string) == &std::string::utf8(b"rooch.network"), 18);

simple_map::drop(map);
}

#[test]
fun test_invalid_cbor_bytes_to_map(){
let invalid_bytes = x"abcd";
let map = to_map(invalid_bytes);
assert!(simple_map::length(&map) == 0, 1);
simple_map::drop(map);
}

#[test]
fun test_invalid_cbor_bytes_from_cbor(){
let invalid_bytes = x"abcd";
let obj = from_cbor_option<Test>(invalid_bytes);
assert!(option::is_none(&obj), 1);
}
}
3 changes: 3 additions & 0 deletions frameworks/moveos-stdlib/src/natives/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub struct GasParameters {
pub move_module: moveos_stdlib::move_module::GasParameters,
pub object: moveos_stdlib::object::GasParameters,
pub json: moveos_stdlib::json::GasParameters,
pub cbor: moveos_stdlib::cbor::GasParameters,
pub wasm: moveos_stdlib::wasm::GasParameters,
pub tx_context: moveos_stdlib::tx_context::GasParameters,
pub base58: moveos_stdlib::base58::GasParameters,
Expand All @@ -45,6 +46,7 @@ impl GasParameters {
move_module: moveos_stdlib::move_module::GasParameters::zeros(),
object: moveos_stdlib::object::GasParameters::zeros(),
json: moveos_stdlib::json::GasParameters::zeros(),
cbor: moveos_stdlib::cbor::GasParameters::zeros(),
wasm: moveos_stdlib::wasm::GasParameters::zeros(),
tx_context: moveos_stdlib::tx_context::GasParameters::zeros(),
base58: moveos_stdlib::base58::GasParameters::zeros(),
Expand Down Expand Up @@ -111,6 +113,7 @@ pub fn all_natives(gas_params: GasParameters) -> NativeFunctionTable {
);
add_natives!("object", moveos_stdlib::object::make_all(gas_params.object));
add_natives!("json", moveos_stdlib::json::make_all(gas_params.json));
add_natives!("cbor", moveos_stdlib::cbor::make_all(gas_params.cbor));
add_natives!("wasm", moveos_stdlib::wasm::make_all(gas_params.wasm));
add_natives!(
"tx_context",
Expand Down
Loading

0 comments on commit 8ce00d7

Please sign in to comment.