Skip to content

Commit

Permalink
[move-stdlib] Implement bcs::constant_serialized_size<T>(): Option<u64>
Browse files Browse the repository at this point in the history
  • Loading branch information
igor-aptos committed Oct 21, 2024
1 parent 9fcf365 commit 4c0b5bc
Show file tree
Hide file tree
Showing 7 changed files with 258 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ use crate::{
gas_feature_versions::{RELEASE_V1_18, RELEASE_V1_23},
gas_schedule::NativeGasParameters,
};
use aptos_gas_algebra::{InternalGas, InternalGasPerArg, InternalGasPerByte};
use aptos_gas_algebra::{
InternalGas, InternalGasPerArg, InternalGasPerByte, InternalGasPerTypeNode,
};

crate::gas_schedule::macros::define_gas_parameters!(
MoveStdlibGasParameters,
Expand Down Expand Up @@ -39,6 +41,8 @@ crate::gas_schedule::macros::define_gas_parameters!(
[bcs_serialized_size_base: InternalGas, { RELEASE_V1_18.. => "bcs.serialized_size.base" }, 735],
[bcs_serialized_size_per_byte_serialized: InternalGasPerByte, { RELEASE_V1_18.. => "bcs.serialized_size.per_byte_serialized" }, 36],
[bcs_serialized_size_failure: InternalGas, { RELEASE_V1_18.. => "bcs.serialized_size.failure" }, 3676],
[bcs_constant_serialized_size_base: InternalGas, { RELEASE_V1_23.. => "bcs.constant_serialized_size.base" }, 735],
[bcs_constant_serialized_size_per_type_node: InternalGasPerTypeNode, { RELEASE_V1_23.. => "bcs.constant_serialized_size.per_type_node" }, 40],

[mem_swap_base: InternalGas, { RELEASE_V1_23.. => "mem.swap.base" }, 1500],

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ module admin::transaction_context_test {
), type_info::type_name<T3>()],
13
);

assert!(option::some(option::destroy_some(payload_opt)) == transaction_context::entry_function_payload(), 13);
} else {
assert!(option::none() == payload_opt, 14);
}
}

Expand All @@ -128,7 +132,10 @@ module admin::transaction_context_test {
store.function_name = transaction_context::function_name(entry);
store.type_arg_names = transaction_context::type_arg_names(entry);
store.args = transaction_context::args(entry);
}
};
assert!(option::some(option::destroy_some(multisig_opt)) == transaction_context::multisig_payload(), 1);
} else {
assert!(option::none() == multisig_opt, 2);
}
}

Expand Down
36 changes: 35 additions & 1 deletion aptos-move/framework/move-stdlib/doc/bcs.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ details on BCS.

