Skip to content

Commit

Permalink
Merge #488: Release 9.0.0
Browse files Browse the repository at this point in the history
a113e35 Release 9.0.0 (sanket1729)
63ceb5b Add psbt example for sign and finalize (sanket1729)
8f86992 Don't run rawpkh logic through pkh (sanket1729)
815fd1c Bug fix: pkh->pk lookup API (sanket1729)
43abc43 Use expr_raw_pk_h for Terminal::RawPkH (sanket1729)

Pull request description:

  - Cleanup some pkh code in satisfaction
  - Fixes #483
  - Fixes another bug dealing with dissatisfaction of thresh inside pkh
  - Adds example test vector from discussions
  - release 9.0.0 with above fixes

ACKs for top commit:
  apoelstra:
    ACK a113e35

Tree-SHA512: 373d80c5bc03032635ce0dfe6426b17970f1590012482ad3cc76b37337fe2f53e51b32e1fe11c449ec38bf4d4a0ba62d38f7316a148773e2b1854b8f9f1a877c
  • Loading branch information
apoelstra committed Nov 14, 2022
2 parents 61210a0 + a113e35 commit a7bfff4
Show file tree
Hide file tree
Showing 7 changed files with 293 additions and 31 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
# 9.0.0 - November 5, 2022

- Fixed a bug dealing with dissatisfying pkh inside thresh
- Changed the signature of `Satisfier::lookup_raw_pkh_pk` API. Only custom implementations
of `Satisfier` need to be updated. The psbt APIs are unchanged.
- Fixed a bug related to display of `raw_pk_h`. These descriptors are experimental
and only usable by opting via `ExtParams` while parsing string.
# 8.0.0 - October 20, 2022

This release contains several significant API overhauls, as well as a bump
Expand Down
6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "miniscript"
version = "8.0.0"
version = "9.0.0"
authors = ["Andrew Poelstra <apoelstra@wpsoftware.net>, Sanket Kanjalkar <sanket1729@gmail.com>"]
license = "CC0-1.0"
homepage = "https://github.com/rust-bitcoin/rust-miniscript/"
Expand Down Expand Up @@ -60,3 +60,7 @@ required-features = ["std"]
[[example]]
name = "taproot"
required-features = ["compiler","std"]

[[example]]
name = "psbt_sign_finalize"
required-features = ["std"]
1 change: 1 addition & 0 deletions contrib/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ then
cargo run --example psbt
cargo run --example xpub_descriptors
cargo run --example taproot --features=compiler
cargo run --example psbt_sign_finalize
fi

if [ "$DO_NO_STD" = true ]
Expand Down
179 changes: 179 additions & 0 deletions examples/psbt_sign_finalize.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
use std::collections::BTreeMap;
use std::str::FromStr;

use bitcoin::consensus::serialize;
use bitcoin::util::sighash::SighashCache;
use bitcoin::{PackedLockTime, PrivateKey};
use bitcoind::bitcoincore_rpc::jsonrpc::base64;
use bitcoind::bitcoincore_rpc::RawTx;
use miniscript::bitcoin::consensus::encode::deserialize;
use miniscript::bitcoin::hashes::hex::FromHex;
use miniscript::bitcoin::util::psbt;
use miniscript::bitcoin::util::psbt::PartiallySignedTransaction as Psbt;
use miniscript::bitcoin::{
self, secp256k1, Address, Network, OutPoint, Script, Sequence, Transaction, TxIn, TxOut,
};
use miniscript::psbt::{PsbtExt, PsbtInputExt};
use miniscript::Descriptor;

