1212// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
1313//
1414
15- //! Example: Signing a 2-of-3 multisignature
15+ //! Example: Signing a 2-of-3 multisignature.
1616
1717use bitcoin;
1818use miniscript;
1919
2020use bitcoin:: blockdata:: witness:: Witness ;
21- use bitcoin:: secp256k1; // secp256k1 re-exported from rust-bitcoin
21+ use bitcoin:: secp256k1;
2222use miniscript:: DescriptorTrait ;
2323use std:: collections:: HashMap ;
2424use std:: str:: FromStr ;
2525
2626fn main ( ) {
27- // Avoid repeatedly typing a pretty-common descriptor type
28- type BitcoinDescriptor = miniscript:: Descriptor < bitcoin:: PublicKey > ;
27+ let mut tx = spending_transaction ( ) ;
28+ let pks = list_of_three_arbitrary_public_keys ( ) ;
29+ let sig = random_signature_from_the_blockchain ( ) ;
2930
30- // Transaction which spends some output
31- let mut tx = bitcoin:: Transaction {
31+ // Descriptor for the output being spent.
32+ let s = format ! ( "wsh(multi(2,{},{},{}))" , pks[ 0 ] , pks[ 1 ] , pks[ 2 ] , ) ;
33+ let descriptor = miniscript:: Descriptor :: < bitcoin:: PublicKey > :: from_str ( & s) . unwrap ( ) ;
34+
35+ // Check weight for witness satisfaction cost ahead of time.
36+ // 4 (scriptSig length of 0) + 1 (witness stack size) + 106 (serialized witnessScript)
37+ // + 73*2 (signature length + signatures + sighash bytes) + 1 (dummy byte) = 258
38+ assert_eq ! ( descriptor. max_satisfaction_weight( ) . unwrap( ) , 258 ) ;
39+
40+ // Sometimes it is necessary to have additional information to get the
41+ // `bitcoin::PublicKey` from the `MiniscriptKey` which can be supplied by
42+ // the `to_pk_ctx` parameter. For example, when calculating the script
43+ // pubkey of a descriptor with xpubs, the secp context and child information
44+ // maybe required.
45+
46+ // Observe the script properties, just for fun.
47+ assert_eq ! (
48+ format!( "{:x}" , descriptor. script_pubkey( ) ) ,
49+ "00200ed49b334a12c37f3df8a2974ad91ff95029215a2b53f78155be737907f06163"
50+ ) ;
51+
52+ assert_eq ! (
53+ format!(
54+ "{:x}" ,
55+ descriptor
56+ . explicit_script( )
57+ . expect( "wsh descriptors have unique inner script" )
58+ ) ,
59+ "52\
60+ 21020202020202020202020202020202020202020202020202020202020202020202\
61+ 21020102030405060708010203040506070801020304050607080000000000000000\
62+ 21030102030405060708010203040506070801020304050607080000000000000000\
63+ 53ae"
64+ ) ;
65+
66+ // Attempt to satisfy at age 0, height 0.
67+ let original_txin = tx. input [ 0 ] . clone ( ) ;
68+
69+ let mut sigs = HashMap :: < bitcoin:: PublicKey , miniscript:: bitcoin:: EcdsaSig > :: new ( ) ;
70+
71+ // Doesn't work with no signatures.
72+ assert ! ( descriptor. satisfy( & mut tx. input[ 0 ] , & sigs) . is_err( ) ) ;
73+ assert_eq ! ( tx. input[ 0 ] , original_txin) ;
74+
75+ // ...or one signature...
76+ sigs. insert ( pks[ 1 ] , sig) ;
77+ assert ! ( descriptor. satisfy( & mut tx. input[ 0 ] , & sigs) . is_err( ) ) ;
78+ assert_eq ! ( tx. input[ 0 ] , original_txin) ;
79+
80+ // ...but two signatures is ok.
81+ sigs. insert ( pks[ 2 ] , sig) ;
82+ assert ! ( descriptor. satisfy( & mut tx. input[ 0 ] , & sigs) . is_ok( ) ) ;
83+ assert_ne ! ( tx. input[ 0 ] , original_txin) ;
84+ assert_eq ! ( tx. input[ 0 ] . witness. len( ) , 4 ) ; // 0, sig, sig, witness script
85+
86+ // ...and even if we give it a third signature, only two are used.
87+ sigs. insert ( pks[ 0 ] , sig) ;
88+ assert ! ( descriptor. satisfy( & mut tx. input[ 0 ] , & sigs) . is_ok( ) ) ;
89+ assert_ne ! ( tx. input[ 0 ] , original_txin) ;
90+ assert_eq ! ( tx. input[ 0 ] . witness. len( ) , 4 ) ; // 0, sig, sig, witness script
91+ }
92+
93+ // Transaction which spends some output.
94+ fn spending_transaction ( ) -> bitcoin:: Transaction {
95+ bitcoin:: Transaction {
3296 version : 2 ,
3397 lock_time : 0 ,
3498 input : vec ! [ bitcoin:: TxIn {
@@ -41,10 +105,12 @@ fn main() {
41105 script_pubkey: bitcoin:: Script :: new( ) ,
42106 value: 100_000_000 ,
43107 } ] ,
44- } ;
108+ }
109+ }
45110
111+ fn list_of_three_arbitrary_public_keys ( ) -> Vec < bitcoin:: PublicKey > {
46112 #[ cfg_attr( feature="cargo-fmt" , rustfmt_skip) ]
47- let public_keys = vec ! [
113+ vec ! [
48114 bitcoin:: PublicKey :: from_slice( & [ 2 ; 33 ] ) . expect( "key 1" ) ,
49115 bitcoin:: PublicKey :: from_slice( & [
50116 0x02 ,
@@ -60,10 +126,13 @@ fn main() {
60126 0x01 , 0x02 , 0x03 , 0x04 , 0x05 , 0x06 , 0x07 , 0x08 ,
61127 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
62128 ] ) . expect( "key 3" ) ,
63- ] ;
64- let bitcoin_sig = bitcoin:: EcdsaSig {
65- // copied at random off the blockchain; this is not actually a valid
66- // signature for this transaction; Miniscript does not verify
129+ ]
130+ }
131+
132+ // Returns a signature copied at random off the blockchain; this is not actually
133+ // a valid signature for this transaction; Miniscript does not verify the validity.
134+ fn random_signature_from_the_blockchain ( ) -> bitcoin:: EcdsaSig {
135+ bitcoin:: EcdsaSig {
67136 sig : secp256k1:: ecdsa:: Signature :: from_str (
68137 "3045\
69138 0221\
@@ -73,70 +142,5 @@ fn main() {
73142 )
74143 . unwrap ( ) ,
75144 hash_ty : bitcoin:: EcdsaSighashType :: All ,
76- } ;
77-
78- let descriptor_str = format ! (
79- "wsh(multi(2,{},{},{}))" ,
80- public_keys[ 0 ] , public_keys[ 1 ] , public_keys[ 2 ] ,
81- ) ;
82-
83- // Descriptor for the output being spent
84- let my_descriptor =
85- BitcoinDescriptor :: from_str ( & descriptor_str[ ..] ) . expect ( "parse descriptor string" ) ;
86-
87- // Check weight for witness satisfaction cost ahead of time.
88- // 4(scriptSig length of 0) + 1(witness stack size) + 106(serialized witnessScript)
89- // + 73*2(signature length + signatures + sighash bytes) + 1(dummy byte) = 258
90- assert_eq ! ( my_descriptor. max_satisfaction_weight( ) . unwrap( ) , 258 ) ;
91-
92- // Sometimes it is necessary to have additional information to get the bitcoin::PublicKey
93- // from the MiniscriptKey which can supplied by `to_pk_ctx` parameter. For example,
94- // when calculating the script pubkey of a descriptor with xpubs, the secp context and
95- // child information maybe required.
96-
97- // Observe the script properties, just for fun
98- assert_eq ! (
99- format!( "{:x}" , my_descriptor. script_pubkey( ) ) ,
100- "00200ed49b334a12c37f3df8a2974ad91ff95029215a2b53f78155be737907f06163"
101- ) ;
102-
103- assert_eq ! (
104- format!(
105- "{:x}" ,
106- my_descriptor
107- . explicit_script( )
108- . expect( "wsh descriptors have unique inner script" )
109- ) ,
110- "52\
111- 21020202020202020202020202020202020202020202020202020202020202020202\
112- 21020102030405060708010203040506070801020304050607080000000000000000\
113- 21030102030405060708010203040506070801020304050607080000000000000000\
114- 53ae"
115- ) ;
116-
117- // Attempt to satisfy at age 0, height 0
118- let original_txin = tx. input [ 0 ] . clone ( ) ;
119-
120- let mut sigs = HashMap :: < bitcoin:: PublicKey , miniscript:: bitcoin:: EcdsaSig > :: new ( ) ;
121-
122- // Doesn't work with no signatures
123- assert ! ( my_descriptor. satisfy( & mut tx. input[ 0 ] , & sigs) . is_err( ) ) ;
124- assert_eq ! ( tx. input[ 0 ] , original_txin) ;
125-
126- // ...or one signature...
127- sigs. insert ( public_keys[ 1 ] , bitcoin_sig) ;
128- assert ! ( my_descriptor. satisfy( & mut tx. input[ 0 ] , & sigs) . is_err( ) ) ;
129- assert_eq ! ( tx. input[ 0 ] , original_txin) ;
130-
131- // ...but two signatures is ok
132- sigs. insert ( public_keys[ 2 ] , bitcoin_sig) ;
133- assert ! ( my_descriptor. satisfy( & mut tx. input[ 0 ] , & sigs) . is_ok( ) ) ;
134- assert_ne ! ( tx. input[ 0 ] , original_txin) ;
135- assert_eq ! ( tx. input[ 0 ] . witness. len( ) , 4 ) ; // 0, sig, sig, witness script
136-
137- // ...and even if we give it a third signature, only two are used
138- sigs. insert ( public_keys[ 0 ] , bitcoin_sig) ;
139- assert ! ( my_descriptor. satisfy( & mut tx. input[ 0 ] , & sigs) . is_ok( ) ) ;
140- assert_ne ! ( tx. input[ 0 ] , original_txin) ;
141- assert_eq ! ( tx. input[ 0 ] . witness. len( ) , 4 ) ; // 0, sig, sig, witness script
145+ }
142146}
0 commit comments