diff --git a/src/consensus/encode.rs b/src/consensus/encode.rs index 4a5fd54..608ee1d 100644 --- a/src/consensus/encode.rs +++ b/src/consensus/encode.rs @@ -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 for Error { } } +#[doc(hidden)] +impl From for Error { + fn from(e: psbt::Error) -> Error { + Error::Psbt(e) + } +} + /// Encode an object into a vector pub fn serialize(data: &T) -> Vec where T: Encodable>>, diff --git a/src/util/mod.rs b/src/util/mod.rs index afad43b..c76517a 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -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}; diff --git a/src/util/psbt/error.rs b/src/util/psbt/error.rs new file mode 100644 index 0000000..34095e2 --- /dev/null +++ b/src/util/psbt/error.rs @@ -0,0 +1,72 @@ +use std::error; +use std::fmt; + +use blockdata::transaction::Transaction; + +/// 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, + /// Keys within key-value map should never be duplicated. + DuplicateKey, + /// 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::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 + | 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", + } + } +} diff --git a/src/util/psbt/mod.rs b/src/util/psbt/mod.rs new file mode 100644 index 0000000..f0438e1 --- /dev/null +++ b/src/util/psbt/mod.rs @@ -0,0 +1,8 @@ +//! # 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. + +mod error; +pub use self::error::Error;