Merge pull request #103 from dongcarl/psbt

BIP174 (de)serialization support
This commit is contained in:
Andrew Poelstra 2019-02-28 17:20:37 +00:00 committed by GitHub
commit 67e4671457
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 1645 additions and 0 deletions

View File

@ -47,3 +47,7 @@ path = "fuzz_targets/deserialize_udecimal.rs"
[[bin]]
name = "outpoint_string"
path = "fuzz_targets/outpoint_string.rs"
[[bin]]
name = "deserialize_psbt"
path = "fuzz_targets/deserialize_psbt.rs"

View File

@ -0,0 +1,52 @@
extern crate bitcoin;
fn do_test(data: &[u8]) {
let _: Result<bitcoin::util::psbt::PartiallySignedTransaction, _> = bitcoin::consensus::encode::deserialize(data);
}
#[cfg(feature = "afl")]
#[macro_use] extern crate afl;
#[cfg(feature = "afl")]
fn main() {
fuzz!(|data| {
do_test(&data);
});
}
#[cfg(feature = "honggfuzz")]
#[macro_use] extern crate honggfuzz;
#[cfg(feature = "honggfuzz")]
fn main() {
loop {
fuzz!(|data| {
do_test(data);
});
}
}
#[cfg(test)]
mod tests {
fn extend_vec_from_hex(hex: &str, out: &mut Vec<u8>) {
let mut b = 0;
for (idx, c) in hex.as_bytes().iter().enumerate() {
b <<= 4;
match *c {
b'A'...b'F' => b |= c - b'A' + 10,
b'a'...b'f' => b |= c - b'a' + 10,
b'0'...b'9' => b |= c - b'0',
_ => panic!("Bad hex"),
}
if (idx & 1) == 1 {
out.push(b);
b = 0;
}
}
}
#[test]
fn duplicate_crash() {
let mut a = Vec::new();
extend_vec_from_hex("00", &mut a);
super::do_test(&a);
}
}

View File

