From f5ea7c8c3e81fc17c255a705f1bbf8e51112e2d9 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Sat, 7 Oct 2023 09:27:27 +0200 Subject: [PATCH 1/3] primitives: add manual serde serialization for Outpoint Required downstream to allow correct string encoding for Outpoint in JSON/YAML/TOML and other text serialization formats --- primitives/src/tx.rs | 63 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/primitives/src/tx.rs b/primitives/src/tx.rs index c550cc38..05d74dfe 100644 --- a/primitives/src/tx.rs +++ b/primitives/src/tx.rs @@ -101,7 +101,6 @@ impl FromStr for Vout { #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Display)] #[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] #[strict_type(lib = LIB_NAME_BITCOIN)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))] #[display("{txid}:{vout}")] pub struct Outpoint { pub txid: Txid, @@ -162,6 +161,68 @@ impl FromStr for Outpoint { } } +#[cfg(feature = "serde")] +mod _serde_outpoint { + use std::fmt; + + use serde::de::{SeqAccess, Visitor}; + use serde::ser::SerializeTuple; + use serde::{Deserialize, Deserializer, Serialize, Serializer}; + + use super::*; + + impl Serialize for Outpoint { + fn serialize(&self, serializer: S) -> Result + where S: Serializer { + if serializer.is_human_readable() { + serializer.serialize_str(&self.to_string()) + } else { + let mut ser = serializer.serialize_tuple(2)?; + ser.serialize_element(&self.txid)?; + ser.serialize_element(&self.vout)?; + ser.end() + } + } + } + + impl<'de> Deserialize<'de> for Outpoint { + fn deserialize(deserializer: D) -> Result + where D: Deserializer<'de> { + use serde::de::Error; + if deserializer.is_human_readable() { + String::deserialize(deserializer).and_then(|string| { + Self::from_str(&string) + .map_err(|_| D::Error::custom("wrong outpoint string representation")) + }) + } else { + struct OutpointVisitor; + + impl<'de> Visitor<'de> for OutpointVisitor { + type Value = Outpoint; + + fn expecting(&self, formatter: &mut Formatter) -> fmt::Result { + write!(formatter, "a transaction outpoint") + } + + fn visit_seq(self, mut seq: A) -> Result + where A: SeqAccess<'de> { + let mut outpoint = Outpoint::coinbase(); + outpoint.txid = seq + .next_element()? + .ok_or_else(|| Error::invalid_length(0, &self))?; + outpoint.vout = seq + .next_element()? + .ok_or_else(|| Error::invalid_length(1, &self))?; + Ok(outpoint) + } + } + + deserializer.deserialize_tuple(2, OutpointVisitor) + } + } + } +} + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] #[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] #[strict_type(lib = LIB_NAME_BITCOIN)] From 6d8c554f5f858e15c50edb4b750b16a2155c9b51 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Sat, 7 Oct 2023 13:01:43 +0200 Subject: [PATCH 2/3] primitives: add todo for making Chain non_exhaustive in the next version --- primitives/src/util.rs | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/primitives/src/util.rs b/primitives/src/util.rs index d7c70642..909cc8a2 100644 --- a/primitives/src/util.rs +++ b/primitives/src/util.rs @@ -53,6 +53,7 @@ pub struct ChainParseError(String); serde(crate = "serde_crate", rename_all = "lowercase") )] #[repr(u8)] +// TODO: v0.11 make non_exhaustive pub enum Chain { #[default] #[display("mainnet")] @@ -68,6 +69,16 @@ pub enum Chain { Signet = 0x84, } +impl Chain { + #[inline] + pub fn is_test_chain(self) -> bool { + match self { + Chain::Bitcoin => false, + Chain::Testnet3 | Chain::Regtest | Chain::Signet => true, + } + } +} + impl FromStr for Chain { type Err = ChainParseError; @@ -82,13 +93,3 @@ impl FromStr for Chain { }) } } - -impl Chain { - #[inline] - pub fn is_test_chain(self) -> bool { - match self { - Chain::Bitcoin => false, - Chain::Testnet3 | Chain::Regtest | Chain::Signet => true, - } - } -} From 5d73e24feb830866725beb87e0a3de52c81ad50b Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Sat, 7 Oct 2023 13:09:04 +0200 Subject: [PATCH 3/3] primitives: add manual serde serialization for Chain Required downstream to allow correct string encoding in JSON/YAML/TOML and other text serialization formats --- primitives/src/util.rs | 37 ++++++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/primitives/src/util.rs b/primitives/src/util.rs index 909cc8a2..4c919992 100644 --- a/primitives/src/util.rs +++ b/primitives/src/util.rs @@ -47,11 +47,6 @@ pub struct ChainParseError(String); #[strict_type(lib = LIB_NAME_BITCOIN, tags = repr, into_u8, try_from_u8)] #[derive(CommitEncode)] #[commit_encode(strategy = strict)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "lowercase") -)] #[repr(u8)] // TODO: v0.11 make non_exhaustive pub enum Chain { @@ -93,3 +88,35 @@ impl FromStr for Chain { }) } } + +#[cfg(feature = "serde")] +mod _serde { + use serde::de::Error; + use serde::{Deserialize, Deserializer, Serialize, Serializer}; + + use super::*; + + impl Serialize for Chain { + fn serialize(&self, serializer: S) -> Result + where S: Serializer { + if serializer.is_human_readable() { + self.to_string().serialize(serializer) + } else { + (*self as u8).serialize(serializer) + } + } + } + + impl<'de> Deserialize<'de> for Chain { + fn deserialize(deserializer: D) -> Result + where D: Deserializer<'de> { + if deserializer.is_human_readable() { + let s = String::deserialize(deserializer)?; + Chain::from_str(&s).map_err(D::Error::custom) + } else { + let v = u8::deserialize(deserializer)?; + Chain::try_from(v).map_err(D::Error::custom) + } + } + } +}