Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
6387350
bitcoin 0.33: bump dependency versions
42Pupusas Apr 20, 2026
68b3d3b
bitcoin 0.33: migrate library to new API
42Pupusas Apr 20, 2026
2b20742
style: cargo +nightly fmt
42Pupusas Apr 20, 2026
3dab109
bitcoin 0.33: migrate descriptor tests and test_utils
42Pupusas Apr 20, 2026
0326000
bitcoin 0.33: migrate examples
42Pupusas Apr 20, 2026
64562ba
bitcoin 0.33: migrate interpreter/miniscript/psbt/plan/policy tests
42Pupusas Apr 20, 2026
36b7721
bitcoin 0.33: migrate fuzz crate
42Pupusas Apr 20, 2026
a688046
bitcoin 0.33: fix test expectations and migrate deprecated APIs
42Pupusas Apr 20, 2026
3f63efc
bitcoin 0.33: restore MasterFingerprint error source
42Pupusas Apr 20, 2026
1a654a3
bitcoin 0.33: drop secp parameter from parse_descriptor and friends
42Pupusas Apr 20, 2026
2e1bb8c
bitcoin 0.33: make Miniscript/Terminal encode generic over script tag
42Pupusas Apr 20, 2026
0762bd0
bitcoin 0.33: use tagged bitcoin::XOnlyPublicKey in Tap context
42Pupusas Apr 20, 2026
11b8c86
style: cargo +nightly fmt
42Pupusas Apr 20, 2026
898fd5e
bitcoin 0.33: drop ScriptBuf/Script/ScriptBuilder re-exports
42Pupusas Apr 20, 2026
c83397c
bitcoin 0.33: drop secp parameter from PsbtExt and Interpreter APIs
42Pupusas Apr 20, 2026
e2cf391
bitcoin 0.33: return tagged bitcoin::XOnlyPublicKey from ToPublicKey
42Pupusas Apr 20, 2026
b976b51
bitcoin 0.33: remove dead AddrP2shError variant
42Pupusas Apr 20, 2026
021bfde
bitcoin 0.33: surface invariant violation in plan.rs p2wsh construction
42Pupusas Apr 20, 2026
306385a
bitcoin 0.33: adapt compiler tests to tag-generic encode() and bitcoi…
42Pupusas Apr 20, 2026
cbf8e64
style: cargo +nightly fmt
42Pupusas Apr 20, 2026
20993cf
ci: regenerate minimal/recent lock files for bitcoin 0.33
42Pupusas Apr 20, 2026
fc4f4ce
bitcoin 0.33: fix broken intra-doc link in taptree.rs
42Pupusas Apr 20, 2026
a6d1e5a
bitcoin 0.33: migrate cfg(bench) benchmarks to secp256k1 0.32 API
42Pupusas Apr 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
411 changes: 314 additions & 97 deletions Cargo-minimal.lock

Large diffs are not rendered by default.

411 changes: 314 additions & 97 deletions Cargo-recent.lock

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,15 @@ base64 = ["bitcoin/base64"]

[dependencies]
bech32 = { version = "0.11.0", default-features = false, features = ["alloc"] }
bitcoin = { version = "0.32.6", default-features = false }
bitcoin = { version = "0.33.0-beta", default-features = false }
hex = { package = "hex-conservative", default-features = false, features = ["alloc"], version = "1.0.0" }

serde = { version = "1.0.103", optional = true }

[dev-dependencies]
serde_test = "1.0.147"
bitcoin = { version = "0.32.0", features = ["base64"] }
secp256k1 = {version = "0.29.0", features = ["rand-std"]}
bitcoin = { version = "0.33.0-beta", features = ["base64"] }
secp256k1 = { version = "0.32.0-beta.2", features = ["rand", "global-context"] }