@ -45,6 +45,7 @@ use bitcoin_hashes::{sha256d, Hash as HashTrait};
use secp256k1;
use util::base58;
use util::psbt;
/// Encoding error
#[derive(Debug)]
@ -59,6 +60,8 @@ pub enum Error {
ByteOrder(io::Error),
/// secp-related error
Secp256k1(secp256k1::Error),
/// PSBT-related error
Psbt(psbt::Error),
/// Network magic was not expected
UnexpectedNetworkMagic {
/// The expected network magic
@ -102,6 +105,7 @@ impl fmt::Display for Error {
Error::Bech32(ref e) => fmt::Display::fmt(e, f),
Error::ByteOrder(ref e) => fmt::Display::fmt(e, f),
Error::Secp256k1(ref e) => fmt::Display::fmt(e, f),
Error::Psbt(ref e) => fmt::Display::fmt(e, f),
Error::UnexpectedNetworkMagic { expected: ref e, actual: ref a } => write!(f, "{}: expected {}, actual {}", error::Error::description(self), e, a),
Error::OversizedVectorAllocation { requested: ref r, max: ref m } => write!(f, "{}: requested {}, maximum {}", error::Error::description(self), r, m),
Error::InvalidChecksum { expected: ref e, actual: ref a } => write!(f, "{}: expected {}, actual {}", error::Error::description(self), hex_encode(e), hex_encode(a)),
@ -123,6 +127,7 @@ impl error::Error for Error {
Error::Bech32(ref e) => Some(e),
Error::ByteOrder(ref e) => Some(e),
Error::Secp256k1(ref e) => Some(e),
Error::Psbt(ref e) => Some(e),
Error::UnexpectedNetworkMagic { .. }
| Error::OversizedVectorAllocation { .. }
| Error::InvalidChecksum { .. }
@ -142,6 +147,7 @@ impl error::Error for Error {
Error::Bech32(ref e) => e.description(),
Error::ByteOrder(ref e) => e.description(),
Error::Secp256k1(ref e) => e.description(),
Error::Psbt(ref e) => e.description(),
Error::UnexpectedNetworkMagic { .. } => "unexpected network magic",
Error::OversizedVectorAllocation { .. } => "allocation of oversized vector requested",
Error::InvalidChecksum { .. } => "invalid checksum",
@ -183,6 +189,13 @@ impl From<io::Error> for Error {
}
}
#[doc(hidden)]
impl From<psbt::Error> for Error {
fn from(e: psbt::Error) -> Error {
Error::Psbt(e)
}
}
/// Encode an object into a vector
pub fn serialize<T: ?Sized>(data: &T) -> Vec<u8>
where T: Encodable<Cursor<Vec<u8>>>,
@ -505,6 +518,7 @@ impl_array!(8);
impl_array!(12);
impl_array!(16);
impl_array!(32);
impl_array!(33);
impl<S: Encoder, T: Encodable<S>> Encodable<S> for [T] {
#[inline]

View File

@ -25,6 +25,7 @@ pub mod contracthash;
pub mod decimal;
pub mod hash;
pub mod misc;
pub mod psbt;
pub mod uint;
use std::{error, fmt};

87
src/util/psbt/error.rs Normal file
View File

@ -0,0 +1,87 @@
// Rust Bitcoin Library
// Written by
// The Rust Bitcoin developers
//
// To the extent possible under law, the author(s) have dedicated all
// copyright and related and neighboring rights to this software to
// the public domain worldwide. This software is distributed without
// any warranty.
//
// You should have received a copy of the CC0 Public Domain Dedication
// along with this software.
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
//
use std::error;
use std::fmt;
use blockdata::transaction::Transaction;
use util::psbt::raw;
/// Ways that a Partially Signed Transaction might fail.
#[derive(Debug)]
pub enum Error {
/// Magic bytes for a PSBT must be the ASCII for "psbt" serialized in most
/// significant byte order.
InvalidMagic,
/// The separator for a PSBT must be `0xff`.
InvalidSeparator,
/// Known keys must be according to spec.
InvalidKey(raw::Key),
/// Keys within key-value map should never be duplicated.
DuplicateKey(raw::Key),
/// The scriptSigs for the unsigned transaction must be empty.
UnsignedTxHasScriptSigs,
/// The scriptWitnesses for the unsigned transaction must be empty.
UnsignedTxHasScriptWitnesses,
/// A PSBT must have an unsigned transaction.
MustHaveUnsignedTx,
/// Signals that there are no more key-value pairs in a key-value map.
NoMorePairs,
/// Attempting to merge with a PSBT describing a different unsigned
/// transaction.
UnexpectedUnsignedTx {
/// Expected
expected: Transaction,
/// Actual
actual: Transaction,
},
/// Unable to parse as a standard SigHash type.
NonStandardSigHashType(u32),
}
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::UnsignedTxHasScriptSigs
| Error::UnsignedTxHasScriptWitnesses
| Error::MustHaveUnsignedTx
| Error::NoMorePairs => f.write_str(error::Error::description(self))
}
}
}
impl error::Error for Error {
fn description(&self) -> &str {
match *self {
Error::InvalidMagic => "invalid magic",
Error::InvalidSeparator => "invalid separator",
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 => {
"partially signed transactions must have an unsigned transaction"
}
Error::NoMorePairs => "no more key-value pairs for this psbt map",
Error::UnexpectedUnsignedTx { .. } => "different unsigned transaction",
Error::NonStandardSigHashType(..) => "non-standard sighash type",
}
}
}

150
src/util/psbt/macros.rs Normal file
View File

@ -0,0 +1,150 @@
// Rust Bitcoin Library
// Written by
// The Rust Bitcoin developers
//
// To the extent possible under law, the author(s) have dedicated all
// copyright and related and neighboring rights to this software to
// the public domain worldwide. This software is distributed without
// any warranty.
//
// You should have received a copy of the CC0 Public Domain Dedication
// along with this software.
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
//
#[allow(unused_macros)]
macro_rules! hex_psbt {
($s:expr) => { ::consensus::encode::deserialize(&::hex::decode($s).unwrap()) };
}
macro_rules! merge {
($thing:ident, $slf:ident, $other:ident) => {
if let (&None, Some($thing)) = (&$slf.$thing, $other.$thing) {
$slf.$thing = Some($thing);
}
};
}
macro_rules! impl_psbt_de_serialize {
($thing:ty) => {
impl_psbt_serialize!($thing);
impl_psbt_deserialize!($thing);
};
}
macro_rules! impl_psbt_deserialize {
($thing:ty) => {
impl ::util::psbt::serialize::Deserialize for $thing {
fn deserialize(bytes: &[u8]) -> Result<Self, ::consensus::encode::Error> {
::consensus::encode::deserialize(&bytes[..])
}
}
};
}
macro_rules! impl_psbt_serialize {
($thing:ty) => {
impl ::util::psbt::serialize::Serialize for $thing {
fn serialize(&self) -> Vec<u8> {
::consensus::encode::serialize(self)
}
}
};
}
macro_rules! impl_psbtmap_consensus_encoding {
($thing:ty) => {
impl<S: ::consensus::encode::Encoder> ::consensus::encode::Encodable<S> for $thing {
fn consensus_encode(&self, s: &mut S) -> Result<(), ::consensus::encode::Error> {
for pair in ::util::psbt::Map::get_pairs(self)? {
::consensus::encode::Encodable::consensus_encode(&pair, s)?
}
::consensus::encode::Encodable::consensus_encode(&0x00_u8, s)
}
}
};
}
macro_rules! impl_psbtmap_consensus_decoding {
($thing:ty) => {
impl<D: ::consensus::encode::Decoder> ::consensus::encode::Decodable<D> for $thing {
fn consensus_decode(d: &mut D) -> Result<Self, ::consensus::encode::Error> {
let mut rv: Self = ::std::default::Default::default();
loop {
match ::consensus::encode::Decodable::consensus_decode(d) {
Ok(pair) => ::util::psbt::Map::insert_pair(&mut rv, pair)?,
Err(::consensus::encode::Error::Psbt(::util::psbt::Error::NoMorePairs)) => return Ok(rv),
Err(e) => return Err(e),
}
}
}
}
};
}
macro_rules! impl_psbtmap_consensus_enc_dec_oding {
($thing:ty) => {
impl_psbtmap_consensus_decoding!($thing);
impl_psbtmap_consensus_encoding!($thing);
};
}
#[cfg_attr(rustfmt, rustfmt_skip)]
macro_rules! impl_psbt_insert_pair {
($slf:ident.$unkeyed_name:ident <= <$raw_key:ident: _>|<$raw_value:ident: $unkeyed_value_type:ty>) => {
if $raw_key.key.is_empty() {
if let None = $slf.$unkeyed_name {
let val: $unkeyed_value_type = ::util::psbt::serialize::Deserialize::deserialize(&$raw_value)?;
$slf.$unkeyed_name = Some(val)
} else {
return Err(::util::psbt::Error::DuplicateKey($raw_key).into());
}
} else {
return Err(::util::psbt::Error::InvalidKey($raw_key).into());
}
};
($slf:ident.$keyed_name:ident <= <$raw_key:ident: $keyed_key_type:ty>|<$raw_value:ident: $keyed_value_type:ty>) => {
if !$raw_key.key.is_empty() {
let key_val: $keyed_key_type = ::util::psbt::serialize::Deserialize::deserialize(&$raw_key.key)?;
if $slf.$keyed_name.contains_key(&key_val) {
return Err(::util::psbt::Error::DuplicateKey($raw_key).into());
} else {
let val: $keyed_value_type = ::util::psbt::serialize::Deserialize::deserialize(&$raw_value)?;
$slf.$keyed_name.insert(key_val, val);
}
} else {
return Err(::util::psbt::Error::InvalidKey($raw_key).into());
}
};
}
#[cfg_attr(rustfmt, rustfmt_skip)]
macro_rules! impl_psbt_get_pair {
($rv:ident.push($slf:ident.$unkeyed_name:ident as <$unkeyed_typeval:expr, _>|<$unkeyed_value_type:ty>)) => {
if let Some(ref $unkeyed_name) = $slf.$unkeyed_name {
$rv.push(::util::psbt::raw::Pair {
key: ::util::psbt::raw::Key {
type_value: $unkeyed_typeval,
key: vec![],
},
value: ::util::psbt::serialize::Serialize::serialize($unkeyed_name),
});
}
};
($rv:ident.push($slf:ident.$keyed_name:ident as <$keyed_typeval:expr, $keyed_key_type:ty>|<$keyed_value_type:ty>)) => {
for (key, val) in &$slf.$keyed_name {
$rv.push(::util::psbt::raw::Pair {
key: ::util::psbt::raw::Key {
type_value: $keyed_typeval,
key: ::util::psbt::serialize::Serialize::serialize(key),
},
value: ::util::psbt::serialize::Serialize::serialize(val),
});
}
};
}

183
src/util/psbt/map/global.rs Normal file
View File

@ -0,0 +1,183 @@
// Rust Bitcoin Library
// Written by
// The Rust Bitcoin developers
//
// To the extent possible under law, the author(s) have dedicated all
// copyright and related and neighboring rights to this software to
// the public domain worldwide. This software is distributed without
// any warranty.
//
// You should have received a copy of the CC0 Public Domain Dedication
// along with this software.
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
//
use std::collections::HashMap;
use std::io::Cursor;
use blockdata::transaction::Transaction;
use consensus::encode::{self, Encodable, Decodable, Decoder};
use util::psbt::map::Map;
use util::psbt::raw;
use util::psbt;
use util::psbt::Error;
/// A key-value map for global data.
#[derive(Clone, Debug, PartialEq)]
pub struct Global {
/// The unsigned transaction, scriptSigs and witnesses for each input must be
/// empty.
pub unsigned_tx: Transaction,
/// Unknown global key-value pairs.
pub unknown: HashMap<raw::Key, Vec<u8>>,
}
impl Global {
/// Create a Global from an unsigned transaction, error if not unsigned
pub fn from_unsigned_tx(tx: Transaction) -> Result<Self, psbt::Error> {
for txin in &tx.input {
if !txin.script_sig.is_empty() {
return Err(Error::UnsignedTxHasScriptSigs);
}
if !txin.witness.is_empty() {
return Err(Error::UnsignedTxHasScriptWitnesses);
}
}
Ok(Global {
unsigned_tx: tx,
unknown: Default::default(),
})
}
}
impl Map for Global {
fn insert_pair(&mut self, pair: raw::Pair) -> Result<(), encode::Error> {
let raw::Pair {
key: raw_key,
value: raw_value,
} = pair;
match raw_key.type_value {
0u8 => {
return Err(Error::DuplicateKey(raw_key).into());
}
_ => {
if self.unknown.contains_key(&raw_key) {
return Err(Error::DuplicateKey(raw_key).into());
} else {
self.unknown.insert(raw_key, raw_value);
}
}
}
Ok(())
}
fn get_pairs(&self) -> Result<Vec<raw::Pair>, encode::Error> {
let mut rv: Vec<raw::Pair> = Default::default();
rv.push(raw::Pair {
key: raw::Key {
type_value: 0u8,
key: vec![],
},
value: {
// Manually serialized to ensure 0-input txs are serialized
// without witnesses.
let mut ret = Vec::new();
self.unsigned_tx.version.consensus_encode(&mut ret)?;
self.unsigned_tx.input.consensus_encode(&mut ret)?;
self.unsigned_tx.output.consensus_encode(&mut ret)?;
self.unsigned_tx.lock_time.consensus_encode(&mut ret)?;
ret
},
});
for (key, value) in self.unknown.iter() {
rv.push(raw::Pair {
key: key.clone(),
value: value.clone(),
});
}
Ok(rv)
}
fn merge(&mut self, other: Self) -> Result<(), psbt::Error> {
if self.unsigned_tx != other.unsigned_tx {
return Err(psbt::Error::UnexpectedUnsignedTx {
expected: self.unsigned_tx.clone(),
actual: other.unsigned_tx,
});
}
self.unknown.extend(other.unknown);
Ok(())
}
}
impl_psbtmap_consensus_encoding!(Global);
impl<D: Decoder> Decodable<D> for Global {
fn consensus_decode(d: &mut D) -> Result<Self, encode::Error> {
let mut tx: Option<Transaction> = None;
let mut unknowns: HashMap<raw::Key, Vec<u8>> = Default::default();
loop {
match raw::Pair::consensus_decode(d) {
Ok(pair) => {
match pair.key.type_value {
0u8 => {
// key has to be empty
if pair.key.key.is_empty() {
// there can only be one unsigned transaction
if tx.is_none() {
let vlen: usize = pair.value.len();
let mut decoder = Cursor::new(pair.value);
// Manually deserialized to ensure 0-input
// txs without witnesses are deserialized
// properly.
tx = Some(Transaction {
version: Decodable::consensus_decode(&mut decoder)?,
input: Decodable::consensus_decode(&mut decoder)?,
output: Decodable::consensus_decode(&mut decoder)?,
lock_time: Decodable::consensus_decode(&mut decoder)?,
});
if decoder.position() != vlen as u64 {
return Err(encode::Error::ParseFailed("data not consumed entirely when explicitly deserializing"))
}
} else {
return Err(Error::DuplicateKey(pair.key).into())
}
} else {
return Err(Error::InvalidKey(pair.key).into())
}
}
_ => {
if unknowns.contains_key(&pair.key) {
return Err(Error::DuplicateKey(pair.key).into());
} else {
unknowns.insert(pair.key, pair.value);
}
}
}
}
Err(::consensus::encode::Error::Psbt(::util::psbt::Error::NoMorePairs)) => break,
Err(e) => return Err(e),
}
}
if let Some(tx) = tx {
let mut rv: Global = Global::from_unsigned_tx(tx)?;
rv.unknown = unknowns;
Ok(rv)
} else {
Err(Error::MustHaveUnsignedTx.into())
}
}
}

197
src/util/psbt/map/input.rs Normal file
View File

@ -0,0 +1,197 @@
// Rust Bitcoin Library
// Written by
// The Rust Bitcoin developers
//
// To the extent possible under law, the author(s) have dedicated all
// copyright and related and neighboring rights to this software to
// the public domain worldwide. This software is distributed without
// any warranty.
//
// You should have received a copy of the CC0 Public Domain Dedication
// along with this software.
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
//
use std::collections::HashMap;
use blockdata::script::Script;
use blockdata::transaction::{SigHashType, Transaction, TxOut};
use consensus::encode;
use util::bip32::{DerivationPath, Fingerprint};
use util::key::PublicKey;
use util::psbt;
use util::psbt::map::Map;
use util::psbt::raw;
use util::psbt::Error;
/// A key-value map for an input of the corresponding index in the unsigned
/// transaction.
#[derive(Clone, Default, Debug, PartialEq)]
pub struct Input {
/// The non-witness transaction this input spends from. Should only be
/// [std::option::Option::Some] for inputs which spend non-segwit outputs or
/// if it is unknown whether an input spends a segwit output.
pub non_witness_utxo: Option<Transaction>,
/// The transaction output this input spends from. Should only be
/// [std::option::Option::Some] for inputs which spend segwit outputs,
/// including P2SH embedded ones.
pub witness_utxo: Option<TxOut>,
/// A map from public keys to their corresponding signature as would be
/// pushed to the stack from a scriptSig or witness.
pub partial_sigs: HashMap<PublicKey, Vec<u8>>,
/// The sighash type to be used for this input. Signatures for this input
/// must use the sighash type.
pub sighash_type: Option<SigHashType>,
/// The redeem script for this input.
pub redeem_script: Option<Script>,
/// The witness script for this input.
pub witness_script: Option<Script>,
/// A map from public keys needed to sign this input to their corresponding
/// master key fingerprints and derivation paths.
pub hd_keypaths: HashMap<PublicKey, (Fingerprint, DerivationPath)>,
/// The finalized, fully-constructed scriptSig with signatures and any other
/// scripts necessary for this input to pass validation.
pub final_script_sig: Option<Script>,
/// The finalized, fully-constructed scriptWitness with signatures and any
/// other scripts necessary for this input to pass validation.
pub final_script_witness: Option<Vec<Vec<u8>>>,
/// Unknown key-value pairs for this input.
pub unknown: HashMap<raw::Key, Vec<u8>>,
}
impl Map for Input {
fn insert_pair(&mut self, pair: raw::Pair) -> Result<(), encode::Error> {
let raw::Pair {
key: raw_key,
value: raw_value,
} = pair;
match raw_key.type_value {
0u8 => {
impl_psbt_insert_pair! {
self.non_witness_utxo <= <raw_key: _>|<raw_value: Transaction>
}
}
1u8 => {
impl_psbt_insert_pair! {
self.witness_utxo <= <raw_key: _>|<raw_value: TxOut>
}
}
3u8 => {
impl_psbt_insert_pair! {
self.sighash_type <= <raw_key: _>|<raw_value: SigHashType>
}
}
4u8 => {
impl_psbt_insert_pair! {
self.redeem_script <= <raw_key: _>|<raw_value: Script>
}
}
5u8 => {
impl_psbt_insert_pair! {
self.witness_script <= <raw_key: _>|<raw_value: Script>
}
}
7u8 => {
impl_psbt_insert_pair! {
self.final_script_sig <= <raw_key: _>|<raw_value: Script>
}
}
8u8 => {
impl_psbt_insert_pair! {
self.final_script_witness <= <raw_key: _>|<raw_value: Vec<Vec<u8>>>
}
}
2u8 => {
impl_psbt_insert_pair! {
self.partial_sigs <= <raw_key: PublicKey>|<raw_value: Vec<u8>>
}
}
6u8 => {
impl_psbt_insert_pair! {
self.hd_keypaths <= <raw_key: PublicKey>|<raw_value: (Fingerprint, DerivationPath)>
}
}
_ => {
if self.unknown.contains_key(&raw_key) {
return Err(Error::DuplicateKey(raw_key).into());
} else {
self.unknown.insert(raw_key, raw_value);
}
}
}
Ok(())
}
fn get_pairs(&self) -> Result<Vec<raw::Pair>, encode::Error> {
let mut rv: Vec<raw::Pair> = Default::default();
impl_psbt_get_pair! {
rv.push(self.non_witness_utxo as <0u8, _>|<Transaction>)
}
impl_psbt_get_pair! {
rv.push(self.witness_utxo as <1u8, _>|<TxOut>)
}
impl_psbt_get_pair! {
rv.push(self.partial_sigs as <2u8, PublicKey>|<Vec<u8>>)
}
impl_psbt_get_pair! {
rv.push(self.sighash_type as <3u8, _>|<SigHashType>)
}
impl_psbt_get_pair! {
rv.push(self.redeem_script as <4u8, _>|<Script>)
}
impl_psbt_get_pair! {
rv.push(self.witness_script as <5u8, _>|<Script>)
}
impl_psbt_get_pair! {
rv.push(self.hd_keypaths as <6u8, PublicKey>|<(Fingerprint, DerivationPath)>)
}
impl_psbt_get_pair! {
rv.push(self.final_script_sig as <7u8, _>|<Script>)
}
impl_psbt_get_pair! {
rv.push(self.final_script_witness as <8u8, _>|<Script>)
}
for (key, value) in self.unknown.iter() {
rv.push(raw::Pair {
key: key.clone(),
value: value.clone(),
});
}
Ok(rv)
}
fn merge(&mut self, other: Self) -> Result<(), psbt::Error> {
merge!(non_witness_utxo, self, other);
if let (&None, Some(witness_utxo)) = (&self.witness_utxo, other.witness_utxo) {
self.witness_utxo = Some(witness_utxo);
self.non_witness_utxo = None; // Clear out any non-witness UTXO when we set a witness one
}
self.partial_sigs.extend(other.partial_sigs);
self.hd_keypaths.extend(other.hd_keypaths);
self.unknown.extend(other.unknown);
merge!(redeem_script, self, other);
merge!(witness_script, self, other);
merge!(final_script_sig, self, other);
merge!(final_script_witness, self, other);
Ok(())
}
}
impl_psbtmap_consensus_enc_dec_oding!(Input);

38
src/util/psbt/map/mod.rs Normal file
View File

@ -0,0 +1,38 @@
// Rust Bitcoin Library
// Written by
// The Rust Bitcoin developers
//
// To the extent possible under law, the author(s) have dedicated all
// copyright and related and neighboring rights to this software to
// the public domain worldwide. This software is distributed without
// any warranty.
//
// You should have received a copy of the CC0 Public Domain Dedication
// along with this software.
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
//
use consensus::encode;
use util::psbt;
use util::psbt::raw;
/// A trait that describes a PSBT key-value map.
pub trait Map {
/// Attempt to insert a key-value pair.
fn insert_pair(&mut self, pair: raw::Pair) -> Result<(), encode::Error>;
/// Attempt to get all key-value pairs.
fn get_pairs(&self) -> Result<Vec<raw::Pair>, encode::Error>;
/// Attempt to merge with another key-value map of the same type.
fn merge(&mut self, other: Self) -> Result<(), psbt::Error>;
}
// place at end to pick up macros
mod global;
mod input;
mod output;
pub use self::global::Global;
pub use self::input::Input;
pub use self::output::Output;

112
src/util/psbt/map/output.rs Normal file
View File

@ -0,0 +1,112 @@
// Rust Bitcoin Library
// Written by
// The Rust Bitcoin developers
//
// To the extent possible under law, the author(s) have dedicated all
// copyright and related and neighboring rights to this software to
// the public domain worldwide. This software is distributed without
// any warranty.
//
// You should have received a copy of the CC0 Public Domain Dedication
// along with this software.
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
//
use std::collections::HashMap;
use blockdata::script::Script;
use consensus::encode;
use util::bip32::{DerivationPath, Fingerprint};
use util::key::PublicKey;
use util::psbt;
use util::psbt::map::Map;
use util::psbt::raw;
use util::psbt::Error;
/// A key-value map for an output of the corresponding index in the unsigned
/// transaction.
#[derive(Clone, Default, Debug, PartialEq)]
pub struct Output {
/// The redeem script for this output.
pub redeem_script: Option<Script>,
/// The witness script for this output.
pub witness_script: Option<Script>,
/// A map from public keys needed to spend this output to their
/// corresponding master key fingerprints and derivation paths.
pub hd_keypaths: HashMap<PublicKey, (Fingerprint, DerivationPath)>,
/// Unknown key-value pairs for this output.
pub unknown: HashMap<raw::Key, Vec<u8>>,
}
impl Map for Output {
fn insert_pair(&mut self, pair: raw::Pair) -> Result<(), encode::Error> {
let raw::Pair {
key: raw_key,
value: raw_value,
} = pair;
match raw_key.type_value {
0u8 => {
impl_psbt_insert_pair! {
self.redeem_script <= <raw_key: _>|<raw_value: Script>
}
}
1u8 => {
impl_psbt_insert_pair! {
self.witness_script <= <raw_key: _>|<raw_value: Script>
}
}
2u8 => {
impl_psbt_insert_pair! {
self.hd_keypaths <= <raw_key: PublicKey>|<raw_value: (Fingerprint, DerivationPath)>
}
}
_ => {
if self.unknown.contains_key(&raw_key) {
return Err(Error::DuplicateKey(raw_key).into());
} else {
self.unknown.insert(raw_key, raw_value);
}
}
}
Ok(())
}
fn get_pairs(&self) -> Result<Vec<raw::Pair>, encode::Error> {
let mut rv: Vec<raw::Pair> = Default::default();
impl_psbt_get_pair! {
rv.push(self.redeem_script as <0u8, _>|<Script>)
}
impl_psbt_get_pair! {
rv.push(self.witness_script as <1u8, _>|<Script>)
}
impl_psbt_get_pair! {
rv.push(self.hd_keypaths as <2u8, PublicKey>|<(Fingerprint, DerivationPath)>)
}
for (key, value) in self.unknown.iter() {
rv.push(raw::Pair {
key: key.clone(),
value: value.clone(),
});
}
Ok(rv)
}
fn merge(&mut self, other: Self) -> Result<(), psbt::Error> {
self.hd_keypaths.extend(other.hd_keypaths);
self.unknown.extend(other.unknown);
merge!(redeem_script, self, other);
merge!(witness_script, self, other);
Ok(())
}
}
impl_psbtmap_consensus_enc_dec_oding!(Output);

555
src/util/psbt/mod.rs Normal file
View File

@ -0,0 +1,555 @@
// Rust Bitcoin Library
// Written by
// The Rust Bitcoin developers
//
// To the extent possible under law, the author(s) have dedicated all
// copyright and related and neighboring rights to this software to
// the public domain worldwide. This software is distributed without
// any warranty.
//
// You should have received a copy of the CC0 Public Domain Dedication
// along with this software.
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
//
//! # Partially Signed Transactions
//!
//! Implementation of BIP174 Partially Signed Bitcoin Transaction Format as
//! defined at https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki
//! except we define PSBTs containing non-standard SigHash types as invalid.
use blockdata::script::Script;
use blockdata::transaction::Transaction;
use consensus::encode::{self, Encodable, Decodable, Encoder, Decoder};
mod error;
pub use self::error::Error;
pub mod raw;
#[macro_use]
mod macros;
pub mod serialize;
mod map;
pub use self::map::{Map, Global, Input, Output};
/// A Partially Signed Transaction.
#[derive(Debug, Clone, PartialEq)]
pub struct PartiallySignedTransaction {
/// The key-value pairs for all global data.
pub global: Global,
/// The corresponding key-value map for each input in the unsigned
/// transaction.
pub inputs: Vec<Input>,
/// The corresponding key-value map for each output in the unsigned
/// transaction.
pub outputs: Vec<Output>,
}
impl PartiallySignedTransaction {
/// Create a PartiallySignedTransaction from an unsigned transaction, error
/// if not unsigned
pub fn from_unsigned_tx(tx: Transaction) -> Result<Self, encode::Error> {
Ok(PartiallySignedTransaction {
inputs: vec![Default::default(); tx.input.len()],
outputs: vec![Default::default(); tx.output.len()],
global: Global::from_unsigned_tx(tx)?,
})
}
/// Extract the Transaction from a PartiallySignedTransaction by filling in
/// the available signature information in place.
pub fn extract_tx(self) -> Transaction {
let mut tx: Transaction = self.global.unsigned_tx;
for (vin, psbtin) in tx.input.iter_mut().zip(self.inputs.into_iter()) {
vin.script_sig = psbtin.final_script_sig.unwrap_or_else(|| Script::new());
vin.witness = psbtin.final_script_witness.unwrap_or_else(|| Vec::new());
}
tx
}
/// Attempt to merge with another `PartiallySignedTransaction`.
pub fn merge(&mut self, other: Self) -> Result<(), self::Error> {
self.global.merge(other.global)?;
for (self_input, other_input) in self.inputs.iter_mut().zip(other.inputs.into_iter()) {
self_input.merge(other_input)?;
}
for (self_output, other_output) in self.outputs.iter_mut().zip(other.outputs.into_iter()) {
self_output.merge(other_output)?;
}
Ok(())
}
}
impl<S: Encoder> Encodable<S> for PartiallySignedTransaction {
fn consensus_encode(&self, s: &mut S) -> Result<(), encode::Error> {
b"psbt".consensus_encode(s)?;
0xff_u8.consensus_encode(s)?;
self.global.consensus_encode(s)?;
for i in &self.inputs {
i.consensus_encode(s)?;
}
for i in &self.outputs {
i.consensus_encode(s)?;
}
Ok(())
}
}
impl<D: Decoder> Decodable<D> for PartiallySignedTransaction {
fn consensus_decode(d: &mut D) -> Result<Self, encode::Error> {
let magic: [u8; 4] = Decodable::consensus_decode(d)?;
if *b"psbt" != magic {
return Err(Error::InvalidMagic.into());
}
if 0xff_u8 != Decodable::consensus_decode(d)? {
return Err(Error::InvalidSeparator.into());
}
let global: Global = Decodable::consensus_decode(d)?;
let inputs: Vec<Input> = {
let inputs_len: usize = (&global.unsigned_tx.input).len();
let mut inputs: Vec<Input> = Vec::with_capacity(inputs_len);
for _ in 0..inputs_len {
inputs.push(Decodable::consensus_decode(d)?);
}
inputs
};
let outputs: Vec<Output> = {
let outputs_len: usize = (&global.unsigned_tx.output).len();
let mut outputs: Vec<Output> = Vec::with_capacity(outputs_len);
for _ in 0..outputs_len {
outputs.push(Decodable::consensus_decode(d)?);
}
outputs
};
Ok(PartiallySignedTransaction {
global: global,
inputs: inputs,
outputs: outputs,
})
}
}
#[cfg(test)]
mod tests {
use bitcoin_hashes::hex::FromHex;
use bitcoin_hashes::sha256d;
use std::collections::HashMap;
use hex::decode as hex_decode;
use secp256k1::Secp256k1;
use blockdata::script::Script;
use blockdata::transaction::{Transaction, TxIn, TxOut, OutPoint};
use network::constants::Network::Bitcoin;
use consensus::encode::{deserialize, serialize, serialize_hex};
use util::bip32::{ChildNumber, DerivationPath, ExtendedPrivKey, ExtendedPubKey, Fingerprint};
use util::key::PublicKey;
use util::psbt::map::{Global, Output};
use util::psbt::raw;
use super::PartiallySignedTransaction;
#[test]
fn trivial_psbt() {
let psbt = PartiallySignedTransaction {
global: Global {
unsigned_tx: Transaction {
version: 2,
lock_time: 0,
input: vec![],
output: vec![],
},
unknown: HashMap::new(),
},
inputs: vec![],
outputs: vec![],
};
assert_eq!(
serialize_hex(&psbt),
"70736274ff01000a0200000000000000000000"
);
}
#[test]
fn serialize_then_deserialize_output() {
let secp = &Secp256k1::new();
let seed = hex_decode("000102030405060708090a0b0c0d0e0f").unwrap();
let mut hd_keypaths: HashMap<PublicKey, (Fingerprint, DerivationPath)> = Default::default();
let mut sk: ExtendedPrivKey = ExtendedPrivKey::new_master(Bitcoin, &seed).unwrap();
let fprint: Fingerprint = sk.fingerprint(&secp);
let dpath: Vec<ChildNumber> = vec![
ChildNumber::from_normal_idx(0).unwrap(),
ChildNumber::from_normal_idx(1).unwrap(),
ChildNumber::from_normal_idx(2).unwrap(),
ChildNumber::from_normal_idx(4).unwrap(),
ChildNumber::from_normal_idx(42).unwrap(),
ChildNumber::from_hardened_idx(69).unwrap(),
ChildNumber::from_normal_idx(420).unwrap(),
ChildNumber::from_normal_idx(31337).unwrap(),
];
sk = sk.derive_priv(secp, &dpath).unwrap();
let pk: ExtendedPubKey = ExtendedPubKey::from_private(&secp, &sk);
hd_keypaths.insert(pk.public_key, (fprint, dpath.into()));
let expected: Output = Output {
redeem_script: Some(hex_script!(
"76a914d0c59903c5bac2868760e90fd521a4665aa7652088ac"
)),
witness_script: Some(hex_script!(
"a9143545e6e33b832c47050f24d3eeb93c9c03948bc787"
)),
hd_keypaths: hd_keypaths,
..Default::default()
};
let actual: Output = deserialize(&serialize(&expected)).unwrap();
assert_eq!(expected, actual);
}
#[test]
fn serialize_then_deserialize_global() {
let expected = Global {
unsigned_tx: Transaction {
version: 2,
lock_time: 1257139,
input: vec![TxIn {
previous_output: OutPoint {
txid: sha256d::Hash::from_hex(
"f61b1742ca13176464adb3cb66050c00787bb3a4eead37e985f2df1e37718126",
).unwrap(),
vout: 0,
},
script_sig: Script::new(),
sequence: 4294967294,
witness: vec![],
}],
output: vec![
TxOut {
value: 99999699,
script_pubkey: hex_script!(
"76a914d0c59903c5bac2868760e90fd521a4665aa7652088ac"
),
},
TxOut {
value: 100000000,
script_pubkey: hex_script!(
"a9143545e6e33b832c47050f24d3eeb93c9c03948bc787"
),
},
],
},
unknown: Default::default(),
};
let actual: Global = deserialize(&serialize(&expected)).unwrap();
assert_eq!(expected, actual);
}
#[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);
}
mod bip_vectors {
use std::collections::HashMap;
use hex::decode as hex_decode;
use bitcoin_hashes::hex::FromHex;
use bitcoin_hashes::sha256d;
use blockdata::script::Script;
use blockdata::transaction::{SigHashType, Transaction, TxIn, TxOut, OutPoint};
use consensus::encode::serialize_hex;
use util::psbt::map::{Map, Global, Input, Output};
use util::psbt::raw;
use util::psbt::PartiallySignedTransaction;
#[test]
fn invalid_vector_1() {
let psbt: Result<PartiallySignedTransaction, _> = hex_psbt!("0200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf6000000006a473044022070b2245123e6bf474d60c5b50c043d4c691a5d2435f09a34a7662a9dc251790a022001329ca9dacf280bdf30740ec0390422422c81cb45839457aeb76fc12edd95b3012102657d118d3357b8e0f4c2cd46db7b39f6d9c38d9a70abcb9b2de5dc8dbfe4ce31feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300");
assert!(psbt.is_err());
}
#[test]
fn invalid_vector_2() {
let psbt: Result<PartiallySignedTransaction, _> = hex_psbt!("70736274ff0100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab30000000000");
assert!(psbt.is_err());
}
#[test]
fn invalid_vector_3() {
let psbt: Result<PartiallySignedTransaction, _> = hex_psbt!("70736274ff0100fd0a010200000002ab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be4000000006a47304402204759661797c01b036b25928948686218347d89864b719e1f7fcf57d1e511658702205309eabf56aa4d8891ffd111fdf1336f3a29da866d7f8486d75546ceedaf93190121035cdc61fc7ba971c0b501a646a2a83b102cb43881217ca682dc86e2d73fa88292feffffffab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40100000000feffffff02603bea0b000000001976a914768a40bbd740cbe81d988e71de2a4d5c71396b1d88ac8e240000000000001976a9146f4620b553fa095e721b9ee0efe9fa039cca459788ac00000000000001012000e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787010416001485d13537f2e265405a34dbafa9e3dda01fb82308000000");
assert!(psbt.is_err());
}
#[test]
fn invalid_vector_4() {
let psbt: Result<PartiallySignedTransaction, _> = hex_psbt!("70736274ff000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab30000000000");
assert!(psbt.is_err());
}
#[test]
fn invalid_vector_5() {
let psbt: Result<PartiallySignedTransaction, _> = hex_psbt!("70736274ff0100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab30000000001003f0200000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000ffffffff010000000000000000036a010000000000000000");
assert!(psbt.is_err());
}
#[test]
fn valid_vector_1() {
let unserialized = PartiallySignedTransaction {
global: Global {
unsigned_tx: Transaction {
version: 2,
lock_time: 1257139,
input: vec![TxIn {
previous_output: OutPoint {
txid: sha256d::Hash::from_hex(
"f61b1742ca13176464adb3cb66050c00787bb3a4eead37e985f2df1e37718126",
).unwrap(),
vout: 0,
},
script_sig: Script::new(),
sequence: 4294967294,
witness: vec![],
}],
output: vec![
TxOut {
value: 99999699,
script_pubkey: hex_script!("76a914d0c59903c5bac2868760e90fd521a4665aa7652088ac"),
},
TxOut {
value: 100000000,
script_pubkey: hex_script!("a9143545e6e33b832c47050f24d3eeb93c9c03948bc787"),
},
],
},
unknown: HashMap::new(),
},
inputs: vec![Input {
non_witness_utxo: Some(Transaction {
version: 1,
lock_time: 0,
input: vec![TxIn {
previous_output: OutPoint {
txid: sha256d::Hash::from_hex(
"e567952fb6cc33857f392efa3a46c995a28f69cca4bb1b37e0204dab1ec7a389",
).unwrap(),
vout: 1,
},
script_sig: hex_script!("160014be18d152a9b012039daf3da7de4f53349eecb985"),
sequence: 4294967295,
witness: vec![
hex_decode("304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c01").unwrap(),
hex_decode("03d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f2105").unwrap(),
],
},
TxIn {
previous_output: OutPoint {
txid: sha256d::Hash::from_hex(
"b490486aec3ae671012dddb2bb08466bef37720a533a894814ff1da743aaf886",
).unwrap(),
vout: 1,
},
script_sig: hex_script!("160014fe3e9ef1a745e974d902c4355943abcb34bd5353"),
sequence: 4294967295,
witness: vec![
hex_decode("3045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01").unwrap(),
hex_decode("0223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab3").unwrap(),
],
}],
output: vec![
TxOut {
value: 200000000,
script_pubkey: hex_script!("76a91485cff1097fd9e008bb34af709c62197b38978a4888ac"),
},
TxOut {
value: 190303501938,
script_pubkey: hex_script!("a914339725ba21efd62ac753a9bcd067d6c7a6a39d0587"),
},
],
}),
..Default::default()
},],
outputs: vec![
Output {
..Default::default()
},
Output {
..Default::default()
},
],
};
let serialized = "70736274ff0100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab300000000000000";
assert_eq!(serialize_hex(&unserialized), serialized);
assert_eq!(unserialized, hex_psbt!(serialized).unwrap());
}
#[test]
fn valid_vector_2() {
let psbt: PartiallySignedTransaction = hex_psbt!("70736274ff0100a00200000002ab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40000000000feffffffab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40100000000feffffff02603bea0b000000001976a914768a40bbd740cbe81d988e71de2a4d5c71396b1d88ac8e240000000000001976a9146f4620b553fa095e721b9ee0efe9fa039cca459788ac000000000001076a47304402204759661797c01b036b25928948686218347d89864b719e1f7fcf57d1e511658702205309eabf56aa4d8891ffd111fdf1336f3a29da866d7f8486d75546ceedaf93190121035cdc61fc7ba971c0b501a646a2a83b102cb43881217ca682dc86e2d73fa882920001012000e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787010416001485d13537f2e265405a34dbafa9e3dda01fb82308000000").unwrap();
assert_eq!(psbt.inputs.len(), 2);
assert_eq!(psbt.outputs.len(), 2);
assert!(&psbt.inputs[0].final_script_sig.is_some());
let redeem_script: &Script = &psbt.inputs[1].redeem_script.as_ref().unwrap();
let expected_out = hex_script!("a9143545e6e33b832c47050f24d3eeb93c9c03948bc787");
assert!(redeem_script.is_v0_p2wpkh());
assert_eq!(
redeem_script.to_p2sh(),
psbt.inputs[1].witness_utxo.as_ref().unwrap().script_pubkey
);
assert_eq!(redeem_script.to_p2sh(), expected_out);
for output in psbt.outputs {
assert_eq!(output.get_pairs().unwrap().len(), 0)
}
}
#[test]
fn valid_vector_3() {
let psbt: PartiallySignedTransaction = hex_psbt!("70736274ff0100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab30000000001030401000000000000").unwrap();
assert_eq!(psbt.inputs.len(), 1);
assert_eq!(psbt.outputs.len(), 2);
let tx_input = &psbt.global.unsigned_tx.input[0];
let psbt_non_witness_utxo = (&psbt.inputs[0].non_witness_utxo).as_ref().unwrap();
assert_eq!(tx_input.previous_output.txid, psbt_non_witness_utxo.txid());
assert!(
psbt_non_witness_utxo.output[tx_input.previous_output.vout as usize]
.script_pubkey
.is_p2pkh()
);
assert_eq!(
(&psbt.inputs[0].sighash_type).as_ref().unwrap(),
&SigHashType::All
);
}
#[test]
fn valid_vector_4() {
let psbt: PartiallySignedTransaction = hex_psbt!("70736274ff0100a00200000002ab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40000000000feffffffab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40100000000feffffff02603bea0b000000001976a914768a40bbd740cbe81d988e71de2a4d5c71396b1d88ac8e240000000000001976a9146f4620b553fa095e721b9ee0efe9fa039cca459788ac00000000000100df0200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf6000000006a473044022070b2245123e6bf474d60c5b50c043d4c691a5d2435f09a34a7662a9dc251790a022001329ca9dacf280bdf30740ec0390422422c81cb45839457aeb76fc12edd95b3012102657d118d3357b8e0f4c2cd46db7b39f6d9c38d9a70abcb9b2de5dc8dbfe4ce31feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e13000001012000e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787010416001485d13537f2e265405a34dbafa9e3dda01fb8230800220202ead596687ca806043edc3de116cdf29d5e9257c196cd055cf698c8d02bf24e9910b4a6ba670000008000000080020000800022020394f62be9df19952c5587768aeb7698061ad2c4a25c894f47d8c162b4d7213d0510b4a6ba6700000080010000800200008000").unwrap();
assert_eq!(psbt.inputs.len(), 2);
assert_eq!(psbt.outputs.len(), 2);
assert!(&psbt.inputs[0].final_script_sig.is_none());
assert!(&psbt.inputs[1].final_script_sig.is_none());
let redeem_script: &Script = &psbt.inputs[1].redeem_script.as_ref().unwrap();
let expected_out = hex_script!("a9143545e6e33b832c47050f24d3eeb93c9c03948bc787");
assert!(redeem_script.is_v0_p2wpkh());
assert_eq!(
redeem_script.to_p2sh(),
psbt.inputs[1].witness_utxo.as_ref().unwrap().script_pubkey
);
assert_eq!(redeem_script.to_p2sh(), expected_out);
for output in psbt.outputs {
assert!(output.get_pairs().unwrap().len() > 0)
}
}
#[test]
fn valid_vector_5() {
let psbt: PartiallySignedTransaction = hex_psbt!("70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000").unwrap();
assert_eq!(psbt.inputs.len(), 1);
assert_eq!(psbt.outputs.len(), 1);
assert!(&psbt.inputs[0].final_script_sig.is_none());
let redeem_script: &Script = &psbt.inputs[0].redeem_script.as_ref().unwrap();
let expected_out = hex_script!("a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87");
assert!(redeem_script.is_v0_p2wsh());
assert_eq!(
redeem_script.to_p2sh(),
psbt.inputs[0].witness_utxo.as_ref().unwrap().script_pubkey
);
assert_eq!(redeem_script.to_p2sh(), expected_out);
}
#[test]
fn valid_vector_6() {
let psbt: PartiallySignedTransaction = hex_psbt!("70736274ff01003f0200000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000ffffffff010000000000000000036a010000000000000a0f0102030405060708090f0102030405060708090a0b0c0d0e0f0000").unwrap();
assert_eq!(psbt.inputs.len(), 1);
assert_eq!(psbt.outputs.len(), 1);
let tx = &psbt.global.unsigned_tx;
assert_eq!(
tx.txid(),
sha256d::Hash::from_hex(
"75c5c9665a570569ad77dd1279e6fd4628a093c4dcbf8d41532614044c14c115"
).unwrap()
);
let mut unknown: HashMap<raw::Key, Vec<u8>> = HashMap::new();
let key: raw::Key = raw::Key {
type_value: 0x0fu8,
key: hex_decode("010203040506070809").unwrap(),
};
let value: Vec<u8> = hex_decode("0102030405060708090a0b0c0d0e0f").unwrap();
unknown.insert(key, value);
assert_eq!(psbt.inputs[0].unknown, unknown)
}
}
}

108
src/util/psbt/raw.rs Normal file
View File

@ -0,0 +1,108 @@
// Rust Bitcoin Library
// Written by
// The Rust Bitcoin developers
//
// To the extent possible under law, the author(s) have dedicated all
// copyright and related and neighboring rights to this software to
// the public domain worldwide. This software is distributed without
// any warranty.
//
// You should have received a copy of the CC0 Public Domain Dedication
// along with this software.
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
//
//! # 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)?,
})
}
}

