Skip to content

Commit

Permalink
WIP: Add PSBT v2
Browse files Browse the repository at this point in the history
  • Loading branch information
tcharding committed Nov 23, 2023
1 parent 04694f5 commit 448e9a5
Show file tree
Hide file tree
Showing 7 changed files with 87 additions and 17 deletions.
7 changes: 7 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
5 changes: 0 additions & 5 deletions src/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
10 changes: 9 additions & 1 deletion src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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),
}
}
}
Expand All @@ -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
Expand Down Expand Up @@ -216,6 +220,10 @@ impl From<io::Error> for Error {
fn from(e: io::Error) -> Self { Error::Io(e) }
}

impl From<absolute::Error> 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
Expand Down
9 changes: 9 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,19 +50,28 @@ mod sighash_type;
pub mod raw;
pub mod serialize;
pub mod v0;
pub mod v2;
mod version;

#[cfg(feature = "std")]
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};
Expand Down
51 changes: 50 additions & 1 deletion src/serialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);

Expand Down Expand Up @@ -146,6 +149,52 @@ impl Deserialize for KeySource {
}
}

impl Serialize for u32 {
fn serialize(&self) -> Vec<u8> { serialize(&self) }
}

impl Deserialize for u32 {
fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
let val: u32 = consensus::deserialize(bytes)?;
Ok(val)
}
}

impl Serialize for Sequence {
fn serialize(&self) -> Vec<u8> { serialize(&self) }
}

impl Deserialize for Sequence {
fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
let n: Sequence = consensus::deserialize(bytes)?;
Ok(n)
}
}

impl Serialize for absolute::Height {
fn serialize(&self) -> Vec<u8> { serialize(&self.to_consensus_u32()) }
}

impl Deserialize for absolute::Height {
fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
let n: u32 = consensus::deserialize(bytes)?;
let lock = absolute::Height::from_consensus(n)?;
Ok(lock)
}
}

impl Serialize for absolute::Time {
fn serialize(&self) -> Vec<u8> { serialize(&self.to_consensus_u32()) }
}

impl Deserialize for absolute::Time {
fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
let n: u32 = consensus::deserialize(bytes)?;
let lock = absolute::Time::from_consensus(n)?;
Ok(lock)
}
}

// partial sigs
impl Serialize for Vec<u8> {
fn serialize(&self) -> Vec<u8> { self.clone() }
Expand Down
10 changes: 4 additions & 6 deletions src/v0/map/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -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)`
Expand Down
12 changes: 8 additions & 4 deletions src/v0/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Psbt, Error> {
Expand Down

0 comments on commit 448e9a5

Please sign in to comment.