// Miniscript // Written in 2019 by // Andrew Poelstra // // To the extent possible under law, the author(s) have dedicated all // copyright and related and neighboring rights to this software to // the public domain worldwide. This software is distributed without // any warranty. // // You should have received a copy of the CC0 Public Domain Dedication // along with this software. // If not, see . // //! Example: Signing a 2-of-3 multisignature. use std::collections::HashMap; use std::str::FromStr; use bitcoin::blockdata::witness::Witness; use bitcoin::{secp256k1, PackedLockTime, Sequence}; fn main() { let mut tx = spending_transaction(); let pks = list_of_three_arbitrary_public_keys(); let sig = random_signature_from_the_blockchain(); // Descriptor for the output being spent. let s = format!("wsh(multi(2,{},{},{}))", pks[0], pks[1], pks[2],); let descriptor = miniscript::Descriptor::::from_str(&s).unwrap(); // Check weight for witness satisfaction cost ahead of time. // 4 (scriptSig length of 0) + 1 (witness stack size) + 106 (serialized witnessScript) // + 73*2 (signature length + signatures + sighash bytes) + 1 (dummy byte) = 258 assert_eq!(descriptor.max_satisfaction_weight().unwrap(), 258); // Sometimes it is necessary to have additional information to get the // `bitcoin::PublicKey` from the `MiniscriptKey` which can be supplied by // the `to_pk_ctx` parameter. For example, when calculating the script // pubkey of a descriptor with xpubs, the secp context and child information // maybe required. // Observe the script properties, just for fun. assert_eq!( format!("{:x}", descriptor.script_pubkey()), "00200ed49b334a12c37f3df8a2974ad91ff95029215a2b53f78155be737907f06163" ); assert_eq!( format!( "{:x}", descriptor .explicit_script() .expect("wsh descriptors have unique inner script") ), "52\ 21020202020202020202020202020202020202020202020202020202020202020202\ 21020102030405060708010203040506070801020304050607080000000000000000\ 21030102030405060708010203040506070801020304050607080000000000000000\ 53ae" ); // Attempt to satisfy at age 0, height 0. let original_txin = tx.input[0].clone(); let mut sigs = HashMap::::new(); // Doesn't work with no signatures. assert!(descriptor.satisfy(&mut tx.input[0], &sigs).is_err()); assert_eq!(tx.input[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); // ...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 // ...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 } // Transaction which spends some output. fn spending_transaction() -> bitcoin::Transaction { bitcoin::Transaction { version: 2, lock_time: PackedLockTime::ZERO, input: vec![bitcoin::TxIn { previous_output: Default::default(), script_sig: bitcoin::Script::new(), sequence: Sequence::MAX, witness: Witness::default(), }], output: vec![bitcoin::TxOut { script_pubkey: bitcoin::Script::new(), value: 100_000_000, }], } } fn list_of_three_arbitrary_public_keys() -> Vec { #[cfg_attr(feature="cargo-fmt", rustfmt_skip)] vec![ bitcoin::PublicKey::from_slice(&[2; 33]).expect("key 1"), bitcoin::PublicKey::from_slice(&[ 0x02, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]).expect("key 2"), bitcoin::PublicKey::from_slice(&[ 0x03, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]).expect("key 3"), ] } // Returns a signature copied at random off the blockchain; this is not actually // a valid signature for this transaction; Miniscript does not verify the validity. fn random_signature_from_the_blockchain() -> bitcoin::EcdsaSig { bitcoin::EcdsaSig { sig: secp256k1::ecdsa::Signature::from_str( "3045\ 0221\ 00f7c3648c390d87578cd79c8016940aa8e3511c4104cb78daa8fb8e429375efc1\ 0220\ 531d75c136272f127a5dc14acc0722301cbddc222262934151f140da345af177", ) .unwrap(), hash_ty: bitcoin::EcdsaSighashType::All, } }