144
src/util/psbt/serialize.rs Normal file
View File

@ -0,0 +1,144 @@
// Rust Bitcoin Library
// Written by
// The Rust Bitcoin developers
//
// To the extent possible under law, the author(s) have dedicated all
// copyright and related and neighboring rights to this software to
// the public domain worldwide. This software is distributed without
// any warranty.
//
// You should have received a copy of the CC0 Public Domain Dedication
// along with this software.
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
//
//! # PSBT Serialization
//!
//! Defines traits used for (de)serializing PSBT values into/from raw
//! bytes in PSBT key-value pairs.
use std::io::{self, Cursor};
use blockdata::script::Script;
use blockdata::transaction::{SigHashType, Transaction, TxOut};
use consensus::encode::{self, serialize, Decodable};
use util::bip32::{ChildNumber, DerivationPath, Fingerprint};
use util::key::PublicKey;
use util::psbt;
/// A trait for serializing a value as raw data for insertion into PSBT
/// key-value pairs.
pub trait Serialize {
/// Serialize a value as raw data.
fn serialize(&self) -> Vec<u8>;
}
/// A trait for deserializing a value from raw data in PSBT key-value pairs.
pub trait Deserialize: Sized {
/// Deserialize a value from raw data.
fn deserialize(bytes: &[u8]) -> Result<Self, encode::Error>;
}
impl_psbt_de_serialize!(Transaction);
impl_psbt_de_serialize!(TxOut);
impl_psbt_de_serialize!(Vec<Vec<u8>>); // scriptWitness
impl Serialize for Script {
fn serialize(&self) -> Vec<u8> {
self.to_bytes()
}
}
impl Deserialize for Script {
fn deserialize(bytes: &[u8]) -> Result<Self, encode::Error> {
Ok(Self::from(bytes.to_vec()))
}
}
impl Serialize for PublicKey {
fn serialize(&self) -> Vec<u8> {
let mut buf = Vec::new();
self.write_into(&mut buf);
buf
}
}
impl Deserialize for PublicKey {
fn deserialize(bytes: &[u8]) -> Result<Self, encode::Error> {
PublicKey::from_slice(bytes)
.map_err(|_| encode::Error::ParseFailed("invalid public key"))
}
}
impl Serialize for (Fingerprint, DerivationPath) {
fn serialize(&self) -> Vec<u8> {
let mut rv: Vec<u8> = Vec::with_capacity(4 + 4 * (self.1).as_ref().len());
rv.append(&mut self.0.to_bytes().to_vec());
for cnum in self.1.into_iter() {
rv.append(&mut serialize(&u32::from(*cnum)))
}
rv
}
}
impl Deserialize for (Fingerprint, DerivationPath) {
fn deserialize(bytes: &[u8]) -> Result<Self, encode::Error> {
if bytes.len() < 4 {
return Err(io::Error::from(io::ErrorKind::UnexpectedEof).into())
}
let fprint: Fingerprint = Fingerprint::from(&bytes[0..4]);
let mut dpath: Vec<ChildNumber> = Default::default();
let d = &mut Cursor::new(&bytes[4..]);
loop {
match Decodable::consensus_decode(d) {
Ok(index) => {
dpath.push(<ChildNumber as From<u32>>::from(index));
if d.position() == (bytes.len() - 4) as u64 {
break;
}
},
Err(e) => return Err(e),
}
}
Ok((fprint, dpath.into()))
}
}
// partial sigs
impl Serialize for Vec<u8> {
fn serialize(&self) -> Vec<u8> {
self.clone()
}
}
impl Deserialize for Vec<u8> {
fn deserialize(bytes: &[u8]) -> Result<Self, encode::Error> {
Ok(bytes.to_vec())
}
}
impl Serialize for SigHashType {
fn serialize(&self) -> Vec<u8> {
serialize(&self.as_u32())
}
}
impl Deserialize for SigHashType {
fn deserialize(bytes: &[u8]) -> Result<Self, encode::Error> {
let raw: u32 = encode::deserialize(bytes)?;
let rv: SigHashType = SigHashType::from_u32(raw);
if rv.as_u32() == raw {
Ok(rv)
} else {
Err(psbt::Error::NonStandardSigHashType(raw).into())
}
}
}