From 769274bd4039e3e9e85dc95303756a95405d8b50 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Fri, 7 Jul 2023 12:45:30 -0600 Subject: [PATCH] Initial serialization support (#219) --- Cargo.lock | 61 ++++- language/move-native/Cargo.lock | 126 ++++++++- language/move-native/Cargo.toml | 2 +- language/move-native/src/lib.rs | 227 ++++++--------- language/move-native/src/serialization.rs | 258 ++++++++++++++++++ .../src/stackless/rttydesc.rs | 5 +- .../tests/rbpf-tests/stdlib-bcs.move | 230 ++++++++++++++++ .../tests/rbpf-tests/vec.move | 56 ++++ 8 files changed, 803 insertions(+), 162 deletions(-) create mode 100644 language/move-native/src/serialization.rs create mode 100644 language/tools/move-mv-llvm-compiler/tests/rbpf-tests/stdlib-bcs.move diff --git a/Cargo.lock b/Cargo.lock index eb181d2a19..4ad45a9c4d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -799,18 +799,41 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "15bf3650200d8bffa99015595e10f1fbd17de07abbc25bb067da79e769939bfa" dependencies = [ - "borsh-derive", + "borsh-derive 0.9.3", "hashbrown 0.11.2", ] +[[package]] +name = "borsh" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4114279215a005bc675e386011e594e1d9b800918cea18fcadadcce864a2046b" +dependencies = [ + "borsh-derive 0.10.3", + "hashbrown 0.13.2", +] + [[package]] name = "borsh-derive" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6441c552f230375d18e3cc377677914d2ca2b0d36e52129fe15450a2dce46775" dependencies = [ - "borsh-derive-internal", - "borsh-schema-derive-internal", + "borsh-derive-internal 0.9.3", + "borsh-schema-derive-internal 0.9.3", + "proc-macro-crate 0.1.5", + "proc-macro2 1.0.58", + "syn 1.0.99", +] + +[[package]] +name = "borsh-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0754613691538d51f329cce9af41d7b7ca150bc973056f1156611489475f54f7" +dependencies = [ + "borsh-derive-internal 0.10.3", + "borsh-schema-derive-internal 0.10.3", "proc-macro-crate 0.1.5", "proc-macro2 1.0.58", "syn 1.0.99", @@ -827,6 +850,17 @@ dependencies = [ "syn 1.0.99", ] +[[package]] +name = "borsh-derive-internal" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afb438156919598d2c7bad7e1c0adf3d26ed3840dbc010db1a882a65583ca2fb" +dependencies = [ + "proc-macro2 1.0.58", + "quote 1.0.27", + "syn 1.0.99", +] + [[package]] name = "borsh-schema-derive-internal" version = "0.9.3" @@ -838,6 +872,17 @@ dependencies = [ "syn 1.0.99", ] +[[package]] +name = "borsh-schema-derive-internal" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634205cc43f74a1b9046ef87c4540ebda95696ec0f315024860cad7c5b0f5ccd" +dependencies = [ + "proc-macro2 1.0.58", + "quote 1.0.27", + "syn 1.0.99", +] + [[package]] name = "brotli" version = "3.3.4" @@ -4299,8 +4344,8 @@ dependencies = [ name = "move-native" version = "0.1.1" dependencies = [ + "borsh 0.10.3", "ethnum", - "serde 1.0.163", "sha2 0.9.3", "sha3 0.9.1", ] @@ -7001,8 +7046,8 @@ dependencies = [ "bincode", "bitflags", "blake3", - "borsh", - "borsh-derive", + "borsh 0.9.3", + "borsh-derive 0.9.3", "bs58", "bv", "bytemuck", @@ -7077,7 +7122,7 @@ dependencies = [ "base64 0.21.0", "bincode", "bitflags", - "borsh", + "borsh 0.9.3", "bs58", "bytemuck", "byteorder", @@ -7810,7 +7855,7 @@ version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ee73e6e4924fe940354b8d4d98cad5231175d615cd855b758adc658c0aac6a0" dependencies = [ - "cfg-if 0.1.10", + "cfg-if 1.0.0", "static_assertions", ] diff --git a/language/move-native/Cargo.lock b/language/move-native/Cargo.lock index 9bf371f11c..2cb284907f 100644 --- a/language/move-native/Cargo.lock +++ b/language/move-native/Cargo.lock @@ -2,6 +2,17 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "ahash" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", +] + [[package]] name = "block-buffer" version = "0.9.0" @@ -18,6 +29,51 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" +[[package]] +name = "borsh" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4114279215a005bc675e386011e594e1d9b800918cea18fcadadcce864a2046b" +dependencies = [ + "borsh-derive", + "hashbrown", +] + +[[package]] +name = "borsh-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0754613691538d51f329cce9af41d7b7ca150bc973056f1156611489475f54f7" +dependencies = [ + "borsh-derive-internal", + "borsh-schema-derive-internal", + "proc-macro-crate", + "proc-macro2", + "syn", +] + +[[package]] +name = "borsh-derive-internal" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afb438156919598d2c7bad7e1c0adf3d26ed3840dbc010db1a882a65583ca2fb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "borsh-schema-derive-internal" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634205cc43f74a1b9046ef87c4540ebda95696ec0f315024860cad7c5b0f5ccd" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -58,6 +114,15 @@ dependencies = [ "version_check", ] +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + [[package]] name = "keccak" version = "0.1.3" @@ -77,18 +142,51 @@ checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" name = "move-native" version = "0.1.1" dependencies = [ + "borsh", "ethnum", - "serde", "sha2", "sha3", ] +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + [[package]] name = "opaque-debug" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "proc-macro-crate" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" +dependencies = [ + "toml", +] + +[[package]] +name = "proc-macro2" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" +dependencies = [ + "proc-macro2", +] + [[package]] name = "serde" version = "1.0.152" @@ -120,12 +218,38 @@ dependencies = [ "opaque-debug", ] +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + [[package]] name = "typenum" version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +[[package]] +name = "unicode-ident" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" + [[package]] name = "version_check" version = "0.9.4" diff --git a/language/move-native/Cargo.toml b/language/move-native/Cargo.toml index ee4ab8d385..e199c49269 100644 --- a/language/move-native/Cargo.toml +++ b/language/move-native/Cargo.toml @@ -19,7 +19,7 @@ crate-type = ["staticlib", "rlib"] solana = [] [dependencies] +borsh = { version = "0.10.3", default-features = false } ethnum = { version = "1.0.4", default-features = false } -serde = { version = "1.0.124", default-features = false } sha2 = { version = "0.9.3", default-features = false } sha3 = { version = "0.9.1", default-features = false } diff --git a/language/move-native/src/lib.rs b/language/move-native/src/lib.rs index 17dda4834a..3e05e4f6e6 100644 --- a/language/move-native/src/lib.rs +++ b/language/move-native/src/lib.rs @@ -275,6 +275,8 @@ extern crate alloc; +mod serialization; + /// Types literally shared with the compiler through crate linkage. pub mod shared { pub use crate::rt_types::TypeDesc; @@ -469,6 +471,7 @@ pub(crate) mod rt_types { #[repr(transparent)] #[derive(Debug, PartialEq)] + #[derive(borsh::BorshSerialize, borsh::BorshDeserialize)] pub struct MoveSigner(pub MoveAddress); /// A Move address. @@ -479,6 +482,7 @@ pub(crate) mod rt_types { /// Bytes are in little-endian order. #[repr(transparent)] #[derive(PartialEq)] + #[derive(borsh::BorshSerialize, borsh::BorshDeserialize)] pub struct MoveAddress(pub [u8; target_defs::ACCOUNT_ADDRESS_LENGTH]); impl core::fmt::Debug for MoveAddress { @@ -698,7 +702,9 @@ mod std { /// Serialize any value. /// - /// This is definitely not correct. Just a first pass. + /// This does not necessarily produce the same serializations that + /// the Move VM does, as the Rust types are not the same, and + /// the serialization format may not actually be bcs. /// /// # References /// @@ -706,9 +712,15 @@ mod std { /// - `move-core-types::value` #[export_name = "move_native_bcs_to_bytes"] unsafe extern "C" fn to_bytes(type_v: &MoveType, v: &AnyValue) -> MoveByteVector { - let v = borrow_move_value_as_rust_value(type_v, v); - let s = todo!(); //bcs::to_bytes(&v).unwrap(); - rust_vec_to_move_byte_vec(s) + crate::serialization::serialize(type_v, v) + } + + /// Deserialize any value. + /// + /// This is not actually in move std, but is used for testing. + #[export_name = "move_native_bcs_test_from_bytes"] + unsafe extern "C" fn test_from_bytes(type_v: &MoveType, bytes: &MoveByteVector, v: *mut AnyValue) { + crate::serialization::deserialize(type_v, bytes, v) } } @@ -1609,6 +1621,54 @@ pub(crate) mod conv { } } + /// The same as `BorrowedTypedMoveValue` but with raw pointers. + /// + /// Allows for uninitialized values. + pub enum RawBorrowedTypedMoveValue { + Bool(*mut bool), + U8(*mut u8), + U16(*mut u16), + U32(*mut u32), + U64(*mut u64), + U128(*mut u128), + U256(*mut U256), + Address(*mut MoveAddress), + Signer(*mut MoveSigner), + Vector(MoveType, *mut MoveUntypedVector), + Struct(MoveType, *mut AnyValue), + Reference(MoveType, *mut MoveUntypedReference), + } + + pub unsafe fn raw_borrow_move_value_as_rust_value( + type_: &MoveType, + value: *mut AnyValue, + ) -> RawBorrowedTypedMoveValue { + match type_.type_desc { + TypeDesc::Bool => RawBorrowedTypedMoveValue::Bool(mem::transmute(value)), + TypeDesc::U8 => RawBorrowedTypedMoveValue::U8(mem::transmute(value)), + TypeDesc::U16 => RawBorrowedTypedMoveValue::U16(mem::transmute(value)), + TypeDesc::U32 => RawBorrowedTypedMoveValue::U32(mem::transmute(value)), + TypeDesc::U64 => RawBorrowedTypedMoveValue::U64(mem::transmute(value)), + TypeDesc::U128 => RawBorrowedTypedMoveValue::U128(mem::transmute(value)), + TypeDesc::U256 => RawBorrowedTypedMoveValue::U256(mem::transmute(value)), + TypeDesc::Address => RawBorrowedTypedMoveValue::Address(mem::transmute(value)), + TypeDesc::Signer => RawBorrowedTypedMoveValue::Signer(mem::transmute(value)), + TypeDesc::Vector => { + let element_type = *(*type_.type_info).vector.element_type; + let move_ref = mem::transmute(value); + RawBorrowedTypedMoveValue::Vector(element_type, move_ref) + } + TypeDesc::Struct => { + RawBorrowedTypedMoveValue::Struct(*type_, value) + } + TypeDesc::Reference => { + let element_type = *(*type_.type_info).reference.element_type; + let move_ref = mem::transmute(value); + RawBorrowedTypedMoveValue::Reference(element_type, move_ref) + } + } + } + pub enum TypedMoveBorrowedRustVec<'mv> { Bool(MoveBorrowedRustVec<'mv, bool>), U8(MoveBorrowedRustVec<'mv, u8>), @@ -1772,154 +1832,19 @@ pub(crate) mod conv { }) } - /// # References - /// - /// - `move-vm-types::values::Value` - /// - `move-core-types::value` - impl<'mv> serde::Serialize for BorrowedTypedMoveValue<'mv> { - fn serialize(&self, serializer: S) -> Result { - use serde::ser::SerializeSeq; - - match self { - BorrowedTypedMoveValue::Bool(v) => serializer.serialize_bool(**v), - BorrowedTypedMoveValue::U8(v) => serializer.serialize_u8(**v), - BorrowedTypedMoveValue::U16(v) => serializer.serialize_u16(**v), - BorrowedTypedMoveValue::U32(v) => serializer.serialize_u32(**v), - BorrowedTypedMoveValue::U64(v) => serializer.serialize_u64(**v), - BorrowedTypedMoveValue::U128(v) => serializer.serialize_u128(**v), - BorrowedTypedMoveValue::U256(v) => v.0.serialize(serializer), - BorrowedTypedMoveValue::Address(v) => v.0.serialize(serializer), - BorrowedTypedMoveValue::Signer(v) => v.0 .0.serialize(serializer), - BorrowedTypedMoveValue::Vector(mt, mv) => unsafe { - let rv = borrow_typed_move_vec_as_rust_vec(mt, mv); - rv.serialize(serializer) - }, - BorrowedTypedMoveValue::Struct(t, mv) => unsafe { - // fixme: probably need serialize_struct here - let st = (*(t.type_info)).struct_; - let len = usize::try_from(st.field_array_len).expect("overflow"); - let mut seq = serializer.serialize_seq(Some(len))?; - let fields = walk_struct_fields(&st, mv); - for (type_, ref_, _) in fields { - let rv = borrow_move_value_as_rust_value(type_, ref_); - seq.serialize_element(&rv); - } - seq.end() - }, - BorrowedTypedMoveValue::Reference(mt, mv) => unsafe { - let rv = borrow_move_value_as_rust_value(mt, &*mv.0); - rv.serialize(serializer) - }, - } - } - } - - impl<'mv> serde::Serialize for TypedMoveBorrowedRustVec<'mv> { - fn serialize(&self, serializer: S) -> Result { - use serde::ser::SerializeSeq; + pub unsafe fn walk_struct_fields_mut<'mv>( + info: &'mv StructTypeInfo, + struct_ref: *mut AnyValue, + ) -> impl Iterator { + let field_len = usize::try_from(info.field_array_len).expect("overflow"); + let fields: &'mv [StructFieldInfo] = slice::from_raw_parts(info.field_array_ptr, field_len); - match self { - TypedMoveBorrowedRustVec::Bool(v) => { - let mut seq = serializer.serialize_seq(Some(v.len()))?; - for e in v.iter() { - seq.serialize_element(e)?; - } - seq.end() - } - TypedMoveBorrowedRustVec::U8(v) => { - let mut seq = serializer.serialize_seq(Some(v.len()))?; - for e in v.iter() { - seq.serialize_element(e)?; - } - seq.end() - } - TypedMoveBorrowedRustVec::U16(v) => { - let mut seq = serializer.serialize_seq(Some(v.len()))?; - for e in v.iter() { - seq.serialize_element(e)?; - } - seq.end() - } - TypedMoveBorrowedRustVec::U32(v) => { - let mut seq = serializer.serialize_seq(Some(v.len()))?; - for e in v.iter() { - seq.serialize_element(e)?; - } - seq.end() - } - TypedMoveBorrowedRustVec::U64(v) => { - let mut seq = serializer.serialize_seq(Some(v.len()))?; - for e in v.iter() { - seq.serialize_element(e)?; - } - seq.end() - } - TypedMoveBorrowedRustVec::U128(v) => { - let mut seq = serializer.serialize_seq(Some(v.len()))?; - for e in v.iter() { - seq.serialize_element(e)?; - } - seq.end() - } - TypedMoveBorrowedRustVec::U256(v) => { - let mut seq = serializer.serialize_seq(Some(v.len()))?; - for e in v.iter() { - seq.serialize_element(&e.0)?; - } - seq.end() - } - TypedMoveBorrowedRustVec::Address(v) => { - let mut seq = serializer.serialize_seq(Some(v.len()))?; - for e in v.iter() { - seq.serialize_element(&e.0)?; - } - seq.end() - } - TypedMoveBorrowedRustVec::Signer(v) => { - let mut seq = serializer.serialize_seq(Some(v.len()))?; - for e in v.iter() { - seq.serialize_element(&e.0 .0)?; - } - seq.end() - } - TypedMoveBorrowedRustVec::Vector(t, v) => { - let mut seq = serializer.serialize_seq(Some(v.len()))?; - for e in v.iter() { - unsafe { - let e = borrow_typed_move_vec_as_rust_vec(t, e); - seq.serialize_element(&e)?; - } - } - seq.end() - } - TypedMoveBorrowedRustVec::Struct(s) => { - let len = usize::try_from(s.type_.field_array_len).expect("overflow"); - let mut seq = serializer.serialize_seq(Some(len))?; - unsafe { - for vref in s.iter() { - let type_ = MoveType { - name: s.name, - type_desc: TypeDesc::Struct, - type_info: &TypeInfo { struct_: *s.type_ }, - }; - let e = borrow_move_value_as_rust_value(&type_, vref); - seq.serialize_element(&e)?; - } - } - seq.end() - } - TypedMoveBorrowedRustVec::Reference(t, v) => { - let mut seq = serializer.serialize_seq(Some(v.len()))?; - for e in v.iter() { - unsafe { - let e = borrow_move_value_as_rust_value(t, &*e.0); - seq.serialize_element(&e)?; - } - } - seq.end() - } - } - } + fields.iter().map(move |field| { + let struct_base_ptr: *mut AnyValue = struct_ref as _; + let field_offset = isize::try_from(field.offset).expect("overflow"); + let field_ptr = struct_base_ptr.offset(field_offset); + (&field.type_, field_ptr, &field.name) + }) } impl<'mv> core::fmt::Debug for BorrowedTypedMoveValue<'mv> { diff --git a/language/move-native/src/serialization.rs b/language/move-native/src/serialization.rs new file mode 100644 index 0000000000..7cddf87ecc --- /dev/null +++ b/language/move-native/src/serialization.rs @@ -0,0 +1,258 @@ +// Copyright (c) The Diem Core Contributors +// Copyright (c) The Move Contributors +// SPDX-License-Identifier: Apache-2.0 + +use crate::conv::*; +use crate::rt_types::*; +use crate::target_defs; +use ethnum::U256; +use borsh::{BorshSerialize, BorshDeserialize}; +use borsh::maybestd::io::Result as BorshResult; +use alloc::vec::Vec; + +/// A type to serialize u256s. +/// +/// Because ethnum::U256 isn't compatible with borsh. +/// This has the same repr(transparent) definition as ethnum::U256. +#[derive(BorshSerialize, BorshDeserialize)] +#[repr(transparent)] +struct U256Placeholder([u128; 2]); + +fn borsh_to_vec(v: &T) -> Vec { + borsh::to_vec(v).expect("serialization_failure") +} + +fn borsh_from_slice(buf: &mut &[u8]) -> T { + BorshDeserialize::deserialize(buf).expect("deserialization failure") +} + +pub unsafe fn serialize(type_v: &MoveType, v: &AnyValue) -> MoveByteVector { + let v = borrow_move_value_as_rust_value(type_v, v); + let s = match v { + BorrowedTypedMoveValue::Bool(v) => { + borsh_to_vec(v) + } + BorrowedTypedMoveValue::U8(v) => { + borsh_to_vec(v) + } + BorrowedTypedMoveValue::U16(v) => { + borsh_to_vec(v) + } + BorrowedTypedMoveValue::U32(v) => { + borsh_to_vec(v) + } + BorrowedTypedMoveValue::U64(v) => { + borsh_to_vec(v) + } + BorrowedTypedMoveValue::U128(v) => { + borsh_to_vec(v) + } + BorrowedTypedMoveValue::U256(v) => { + let v = U256Placeholder(v.0); + borsh_to_vec(&v.0) + } + BorrowedTypedMoveValue::Address(v) => { + borsh_to_vec(v) + } + BorrowedTypedMoveValue::Signer(v) => { + borsh_to_vec(v) + } + BorrowedTypedMoveValue::Vector(t, v) => { + serialize_vector(&t, v) + } + BorrowedTypedMoveValue::Struct(t, v) => { + serialize_struct(&t, v) + } + BorrowedTypedMoveValue::Reference(_, _) => { + todo!("impossible case?"); + } + }; + rust_vec_to_move_byte_vec(s) +} + +pub unsafe fn deserialize(type_v: &MoveType, bytes: &MoveByteVector, v: *mut AnyValue) { + let bytes = borrow_move_byte_vec_as_rust_vec(bytes); + let bytes = &mut &bytes[..]; + deserialize_from_slice(type_v, bytes, v); + assert!(bytes.is_empty()); +} + +unsafe fn deserialize_from_slice(type_v: &MoveType, bytes: &mut &[u8], v: *mut AnyValue) { + let v = raw_borrow_move_value_as_rust_value(type_v, v); + match v { + RawBorrowedTypedMoveValue::Bool(vptr) => { + *vptr = borsh_from_slice(bytes); + } + RawBorrowedTypedMoveValue::U8(vptr) => { + *vptr = borsh_from_slice(bytes); + } + RawBorrowedTypedMoveValue::U16(vptr) => { + *vptr = borsh_from_slice(bytes); + } + RawBorrowedTypedMoveValue::U32(vptr) => { + *vptr = borsh_from_slice(bytes); + } + RawBorrowedTypedMoveValue::U64(vptr) => { + *vptr = borsh_from_slice(bytes); + } + RawBorrowedTypedMoveValue::U128(vptr) => { + *vptr = borsh_from_slice(bytes); + } + RawBorrowedTypedMoveValue::U256(vptr) => { + let v: U256Placeholder = borsh_from_slice(bytes); + *vptr = U256(v.0); + } + RawBorrowedTypedMoveValue::Address(vptr) => { + *vptr = borsh_from_slice(bytes); + } + RawBorrowedTypedMoveValue::Signer(vptr) => { + *vptr = borsh_from_slice(bytes); + } + RawBorrowedTypedMoveValue::Vector(t, vptr) => { + let v = deserialize_vector(&t, bytes); + // Have to use `write` here because an assignment + // will cause Rust to run the dtor on the uninitialized vptr, + // setting off the "drop bomb". + core::ptr::write(vptr, v); + } + RawBorrowedTypedMoveValue::Struct(t, vptr) => { + deserialize_struct(&t, bytes, vptr); + } + RawBorrowedTypedMoveValue::Reference(_, _) => { + todo!("impossible case?"); + } + } +} + +unsafe fn serialize_vector(type_elt: &MoveType, v: &MoveUntypedVector) -> Vec { + let v = borrow_typed_move_vec_as_rust_vec(type_elt, v); + match v { + TypedMoveBorrowedRustVec::Bool(v) => { + borsh_to_vec(&*v) + } + TypedMoveBorrowedRustVec::U8(v) => { + borsh_to_vec(&*v) + } + TypedMoveBorrowedRustVec::U16(v) => { + borsh_to_vec(&*v) + } + TypedMoveBorrowedRustVec::U32(v) => { + borsh_to_vec(&*v) + } + TypedMoveBorrowedRustVec::U64(v) => { + borsh_to_vec(&*v) + } + TypedMoveBorrowedRustVec::U128(v) => { + borsh_to_vec(&*v) + } + TypedMoveBorrowedRustVec::U256(v) => { + let v: &Vec = &*v; + // safety: U256Placeholder and U256 have the same well-defined representation. + let v: &Vec = core::mem::transmute(v); + borsh_to_vec(&*v) + } + TypedMoveBorrowedRustVec::Address(v) => { + borsh_to_vec(&*v) + } + TypedMoveBorrowedRustVec::Signer(v) => { + borsh_to_vec(&*v) + } + TypedMoveBorrowedRustVec::Vector(t, v) => { + // fixme lots of allocations here + let len: u32 = v.len().try_into().expect("overlong vector"); + let mut buf = borsh_to_vec(&len); + for elt in v.iter() { + let mut elt_buf = serialize_vector(&t, elt); + buf.append(&mut elt_buf); + } + buf + } + TypedMoveBorrowedRustVec::Struct(v) => { + todo!() + } + TypedMoveBorrowedRustVec::Reference(_, _) => { + todo!("impossible case?"); + } + } +} + +unsafe fn deserialize_vector(type_elt: &MoveType, bytes: &mut &[u8]) -> MoveUntypedVector { + match type_elt.type_desc { + TypeDesc::Bool => { + let v: Vec = borsh_from_slice(bytes); + rust_vec_to_move_vec(v) + } + TypeDesc::U8 => { + let v: Vec = borsh_from_slice(bytes); + rust_vec_to_move_vec(v) + } + TypeDesc::U16 => { + let v: Vec = borsh_from_slice(bytes); + rust_vec_to_move_vec(v) + } + TypeDesc::U32 => { + let v: Vec = borsh_from_slice(bytes); + rust_vec_to_move_vec(v) + } + TypeDesc::U64 => { + let v: Vec = borsh_from_slice(bytes); + rust_vec_to_move_vec(v) + } + TypeDesc::U128 => { + let v: Vec = borsh_from_slice(bytes); + rust_vec_to_move_vec(v) + } + TypeDesc::U256 => { + unsafe { + let v: Vec = borsh_from_slice(bytes); + // safety: U256Placeholder and U256 have the same well-defined representation. + let v: Vec = core::mem::transmute(v); + rust_vec_to_move_vec(v) + } + } + TypeDesc::Address => { + let v: Vec = borsh_from_slice(bytes); + rust_vec_to_move_vec(v) + } + TypeDesc::Signer => { + let v: Vec = borsh_from_slice(bytes); + rust_vec_to_move_vec(v) + } + TypeDesc::Vector => { + let vecinfo = &(*type_elt.type_info).vector; + let inner_elt_type = vecinfo.element_type; + let len: u32 = borsh_from_slice(bytes); + let mut v: Vec = Vec::with_capacity(len as usize); + for _ in 0..len { + let eltv = deserialize_vector(&inner_elt_type, bytes); + v.push(eltv); + } + rust_vec_to_move_vec(v) + } + TypeDesc::Struct => { + todo!() + } + TypeDesc::Reference => { + todo!("impossible case?"); + } + } +} + +// fixme this allocates more than it should +unsafe fn serialize_struct(t: &MoveType, v: &AnyValue) -> Vec { + let mut buf = Vec::new(); + let structinfo = &(*(t.type_info)).struct_; + for (ft, fv, _) in walk_struct_fields(structinfo, v) { + let field_buf = serialize(ft, fv); + let mut field_buf = move_byte_vec_to_rust_vec(field_buf); + buf.append(&mut field_buf); + } + buf +} + +unsafe fn deserialize_struct(t: &MoveType, bytes: &mut &[u8], v: *mut AnyValue) { + let structinfo = &(*(t.type_info)).struct_; + for (ft, fv, _) in walk_struct_fields_mut(structinfo, v) { + deserialize_from_slice(ft, bytes, fv); + } +} diff --git a/language/tools/move-mv-llvm-compiler/src/stackless/rttydesc.rs b/language/tools/move-mv-llvm-compiler/src/stackless/rttydesc.rs index bc7e63129f..06fd4629f5 100644 --- a/language/tools/move-mv-llvm-compiler/src/stackless/rttydesc.rs +++ b/language/tools/move-mv-llvm-compiler/src/stackless/rttydesc.rs @@ -177,12 +177,15 @@ fn define_type_info_global( match mty { _ if !has_type_info(mty) => define_type_info_global_nil(module_cx, &symbol_name), Type::Vector(elt_ty) => match **elt_ty { - Type::Primitive(PrimitiveType::U8) + Type::Primitive(PrimitiveType::Bool) + | Type::Primitive(PrimitiveType::U8) | Type::Primitive(PrimitiveType::U16) | Type::Primitive(PrimitiveType::U32) | Type::Primitive(PrimitiveType::U64) | Type::Primitive(PrimitiveType::U128) | Type::Primitive(PrimitiveType::U256) + | Type::Primitive(PrimitiveType::Address) + | Type::Vector(_) | Type::Struct(_, _, _) => define_type_info_global_vec( module_cx, &symbol_name, diff --git a/language/tools/move-mv-llvm-compiler/tests/rbpf-tests/stdlib-bcs.move b/language/tools/move-mv-llvm-compiler/tests/rbpf-tests/stdlib-bcs.move new file mode 100644 index 0000000000..1025fb951d --- /dev/null +++ b/language/tools/move-mv-llvm-compiler/tests/rbpf-tests/stdlib-bcs.move @@ -0,0 +1,230 @@ +// signers 0xcafe + +module 0x10::bcs { + native public fun to_bytes(v: &MoveValue): vector; + native public fun test_from_bytes(v: &vector): MoveValue; +} + +module 0x10::vector { + native public fun empty(): vector; + native public fun push_back(v: &mut vector, e: Element); + native public fun pop_back(v: &mut vector): Element; + native public fun length(v: &vector): u64; +} + +module 0x10::debug { + native public fun print(x: &T); +} + +module 0x10::tests { + use 0x10::bcs; + use 0x10::vector; + use 0x10::debug; + + public fun test_bool() { + let v: bool = true; + let vs: vector = bcs::to_bytes(&v); + let vv: bool = bcs::test_from_bytes(&vs); + assert!(v == vv, 11); + } + + public fun test_u8() { + let v: u8 = 50; + let vs: vector = bcs::to_bytes(&v); + let vv: u8 = bcs::test_from_bytes(&vs); + assert!(v == vv, 11); + } + + public fun test_u16() { + let v: u16 = 50; + let vs: vector = bcs::to_bytes(&v); + let vv: u16 = bcs::test_from_bytes(&vs); + assert!(v == vv, 11); + } + + public fun test_u32() { + let v: u32 = 50; + let vs: vector = bcs::to_bytes(&v); + let vv: u32 = bcs::test_from_bytes(&vs); + assert!(v == vv, 11); + } + + public fun test_u64() { + let v: u64 = 50; + let vs: vector = bcs::to_bytes(&v); + let vv: u64 = bcs::test_from_bytes(&vs); + assert!(v == vv, 11); + } + + public fun test_u128() { + let v: u128 = 50; + let vs: vector = bcs::to_bytes(&v); + let vv: u128 = bcs::test_from_bytes(&vs); + assert!(v == vv, 11); + } + + public fun test_u256() { + let v: u256 = 50; + let vs: vector = bcs::to_bytes(&v); + let vv: u256 = bcs::test_from_bytes(&vs); + assert!(v == vv, 11); + } + + public fun test_address() { + let v: address = @50; + let vs: vector = bcs::to_bytes(&v); + let vv: address = bcs::test_from_bytes(&vs); + assert!(v == vv, 11); + } + + // fixme this asserts in the compiler + /*public fun test_signer(v: signer) { + let vs: vector = bcs::to_bytes(&v); + let vv: signer = bcs::test_from_bytes(&vs); + assert!(v == vv, 11); + }*/ + + struct TestStruct has drop { + a: u8, + b: u8, + } + + public fun test_struct() { + let v: TestStruct = TestStruct { a: 1, b: 2 }; + let vs: vector = bcs::to_bytes(&v); + let vv: TestStruct = bcs::test_from_bytes(&vs); + assert!(v == vv, 11); + } + + public fun test_vec_bool() { + let v: vector = vector::empty(); + vector::push_back(&mut v, true); + vector::push_back(&mut v, false); + let vs: vector = bcs::to_bytes(&v); + let vv: vector = bcs::test_from_bytes(&vs); + assert!(v == vv, 11); + } + + public fun test_vec_u8() { + let v: vector = vector::empty(); + vector::push_back(&mut v, 1); + vector::push_back(&mut v, 2); + let vs: vector = bcs::to_bytes(&v); + let vv: vector = bcs::test_from_bytes(&vs); + assert!(v == vv, 11); + } + + public fun test_vec_u16() { + let v: vector = vector::empty(); + vector::push_back(&mut v, 1); + vector::push_back(&mut v, 2); + let vs: vector = bcs::to_bytes(&v); + let vv: vector = bcs::test_from_bytes(&vs); + assert!(v == vv, 11); + } + + public fun test_vec_u32() { + let v: vector = vector::empty(); + vector::push_back(&mut v, 1); + vector::push_back(&mut v, 2); + let vs: vector = bcs::to_bytes(&v); + let vv: vector = bcs::test_from_bytes(&vs); + assert!(v == vv, 11); + } + + public fun test_vec_u64() { + let v: vector = vector::empty(); + vector::push_back(&mut v, 1); + vector::push_back(&mut v, 2); + let vs: vector = bcs::to_bytes(&v); + let vv: vector = bcs::test_from_bytes(&vs); + assert!(v == vv, 11); + } + + public fun test_vec_u128() { + let v: vector = vector::empty(); + vector::push_back(&mut v, 1); + vector::push_back(&mut v, 2); + let vs: vector = bcs::to_bytes(&v); + let vv: vector = bcs::test_from_bytes(&vs); + assert!(v == vv, 11); + } + + public fun test_vec_u256() { + let v: vector = vector::empty(); + vector::push_back(&mut v, 1); + vector::push_back(&mut v, 2); + let vs: vector = bcs::to_bytes(&v); + let vv: vector = bcs::test_from_bytes(&vs); + assert!(v == vv, 11); + } + + public fun test_vec_address() { + let v: vector
= vector::empty(); + vector::push_back(&mut v, @1); + vector::push_back(&mut v, @2); + let vs: vector = bcs::to_bytes(&v); + let vv: vector
= bcs::test_from_bytes(&vs); + assert!(v == vv, 11); + } + + public fun test_vec_vec_bool() { + let v: vector> = vector::empty(); + { + let velt: vector = vector::empty(); + vector::push_back(&mut velt, true); + vector::push_back(&mut velt, false); + vector::push_back(&mut v, velt); + let velt: vector = vector::empty(); + vector::push_back(&mut velt, false); + vector::push_back(&mut velt, true); + vector::push_back(&mut v, velt); + }; + let vs: vector = bcs::to_bytes(&v); + let vv: vector> = bcs::test_from_bytes(&vs); + //assert!(v == vv, 11); // fixme internal compiler error + } + + struct TestVecStruct has drop { + a: u8, + b: u8, + } + + public fun test_vec_struct() { + let v: vector = vector::empty(); + vector::push_back(&mut v, TestVecStruct { a: 1, b: 2 }); + vector::push_back(&mut v, TestVecStruct { a: 3, b: 4 }); + let vs: vector = bcs::to_bytes(&v); + let vv: vector = bcs::test_from_bytes(&vs); + // fixme this asserts in the compiler + //assert!(&v == &vv, 11); + } +} + +script { + use 0x10::tests; + + fun main(s: signer) { + tests::test_bool(); + tests::test_u8(); + tests::test_u16(); + tests::test_u32(); + tests::test_u64(); + tests::test_u128(); + tests::test_u256(); + tests::test_address(); + tests::test_struct(); + //tests::test_signer(s); // fixme + tests::test_vec_bool(); + tests::test_vec_u8(); + tests::test_vec_u16(); + tests::test_vec_u32(); + tests::test_vec_u64(); + tests::test_vec_u128(); + tests::test_vec_u256(); + tests::test_vec_address(); + //tests::test_vec_signer(); // fixme + //tests::test_vec_vec_bool(); // fixme + //tests::test_vec_struct(); // fixme + } +} diff --git a/language/tools/move-mv-llvm-compiler/tests/rbpf-tests/vec.move b/language/tools/move-mv-llvm-compiler/tests/rbpf-tests/vec.move index 0e6d3dc7ce..459f41f1c1 100644 --- a/language/tools/move-mv-llvm-compiler/tests/rbpf-tests/vec.move +++ b/language/tools/move-mv-llvm-compiler/tests/rbpf-tests/vec.move @@ -13,6 +13,33 @@ module 0x10::tests { use 0x10::vector; + public fun test_bool() { + let v: vector = vector::empty(); + + let len = vector::length(&v); + assert!(len == 0, 10); + + vector::push_back(&mut v, true); + vector::push_back(&mut v, false); + + let len = vector::length(&v); + assert!(len == 2, 10); + + vector::swap(&mut v, 0, 1); + + let elt = vector::borrow(&v, 0); + assert!(*elt == false, 10); + let elt = vector::borrow_mut(&mut v, 0); + assert!(*elt == false, 10); + + let elt = vector::pop_back(&mut v); + assert!(elt == true, 10); + let elt = vector::pop_back(&mut v); + assert!(elt == false, 10); + + vector::destroy_empty(v); + } + public fun test_u8() { let v: vector = vector::empty(); @@ -147,16 +174,45 @@ module 0x10::tests { vector::destroy_empty(v); } + + public fun test_address() { + let v: vector
= vector::empty(); + + let len = vector::length(&v); + assert!(len == 0, 10); + + vector::push_back(&mut v, @2); + vector::push_back(&mut v, @3); + + let len = vector::length(&v); + assert!(len == 2, 10); + + vector::swap(&mut v, 0, 1); + + let elt = vector::borrow(&v, 0); + assert!(*elt == @3, 10); + let elt = vector::borrow_mut(&mut v, 0); + assert!(*elt == @3, 10); + + let elt = vector::pop_back(&mut v); + assert!(elt == @2, 10); + let elt = vector::pop_back(&mut v); + assert!(elt == @3, 10); + + vector::destroy_empty(v); + } } script { use 0x10::tests; fun main() { + tests::test_bool(); tests::test_u8(); tests::test_u16(); tests::test_u64(); tests::test_u128(); tests::test_u256(); + tests::test_address(); } }