diff --git a/src/util/psbt/error.rs b/src/util/psbt/error.rs index 34095e2..bb5cf90 100644 --- a/src/util/psbt/error.rs +++ b/src/util/psbt/error.rs @@ -2,6 +2,7 @@ use std::error; use std::fmt; use blockdata::transaction::Transaction; +use util::psbt::raw; /// Ways that a Partially Signed Transaction might fail. #[derive(Debug)] @@ -12,9 +13,9 @@ pub enum Error { /// The separator for a PSBT must be `0xff`. InvalidSeparator, /// Known keys must be according to spec. - InvalidKey, + InvalidKey(raw::Key), /// Keys within key-value map should never be duplicated. - DuplicateKey, + DuplicateKey(raw::Key), /// The scriptSigs for the unsigned transaction must be empty. UnsignedTxHasScriptSigs, /// The scriptWitnesses for the unsigned transaction must be empty. @@ -38,12 +39,12 @@ pub enum Error { impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { + Error::InvalidKey(ref rkey) => write!(f, "{}: {}", error::Error::description(self), rkey), + Error::DuplicateKey(ref rkey) => write!(f, "{}: {}", error::Error::description(self), rkey), Error::UnexpectedUnsignedTx { expected: ref e, actual: ref a } => write!(f, "{}: expected {}, actual {}", error::Error::description(self), e.txid(), a.txid()), Error::NonStandardSigHashType(ref sht) => write!(f, "{}: {}", error::Error::description(self), sht), Error::InvalidMagic | Error::InvalidSeparator - | Error::InvalidKey - | Error::DuplicateKey | Error::UnsignedTxHasScriptSigs | Error::UnsignedTxHasScriptWitnesses | Error::MustHaveUnsignedTx @@ -57,8 +58,8 @@ impl error::Error for Error { match *self { Error::InvalidMagic => "invalid magic", Error::InvalidSeparator => "invalid separator", - Error::InvalidKey => "invalid key", - Error::DuplicateKey => "duplicate key", + Error::InvalidKey(..) => "invalid key", + Error::DuplicateKey(..) => "duplicate key", Error::UnsignedTxHasScriptSigs => "the unsigned transaction has script sigs", Error::UnsignedTxHasScriptWitnesses => "the unsigned transaction has script witnesses", Error::MustHaveUnsignedTx => { diff --git a/src/util/psbt/mod.rs b/src/util/psbt/mod.rs index f0438e1..421c7cb 100644 --- a/src/util/psbt/mod.rs +++ b/src/util/psbt/mod.rs @@ -6,3 +6,26 @@ mod error; pub use self::error::Error; + +pub mod raw; + +#[cfg(test)] +mod tests { + use consensus::encode::{deserialize, serialize}; + use util::psbt::raw; + + #[test] + fn serialize_then_deserialize_psbtkvpair() { + let expected = raw::Pair { + key: raw::Key { + type_value: 0u8, + key: vec![42u8, 69u8], + }, + value: vec![69u8, 42u8, 4u8], + }; + + let actual: raw::Pair = deserialize(&serialize(&expected)).unwrap(); + + assert_eq!(expected, actual); + } +} diff --git a/src/util/psbt/raw.rs b/src/util/psbt/raw.rs new file mode 100644 index 0000000..04abc70 --- /dev/null +++ b/src/util/psbt/raw.rs @@ -0,0 +1,94 @@ +//! # Raw PSBT Key-Value Pairs +//! +//! Raw PSBT key-value pairs as defined at +//! https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki. + +use std::fmt; + +use consensus::encode::{Decodable, Encodable, VarInt, MAX_VEC_SIZE}; +use consensus::encode::{self, Decoder, Encoder}; +use util::psbt::Error; + +/// A PSBT key in its raw byte form. +#[derive(Debug, PartialEq, Hash, Eq, Clone)] +pub struct Key { + /// The type of this PSBT key. + pub type_value: u8, + /// The key itself in raw byte form. + pub key: Vec, +} + +/// A PSBT key-value pair in its raw byte form. +#[derive(Debug, PartialEq)] +pub struct Pair { + /// The key of this key-value pair. + pub key: Key, + /// The value of this key-value pair in raw byte form. + pub value: Vec, +} + +impl fmt::Display for Key { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use hex; + + write!(f, "type: {:#x}, key: {}", self.type_value, hex::encode(&self.key)) + } +} + +impl Decodable for Key { + fn consensus_decode(d: &mut D) -> Result { + let VarInt(byte_size): VarInt = Decodable::consensus_decode(d)?; + + if byte_size == 0 { + return Err(Error::NoMorePairs.into()); + } + + let key_byte_size: u64 = byte_size - 1; + + if key_byte_size > MAX_VEC_SIZE as u64 { + return Err(encode::Error::OversizedVectorAllocation { requested: key_byte_size as usize, max: MAX_VEC_SIZE } ) + } + + let type_value: u8 = Decodable::consensus_decode(d)?; + + let mut key = Vec::with_capacity(key_byte_size as usize); + for _ in 0..key_byte_size { + key.push(Decodable::consensus_decode(d)?); + } + + Ok(Key { + type_value: type_value, + key: key, + }) + } +} + +impl Encodable for Key { + fn consensus_encode(&self, s: &mut S) -> Result<(), encode::Error> { + VarInt((self.key.len() + 1) as u64).consensus_encode(s)?; + + self.type_value.consensus_encode(s)?; + + for key in &self.key { + key.consensus_encode(s)? + } + + Ok(()) + } +} + +impl Encodable for Pair { + fn consensus_encode(&self, s: &mut S) -> Result<(), encode::Error> { + self.key.consensus_encode(s)?; + self.value.consensus_encode(s) + } +} + +impl Decodable for Pair { + fn consensus_decode(d: &mut D) -> Result { + Ok(Pair { + key: Decodable::consensus_decode(d)?, + value: Decodable::consensus_decode(d)?, + }) + } +}