From 39cec3d2b1a508cc0eef5e9e0e63700d414f292b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Palmer?= Date: Wed, 3 Dec 2025 16:48:23 +0100 Subject: [PATCH 1/5] SDK/rust: remove Clone requirement for nom::optional_field output --- sdk/rust/CHANGELOG.md | 1 + sdk/rust/encoding/src/nom.rs | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sdk/rust/CHANGELOG.md b/sdk/rust/CHANGELOG.md index 41a8f9dfb95d..c9e37865a4c7 100644 --- a/sdk/rust/CHANGELOG.md +++ b/sdk/rust/CHANGELOG.md @@ -25,6 +25,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). *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. - `tezos_data_encoding_derive`: derivations of `NomReader` and `BinWriter` require those same constraints on their fields. If such a field doesn't meet the constraint, the implementation will not be available for use, however compilation will still succeed. +- `nom::optional_field` no longer requires inner type to be `Clone`. ### Fixed diff --git a/sdk/rust/encoding/src/nom.rs b/sdk/rust/encoding/src/nom.rs index 0a471d056560..259a70c9eba4 100644 --- a/sdk/rust/encoding/src/nom.rs +++ b/sdk/rust/encoding/src/nom.rs @@ -1,6 +1,6 @@ // Copyright (c) SimpleStaking, Viable Systems, Nomadic Labs and Tezedge Contributors // SPDX-CopyrightText: 2022-2024 TriliTech -// +// SPDX-CopyrightText: 2025 Functori // SPDX-License-Identifier: MIT use bitvec::slice::BitSlice; @@ -328,10 +328,9 @@ where pub fn optional_field<'a, O, F>(parser: F) -> impl FnMut(NomInput<'a>) -> NomResult<'a, Option> where F: FnMut(NomInput<'a>) -> NomResult<'a, O>, - O: Clone, { alt(( - preceded(tag(0x00u8.to_be_bytes()), success(None)), + map(tag(0x00u8.to_be_bytes()), |_| None), preceded(tag(0xffu8.to_be_bytes()), map(parser, Some)), )) } -- GitLab From 7e1cff5aa04888f907b121dfbfb080fb865e4775 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Palmer?= Date: Thu, 4 Dec 2025 08:52:35 +0100 Subject: [PATCH 2/5] SDK/rust: implement BinWriter for &T --- sdk/rust/CHANGELOG.md | 1 + sdk/rust/encoding/src/enc.rs | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/sdk/rust/CHANGELOG.md b/sdk/rust/CHANGELOG.md index c9e37865a4c7..6ba12b5cd369 100644 --- a/sdk/rust/CHANGELOG.md +++ b/sdk/rust/CHANGELOG.md @@ -17,6 +17,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Add `OperationContent`, `ManagerOperationContent`, `RevealContent`, `TransactionContent` defining operations contents for reveal and transaction. - Add `DelegationContent` defining operation content for delegation. - Add `OriginationContent` defining operation content for origination. +- Implement trait `BinWriter` for `&T` whenever `T` implements `BinWriter`. ### Changed diff --git a/sdk/rust/encoding/src/enc.rs b/sdk/rust/encoding/src/enc.rs index 62182c7c4991..130ace42fe05 100644 --- a/sdk/rust/encoding/src/enc.rs +++ b/sdk/rust/encoding/src/enc.rs @@ -1,5 +1,6 @@ // Copyright (c) SimpleStaking, Nomadic Labs and Tezedge Contributors // SPDX-CopyrightText: 2022-2023 TriliTech +// SPDX-CopyrightText: 2025 Functori // SPDX-License-Identifier: MIT use std::convert::TryFrom; @@ -178,6 +179,15 @@ pub trait BinWriter { } } +impl BinWriter for &T +where + T: BinWriter, +{ + fn bin_write(&self, output: &mut Vec) -> BinResult { + T::bin_write(self, output) + } +} + impl BinWriter for Box where T: ?Sized + BinWriter, -- GitLab From d14ee5848ad81764d9dda162474207b461cc0c3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Palmer?= Date: Thu, 4 Dec 2025 11:05:16 +0100 Subject: [PATCH 3/5] SDK/rust: introduce an internal type InternalTransactionContent It will hold TransactionContent encodings --- sdk/rust/protocol/src/operation.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/sdk/rust/protocol/src/operation.rs b/sdk/rust/protocol/src/operation.rs index 5b9885e7fafa..268822608ab3 100644 --- a/sdk/rust/protocol/src/operation.rs +++ b/sdk/rust/protocol/src/operation.rs @@ -54,6 +54,23 @@ pub struct RevealContent { pub proof: Option, } +mod internal { + use super::*; + + /// Encoded representation of the `TransactionContent` type used for binary + /// serialization and deserialization via the `NomReader` and `BinWriter` traits. + /// + /// The `parameters` field is an `Option` to optimize encoding: `None` represents + /// the default `Parameters` value. During decoding, `None` is restored as the + /// default value. + #[derive(PartialEq, Debug, Clone, NomReader, BinWriter)] + pub struct EncodedTransactionContent { + amount: Amount, + destination: Destination, + parameters: Option, + } +} + #[derive(PartialEq, Debug, Clone, NomReader, BinWriter)] pub struct TransactionContent { pub amount: Narith, -- GitLab From 686465ee946061c1af6662620e7c59ad23fc4c13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Palmer?= Date: Thu, 4 Dec 2025 11:07:36 +0100 Subject: [PATCH 4/5] SDK/rust: remove WithDefaultValue from TransactionContent.parameters This wrapper was only use for (de/)encoding purpose. Co-authored-by: Emma Turner <1623821-emturner@users.noreply.gitlab.com> --- contrib/sdk-bindings/rust/src/forge.rs | 3 +- sdk/rust/protocol/src/operation.rs | 74 +++++++++++++++++++++----- 2 files changed, 63 insertions(+), 14 deletions(-) diff --git a/contrib/sdk-bindings/rust/src/forge.rs b/contrib/sdk-bindings/rust/src/forge.rs index 674c056af28c..52a04c05d5dd 100644 --- a/contrib/sdk-bindings/rust/src/forge.rs +++ b/contrib/sdk-bindings/rust/src/forge.rs @@ -136,8 +136,7 @@ pub mod operation { ), // octez-client convert data "Unit" from Michelson to binary value: value.unwrap_or(vec![0x03, 0x0b]).clone(), - } - .into(), + }, }, })) } diff --git a/sdk/rust/protocol/src/operation.rs b/sdk/rust/protocol/src/operation.rs index 268822608ab3..0d3cb320d73a 100644 --- a/sdk/rust/protocol/src/operation.rs +++ b/sdk/rust/protocol/src/operation.rs @@ -11,7 +11,7 @@ use tezos_crypto_rs::{ public_key::PublicKey, public_key_hash::PublicKeyHash, }; -use tezos_data_encoding::{enc::BinWriter, nom::NomReader, types::Narith, types::WithDefaultValue}; +use tezos_data_encoding::{enc::BinWriter, nom::NomReader, types::Narith}; #[derive(PartialEq, Debug, Clone, NomReader, BinWriter)] pub struct UnsignedOperation { @@ -69,13 +69,66 @@ mod internal { destination: Destination, parameters: Option, } + + impl<'a> From<&'a TransactionContent> + for EncodedTransactionContent<&'a Narith, &'a Contract, &'a Parameters> + { + fn from(c: &'a TransactionContent) -> Self { + let TransactionContent { + amount, + destination, + parameters, + } = c; + let parameters = if parameters == &Parameters::default() { + None + } else { + Some(parameters) + }; + Self { + amount, + destination, + parameters, + } + } + } + + impl From> for TransactionContent { + fn from(c: EncodedTransactionContent) -> Self { + let EncodedTransactionContent { + amount, + destination, + parameters, + } = c; + let parameters = parameters.unwrap_or_default(); + Self { + amount, + destination, + parameters, + } + } + } + + impl NomReader<'_> for TransactionContent { + fn nom_read(input: &[u8]) -> tezos_data_encoding::nom::NomResult { + nom::combinator::map( + EncodedTransactionContent::nom_read, + TransactionContent::from, + )(input) + } + } + + impl BinWriter for TransactionContent { + fn bin_write(&self, out: &mut Vec) -> tezos_data_encoding::enc::BinResult { + EncodedTransactionContent::from(self).bin_write(out) + } + } } -#[derive(PartialEq, Debug, Clone, NomReader, BinWriter)] +#[derive(PartialEq, Debug, Clone)] pub struct TransactionContent { pub amount: Narith, pub destination: Contract, - pub parameters: WithDefaultValue, + pub parameters: Parameters, } #[derive(PartialEq, Debug, Clone, NomReader, BinWriter)] @@ -239,8 +292,7 @@ mod tests { entrypoint: Entrypoint::try_from("B").unwrap(), // octez-client convert data '"Hello"' from Michelson to binary value: hex::decode("010000000548656c6c6f").unwrap(), - } - .into(), + }, }, gas_limit: 1380_u64.into(), storage_limit: 0_u64.into(), @@ -278,7 +330,7 @@ mod tests { amount: 10.into(), destination: Contract::from_b58check("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx") .unwrap(), - parameters: Parameters::default().into(), + parameters: Parameters::default(), }, gas_limit: 0.into(), storage_limit: 1405.into(), @@ -322,7 +374,7 @@ mod tests { amount: 10.into(), destination: Contract::from_b58check("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx") .unwrap(), - parameters: Parameters::default().into(), + parameters: Parameters::default(), }, gas_limit: 0.into(), storage_limit: 1405.into(), @@ -370,8 +422,7 @@ mod tests { entrypoint: Entrypoint::try_from("remove_delegate").unwrap(), // octez-client convert data "Unit" from Michelson to binary value: hex::decode("030b").unwrap(), - } - .into(), + }, }, gas_limit: 0.into(), storage_limit: 0.into(), @@ -655,7 +706,7 @@ mod tests { amount: 20000.into(), destination: Contract::from_b58check("tz1Rc6wtS349fFTyuUhDXTXoBUZ9j7XiN61o") .unwrap(), - parameters: Parameters::default().into(), + parameters: Parameters::default(), }, }); @@ -774,8 +825,7 @@ mod tests { entrypoint: Entrypoint::try_from("balance_of").unwrap(), // octez-client convert data '"tz3j873xGK219DrCKYL4usxM8EQgHUmDdbJB"' from Michelson to binary value: hex::decode("0100000024747a336a38373378474b3231394472434b594c347573784d3845516748556d4464624a42").unwrap(), - } - .into(), + }, }, }); -- GitLab From a1760708baa13a1a9eda82e303f5218784ed90b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Palmer?= Date: Thu, 4 Dec 2025 11:41:30 +0100 Subject: [PATCH 5/5] SDK/rust: remove unused WithDefaultValue --- sdk/rust/encoding/src/types.rs | 47 ---------------------------------- 1 file changed, 47 deletions(-) diff --git a/sdk/rust/encoding/src/types.rs b/sdk/rust/encoding/src/types.rs index d9b11e4aa462..8f955e3375ef 100644 --- a/sdk/rust/encoding/src/types.rs +++ b/sdk/rust/encoding/src/types.rs @@ -443,53 +443,6 @@ impl<'de> serde::Deserialize<'de> for Bytes { } } -/// This is a wrapper to encode an `A` as an `Option` such that -/// `A::default()` is encoded as `None`. -#[derive(PartialEq, Debug, Clone)] -pub struct WithDefaultValue { - as_option: Option, -} - -impl From for WithDefaultValue { - fn from(a: A) -> Self { - let as_option = if a == A::default() { None } else { Some(a) }; - Self { as_option } - } -} - -impl WithDefaultValue { - pub fn into(self) -> A { - self.as_option.unwrap_or_default() - } -} - -impl BinWriter for WithDefaultValue -where - A: BinWriter + PartialEq + Default + Clone, -{ - fn bin_write(&self, out: &mut Vec) -> crate::enc::BinResult { - crate::enc::field( - "WithDefaultValue::as_option", - crate::enc::optional_field(A::bin_write), - )(&self.as_option, out) - } -} - -impl<'a, A> NomReader<'a> for WithDefaultValue -where - A: NomReader<'a> + PartialEq + Default + Clone, -{ - fn nom_read(bytes: &'a [u8]) -> crate::nom::NomResult<'a, Self> { - nom::combinator::map( - crate::nom::field( - "WithDefaultValue::as_option", - crate::nom::optional_field(A::nom_read), - ), - |as_option| WithDefaultValue { as_option }, - )(bytes) - } -} - /// Represents `true` value in binary format. pub const BYTE_VAL_TRUE: u8 = 0xFF; /// Represents `false` value in binary format. -- GitLab