fn main() {
let secp256k1 = secp256k1::Secp256k1::new();

let s = "wsh(t:or_c(pk(027a3565454fe1b749bccaef22aff72843a9c3efefd7b16ac54537a0c23f0ec0de),v:thresh(1,pkh(032d672a1a91cc39d154d366cd231983661b0785c7f27bc338447565844f4a6813),a:pkh(03417129311ed34c242c012cd0a3e0b9bca0065f742d0dfb63c78083ea6a02d4d9),a:pkh(025a687659658baeabdfc415164528065be7bcaade19342241941e556557f01e28))))#7hut9ukn";
let bridge_descriptor = Descriptor::from_str(&s).unwrap();
//let bridge_descriptor = Descriptor::<bitcoin::PublicKey>::from_str(&s).expect("parse descriptor string");
assert!(bridge_descriptor.sanity_check().is_ok());
println!(
"Bridge pubkey script: {}",
bridge_descriptor.script_pubkey()
);
println!(
"Bridge address: {}",
bridge_descriptor.address(Network::Regtest).unwrap()
);
println!(
"Weight for witness satisfaction cost {}",
bridge_descriptor.max_satisfaction_weight().unwrap()
);

let master_private_key_str = "cQhdvB3McbBJdx78VSSumqoHQiSXs75qwLptqwxSQBNBMDxafvaw";
let _master_private_key =
PrivateKey::from_str(master_private_key_str).expect("Can't create private key");
println!(
"Master public key: {}",
_master_private_key.public_key(&secp256k1)
);

let backup1_private_key_str = "cWA34TkfWyHa3d4Vb2jNQvsWJGAHdCTNH73Rht7kAz6vQJcassky";
let backup1_private =
PrivateKey::from_str(backup1_private_key_str).expect("Can't create private key");

println!(
"Backup1 public key: {}",
backup1_private.public_key(&secp256k1)
);

let backup2_private_key_str = "cPJFWUKk8sdL7pcDKrmNiWUyqgovimmhaaZ8WwsByDaJ45qLREkh";
let backup2_private =
PrivateKey::from_str(backup2_private_key_str).expect("Can't create private key");

println!(
"Backup2 public key: {}",
backup2_private.public_key(&secp256k1)
);

let backup3_private_key_str = "cT5cH9UVm81W5QAf5KABXb23RKNSMbMzMx85y6R2mF42L94YwKX6";
let _backup3_private =
PrivateKey::from_str(backup3_private_key_str).expect("Can't create private key");

println!(
"Backup3 public key: {}",
_backup3_private.public_key(&secp256k1)
);

let spend_tx = Transaction {
version: 2,
lock_time: PackedLockTime(5000),
input: vec![],
output: vec![],
};

// Spend one input and spend one output for simplicity.
let mut psbt = Psbt {
unsigned_tx: spend_tx,
unknown: BTreeMap::new(),
proprietary: BTreeMap::new(),
xpub: BTreeMap::new(),
version: 0,
inputs: vec![],
outputs: vec![],
};

let hex_tx = "020000000001018ff27041f3d738f5f84fd5ee62f1c5b36afebfb15f6da0c9d1382ddd0eaaa23c0000000000feffffff02b3884703010000001600142ca3b4e53f17991582d47b15a053b3201891df5200e1f50500000000220020c0ebf552acd2a6f5dee4e067daaef17b3521e283aeaa44a475278617e3d2238a0247304402207b820860a9d425833f729775880b0ed59dd12b64b9a3d1ab677e27e4d6b370700220576003163f8420fe0b9dc8df726cff22cbc191104a2d4ae4f9dfedb087fcec72012103817e1da42a7701df4db94db8576f0e3605f3ab3701608b7e56f92321e4d8999100000000";
let depo_tx: Transaction = deserialize(&Vec::<u8>::from_hex(hex_tx).unwrap()).unwrap();

let receiver = Address::from_str("bcrt1qsdks5za4t6sevaph6tz9ddfjzvhkdkxe9tfrcy").unwrap();

let amount = 100000000;

let (outpoint, witness_utxo) = get_vout(&depo_tx, bridge_descriptor.script_pubkey());

let mut txin = TxIn::default();
txin.previous_output = outpoint;

txin.sequence = Sequence::from_height(26); //Sequence::MAX; //
psbt.unsigned_tx.input.push(txin);

psbt.unsigned_tx.output.push(TxOut {
script_pubkey: receiver.script_pubkey(),
value: amount / 5 - 500,
});

psbt.unsigned_tx.output.push(TxOut {
script_pubkey: bridge_descriptor.script_pubkey(),
value: amount * 4 / 5,
});

// Generating signatures & witness data

let mut input = psbt::Input::default();
input
.update_with_descriptor_unchecked(&bridge_descriptor)
.unwrap();

input.witness_utxo = Some(witness_utxo.clone());
psbt.inputs.push(input);
psbt.outputs.push(psbt::Output::default());

let mut sighash_cache = SighashCache::new(&psbt.unsigned_tx);

let msg = psbt
.sighash_msg(0, &mut sighash_cache, None)
.unwrap()
.to_secp_msg();

// Fixme: Take a parameter
let hash_ty = bitcoin::EcdsaSighashType::All;

let sk1 = backup1_private.inner;
let sk2 = backup2_private.inner;

// Finally construct the signature and add to psbt
let sig1 = secp256k1.sign_ecdsa(&msg, &sk1);
let pk1 = backup1_private.public_key(&secp256k1);
assert!(secp256k1.verify_ecdsa(&msg, &sig1, &pk1.inner).is_ok());

// Second key just in case
let sig2 = secp256k1.sign_ecdsa(&msg, &sk2);
let pk2 = backup2_private.public_key(&secp256k1);
assert!(secp256k1.verify_ecdsa(&msg, &sig2, &pk2.inner).is_ok());

psbt.inputs[0].partial_sigs.insert(
pk1,
bitcoin::EcdsaSig {
sig: sig1,
hash_ty: hash_ty,
},
);

println!("{:#?}", psbt);

let serialized = serialize(&psbt);
println!("{}", base64::encode(&serialized));

psbt.finalize_mut(&secp256k1).unwrap();
println!("{:#?}", psbt);

let tx = psbt.extract_tx();
println!("{}", tx.raw_hex());
}

