From 3ad5bb714e8657d7c33d4bb972c9546b8b831357 Mon Sep 17 00:00:00 2001 From: arnaud Date: Tue, 29 Apr 2025 14:05:41 +0200 Subject: [PATCH 1/6] Kernel/SDK: Switch Mutez to Narith (and reintroudce a type Mutez) --- sdk/rust/encoding-derive/src/bin.rs | 2 +- sdk/rust/encoding-derive/src/enc.rs | 4 +-- sdk/rust/encoding-derive/src/encoding.rs | 2 +- sdk/rust/encoding-derive/src/make.rs | 4 ++- sdk/rust/encoding-derive/src/nom.rs | 2 +- sdk/rust/encoding-derive/src/symbol.rs | 1 + sdk/rust/encoding/src/enc.rs | 10 +++---- sdk/rust/encoding/src/encoding.rs | 2 +- sdk/rust/encoding/src/nom.rs | 4 +-- sdk/rust/encoding/src/types.rs | 37 +++++++++++++----------- 10 files changed, 37 insertions(+), 31 deletions(-) diff --git a/sdk/rust/encoding-derive/src/bin.rs b/sdk/rust/encoding-derive/src/bin.rs index 075f6a22638e..768c53149a5d 100644 --- a/sdk/rust/encoding-derive/src/bin.rs +++ b/sdk/rust/encoding-derive/src/bin.rs @@ -50,7 +50,7 @@ fn generate_bin_write(encoding: &Encoding) -> TokenStream { generate_dynamic_bin_write(size, encoding, *span) } Encoding::Zarith(span) => quote_spanned!(*span=> tezos_data_encoding::enc::zarith), - Encoding::MuTez(span) => quote_spanned!(*span=> tezos_data_encoding::enc::mutez), + Encoding::Narith(span) => quote_spanned!(*span=> tezos_data_encoding::enc::narith), } } diff --git a/sdk/rust/encoding-derive/src/enc.rs b/sdk/rust/encoding-derive/src/enc.rs index fc2f139a4606..fb851db2cc67 100644 --- a/sdk/rust/encoding-derive/src/enc.rs +++ b/sdk/rust/encoding-derive/src/enc.rs @@ -46,8 +46,8 @@ pub(crate) fn generate_encoding(encoding: &Encoding) -> TokenStream { Encoding::Zarith(span) => { quote_spanned!(*span=> tezos_data_encoding::encoding::Encoding::Z) } - Encoding::MuTez(span) => { - quote_spanned!(*span=> tezos_data_encoding::encoding::Encoding::Mutez) + Encoding::Narith(span) => { + quote_spanned!(*span=> tezos_data_encoding::encoding::Encoding::N) } } } diff --git a/sdk/rust/encoding-derive/src/encoding.rs b/sdk/rust/encoding-derive/src/encoding.rs index f52f1be7a322..298e0a62b390 100644 --- a/sdk/rust/encoding-derive/src/encoding.rs +++ b/sdk/rust/encoding-derive/src/encoding.rs @@ -67,7 +67,7 @@ pub enum Encoding<'a> { Bytes(Span), Path(&'a syn::Path), Zarith(Span), - MuTez(Span), + Narith(Span), String(Option, Span), diff --git a/sdk/rust/encoding-derive/src/make.rs b/sdk/rust/encoding-derive/src/make.rs index e1bc66caa1b5..8029adc40e7d 100644 --- a/sdk/rust/encoding-derive/src/make.rs +++ b/sdk/rust/encoding-derive/src/make.rs @@ -187,8 +187,10 @@ fn get_basic_encoding_from_meta<'a>( Encoding::String(string.param, string.span) } else if let Some(zarith) = get_attribute_no_param(meta, &symbol::Z_ARITH)? { Encoding::Zarith(zarith.span) + } else if let Some(mutez) = get_attribute_no_param(meta, &symbol::N_ARITH)? { + Encoding::Narith(mutez.span) } else if let Some(mutez) = get_attribute_no_param(meta, &symbol::MU_TEZ)? { - Encoding::MuTez(mutez.span) + Encoding::Narith(mutez.span) } else if let Some(builtin) = get_attribute_with_param(meta, &symbol::BUILTIN, Some(&symbol::KIND), true)? { diff --git a/sdk/rust/encoding-derive/src/nom.rs b/sdk/rust/encoding-derive/src/nom.rs index 0e75fa53b89c..76724f031f33 100644 --- a/sdk/rust/encoding-derive/src/nom.rs +++ b/sdk/rust/encoding-derive/src/nom.rs @@ -59,7 +59,7 @@ fn generate_nom_read(encoding: &Encoding) -> TokenStream { Encoding::ShortDynamic(encoding, span) => generate_short_dynamic_nom_read(encoding, *span), Encoding::Dynamic(size, encoding, span) => generate_dynamic_nom_read(size, encoding, *span), Encoding::Zarith(span) => quote_spanned!(*span=> tezos_data_encoding::nom::zarith), - Encoding::MuTez(span) => quote_spanned!(*span=> tezos_data_encoding::nom::mutez), + Encoding::Narith(span) => quote_spanned!(*span=> tezos_data_encoding::nom::narith), } } diff --git a/sdk/rust/encoding-derive/src/symbol.rs b/sdk/rust/encoding-derive/src/symbol.rs index 9285d71ad8d9..ee248fdda156 100644 --- a/sdk/rust/encoding-derive/src/symbol.rs +++ b/sdk/rust/encoding-derive/src/symbol.rs @@ -68,6 +68,7 @@ pub const IGNORE_UNKNOWN: Symbol = Symbol("ignore_unknown"); pub const TAG: Symbol = Symbol("tag"); pub const Z_ARITH: Symbol = Symbol("zarith"); +pub const N_ARITH: Symbol = Symbol("narith"); pub const MU_TEZ: Symbol = Symbol("mutez"); pub const RESERVE: Symbol = Symbol("reserve"); diff --git a/sdk/rust/encoding/src/enc.rs b/sdk/rust/encoding/src/enc.rs index 2f4f0078346b..712734081e46 100644 --- a/sdk/rust/encoding/src/enc.rs +++ b/sdk/rust/encoding/src/enc.rs @@ -6,7 +6,7 @@ use std::convert::TryFrom; use std::fmt; use crate::bit_utils::BitReverse; -use crate::types::{Mutez, Zarith}; +use crate::types::{Narith, Zarith}; use num_bigint::BigUint; pub use tezos_data_encoding_derive::BinWriter; @@ -318,7 +318,7 @@ mod integers { pub use integers::*; -impl BinWriter for Mutez { +impl BinWriter for Narith { fn bin_write(&self, out: &mut Vec) -> BinResult { n_bignum(self.0.magnitude(), out) } @@ -543,7 +543,7 @@ mod test { } #[test] - fn mutez() { + fn narith() { let data = [ ("0", "00"), ("1", "01"), @@ -561,12 +561,12 @@ mod test { ("10001", "818004"), ]; - use super::{BinWriter, Mutez}; + use super::{BinWriter, Narith}; use num_traits::FromPrimitive; for (hex, enc) in data { let num = num_bigint::BigInt::from_u64(u64::from_str_radix(hex, 16).unwrap()).unwrap(); - let num = Mutez(num); + let num = Narith(num); let mut bytes = vec![]; num.bin_write(&mut bytes).unwrap(); assert_eq!(enc, hex::encode(bytes)); diff --git a/sdk/rust/encoding/src/encoding.rs b/sdk/rust/encoding/src/encoding.rs index f41001a773b9..bc07484f2614 100644 --- a/sdk/rust/encoding/src/encoding.rs +++ b/sdk/rust/encoding/src/encoding.rs @@ -142,7 +142,7 @@ pub enum Encoding { /// Big number /// Almost identical to [Encoding::Z], but does not contain the sign bit in the second most /// significant bit of the first byte - Mutez, + N, /// Encoding of floating point number (encoded as a floating point number in JSON and a double in binary). Float, /// Float with bounds in a given range. Both bounds are inclusive. diff --git a/sdk/rust/encoding/src/nom.rs b/sdk/rust/encoding/src/nom.rs index 9abfa7ec3a23..4837e35a4c71 100644 --- a/sdk/rust/encoding/src/nom.rs +++ b/sdk/rust/encoding/src/nom.rs @@ -18,7 +18,7 @@ use nom::{ use num_bigint::{BigInt, BigUint, Sign}; pub use tezos_data_encoding_derive::NomReader; -use crate::types::{Mutez, Zarith}; +use crate::types::{Narith, Zarith}; use self::error::{BoundedEncodingKind, DecodeError, DecodeErrorKind}; @@ -225,7 +225,7 @@ impl NomReader<'_> for Zarith { } } -impl NomReader<'_> for Mutez { +impl NomReader<'_> for Narith { fn nom_read(bytes: &[u8]) -> NomResult { map(n_bignum, |big_uint| { BigInt::from_biguint(Sign::Plus, big_uint).into() diff --git a/sdk/rust/encoding/src/types.rs b/sdk/rust/encoding/src/types.rs index d12f19233d2f..461e232dd0bd 100644 --- a/sdk/rust/encoding/src/types.rs +++ b/sdk/rust/encoding/src/types.rs @@ -86,10 +86,13 @@ impl From<&Zarith> for BigInt { has_encoding!(Zarith, ZARITH_ENCODING, { Encoding::Z }); /// Mutez number -#[derive(Clone, Debug)] -pub struct Mutez(pub num_bigint::BigInt); +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct Narith(pub num_bigint::BigInt); -impl<'de> Deserialize<'de> for Mutez { +#[deprecated = "Mutez has been replaced by Narith, which has identical semantics for encoding & decoding"] +pub type Mutez = Narith; + +impl<'de> Deserialize<'de> for Narith { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, @@ -109,7 +112,7 @@ impl<'de> Deserialize<'de> for Mutez { } } -impl Serialize for Mutez { +impl Serialize for Narith { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, @@ -123,43 +126,43 @@ impl Serialize for Mutez { } } -impl From for Mutez { +impl From for Narith { fn from(from: num_bigint::BigInt) -> Self { - Mutez(from) + Narith(from) } } -impl From for num_bigint::BigInt { - fn from(from: Mutez) -> Self { +impl From for num_bigint::BigInt { + fn from(from: Narith) -> Self { from.0 } } -impl From<&num_bigint::BigInt> for Mutez { +impl From<&num_bigint::BigInt> for Narith { fn from(from: &num_bigint::BigInt) -> Self { - Mutez(from.clone()) + Narith(from.clone()) } } -impl From<&Mutez> for num_bigint::BigInt { - fn from(from: &Mutez) -> Self { +impl From<&Narith> for num_bigint::BigInt { + fn from(from: &Narith) -> Self { from.0.clone() } } -impl From for BigInt { - fn from(source: Mutez) -> Self { +impl From for BigInt { + fn from(source: Narith) -> Self { Self(source.0) } } -impl From<&Mutez> for BigInt { - fn from(source: &Mutez) -> Self { +impl From<&Narith> for BigInt { + fn from(source: &Narith) -> Self { Self(source.0.clone()) } } -has_encoding!(Mutez, MUTEZ_ENCODING, { Encoding::Mutez }); +has_encoding!(Narith, NARITH_ENCODING, { Encoding::N }); #[derive(Clone, PartialEq, Eq)] pub struct SizedBytes(pub [u8; SIZE]); -- GitLab From a76d4c2349c03233c818ea9d82d6f97f18af3add Mon Sep 17 00:00:00 2001 From: arnaud Date: Tue, 6 May 2025 14:57:46 +0200 Subject: [PATCH 2/6] Kernel/Sdk: Narith use BigUint instead of BigInt with Sign forced to Plus --- sdk/rust/encoding/src/enc.rs | 5 ++--- sdk/rust/encoding/src/nom.rs | 4 +--- sdk/rust/encoding/src/types.rs | 34 ++++++++++++---------------------- 3 files changed, 15 insertions(+), 28 deletions(-) diff --git a/sdk/rust/encoding/src/enc.rs b/sdk/rust/encoding/src/enc.rs index 712734081e46..958008dd12fb 100644 --- a/sdk/rust/encoding/src/enc.rs +++ b/sdk/rust/encoding/src/enc.rs @@ -320,7 +320,7 @@ pub use integers::*; impl BinWriter for Narith { fn bin_write(&self, out: &mut Vec) -> BinResult { - n_bignum(self.0.magnitude(), out) + n_bignum(&self.0, out) } } @@ -562,10 +562,9 @@ mod test { ]; use super::{BinWriter, Narith}; - use num_traits::FromPrimitive; for (hex, enc) in data { - let num = num_bigint::BigInt::from_u64(u64::from_str_radix(hex, 16).unwrap()).unwrap(); + let num: num_bigint::BigUint = u64::from_str_radix(hex, 16).unwrap().into(); let num = Narith(num); let mut bytes = vec![]; num.bin_write(&mut bytes).unwrap(); diff --git a/sdk/rust/encoding/src/nom.rs b/sdk/rust/encoding/src/nom.rs index 4837e35a4c71..7bfd016c1849 100644 --- a/sdk/rust/encoding/src/nom.rs +++ b/sdk/rust/encoding/src/nom.rs @@ -227,9 +227,7 @@ impl NomReader<'_> for Zarith { impl NomReader<'_> for Narith { fn nom_read(bytes: &[u8]) -> NomResult { - map(n_bignum, |big_uint| { - BigInt::from_biguint(Sign::Plus, big_uint).into() - })(bytes) + map(n_bignum, |big_uint| big_uint.into())(bytes) } } diff --git a/sdk/rust/encoding/src/types.rs b/sdk/rust/encoding/src/types.rs index 461e232dd0bd..ecd2250692d2 100644 --- a/sdk/rust/encoding/src/types.rs +++ b/sdk/rust/encoding/src/types.rs @@ -12,7 +12,6 @@ use crate::has_encoding; use crate::nom::NomReader; use hex::FromHexError; -use num_bigint::Sign; use serde::{Deserialize, Serialize}; /// This is a wrapper for [num_bigint::BigInt] type. @@ -87,7 +86,7 @@ has_encoding!(Zarith, ZARITH_ENCODING, { Encoding::Z }); /// Mutez number #[derive(Clone, PartialEq, Eq, Debug)] -pub struct Narith(pub num_bigint::BigInt); +pub struct Narith(pub num_bigint::BigUint); #[deprecated = "Mutez has been replaced by Narith, which has identical semantics for encoding & decoding"] pub type Mutez = Narith; @@ -99,13 +98,10 @@ impl<'de> Deserialize<'de> for Narith { { if deserializer.is_human_readable() { let string: String = serde::Deserialize::deserialize(deserializer)?; - let big_int: num_bigint::BigInt = string + let big_uint: num_bigint::BigUint = string .parse() .map_err(|err| serde::de::Error::custom(format!("cannot parse big int: {err}")))?; - if big_int.sign() == Sign::Minus { - return Err(serde::de::Error::custom("negative number for natural")); - } - Ok(Self(big_int)) + Ok(Self(big_uint)) } else { Ok(Self(serde::Deserialize::deserialize(deserializer)?)) } @@ -126,39 +122,33 @@ impl Serialize for Narith { } } -impl From for Narith { - fn from(from: num_bigint::BigInt) -> Self { +impl From for Narith { + fn from(from: num_bigint::BigUint) -> Self { Narith(from) } } -impl From for num_bigint::BigInt { +impl From for num_bigint::BigUint { fn from(from: Narith) -> Self { from.0 } } -impl From<&num_bigint::BigInt> for Narith { - fn from(from: &num_bigint::BigInt) -> Self { +impl From<&num_bigint::BigUint> for Narith { + fn from(from: &num_bigint::BigUint) -> Self { Narith(from.clone()) } } -impl From<&Narith> for num_bigint::BigInt { +impl From<&Narith> for num_bigint::BigUint { fn from(from: &Narith) -> Self { from.0.clone() } } -impl From for BigInt { - fn from(source: Narith) -> Self { - Self(source.0) - } -} - -impl From<&Narith> for BigInt { - fn from(source: &Narith) -> Self { - Self(source.0.clone()) +impl From for Narith { + fn from(value: u64) -> Self { + Narith(value.into()) } } -- GitLab From 39496a1a3e62917fa4a000ede8b0579900b6bb5e Mon Sep 17 00:00:00 2001 From: arnaud Date: Tue, 6 May 2025 13:27:34 +0200 Subject: [PATCH 3/6] Kernel/Sdk: Correct short dynamic and doc --- sdk/rust/encoding/src/encoding.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sdk/rust/encoding/src/encoding.rs b/sdk/rust/encoding/src/encoding.rs index bc07484f2614..41aa628579cd 100644 --- a/sdk/rust/encoding/src/encoding.rs +++ b/sdk/rust/encoding/src/encoding.rs @@ -231,7 +231,7 @@ impl Encoding { Encoding::List(Box::new(encoding)) } - /// Utility function to construct [Encoding::List] without the need + /// Utility function to construct [Encoding::BoundedList] without the need /// to manually create new [Box]. #[inline] pub fn bounded_list(max: usize, encoding: Encoding) -> Encoding { @@ -245,7 +245,7 @@ impl Encoding { Encoding::Sized(bytes_sz, Box::new(encoding)) } - /// Utility function to construct [Encoding::Sized] without the need + /// Utility function to construct [Encoding::Bounded] without the need /// to manually create new [Box]. #[inline] pub fn bounded(max: usize, encoding: Encoding) -> Encoding { @@ -263,7 +263,7 @@ impl Encoding { /// to manually create new [Box]. #[inline] pub fn short_dynamic(encoding: Encoding) -> Encoding { - Encoding::Dynamic(Box::new(encoding)) + Encoding::ShortDynamic(Box::new(encoding)) } /// Utility function to construct [Encoding::Dynamic] without the need @@ -273,7 +273,7 @@ impl Encoding { Encoding::Dynamic(Box::new(encoding)) } - /// Utility function to construct [Encoding::Dynamic] without the need + /// Utility function to construct [Encoding::BoundedDynamic] without the need /// to manually create new [Box]. #[inline] pub fn bounded_dynamic(max: usize, encoding: Encoding) -> Encoding { -- GitLab From 2328188d2ffc02b1a759b37f73279d41a8d4691b Mon Sep 17 00:00:00 2001 From: arnaud Date: Tue, 6 May 2025 14:19:08 +0200 Subject: [PATCH 4/6] Kernel/Sdk: Stop force encoding to Path when deriving an enum When deriving NomReader, BinWriter or HasEncoding, we build an encoding that will be our guideline to use the right function. But when building the encoding of an enum, we force the encoding to be a Path. It means that the type in the enum must explicitly implement the trait we are trying to derive. For example, a 'Vec' is a good field for a struct when deriving NomReader, but when putting 'Vec' in an enum, it doesn't work anymore and we must explicitly implement NomReader for 'Vec' --- sdk/rust/encoding-derive/src/make.rs | 5 +--- sdk/rust/encoding/src/lib.rs | 35 ++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/sdk/rust/encoding-derive/src/make.rs b/sdk/rust/encoding-derive/src/make.rs index 8029adc40e7d..c48f80ea9022 100644 --- a/sdk/rust/encoding-derive/src/make.rs +++ b/sdk/rust/encoding-derive/src/make.rs @@ -490,10 +490,7 @@ fn make_tag<'a>( } syn::Fields::Unnamed(fields) if fields.unnamed.len() == 1 => { let ty = &fields.unnamed.first().unwrap().ty; - match ty { - syn::Type::Path(type_path) => Encoding::Path(&type_path.path), - _ => return Err(error_spanned(ty, "Unsupported type for enum variant")), - } + make_type_encoding(ty, meta)? } syn::Fields::Unnamed(fields) => { return Err(error_spanned(fields, "Only single field is supported")) diff --git a/sdk/rust/encoding/src/lib.rs b/sdk/rust/encoding/src/lib.rs index 163bb5d9de15..a6327a263ba6 100644 --- a/sdk/rust/encoding/src/lib.rs +++ b/sdk/rust/encoding/src/lib.rs @@ -47,6 +47,41 @@ //! # assert!(_remaining_input.is_empty()); //! # assert_eq!(outer, result); //! ``` +//! Derivation is also supported for rust enum with one unnamed field. +//! +//! Let's create encoding for a simple enum that holds a String or a Vec +//! ```rust +//! use tezos_data_encoding::nom::NomReader; +//! use tezos_data_encoding::enc::BinWriter; +//! use tezos_data_encoding::encoding::HasEncoding; +//! +//! #[derive(Debug, PartialEq, HasEncoding, NomReader, BinWriter)] +//! enum Message { +//! Readable(String), +//! Bytes(Vec), +//! } +//! +//! # let message_1 = Message::Readable(String::from("Hello World !")); +//! # let message_2 = Message::Bytes(vec![1_u8; 10_usize]); +//! # +//! # let mut encoded_1 = Vec::new(); +//! # message_1.bin_write(&mut encoded_1).expect("encoding works"); +//! # +//! # let (_remaining_input_1, result_1) = Message::nom_read(&encoded_1) +//! # .expect("decoding works"); +//! # +//! # assert!(_remaining_input_1.is_empty()); +//! # assert_eq!(message_1, result_1); +//! +//! # let mut encoded_2 = Vec::new(); +//! # message_2.bin_write(&mut encoded_2).expect("encoding works"); +//! # +//! # let (_remaining_input_2, result_2) = Message::nom_read(&encoded_2) +//! # .expect("decoding works"); +//! # +//! # assert!(_remaining_input_2.is_empty()); +//! # assert_eq!(message_2, result_2); +//! ``` mod bit_utils; pub mod types; -- GitLab From cacab75db27de32df82117f2885d2ccc09dbdb5d Mon Sep 17 00:00:00 2001 From: arnaud Date: Wed, 7 May 2025 10:59:45 +0200 Subject: [PATCH 5/6] Kernel/SDK: Test the short dynamic deriving on a data structure --- sdk/rust/encoding/src/encoding.rs | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/sdk/rust/encoding/src/encoding.rs b/sdk/rust/encoding/src/encoding.rs index 41aa628579cd..35e8df52416d 100644 --- a/sdk/rust/encoding/src/encoding.rs +++ b/sdk/rust/encoding/src/encoding.rs @@ -311,3 +311,34 @@ macro_rules! has_encoding { } }; } + +#[cfg(test)] +mod tests { + use crate as tezos_data_encoding; + use tezos_data_encoding_derive::{BinWriter, NomReader}; + + #[derive(Debug, PartialEq, BinWriter, NomReader)] + struct Bytes { + #[encoding(short_dynamic, list)] + data: Vec, + } + + #[test] + fn short_dynamic() { + let test = Bytes { + data: vec![1_u8, 2_u8, 3_u8], + }; + let mut output = Vec::new(); + crate::enc::BinWriter::bin_write(&test, &mut output) + .expect("BinWriting should have succeed"); + + // Verify that the size of the encoded data is a short (one byte) + assert_eq!(output, vec![3_u8, 1_u8, 2_u8, 3_u8]); + + let (rem, decoded_test) = + crate::nom::NomReader::nom_read(&output).expect("NomReading should have succeed"); + assert!(rem.is_empty()); + + assert_eq!(test, decoded_test); + } +} -- GitLab From 18aa88a0b6da7c81359c5ffa3d54f05d7f1b7a76 Mon Sep 17 00:00:00 2001 From: arnaud Date: Tue, 6 May 2025 17:37:07 +0200 Subject: [PATCH 6/6] Kernel/SDK: CHANGELOG --- sdk/rust/CHANGELOG.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/sdk/rust/CHANGELOG.md b/sdk/rust/CHANGELOG.md index 14fa644d138e..8b465ca8c5aa 100644 --- a/sdk/rust/CHANGELOG.md +++ b/sdk/rust/CHANGELOG.md @@ -13,11 +13,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Changed -- Nothing. +- Add `types::Narith`, which wraps a `BigUint`. +- Deprecate `types::Mutez`. It is left as a type-alias to `types::Narith`. + *NB* The internally wrapped type is changed from `BigInt` to `BigUint` as a result. +- Add `#[encoding]` attribute support for enum fields, in addition to struct fields. ### Fixed -- Nothing. +- Fix `short_dynamic` function in `encoding` - was incorrectly using `dynamic` internally. ### Security -- GitLab