[[example]]
name = "htlc"
Expand Down
8 changes: 3 additions & 5 deletions examples/big.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ use miniscript::{
translate_hash_fail, DefiniteDescriptorKey, Descriptor, DescriptorPublicKey, MiniscriptKey,
Translator,
};
use secp256k1::Secp256k1;
fn main() {
let empty = "".to_string();
let mut args = std::env::args().collect::<Vec<_>>();
Expand All @@ -39,8 +38,7 @@ fn main() {
.unwrap();
println!("{}", a);

let secp = Secp256k1::new();
let (d, m) = Descriptor::parse_descriptor(&secp, &i).unwrap();
let (d, m) = Descriptor::parse_descriptor(&i).unwrap();
use_descriptor(d);
println!("{:?}", m);

Expand All @@ -52,13 +50,13 @@ fn main() {
println!("{:?}", h.address(bitcoin::Network::Bitcoin));

let psbt: bitcoin::Psbt = i.parse().unwrap();
let psbt = psbt.finalize(&secp).unwrap();
let psbt = psbt.finalize().unwrap();
let mut tx = psbt.extract_tx().unwrap();
println!("{:?}", tx);

let d = miniscript::Descriptor::<bitcoin::PublicKey>::from_str(&i).unwrap();
let sigs = HashMap::<bitcoin::PublicKey, ecdsa::Signature>::new();
d.satisfy(&mut tx.input[0], &sigs).unwrap();
d.satisfy(&mut tx.inputs[0], &sigs).unwrap();

let pol = Concrete::<String>::from_str(&i).unwrap();
let desc = pol.compile_tr(Some("UNSPENDABLE_KEY".to_string())).unwrap();
Expand Down
58 changes: 29 additions & 29 deletions examples/psbt_sign_finalize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,19 @@
use std::collections::BTreeMap;
use std::str::FromStr;

use miniscript::bitcoin::blockdata::witness::Witness;
use miniscript::bitcoin::consensus::encode::deserialize_hex;
use miniscript::bitcoin::psbt::{self, Psbt};
use miniscript::bitcoin::sighash::SighashCache;
use miniscript::bitcoin::ScriptPubKey;
//use miniscript::bitcoin::secp256k1; // https://github.com/rust-lang/rust/issues/121684
use miniscript::bitcoin::{
transaction, Address, Amount, Network, OutPoint, PrivateKey, Script, Sequence, Transaction,
TxIn, TxOut,
transaction, Address, Amount, Network, OutPoint, PrivateKey, 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");
Expand All @@ -31,31 +30,31 @@ fn main() {
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));
println!("Master public key: {}", _master_private_key.public_key());

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));
println!("Backup1 public key: {}", backup1_private.public_key());

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));
println!("Backup2 public key: {}", backup2_private.public_key());

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));
println!("Backup3 public key: {}", _backup3_private.public_key());

let spend_tx = Transaction {
version: transaction::Version::TWO,
lock_time: bitcoin::absolute::LockTime::from_consensus(5000),
input: vec![],
output: vec![],
inputs: vec![],
outputs: vec![],
};