// Find the Outpoint by spk
fn get_vout(tx: &Transaction, spk: Script) -> (OutPoint, TxOut) {
for (i, txout) in tx.clone().output.into_iter().enumerate() {
if spk == txout.script_pubkey {
return (OutPoint::new(tx.txid(), i as u32), txout);
}
}
panic!("Only call get vout on functions which have the expected outpoint");
}
4 changes: 2 additions & 2 deletions src/miniscript/astelem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> fmt::Debug for Terminal<Pk, Ctx> {
match *self {
Terminal::PkK(ref pk) => write!(f, "pk_k({:?})", pk),
Terminal::PkH(ref pk) => write!(f, "pk_h({:?})", pk),
Terminal::RawPkH(ref pkh) => write!(f, "pk_h({:?})", pkh),
Terminal::RawPkH(ref pkh) => write!(f, "expr_raw_pk_h({:?})", pkh),
Terminal::After(t) => write!(f, "after({})", t),
Terminal::Older(t) => write!(f, "older({})", t),
Terminal::Sha256(ref h) => write!(f, "sha256({})", h),
Expand Down Expand Up @@ -307,7 +307,7 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> fmt::Display for Terminal<Pk, Ctx> {
match *self {
Terminal::PkK(ref pk) => write!(f, "pk_k({})", pk),
Terminal::PkH(ref pk) => write!(f, "pk_h({})", pk),
Terminal::RawPkH(ref pkh) => write!(f, "pk_h({})", pkh),
Terminal::RawPkH(ref pkh) => write!(f, "expr_raw_pk_h({})", pkh),
Terminal::After(t) => write!(f, "after({})", t),
Terminal::Older(t) => write!(f, "older({})", t),
Terminal::Sha256(ref h) => write!(f, "sha256({})", h),
Expand Down
Loading

0 comments on commit a7bfff4

Please sign in to comment.