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 16, 2024
1 parent 3bcedb1 commit 8a929c9
Show file tree
Hide file tree
Showing 6 changed files with 171 additions and 7 deletions.
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
29 changes: 28 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,31 @@ 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.


<pre><code><b>public</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>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
12 changes: 12 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,18 @@ 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;

/// 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 fun constant_serialized_size<MoveValue>(): std::option::Option<u64>;

// ==============================
// Module Specification
spec module {} // switch to module documentation context
Expand Down
41 changes: 38 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,22 @@ use aptos_native_interface::{
SafeNativeResult,
};
use move_core_types::{
gas_algebra::NumBytes, vm_status::sub_status::NFE_BCS_SERIALIZATION_FAILURE,
gas_algebra::NumBytes, 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},
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 +130,36 @@ 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_SERIALIZED_SIZE_BASE)?;

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

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,
});
},
};

// Charge based on type layout size?
// context.charge(BCS_SERIALIZED_SIZE_PER_BYTE_SERIALIZED * NumBytes::new(serialized_size))?;

Ok(smallvec![result])
}

/***************************************************************************************************
* module
**************************************************************************************************/
Expand All @@ -135,6 +169,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>>());
}
}
47 changes: 45 additions & 2 deletions third_party/move/move-vm/types/src/value_serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ use crate::{
};
use move_binary_format::errors::{PartialVMError, PartialVMResult};
use move_core_types::{
value::{IdentifierMappingKind, MoveTypeLayout},
vm_status::StatusCode,
account_address::AccountAddress, u256, value::{IdentifierMappingKind, MoveStructLayout, MoveTypeLayout}, vm_status::StatusCode
};
use serde::{
de::{DeserializeSeed, Error as DeError},
Expand Down Expand Up @@ -172,6 +171,50 @@ pub fn serialized_size_allowing_delayed_values(
})
}

/// If given type has a constant serialized size (irrespective of the instance), it returns the serialized
/// size in bytes any value would have.
/// Otherwise it returns None.
pub fn constant_serialized_size(ty_layout: &MoveTypeLayout) -> PartialVMResult<Option<usize>> {
let bcs_size_result = match ty_layout {
MoveTypeLayout::Bool => bcs::serialized_size(&false).map(|size| Some(size)),
MoveTypeLayout::U8 => bcs::serialized_size(&0u8).map(|size| Some(size)),
MoveTypeLayout::U64 => bcs::serialized_size(&0u64).map(|size| Some(size)),
MoveTypeLayout::U128 => bcs::serialized_size(&0u128).map(|size| Some(size)),
MoveTypeLayout::Address => bcs::serialized_size(&AccountAddress::ZERO).map(|size| Some(size)),
// vectors have no constant size
MoveTypeLayout::Vector(_) => Ok(None),
// enums have no constant size
MoveTypeLayout::Struct(MoveStructLayout::RuntimeVariants(_) | MoveStructLayout::WithVariants(_)) => Ok(None),
MoveTypeLayout::Struct(MoveStructLayout::Runtime(fields)) => {
Ok(fields
.iter()
.map(|field| constant_serialized_size(field))
.collect::<Result<Option<Vec<_>>, _>>()
.map(|o| o.map(|v| v.iter().sum()))?)
},
MoveTypeLayout::Struct(MoveStructLayout::WithFields(fields))
| MoveTypeLayout::Struct(MoveStructLayout::WithTypes{fields, ..}) => {
Ok(fields
.iter()
.map(|field| constant_serialized_size(&field.layout))
.collect::<Result<Option<Vec<_>>, _>>()
.map(|o| o.map(|v| v.iter().sum()))?)
},
// signer's size is VM implementation detail, and can change at will.
MoveTypeLayout::Signer => Ok(None),
MoveTypeLayout::U16 => bcs::serialized_size(&0u16).map(|size| Some(size)),
MoveTypeLayout::U32 => bcs::serialized_size(&0u32).map(|size| Some(size)),
MoveTypeLayout::U256 => bcs::serialized_size(&u256::U256::zero()).map(|size| Some(size)),
MoveTypeLayout::Native(_, inner) => Ok(constant_serialized_size(inner)?),
};
bcs_size_result.map_err(|e| {
PartialVMError::new(StatusCode::VALUE_SERIALIZATION_ERROR).with_message(format!(
"failed to compute serialized size of a value: {:?}",
e
))
})
}

/// Allow conversion between values and identifiers (delayed values). For example,
/// this trait can be implemented to fetch a concrete Move value from the global
/// state based on the identifier stored inside a delayed value.
Expand Down

0 comments on commit 8a929c9

Please sign in to comment.