Skip to content

Commit eae1634

Browse files
authored
Merge pull request #119 from sanket1729/psbt_fixes
Psbt: Add finalizer and extractor
2 parents e0fc688 + c2b1fa7 commit eae1634

File tree

8 files changed

+624
-146
lines changed

8 files changed

+624
-146
lines changed

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,6 @@ name = "sign_multisig"
3232

3333
[[example]]
3434
name = "verify_tx"
35+
36+
[[example]]
37+
name = "psbt"

contrib/test.sh

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,8 @@ done
4141

4242
# Also build and run each example to catch regressions
4343
cargo build --examples
44-
./target/debug/examples/htlc
45-
./target/debug/examples/parse
46-
./target/debug/examples/sign_multisig
47-
./target/debug/examples/verify_tx
44+
# run all examples
45+
run-parts ./target/debug/examples
4846

4947
# Miri Checks if told to
5048
# Only supported in nightly

examples/psbt.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
extern crate bitcoin;
2+
extern crate miniscript;
3+
4+
use bitcoin::consensus::encode::deserialize;
5+
use bitcoin::hashes::hex::FromHex;
6+
7+
use miniscript::psbt::{extract, finalize};
8+
9+
fn main() {
10+
// Test vectors from BIP 174
11+
12+
let mut psbt: bitcoin::util::psbt::PartiallySignedTransaction = deserialize(&Vec::<u8>::from_hex("70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000002202029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01220202dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d7483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01010304010000000104475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae2206029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f10d90c6a4f000000800000008000000080220602dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d710d90c6a4f0000008000000080010000800001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e887220203089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f012202023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e73473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d2010103040100000001042200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903010547522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae2206023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7310d90c6a4f000000800000008003000080220603089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc10d90c6a4f00000080000000800200008000220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000").unwrap()).unwrap();
13+
// println!("{:?}", psbt);
14+
15+
let expected_finalized_psbt: bitcoin::util::psbt::PartiallySignedTransaction = deserialize(&Vec::<u8>::from_hex("70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000000107da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae0001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870107232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b20289030108da0400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000").unwrap()).unwrap();
16+
17+
// Assuming all partial sigs are filled in.
18+
// Construct a generic finalizer
19+
finalize(&mut psbt).unwrap();
20+
// println!("{:?}", psbt);
21+
22+
assert_eq!(psbt, expected_finalized_psbt);
23+
24+
// Extract the transaction from the psbt
25+
let tx = extract(&psbt).unwrap();
26+
27+
let expected: bitcoin::Transaction = deserialize(&Vec::<u8>::from_hex("0200000000010258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd7500000000da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752aeffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d01000000232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f000400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00000000").unwrap()).unwrap();
28+
// println!("{:?}", tx);
29+
assert_eq!(tx, expected);
30+
}

src/descriptor/mod.rs

Lines changed: 42 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -482,14 +482,13 @@ impl<Pk: MiniscriptKey + ToPublicKey> Descriptor<Pk> {
482482
}
483483
}
484484

485-
/// Attempts to produce a satisfying witness and scriptSig to spend an
486-
/// output controlled by the given descriptor; add the data to a given
487-
/// `TxIn` output.
488-
pub fn satisfy<S: Satisfier<Pk>>(
485+
/// Returns satisfying witness and scriptSig to spend an
486+
/// output controlled by the given descriptor if it possible to
487+
/// construct one using the satisfier.
488+
pub fn get_satisfication<S: Satisfier<Pk>>(
489489
&self,
490-
txin: &mut bitcoin::TxIn,
491490
satisfier: S,
492-
) -> Result<(), Error> {
491+
) -> Result<(Vec<Vec<u8>>, Script), Error> {
493492
fn witness_to_scriptsig(witness: &[Vec<u8>]) -> Script {
494493
let mut b = script::Builder::new();
495494
for wit in witness {
@@ -508,19 +507,19 @@ impl<Pk: MiniscriptKey + ToPublicKey> Descriptor<Pk> {
508507
Some(wit) => wit,
509508
None => return Err(Error::CouldNotSatisfy),
510509
};
511-
txin.script_sig = witness_to_scriptsig(&wit);
512-
txin.witness = vec![];
513-
Ok(())
510+
let script_sig = witness_to_scriptsig(&wit);
511+
let witness = vec![];
512+
Ok((witness, script_sig))
514513
}
515514
Descriptor::Pk(ref pk) => {
516515
if let Some(sig) = satisfier.lookup_sig(pk) {
517516
let mut sig_vec = sig.0.serialize_der().to_vec();
518517
sig_vec.push(sig.1.as_u32() as u8);
519-
txin.script_sig = script::Builder::new()
518+
let script_sig = script::Builder::new()
520519
.push_slice(&sig_vec[..])
521520
.into_script();
522-
txin.witness = vec![];
523-
Ok(())
521+
let witness = vec![];
522+
Ok((witness, script_sig))
524523
} else {
525524
Err(Error::MissingSig(pk.to_public_key()))
526525
}
@@ -529,12 +528,12 @@ impl<Pk: MiniscriptKey + ToPublicKey> Descriptor<Pk> {
529528
if let Some(sig) = satisfier.lookup_sig(pk) {
530529
let mut sig_vec = sig.0.serialize_der().to_vec();
531530
sig_vec.push(sig.1.as_u32() as u8);
532-
txin.script_sig = script::Builder::new()
531+
let script_sig = script::Builder::new()
533532
.push_slice(&sig_vec[..])
534533
.push_key(&pk.to_public_key())
535534
.into_script();
536-
txin.witness = vec![];
537-
Ok(())
535+
let witness = vec![];
536+
Ok((witness, script_sig))
538537
} else {
539538
Err(Error::MissingSig(pk.to_public_key()))
540539
}
@@ -543,9 +542,9 @@ impl<Pk: MiniscriptKey + ToPublicKey> Descriptor<Pk> {
543542
if let Some(sig) = satisfier.lookup_sig(pk) {
544543
let mut sig_vec = sig.0.serialize_der().to_vec();
545544
sig_vec.push(sig.1.as_u32() as u8);
546-
txin.script_sig = Script::new();
547-
txin.witness = vec![sig_vec, pk.to_public_key().to_bytes()];
548-
Ok(())
545+
let script_sig = Script::new();
546+
let witness = vec![sig_vec, pk.to_public_key().to_bytes()];
547+
Ok((witness, script_sig))
549548
} else {
550549
Err(Error::MissingSig(pk.to_public_key()))
551550
}
@@ -559,38 +558,37 @@ impl<Pk: MiniscriptKey + ToPublicKey> Descriptor<Pk> {
559558
.expect("wpkh descriptors have compressed keys");
560559
let redeem_script = addr.script_pubkey();
561560

562-
txin.script_sig = script::Builder::new()
561+
let script_sig = script::Builder::new()
563562
.push_slice(&redeem_script[..])
564563
.into_script();
565-
txin.witness = vec![sig_vec, pk.to_public_key().to_bytes()];
566-
Ok(())
564+
let witness = vec![sig_vec, pk.to_public_key().to_bytes()];
565+
Ok((witness, script_sig))
567566
} else {
568567
Err(Error::MissingSig(pk.to_public_key()))
569568
}
570569
}
571570
Descriptor::Sh(ref d) => {
572-
let mut witness = match d.satisfy(satisfier) {
571+
let mut script_witness = match d.satisfy(satisfier) {
573572
Some(wit) => wit,
574573
None => return Err(Error::CouldNotSatisfy),
575574
};
576-
witness.push(d.encode().into_bytes());
577-
txin.script_sig = witness_to_scriptsig(&witness);
578-
txin.witness = vec![];
579-
Ok(())
575+
script_witness.push(d.encode().into_bytes());
576+
let script_sig = witness_to_scriptsig(&script_witness);
577+
let witness = vec![];
578+
Ok((witness, script_sig))
580579
}
581580
Descriptor::Wsh(ref d) => {
582581
let mut witness = match d.satisfy(satisfier) {
583582
Some(wit) => wit,
584583
None => return Err(Error::CouldNotSatisfy),
585584
};
586585
witness.push(d.encode().into_bytes());
587-
txin.script_sig = Script::new();
588-
txin.witness = witness;
589-
Ok(())
586+
let script_sig = Script::new();
587+
Ok((witness, script_sig))
590588
}
591589
Descriptor::ShWsh(ref d) => {
592590
let witness_script = d.encode();
593-
txin.script_sig = script::Builder::new()
591+
let script_sig = script::Builder::new()
594592
.push_slice(&witness_script.to_v0_p2wsh()[..])
595593
.into_script();
596594

@@ -599,11 +597,23 @@ impl<Pk: MiniscriptKey + ToPublicKey> Descriptor<Pk> {
599597
None => return Err(Error::CouldNotSatisfy),
600598
};
601599
witness.push(witness_script.into_bytes());
602-
txin.witness = witness;
603-
Ok(())
600+
Ok((witness, script_sig))
604601
}
605602
}
606603
}
604+
/// Attempts to produce a satisfying witness and scriptSig to spend an
605+
/// output controlled by the given descriptor; add the data to a given
606+
/// `TxIn` output.
607+
pub fn satisfy<S: Satisfier<Pk>>(
608+
&self,
609+
txin: &mut bitcoin::TxIn,
610+
satisfier: S,
611+
) -> Result<(), Error> {
612+
let (witness, script_sig) = self.get_satisfication(satisfier)?;
613+
txin.witness = witness;
614+
txin.script_sig = script_sig;
615+
Ok(())
616+
}
607617

608618
/// Computes an upper bound on the weight of a satisfying witness to the
609619
/// transaction. Assumes all signatures are 73 bytes, including push opcode

src/lib.rs

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -269,8 +269,6 @@ pub enum Error {
269269
NonMinimalVerify(miniscript::lex::Token),
270270
/// Push was illegal in some context
271271
InvalidPush(Vec<u8>),
272-
/// PSBT-related error
273-
Psbt(psbt::Error),
274272
/// rust-bitcoin script error
275273
Script(script::Error),
276274
/// A `CHECKMULTISIG` opcode was preceded by a number > 20
@@ -369,6 +367,13 @@ impl From<miniscript::context::ScriptContextError> for Error {
369367
}
370368
}
371369

370+
#[doc(hidden)]
371+
impl From<bitcoin::secp256k1::Error> for Error {
372+
fn from(e: bitcoin::secp256k1::Error) -> Error {
373+
Error::Secp(e)
374+
}
375+
}
376+
372377
fn errstr(s: &str) -> Error {
373378
Error::Unexpected(s.to_owned())
374379
}
@@ -377,7 +382,6 @@ impl error::Error for Error {
377382
fn cause(&self) -> Option<&error::Error> {
378383
match *self {
379384
Error::BadPubkey(ref e) => Some(e),
380-
Error::Psbt(ref e) => Some(e),
381385
_ => None,
382386
}
383387
}
@@ -398,7 +402,6 @@ impl fmt::Display for Error {
398402
Error::InvalidOpcode(op) => write!(f, "invalid opcode {}", op),
399403
Error::NonMinimalVerify(tok) => write!(f, "{} VERIFY", tok),
400404
Error::InvalidPush(ref push) => write!(f, "invalid push {:?}", push), // TODO hexify this
401-
Error::Psbt(ref e) => fmt::Display::fmt(e, f),
402405
Error::Script(ref e) => fmt::Display::fmt(e, f),
403406
Error::CmsTooManyKeys(n) => write!(f, "checkmultisig with {} keys", n),
404407
Error::Unprintable(x) => write!(f, "unprintable character 0x{:02x}", x),
@@ -459,13 +462,6 @@ impl fmt::Display for Error {
459462
}
460463
}
461464

462-
#[doc(hidden)]
463-
impl From<psbt::Error> for Error {
464-
fn from(e: psbt::Error) -> Error {
465-
Error::Psbt(e)
466-
}
467-
}
468-
469465
#[doc(hidden)]
470466
#[cfg(feature = "compiler")]
471467
impl From<policy::compiler::CompilerError> for Error {
@@ -481,6 +477,13 @@ impl From<policy::concrete::PolicyError> for Error {
481477
}
482478
}
483479

480+
#[doc(hidden)]
481+
impl From<descriptor::InterpreterError> for Error {
482+
fn from(e: descriptor::InterpreterError) -> Error {
483+
Error::InterpreterError(e)
484+
}
485+
}
486+
484487
/// The size of an encoding of a number in Script
485488
pub fn script_num_size(n: usize) -> usize {
486489
match n {

src/miniscript/satisfy.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,22 @@ use bitcoin::{self, secp256k1};
2626
use {MiniscriptKey, ToPublicKey};
2727

2828
use miniscript::types::extra_props::HEIGHT_TIME_THRESHOLD;
29+
use Error;
2930
use ScriptContext;
3031
use Terminal;
3132

3233
/// Type alias for a signature/hashtype pair
3334
pub type BitcoinSig = (secp256k1::Signature, bitcoin::SigHashType);
3435

36+
/// Helper function to create BitcoinSig from Rawsig
37+
/// Useful for downstream when implementing Satisfier.
38+
/// Returns underlying secp if the Signature is not of correct formart
39+
pub fn bitcoinsig_from_rawsig(rawsig: &[u8]) -> Result<BitcoinSig, Error> {
40+
let (flag, sig) = rawsig.split_last().unwrap();
41+
let flag = bitcoin::SigHashType::from_u32(*flag as u32);
42+
let sig = secp256k1::Signature::from_der(sig)?;
43+
Ok((sig, flag))
44+
}
3545
/// Trait describing a lookup table for signatures, hash preimages, etc.
3646
/// Every method has a default implementation that simply returns `None`
3747
/// on every query. Users are expected to override the methods that they

0 commit comments

Comments
 (0)