Add data types for raw PSBT key-value pairs
- Add (en)decoding logic for said data types
This commit is contained in:
parent
4fa39c4a3e
commit
528e39334c
|
@ -2,6 +2,7 @@ use std::error;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use blockdata::transaction::Transaction;
|
use blockdata::transaction::Transaction;
|
||||||
|
use util::psbt::raw;
|
||||||
|
|
||||||
/// Ways that a Partially Signed Transaction might fail.
|
/// Ways that a Partially Signed Transaction might fail.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -12,9 +13,9 @@ pub enum Error {
|
||||||
/// The separator for a PSBT must be `0xff`.
|
/// The separator for a PSBT must be `0xff`.
|
||||||
InvalidSeparator,
|
InvalidSeparator,
|
||||||
/// Known keys must be according to spec.
|
/// Known keys must be according to spec.
|
||||||
InvalidKey,
|
InvalidKey(raw::Key),
|
||||||
/// Keys within key-value map should never be duplicated.
|
/// Keys within key-value map should never be duplicated.
|
||||||
DuplicateKey,
|
DuplicateKey(raw::Key),
|
||||||
/// The scriptSigs for the unsigned transaction must be empty.
|
/// The scriptSigs for the unsigned transaction must be empty.
|
||||||
UnsignedTxHasScriptSigs,
|
UnsignedTxHasScriptSigs,
|
||||||
/// The scriptWitnesses for the unsigned transaction must be empty.
|
/// The scriptWitnesses for the unsigned transaction must be empty.
|
||||||
|
@ -38,12 +39,12 @@ pub enum Error {
|
||||||
impl fmt::Display for Error {
|
impl fmt::Display for Error {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match *self {
|
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::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::NonStandardSigHashType(ref sht) => write!(f, "{}: {}", error::Error::description(self), sht),
|
||||||
Error::InvalidMagic
|
Error::InvalidMagic
|
||||||
| Error::InvalidSeparator
|
| Error::InvalidSeparator
|
||||||
| Error::InvalidKey
|
|
||||||
| Error::DuplicateKey
|
|
||||||
| Error::UnsignedTxHasScriptSigs
|
| Error::UnsignedTxHasScriptSigs
|
||||||
| Error::UnsignedTxHasScriptWitnesses
|
| Error::UnsignedTxHasScriptWitnesses
|
||||||
| Error::MustHaveUnsignedTx
|
| Error::MustHaveUnsignedTx
|
||||||
|
@ -57,8 +58,8 @@ impl error::Error for Error {
|
||||||
match *self {
|
match *self {
|
||||||
Error::InvalidMagic => "invalid magic",
|
Error::InvalidMagic => "invalid magic",
|
||||||
Error::InvalidSeparator => "invalid separator",
|
Error::InvalidSeparator => "invalid separator",
|
||||||
Error::InvalidKey => "invalid key",
|
Error::InvalidKey(..) => "invalid key",
|
||||||
Error::DuplicateKey => "duplicate key",
|
Error::DuplicateKey(..) => "duplicate key",
|
||||||
Error::UnsignedTxHasScriptSigs => "the unsigned transaction has script sigs",
|
Error::UnsignedTxHasScriptSigs => "the unsigned transaction has script sigs",
|
||||||
Error::UnsignedTxHasScriptWitnesses => "the unsigned transaction has script witnesses",
|
Error::UnsignedTxHasScriptWitnesses => "the unsigned transaction has script witnesses",
|
||||||
Error::MustHaveUnsignedTx => {
|
Error::MustHaveUnsignedTx => {
|
||||||
|
|
|
@ -6,3 +6,26 @@
|
||||||
|
|
||||||
mod error;
|
mod error;
|
||||||
pub use self::error::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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<D: Decoder> Decodable<D> for Key {
|
||||||
|
fn consensus_decode(d: &mut D) -> Result<Self, encode::Error> {
|
||||||
|
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<S: Encoder> Encodable<S> 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<S: Encoder> Encodable<S> for Pair {
|
||||||
|
fn consensus_encode(&self, s: &mut S) -> Result<(), encode::Error> {
|
||||||
|
self.key.consensus_encode(s)?;
|
||||||
|
self.value.consensus_encode(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<D: Decoder> Decodable<D> for Pair {
|
||||||
|
fn consensus_decode(d: &mut D) -> Result<Self, encode::Error> {
|
||||||
|
Ok(Pair {
|
||||||
|
key: Decodable::consensus_decode(d)?,
|
||||||
|
value: Decodable::consensus_decode(d)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue