From e5f3bca2b4c90dc429e8919c0f37a6eaabd73bc0 Mon Sep 17 00:00:00 2001 From: Steven Roose Date: Sun, 25 Oct 2020 18:27:45 +0000 Subject: [PATCH] Add serde_utils module to fix JSON serialization --- src/lib.rs | 3 + src/serde_utils.rs | 107 ++++++++++++++++++++++++++++++++++++ src/util/psbt/map/global.rs | 3 + src/util/psbt/map/input.rs | 4 ++ src/util/psbt/map/output.rs | 3 + src/util/psbt/mod.rs | 94 +++++++++++++++++++++++++++++++ src/util/psbt/raw.rs | 4 ++ 7 files changed, 218 insertions(+) create mode 100644 src/serde_utils.rs diff --git a/src/lib.rs b/src/lib.rs index 5f61648e..7007cf9e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -57,6 +57,9 @@ compile_error!("rust-bitcoin cannot be used on 16-bit architectures"); mod test_macros; #[macro_use] mod internal_macros; +#[cfg(feature = "serde")] +mod serde_utils; + #[macro_use] pub mod network; pub mod blockdata; diff --git a/src/serde_utils.rs b/src/serde_utils.rs new file mode 100644 index 00000000..0c0b243d --- /dev/null +++ b/src/serde_utils.rs @@ -0,0 +1,107 @@ + +//! Module for special serde serializations. + +pub mod btreemap { + //! Module for serialization of BTreeMaps because serde_json will + //! not serialize hashmaps with non-string keys be default. + #![allow(missing_docs)] + + // NOTE: This module can be exactly copied to use with HashMap. + + use ::std::collections::BTreeMap; + use serde; + + pub fn serialize(v: &BTreeMap, s: S) + -> Result where + S: serde::Serializer, + T: serde::Serialize + ::std::hash::Hash + Eq + Ord, + U: serde::Serialize, + { + use serde::ser::SerializeSeq; + + let mut seq = s.serialize_seq(Some(v.len()))?; + for pair in v.iter() { + seq.serialize_element(&pair)?; + } + seq.end() + } + + pub fn deserialize<'de, D, T, U>(d: D) + -> Result, D::Error> where + D: serde::Deserializer<'de>, + T: serde::Deserialize<'de> + ::std::hash::Hash + Eq + Ord, + U: serde::Deserialize<'de>, + { + use ::std::marker::PhantomData; + + struct Visitor(PhantomData<(T, U)>); + impl<'de, T, U> serde::de::Visitor<'de> for Visitor where + T: serde::Deserialize<'de> + ::std::hash::Hash + Eq + Ord, + U: serde::Deserialize<'de>, + { + type Value = BTreeMap; + + fn expecting(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + write!(f, "a sequence of pairs") + } + + fn visit_seq>(self, mut a: A) + -> Result + { + let mut ret = BTreeMap::new(); + while let Some((key, value)) = a.next_element()? { + ret.insert(key, value); + } + Ok(ret) + } + } + + d.deserialize_seq(Visitor(PhantomData)) + } +} + +pub mod hex_bytes { + //! Module for serialization of byte arrays as hex strings. + #![allow(missing_docs)] + + use hashes::hex::{FromHex, ToHex}; + use serde; + + pub fn serialize(bytes: &T, serializer: S) -> Result + where T: AsRef<[u8]>, S: serde::Serializer + { + serializer.serialize_str(&bytes.as_ref().to_hex()) + } + + pub fn deserialize<'de, D, B>(d: D) -> Result + where D: serde::Deserializer<'de>, B: FromHex, + { + struct Visitor(::std::marker::PhantomData); + + impl<'de, B: FromHex> serde::de::Visitor<'de> for Visitor { + type Value = B; + + fn expecting(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + formatter.write_str("an ASCII hex string") + } + + fn visit_bytes(self, v: &[u8]) -> Result + where E: serde::de::Error, + { + if let Ok(hex) = ::std::str::from_utf8(v) { + FromHex::from_hex(hex).map_err(E::custom) + } else { + return Err(E::invalid_value(serde::de::Unexpected::Bytes(v), &self)); + } + } + + fn visit_str(self, v: &str) -> Result + where E: serde::de::Error, + { + FromHex::from_hex(v).map_err(E::custom) + } + } + + d.deserialize_str(Visitor(::std::marker::PhantomData)) + } +} diff --git a/src/util/psbt/map/global.rs b/src/util/psbt/map/global.rs index 1897e49b..aa5d6254 100644 --- a/src/util/psbt/map/global.rs +++ b/src/util/psbt/map/global.rs @@ -46,10 +46,13 @@ pub struct Global { pub version: u32, /// A global map from extended public keys to the used key fingerprint and /// derivation path as defined by BIP 32 + #[cfg_attr(feature = "serde", serde(with = "::serde_utils::btreemap"))] pub xpub: BTreeMap, /// Global proprietary key-value pairs. + #[cfg_attr(feature = "serde", serde(with = "::serde_utils::btreemap"))] pub proprietary: BTreeMap>, /// Unknown global key-value pairs. + #[cfg_attr(feature = "serde", serde(with = "::serde_utils::btreemap"))] pub unknown: BTreeMap>, } diff --git a/src/util/psbt/map/input.rs b/src/util/psbt/map/input.rs index 0e79fc2c..112eae9a 100644 --- a/src/util/psbt/map/input.rs +++ b/src/util/psbt/map/input.rs @@ -70,6 +70,7 @@ pub struct Input { pub witness_utxo: Option, /// A map from public keys to their corresponding signature as would be /// pushed to the stack from a scriptSig or witness. + #[cfg_attr(feature = "serde", serde(with = "::serde_utils::btreemap"))] pub partial_sigs: BTreeMap>, /// The sighash type to be used for this input. Signatures for this input /// must use the sighash type. @@ -80,6 +81,7 @@ pub struct Input { pub witness_script: Option