- [Function `to_bytes`](#0x1_bcs_to_bytes)
- [Function `serialized_size`](#0x1_bcs_serialized_size)
- [Function `constant_serialized_size`](#0x1_bcs_constant_serialized_size)
- [Specification](#@Specification_0)
- [Function `serialized_size`](#@Specification_0_serialized_size)


<pre><code></code></pre>
<pre><code><b>use</b> <a href="option.md#0x1_option">0x1::option</a>;
</code></pre>



Expand Down Expand Up @@ -65,6 +67,38 @@ Aborts with <code>0x1c5</code> error code if there is a failure when calculating



</details>

<a id="0x1_bcs_constant_serialized_size"></a>

## Function `constant_serialized_size`

If the type has known constant (always the same, independent of instance) serialized size
in BCS (Binary Canonical Serialization) format, returns it, otherwise returns None.
Aborts with <code>0x1c5</code> error code if there is a failure when calculating serialized size.

Note:
For some types it might not be known they have constant size, and function might return None.
For example, signer appears to have constant size, but it's size might change.
If this function returned Some() for some type before - it is guaranteed to continue returning Some()
On the other hand, if function has returned None for some type,
it might change in the future to return Some() instead, if size becomes "known".


<pre><code><b>public</b>(<b>friend</b>) <b>fun</b> <a href="bcs.md#0x1_bcs_constant_serialized_size">constant_serialized_size</a>&lt;MoveValue&gt;(): <a href="option.md#0x1_option_Option">option::Option</a>&lt;u64&gt;
</code></pre>



<details>
<summary>Implementation</summary>


<pre><code><b>native</b> <b>public</b>(<b>friend</b>) <b>fun</b> <a href="bcs.md#0x1_bcs_constant_serialized_size">constant_serialized_size</a>&lt;MoveValue&gt;(): std::option::Option&lt;u64&gt;;
</code></pre>



</details>

<a id="@Specification_0"></a>
Expand Down
17 changes: 17 additions & 0 deletions aptos-move/framework/move-stdlib/sources/bcs.move
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,23 @@ module std::bcs {
/// Aborts with `0x1c5` error code if there is a failure when calculating serialized size.
native public fun serialized_size<MoveValue>(v: &MoveValue): u64;

// TODO - function `constant_serialized_size1 is `public(friend)` here for one release,
// and to be changed to `public` one release later.
#[test_only]
friend std::bcs_tests;

/// If the type has known constant (always the same, independent of instance) serialized size
/// in BCS (Binary Canonical Serialization) format, returns it, otherwise returns None.
/// Aborts with `0x1c5` error code if there is a failure when calculating serialized size.
///
/// Note:
/// For some types it might not be known they have constant size, and function might return None.
/// For example, signer appears to have constant size, but it's size might change.
/// If this function returned Some() for some type before - it is guaranteed to continue returning Some()
/// On the other hand, if function has returned None for some type,
/// it might change in the future to return Some() instead, if size becomes "known".
native public(friend) fun constant_serialized_size<MoveValue>(): std::option::Option<u64>;

// ==============================
// Module Specification
spec module {} // switch to module documentation context
Expand Down
47 changes: 44 additions & 3 deletions aptos-move/framework/move-stdlib/src/natives/bcs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,26 @@ use aptos_native_interface::{
SafeNativeResult,
};
use move_core_types::{
gas_algebra::NumBytes, vm_status::sub_status::NFE_BCS_SERIALIZATION_FAILURE,
gas_algebra::{NumBytes, NumTypeNodes},
vm_status::sub_status::NFE_BCS_SERIALIZATION_FAILURE,
};
use move_vm_runtime::native_functions::NativeFunction;
use move_vm_types::{
loaded_data::runtime_types::Type,
natives::function::PartialVMResult,
value_serde::serialized_size_allowing_delayed_values,
values::{values_impl::Reference, Value},
value_serde::{
constant_serialized_size, serialized_size_allowing_delayed_values,
type_visit_count_for_constant_serialized_size,
},
values::{values_impl::Reference, Struct, Value},
};
use smallvec::{smallvec, SmallVec};
use std::collections::VecDeque;

pub fn create_option_u64(value: Option<u64>) -> Value {
Value::struct_(Struct::pack(vec![Value::vector_u64(value)]))
}

/***************************************************************************************************
* native fun to_bytes
*
Expand Down Expand Up @@ -126,6 +134,38 @@ fn serialized_size_impl(
serialized_size_allowing_delayed_values(&value, &ty_layout)
}

fn native_constant_serialized_size(
context: &mut SafeNativeContext,
mut ty_args: Vec<Type>,
_args: VecDeque<Value>,
) -> SafeNativeResult<SmallVec<[Value; 1]>> {
debug_assert!(ty_args.len() == 1);

context.charge(BCS_CONSTANT_SERIALIZED_SIZE_BASE)?;

let ty = ty_args.pop().unwrap();
let ty_layout = context.type_to_type_layout(&ty)?;

context.charge(
BCS_CONSTANT_SERIALIZED_SIZE_PER_TYPE_NODE
* NumTypeNodes::new(type_visit_count_for_constant_serialized_size(&ty_layout)),
)?;

let result = match constant_serialized_size(&ty_layout) {
Ok(value) => create_option_u64(value.map(|v| v as u64)),
Err(_) => {
context.charge(BCS_SERIALIZED_SIZE_FAILURE)?;

// Re-use the same abort code as bcs::to_bytes.
return Err(SafeNativeError::Abort {
abort_code: NFE_BCS_SERIALIZATION_FAILURE,
});
},
};

Ok(smallvec![result])
}

/***************************************************************************************************
* module
**************************************************************************************************/
Expand All @@ -135,6 +175,7 @@ pub fn make_all(
let funcs = [
("to_bytes", native_to_bytes as RawSafeNative),
("serialized_size", native_serialized_size),
("constant_serialized_size", native_constant_serialized_size),
];

builder.make_named_natives(funcs)
Expand Down
40 changes: 40 additions & 0 deletions aptos-move/framework/move-stdlib/tests/bcs_tests.move
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
module std::bcs_tests {
use std::bcs;
use std::vector;
use std::option;
use std::signer;

struct Box<T> has copy, drop, store { x: T }
struct Box3<T> has copy, drop, store { x: Box<Box<T>> }
Expand All @@ -20,6 +22,8 @@ module std::bcs_tests {
let expected_size = vector::length(&actual_bytes);
let actual_size = bcs::serialized_size(&true);
assert!(actual_size == expected_size, 1);

assert!(option::some(actual_size) == bcs::constant_serialized_size<bool>());
}

#[test]
Expand All @@ -31,6 +35,8 @@ module std::bcs_tests {
let expected_size = vector::length(&actual_bytes);
let actual_size = bcs::serialized_size(&1u8);
assert!(actual_size == expected_size, 1);

assert!(option::some(actual_size) == bcs::constant_serialized_size<u8>());
}

#[test]
Expand All @@ -42,6 +48,8 @@ module std::bcs_tests {
let expected_size = vector::length(&actual_bytes);
let actual_size = bcs::serialized_size(&1);
assert!(actual_size == expected_size, 1);

assert!(option::some(actual_size) == bcs::constant_serialized_size<u64>());
}

#[test]
Expand All @@ -53,6 +61,8 @@ module std::bcs_tests {
let expected_size = vector::length(&actual_bytes);
let actual_size = bcs::serialized_size(&1u128);
assert!(actual_size == expected_size, 1);

assert!(option::some(actual_size) == bcs::constant_serialized_size<u128>());
}

#[test]
Expand All @@ -66,6 +76,23 @@ module std::bcs_tests {
let expected_size = vector::length(&actual_bytes);
let actual_size = bcs::serialized_size(&v);
assert!(actual_size == expected_size, 1);

assert!(option::none() == bcs::constant_serialized_size<vector<u8>>());
}

#[test(creator = @0xcafe)]
fun bcs_address(creator: &signer) {
let v = signer::address_of(creator);

let expected_bytes = x"000000000000000000000000000000000000000000000000000000000000CAFE";
let actual_bytes = bcs::to_bytes(&v);
assert!(actual_bytes == expected_bytes, 0);

let expected_size = vector::length(&actual_bytes);
let actual_size = bcs::serialized_size(&v);
assert!(actual_size == expected_size, 1);

assert!(option::some(actual_size) == bcs::constant_serialized_size<address>());
}

fun box3<T>(x: T): Box3<T> {
Expand Down Expand Up @@ -101,5 +128,18 @@ module std::bcs_tests {

let actual_size = bcs::serialized_size(&box);
assert!(actual_size == expected_size, 0);

assert!(option::some(actual_size) == bcs::constant_serialized_size<Box127<bool>>());
assert!(option::none() == bcs::constant_serialized_size<Box63<vector<bool>>>());
assert!(option::none() == bcs::constant_serialized_size<Box63<option::Option<bool>>>());
}

enum Singleton {
V1(u64),
}

fun encode_enum() {
assert!(option::none() == bcs::constant_serialized_size<Singleton>());
assert!(option::none() == bcs::constant_serialized_size<Box3<Singleton>>());
}
}
Loading

0 comments on commit 4c0b5bc

Please sign in to comment.