// Spend one input and spend one output for simplicity.
Expand All @@ -82,19 +81,20 @@ fn main() {

let txin = TxIn {
previous_output: outpoint,
script_sig: bitcoin::ScriptSigBuf::new(),
sequence: Sequence::from_height(26),
..Default::default()
witness: Witness::default(),
};
psbt.unsigned_tx.input.push(txin);
psbt.unsigned_tx.inputs.push(txin);

psbt.unsigned_tx.output.push(TxOut {
psbt.unsigned_tx.outputs.push(TxOut {
script_pubkey: receiver.script_pubkey(),
value: Amount::from_sat(amount / 5 - 500),
amount: Amount::from_sat(amount / 5 - 500).expect("in range"),
});

psbt.unsigned_tx.output.push(TxOut {
psbt.unsigned_tx.outputs.push(TxOut {
script_pubkey: bridge_descriptor.script_pubkey(),
value: Amount::from_sat(amount * 4 / 5),
amount: Amount::from_sat(amount * 4 / 5).expect("in range"),
});

// Generating signatures & witness data
Expand All @@ -118,18 +118,18 @@ fn main() {
// Fixme: Take a parameter
let hash_ty = bitcoin::sighash::EcdsaSighashType::All;

let sk1 = backup1_private.inner;
let sk2 = backup2_private.inner;
let sk1 = *backup1_private.as_inner();
let sk2 = *backup2_private.as_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());
let sig1 = secp256k1::ecdsa::sign(msg, &sk1);
let pk1 = backup1_private.public_key();
assert!(secp256k1::ecdsa::verify(&sig1, msg, &pk1.to_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());
let sig2 = secp256k1::ecdsa::sign(msg, &sk2);
let pk2 = backup2_private.public_key();
assert!(secp256k1::ecdsa::verify(&sig2, msg, &pk2.to_inner()).is_ok());

psbt.inputs[0]
.partial_sigs
Expand All @@ -138,18 +138,18 @@ fn main() {
println!("{:#?}", psbt);
println!("{}", psbt);

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

let tx = psbt.extract_tx().expect("failed to extract tx");
println!("{}", bitcoin::consensus::encode::serialize_hex(&tx));
}

// 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.compute_txid(), i as u32), txout);
fn get_vout(tx: &Transaction, spk: &ScriptPubKey) -> (OutPoint, TxOut) {
for (i, txout) in tx.clone().outputs.into_iter().enumerate() {
if spk == AsRef::<ScriptPubKey>::as_ref(&txout.script_pubkey) {
return (OutPoint { txid: tx.compute_txid(), vout: i as u32 }, txout);
}
}
panic!("Only call get vout on functions which have the expected outpoint");
Expand Down
37 changes: 19 additions & 18 deletions examples/sign_multisig.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ use std::collections::HashMap;
use std::str::FromStr;

use bitcoin::blockdata::witness::Witness;
use bitcoin::{absolute, ecdsa, transaction, Amount, Sequence};
use bitcoin::{absolute, ecdsa, transaction, Amount, OutPoint, Sequence};
use miniscript::bitcoin::script::ScriptPubKeyBuf as ScriptBuf;

fn main() {
let mut tx = spending_transaction();
Expand Down Expand Up @@ -49,46 +50,46 @@ fn main() {
);

// Attempt to satisfy at age 0, height 0.
let original_txin = tx.input[0].clone();
let original_txin = tx.inputs[0].clone();

let mut sigs = HashMap::<bitcoin::PublicKey, ecdsa::Signature>::new();

// Doesn't work with no signatures.
assert!(descriptor.satisfy(&mut tx.input[0], &sigs).is_err());
assert_eq!(tx.input[0], original_txin);
assert!(descriptor.satisfy(&mut tx.inputs[0], &sigs).is_err());
assert_eq!(tx.inputs[0], original_txin);

// ...or one signature...
sigs.insert(pks[1], sig);
assert!(descriptor.satisfy(&mut tx.input[0], &sigs).is_err());
assert_eq!(tx.input[0], original_txin);
assert!(descriptor.satisfy(&mut tx.inputs[0], &sigs).is_err());
assert_eq!(tx.inputs[0], original_txin);

// ...but two signatures is ok.
sigs.insert(pks[2], sig);
assert!(descriptor.satisfy(&mut tx.input[0], &sigs).is_ok());
assert_ne!(tx.input[0], original_txin);
assert_eq!(tx.input[0].witness.len(), 4); // 0, sig, sig, witness script
assert!(descriptor.satisfy(&mut tx.inputs[0], &sigs).is_ok());
assert_ne!(tx.inputs[0], original_txin);
assert_eq!(tx.inputs[0].witness.len(), 4); // 0, sig, sig, witness script

// ...and even if we give it a third signature, only two are used.
sigs.insert(pks[0], sig);
assert!(descriptor.satisfy(&mut tx.input[0], &sigs).is_ok());
assert_ne!(tx.input[0], original_txin);
assert_eq!(tx.input[0].witness.len(), 4); // 0, sig, sig, witness script
assert!(descriptor.satisfy(&mut tx.inputs[0], &sigs).is_ok());
assert_ne!(tx.inputs[0], original_txin);
assert_eq!(tx.inputs[0].witness.len(), 4); // 0, sig, sig, witness script
}

// Transaction which spends some output.
fn spending_transaction() -> bitcoin::Transaction {
bitcoin::Transaction {
version: transaction::Version::TWO,
lock_time: absolute::LockTime::ZERO,
input: vec![bitcoin::TxIn {
previous_output: Default::default(),
script_sig: bitcoin::ScriptBuf::new(),
inputs: vec![bitcoin::TxIn {
previous_output: OutPoint::COINBASE_PREVOUT,
script_sig: bitcoin::ScriptSigBuf::new(),
sequence: Sequence::MAX,
witness: Witness::default(),
}],
output: vec![bitcoin::TxOut {
script_pubkey: bitcoin::ScriptBuf::new(),
value: Amount::from_sat(100_000_000),
outputs: vec![bitcoin::TxOut {
script_pubkey: ScriptBuf::new(),
amount: Amount::from_sat(100_000_000).expect("in range"),
}],
}
}
Expand Down
14 changes: 9 additions & 5 deletions examples/taproot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::collections::HashMap;
use std::str::FromStr;

use miniscript::bitcoin::key::{Keypair, XOnlyPublicKey};
use miniscript::bitcoin::secp256k1::rand;
use miniscript::bitcoin::secp256k1::{self, rand};
use miniscript::bitcoin::{Network, WitnessVersion};
use miniscript::descriptor::DescriptorType;
use miniscript::policy::Concrete;
Expand Down Expand Up @@ -86,10 +86,10 @@ fn main() {
let mut pk_map = HashMap::new();

// We require secp for generating a random XOnlyPublicKey
let secp = secp256k1::Secp256k1::new();
let key_pair = Keypair::new(&secp, &mut rand::thread_rng());
let _secp = secp256k1::Secp256k1::new();
let key_pair = Keypair::from_secp(secp256k1::Keypair::new(&mut rand::rng()));
// Random unspendable XOnlyPublicKey provided for compilation to Taproot Descriptor
let (unspendable_pubkey, _parity) = XOnlyPublicKey::from_keypair(&key_pair);
let unspendable_pubkey = XOnlyPublicKey::from_keypair(&key_pair);

pk_map.insert("UNSPENDABLE_KEY".to_string(), unspendable_pubkey);
let pubkeys = hardcoded_xonlypubkeys();
Expand Down Expand Up @@ -142,7 +142,11 @@ fn hardcoded_xonlypubkeys() -> Vec<XOnlyPublicKey> {
];
let mut keys: Vec<XOnlyPublicKey> = vec![];
for key in serialized_keys {
keys.push(XOnlyPublicKey::from_slice(&key).unwrap());
keys.push(
secp256k1::XOnlyPublicKey::from_byte_array(key)
.unwrap()
.into(),
);
}
keys
}
8 changes: 3 additions & 5 deletions examples/taptree_of_horror/helper_fns.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
use std::str::FromStr;

use bitcoin::bip32::{DerivationPath, Xpriv};
use bitcoin::hashes::{ripemd160, sha256, Hash};
use bitcoin::hashes::{ripemd160, sha256};
use miniscript::descriptor::DescriptorSecretKey;
use miniscript::ToPublicKey;
use secp256k1::Secp256k1;

use crate::KEYS_PER_PERSONA;

Expand All @@ -28,7 +27,6 @@ pub fn produce_kelly_hash(secret: &str) -> (sha256::Hash, sha256::Hash) {

pub fn produce_key_pairs(
desc: DescriptorSecretKey,
secp: &Secp256k1<secp256k1::All>,
derivation_without_index: &str,
_alias: &str,
) -> (Vec<bitcoin::PublicKey>, Vec<Xpriv>) {
Expand All @@ -42,15 +40,15 @@ pub fn produce_key_pairs(

for i in 0..KEYS_PER_PERSONA {
let pk = desc
.to_public(secp)
.to_public()
.unwrap()
.at_derivation_index(i.try_into().unwrap())
.unwrap()
.to_public_key();

let derivation_with_index = format!("{}/{}", derivation_without_index, i);
let derivation_path = DerivationPath::from_str(&derivation_with_index).unwrap();
let derived_xpriv: Xpriv = xprv.xkey.derive_priv(secp, &derivation_path).unwrap();
let derived_xpriv: Xpriv = xprv.xkey.derive_priv(&derivation_path).unwrap();

pks.push(pk);
prvs.push(derived_xpriv);
Expand Down
Loading