diff --git a/Cargo.toml b/Cargo.toml index 72675f7..119ada1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,5 +48,12 @@ secp256k1 = { version = "0.28", features = ["rand-std", "global-context"] } name = "v0-2-of-2-multisig" required-features = ["miniscript-std"] +[[example]] +name = "v2" +required-features = ["miniscript-std"] + +[[example]] +name = "v2-separate-creator-constructor" + [patch.crates-io.miniscript] path = "../../rust-miniscript/dev-psbt" diff --git a/src/consts.rs b/src/consts.rs index ccfa1b7..29aca64 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -59,19 +59,14 @@ pub(crate) const PSBT_IN_HASH160: u8 = 0x0c; /// Type: HASH256 preimage PSBT_IN_HASH256 = 0x0d pub(crate) const PSBT_IN_HASH256: u8 = 0x0d; /// Type: Previous TXID PSBT_IN_PREVIOUS_TXID = 0x0e -#[allow(unused)] // PSBT v2 pub(crate) const PSBT_IN_PREVIOUS_TXID: u8 = 0x0e; /// Type: Spent Output Index PSBT_IN_OUTPUT_INDEX = 0x0f -#[allow(unused)] // PSBT v2 pub(crate) const PSBT_IN_OUTPUT_INDEX: u8 = 0x0f; /// Type: Sequence Number PSBT_IN_SEQUENCE = 0x10 -#[allow(unused)] // PSBT v2 pub(crate) const PSBT_IN_SEQUENCE: u8 = 0x10; /// Type: Required Time-based Locktime PSBT_IN_REQUIRED_TIME_LOCKTIME = 0x11 -#[allow(unused)] // PSBT v2 pub(crate) const PSBT_IN_REQUIRED_TIME_LOCKTIME: u8 = 0x11; /// Type: Required Height-based Locktime PSBT_IN_REQUIRED_HEIGHT_LOCKTIME = 0x12 -#[allow(unused)] // PSBT v2 pub(crate) const PSBT_IN_REQUIRED_HEIGHT_LOCKTIME: u8 = 0x12; /// Type: Taproot Signature in Key Spend PSBT_IN_TAP_KEY_SIG = 0x13 pub(crate) const PSBT_IN_TAP_KEY_SIG: u8 = 0x13; diff --git a/src/error.rs b/src/error.rs index 59d9c66..4bf9efa 100644 --- a/src/error.rs +++ b/src/error.rs @@ -6,7 +6,7 @@ use bitcoin::bip32::Xpub; // TODO: This should be exposed like this in rust-bitcoin. use bitcoin::consensus::encode as consensus; use bitcoin::transaction::Transaction; -use bitcoin::{hashes, secp256k1, taproot}; +use bitcoin::{absolute, hashes, secp256k1, taproot}; use crate::prelude::*; use crate::{io, raw}; @@ -104,6 +104,8 @@ pub enum Error { PartialDataConsumption, /// I/O error. Io(io::Error), + /// Couldn't converting parsed u32 to a lock time. + LockTime(absolute::Error), } impl fmt::Display for Error { @@ -158,6 +160,7 @@ impl fmt::Display for Error { PartialDataConsumption => f.write_str("data not consumed entirely when explicitly deserializing"), Io(ref e) => write_err!(f, "I/O error"; e), + LockTime(ref e) => write_err!(f, "parsed locktime invalid"; e), } } } @@ -171,6 +174,7 @@ impl std::error::Error for Error { InvalidHash(ref e) => Some(e), ConsensusEncoding(ref e) => Some(e), Io(ref e) => Some(e), + LockTime(ref e) => Some(e), InvalidMagic | MissingUtxo | InvalidSeparator @@ -216,6 +220,10 @@ impl From for Error { fn from(e: io::Error) -> Self { Error::Io(e) } } +impl From for Error { + fn from(e: absolute::Error) -> Self { Error::LockTime(e) } +} + /// Formats error. /// /// If `std` feature is OFF appends error source (delimited by `: `). We do this because diff --git a/src/lib.rs b/src/lib.rs index 9f73316..b44ec2f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -50,6 +50,8 @@ mod sighash_type; pub mod raw; pub mod serialize; pub mod v0; +pub mod v2; +mod version; #[cfg(feature = "std")] use std::io; @@ -57,12 +59,19 @@ use std::io; #[cfg(not(feature = "std"))] use core2::io; +use crate::version::Version; + #[rustfmt::skip] // Keep pubic re-exports separate pub use crate::{ error::Error, sighash_type::PsbtSighashType, }; +/// PSBT version 0 - the original PSBT version. +pub const V0: Version = Version::ZERO; +/// PSBT version 2 - the second PSBT version. +pub const V2: Version = Version::TWO; + #[cfg(feature = "base64")] mod display_from_str { use core::fmt::{self, Display, Formatter}; diff --git a/src/serialize.rs b/src/serialize.rs index 2c9695e..d06bea2 100644 --- a/src/serialize.rs +++ b/src/serialize.rs @@ -18,7 +18,9 @@ use bitcoin::secp256k1::{self, XOnlyPublicKey}; use bitcoin::taproot::{ ControlBlock, LeafVersion, TapLeafHash, TapNodeHash, TapTree, TaprootBuilder, }; -use bitcoin::{ecdsa, taproot, ScriptBuf, Transaction, TxOut, VarInt, Witness}; +use bitcoin::{ + absolute, ecdsa, taproot, ScriptBuf, Sequence, Transaction, TxOut, Txid, VarInt, Witness, +}; use crate::prelude::*; use crate::sighash_type::PsbtSighashType; @@ -44,6 +46,7 @@ impl_psbt_hash_de_serialize!(ripemd160::Hash); impl_psbt_hash_de_serialize!(sha256::Hash); impl_psbt_hash_de_serialize!(TapLeafHash); impl_psbt_hash_de_serialize!(TapNodeHash); +impl_psbt_hash_de_serialize!(Txid); impl_psbt_hash_de_serialize!(hash160::Hash); impl_psbt_hash_de_serialize!(sha256d::Hash); @@ -146,6 +149,52 @@ impl Deserialize for KeySource { } } +impl Serialize for u32 { + fn serialize(&self) -> Vec { serialize(&self) } +} + +impl Deserialize for u32 { + fn deserialize(bytes: &[u8]) -> Result { + let val: u32 = consensus::deserialize(bytes)?; + Ok(val) + } +} + +impl Serialize for Sequence { + fn serialize(&self) -> Vec { serialize(&self) } +} + +impl Deserialize for Sequence { + fn deserialize(bytes: &[u8]) -> Result { + let n: Sequence = consensus::deserialize(bytes)?; + Ok(n) + } +} + +impl Serialize for absolute::Height { + fn serialize(&self) -> Vec { serialize(&self.to_consensus_u32()) } +} + +impl Deserialize for absolute::Height { + fn deserialize(bytes: &[u8]) -> Result { + let n: u32 = consensus::deserialize(bytes)?; + let lock = absolute::Height::from_consensus(n)?; + Ok(lock) + } +} + +impl Serialize for absolute::Time { + fn serialize(&self) -> Vec { serialize(&self.to_consensus_u32()) } +} + +impl Deserialize for absolute::Time { + fn deserialize(bytes: &[u8]) -> Result { + let n: u32 = consensus::deserialize(bytes)?; + let lock = absolute::Time::from_consensus(n)?; + Ok(lock) + } +} + // partial sigs impl Serialize for Vec { fn serialize(&self) -> Vec { self.clone() } diff --git a/src/v0/map/global.rs b/src/v0/map/global.rs index 036920a..b28b93e 100644 --- a/src/v0/map/global.rs +++ b/src/v0/map/global.rs @@ -10,16 +10,13 @@ use bitcoin::consensus::encode::MAX_VEC_SIZE; use bitcoin::consensus::Decodable; use bitcoin::transaction::Transaction; +use crate::consts::{ + PSBT_GLOBAL_PROPRIETARY, PSBT_GLOBAL_UNSIGNED_TX, PSBT_GLOBAL_VERSION, PSBT_GLOBAL_XPUB, +}; use crate::io::{self, Cursor, Read}; use crate::prelude::*; use crate::v0::map::Map; use crate::{raw, Error}; -use crate::consts::{ -PSBT_GLOBAL_UNSIGNED_TX, -PSBT_GLOBAL_XPUB, -PSBT_GLOBAL_VERSION, -PSBT_GLOBAL_PROPRIETARY, -}; /// The global key-value map. #[derive(Clone, Debug, PartialEq, Eq, Hash)] @@ -213,6 +210,7 @@ impl Global { Ok(()) } + /// Combines this [`Psbt`] with `other` PSBT as described by BIP 174. /// /// In accordance with BIP 174 this function is commutative i.e., `A.combine(B) == B.combine(A)` diff --git a/src/v0/mod.rs b/src/v0/mod.rs index 66f1268..8b28074 100644 --- a/src/v0/mod.rs +++ b/src/v0/mod.rs @@ -24,11 +24,15 @@ use bitcoin::sighash::{EcdsaSighashType, SighashCache}; use bitcoin::transaction::{Transaction, TxOut}; use bitcoin::{ecdsa, Amount}; -use crate::error::write_err; +use crate::error::{write_err, Error}; use crate::prelude::*; -use crate::v0::error::{IndexOutOfBoundsError, SignError}; -use crate::v0::map::{Global, Input, Map, Output}; -use crate::Error; +use crate::v0::map::Map; + +#[rustfmt::skip] // Keep pubic re-exports separate +pub use self::{ + error::{IndexOutOfBoundsError, SignError}, + map::{Input, Output, Global}, +}; /// Combines this two PSBTs as described by BIP 174. pub fn combine(mut this: Psbt, that: Psbt) -> Result {