diff --git a/src/serde_utils.rs b/src/serde_utils.rs index 30d4d32f..08562e65 100644 --- a/src/serde_utils.rs +++ b/src/serde_utils.rs @@ -1,9 +1,65 @@ //! Module for special serde serializations. +pub mod btreemap_byte_values { + //! Module for serialization of BTreeMaps with hex byte values. + #![allow(missing_docs)] + + // NOTE: This module can be exactly copied to use with HashMap. + + use ::std::collections::BTreeMap; + use hashes::hex::{FromHex, ToHex}; + use serde; + + pub fn serialize(v: &BTreeMap>, s: S) + -> Result where + S: serde::Serializer, + T: serde::Serialize + ::std::hash::Hash + Eq + Ord, + { + use serde::ser::SerializeMap; + + let mut map = s.serialize_map(Some(v.len()))?; + for (key, value) in v.iter() { + map.serialize_entry(key, &value.to_hex())?; + } + map.end() + } + + pub fn deserialize<'de, D, T>(d: D) + -> Result>, D::Error> where + D: serde::Deserializer<'de>, + T: serde::Deserialize<'de> + ::std::hash::Hash + Eq + Ord, + { + use ::std::marker::PhantomData; + + struct Visitor(PhantomData); + impl<'de, T> serde::de::Visitor<'de> for Visitor where + T: serde::Deserialize<'de> + ::std::hash::Hash + Eq + Ord, + { + type Value = BTreeMap>; + + fn expecting(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + write!(f, "a map with hexadecimal values") + } + + fn visit_map>(self, mut a: A) + -> Result + { + let mut ret = BTreeMap::new(); + while let Some((key, value)) = a.next_entry()? { + ret.insert(key, FromHex::from_hex(value).map_err(serde::de::Error::custom)?); + } + Ok(ret) + } + } + + d.deserialize_map(Visitor(PhantomData)) + } +} + pub mod btreemap_as_seq { - //! Module for serialization of BTreeMaps because serde_json will - //! not serialize hashmaps with non-string keys be default. + //! Module for serialization of BTreeMaps as lists of sequences 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. @@ -61,7 +117,7 @@ pub mod btreemap_as_seq { } pub mod btreemap_as_seq_byte_values { - //! Module for serialization of BTreeMaps with Vec values because + //! Module for serialization of BTreeMaps as lists of sequences because //! serde_json will not serialize hashmaps with non-string keys be default. #![allow(missing_docs)] diff --git a/src/util/psbt/map/input.rs b/src/util/psbt/map/input.rs index a8cb0161..208f1ce0 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_byte_values"))] pub partial_sigs: BTreeMap>, /// The sighash type to be used for this input. Signatures for this input /// must use the sighash type. @@ -90,12 +91,16 @@ pub struct Input { pub final_script_witness: Option>>, /// TODO: Proof of reserves commitment /// RIPEMD160 hash to preimage map + #[cfg_attr(feature = "serde", serde(with = "::serde_utils::btreemap_byte_values"))] pub ripemd160_preimages: BTreeMap>, /// SHA256 hash to preimage map + #[cfg_attr(feature = "serde", serde(with = "::serde_utils::btreemap_byte_values"))] pub sha256_preimages: BTreeMap>, /// HSAH160 hash to preimage map + #[cfg_attr(feature = "serde", serde(with = "::serde_utils::btreemap_byte_values"))] pub hash160_preimages: BTreeMap>, /// HAS256 hash to preimage map + #[cfg_attr(feature = "serde", serde(with = "::serde_utils::btreemap_byte_values"))] pub hash256_preimages: BTreeMap>, /// Proprietary key-value pairs for this input. #[cfg_attr(feature = "serde", serde(with = "::serde_utils::btreemap_as_seq_byte_values"))] diff --git a/src/util/psbt/mod.rs b/src/util/psbt/mod.rs index feadf195..0bb430d1 100644 --- a/src/util/psbt/mod.rs +++ b/src/util/psbt/mod.rs @@ -319,6 +319,7 @@ mod tests { #[test] fn test_serde_psbt() { //! Create a full PSBT value with various fields filled and make sure it can be JSONized. + use hashes::sha256d; use util::psbt::map::Input; // create some values to use in the PSBT @@ -393,6 +394,10 @@ mod tests { )].into_iter().collect(), bip32_derivation: keypaths.clone(), final_script_witness: Some(vec![vec![1, 3], vec![5]]), + ripemd160_preimages: vec![(ripemd160::Hash::hash(&[]), vec![1, 2])].into_iter().collect(), + sha256_preimages: vec![(sha256::Hash::hash(&[]), vec![1, 2])].into_iter().collect(), + hash160_preimages: vec![(hash160::Hash::hash(&[]), vec![1, 2])].into_iter().collect(), + hash256_preimages: vec![(sha256d::Hash::hash(&[]), vec![1, 2])].into_iter().collect(), proprietary: proprietary.clone(), unknown: unknown.clone(), ..Default::default() @@ -405,6 +410,7 @@ mod tests { }], }; let encoded = ::serde_json::to_string(&psbt).unwrap(); + println!("encoded PSBT: {}", encoded); let decoded: PartiallySignedTransaction = ::serde_json::from_str(&encoded).unwrap(); assert_eq!(psbt, decoded); }