1- // SPDX-License-Identifier: CC0-1.0
2-
31use std:: collections:: BTreeMap ;
42use std:: str:: FromStr ;
53
4+ use bitcoin:: sighash:: SighashCache ;
5+ use bitcoin:: PrivateKey ;
66use miniscript:: bitcoin:: consensus:: encode:: deserialize;
77use miniscript:: bitcoin:: hashes:: hex:: FromHex ;
8- use miniscript:: bitcoin:: psbt:: { self , Psbt } ;
9- use miniscript:: bitcoin:: sighash:: SighashCache ;
8+ use miniscript:: bitcoin:: psbt:: PartiallySignedTransaction as Psbt ;
109use miniscript:: bitcoin:: {
11- self , base64, secp256k1, Address , Network , OutPoint , PrivateKey , Script , Sequence , Transaction ,
12- TxIn , TxOut ,
10+ self , base64, psbt , secp256k1, Address , Network , OutPoint , Script , Sequence , Transaction , TxIn ,
11+ TxOut ,
1312} ;
1413use miniscript:: psbt:: { PsbtExt , PsbtInputExt } ;
15- use miniscript:: Descriptor ;
14+ use miniscript:: { Descriptor , DescriptorPublicKey } ;
1615
1716fn main ( ) {
17+ // Defining the descriptor keys
1818 let secp256k1 = secp256k1:: Secp256k1 :: new ( ) ;
19+ let keys = vec ! [
20+ "027a3565454fe1b749bccaef22aff72843a9c3efefd7b16ac54537a0c23f0ec0de" ,
21+ "032d672a1a91cc39d154d366cd231983661b0785c7f27bc338447565844f4a6813" ,
22+ "03417129311ed34c242c012cd0a3e0b9bca0065f742d0dfb63c78083ea6a02d4d9" ,
23+ "025a687659658baeabdfc415164528065be7bcaade19342241941e556557f01e28" ,
24+ ] ;
25+ // The wsh descriptor indicates a Witness Script Hash, meaning the descriptor is for a SegWit script.
26+ // wsh(or(pk(A),thresh(1,pkh(B),pkh(C),pkh(D))))
27+
28+ // Let's break it down:
29+ // t:or_c specifies an "or" construct, which means the script can be satisfied by one of the given conditions:
30+ // pk(A) OR thresh(1,pkh(B),pkh(C),pkh(D))
31+ // Inside threshold condition atleast 1 out of all given conditions should satisfy.
32+
33+ // By constructing transactions using this wsh descriptor and signing them appropriately,
34+ // you can create flexible spending policies that enable different spending paths and conditions depending on the
35+ // transaction's inputs and outputs.
36+ let s = format ! (
37+ "wsh(t:or_c(pk({}),v:thresh(1,pkh({}),a:pkh({}),a:pkh({}))))" ,
38+ keys[ 0 ] , // key A
39+ keys[ 1 ] , // key B
40+ keys[ 2 ] , // key C
41+ keys[ 3 ] , // key D
42+ ) ;
43+ let descriptor = Descriptor :: from_str ( & s) . expect ( "parse descriptor string" ) ;
1944
20- let s = "wsh(t:or_c(pk(027a3565454fe1b749bccaef22aff72843a9c3efefd7b16ac54537a0c23f0ec0de),v:thresh(1,pkh(032d672a1a91cc39d154d366cd231983661b0785c7f27bc338447565844f4a6813),a:pkh(03417129311ed34c242c012cd0a3e0b9bca0065f742d0dfb63c78083ea6a02d4d9),a:pkh(025a687659658baeabdfc415164528065be7bcaade19342241941e556557f01e28))))#7hut9ukn" ;
21- let bridge_descriptor = Descriptor :: from_str ( & s) . unwrap ( ) ;
22- //let bridge_descriptor = Descriptor::<bitcoin::PublicKey>::from_str(&s).expect("parse descriptor string");
23- assert ! ( bridge_descriptor. sanity_check( ) . is_ok( ) ) ;
24- println ! ( "Bridge pubkey script: {}" , bridge_descriptor. script_pubkey( ) ) ;
25- println ! ( "Bridge address: {}" , bridge_descriptor. address( Network :: Regtest ) . unwrap( ) ) ;
45+ assert ! ( descriptor. sanity_check( ) . is_ok( ) ) ;
46+ println ! ( "descriptor pubkey script: {}" , descriptor. script_pubkey( ) ) ;
47+ println ! ( "descriptor address: {}" , descriptor. address( Network :: Regtest ) . unwrap( ) ) ;
2648 println ! (
2749 "Weight for witness satisfaction cost {}" ,
28- bridge_descriptor . max_weight_to_satisfy( ) . unwrap( )
50+ descriptor . max_weight_to_satisfy( ) . unwrap( )
2951 ) ;
3052
3153 let master_private_key_str = "cQhdvB3McbBJdx78VSSumqoHQiSXs75qwLptqwxSQBNBMDxafvaw" ;
@@ -51,24 +73,14 @@ fn main() {
5173
5274 println ! ( "Backup3 public key: {}" , _backup3_private. public_key( & secp256k1) ) ;
5375
76+ // Create a spending transaction
5477 let spend_tx = Transaction {
5578 version : 2 ,
5679 lock_time : bitcoin:: absolute:: LockTime :: from_consensus ( 5000 ) ,
5780 input : vec ! [ ] ,
5881 output : vec ! [ ] ,
5982 } ;
6083
61- // Spend one input and spend one output for simplicity.
62- let mut psbt = Psbt {
63- unsigned_tx : spend_tx,
64- unknown : BTreeMap :: new ( ) ,
65- proprietary : BTreeMap :: new ( ) ,
66- xpub : BTreeMap :: new ( ) ,
67- version : 0 ,
68- inputs : vec ! [ ] ,
69- outputs : vec ! [ ] ,
70- } ;
71-
7284 let hex_tx = "020000000001018ff27041f3d738f5f84fd5ee62f1c5b36afebfb15f6da0c9d1382ddd0eaaa23c0000000000feffffff02b3884703010000001600142ca3b4e53f17991582d47b15a053b3201891df5200e1f50500000000220020c0ebf552acd2a6f5dee4e067daaef17b3521e283aeaa44a475278617e3d2238a0247304402207b820860a9d425833f729775880b0ed59dd12b64b9a3d1ab677e27e4d6b370700220576003163f8420fe0b9dc8df726cff22cbc191104a2d4ae4f9dfedb087fcec72012103817e1da42a7701df4db94db8576f0e3605f3ab3701608b7e56f92321e4d8999100000000" ;
7385 let depo_tx: Transaction = deserialize ( & Vec :: < u8 > :: from_hex ( hex_tx) . unwrap ( ) ) . unwrap ( ) ;
7486
@@ -78,70 +90,97 @@ fn main() {
7890
7991 let amount = 100000000 ;
8092
81- let ( outpoint, witness_utxo) = get_vout ( & depo_tx, & bridge_descriptor. script_pubkey ( ) ) ;
82-
83- let mut txin = TxIn :: default ( ) ;
84- txin. previous_output = outpoint;
85-
86- txin. sequence = Sequence :: from_height ( 26 ) ; //Sequence::MAX; //
87- psbt. unsigned_tx . input . push ( txin) ;
88-
89- psbt. unsigned_tx
90- . output
91- . push ( TxOut { script_pubkey : receiver. script_pubkey ( ) , value : amount / 5 - 500 } ) ;
92-
93- psbt. unsigned_tx
94- . output
95- . push ( TxOut { script_pubkey : bridge_descriptor. script_pubkey ( ) , value : amount * 4 / 5 } ) ;
96-
97- // Generating signatures & witness data
98-
99- let mut input = psbt:: Input :: default ( ) ;
100- input
101- . update_with_descriptor_unchecked ( & bridge_descriptor)
102- . unwrap ( ) ;
103-
104- input. witness_utxo = Some ( witness_utxo. clone ( ) ) ;
105- psbt. inputs . push ( input) ;
106- psbt. outputs . push ( psbt:: Output :: default ( ) ) ;
93+ let ( outpoint, witness_utxo) = get_vout ( & depo_tx, & descriptor. script_pubkey ( ) ) ;
10794
108- let mut sighash_cache = SighashCache :: new ( & psbt. unsigned_tx ) ;
109-
110- let msg = psbt
111- . sighash_msg ( 0 , & mut sighash_cache, None )
95+ let all_assets = Descriptor :: < DescriptorPublicKey > :: from_str ( & s)
11296 . unwrap ( )
113- . to_secp_msg ( ) ;
114-
115- // Fixme: Take a parameter
116- let hash_ty = bitcoin:: sighash:: EcdsaSighashType :: All ;
117-
118- let sk1 = backup1_private. inner ;
119- let sk2 = backup2_private. inner ;
120-
121- // Finally construct the signature and add to psbt
122- let sig1 = secp256k1. sign_ecdsa ( & msg, & sk1) ;
123- let pk1 = backup1_private. public_key ( & secp256k1) ;
124- assert ! ( secp256k1. verify_ecdsa( & msg, & sig1, & pk1. inner) . is_ok( ) ) ;
125-
126- // Second key just in case
127- let sig2 = secp256k1. sign_ecdsa ( & msg, & sk2) ;
128- let pk2 = backup2_private. public_key ( & secp256k1) ;
129- assert ! ( secp256k1. verify_ecdsa( & msg, & sig2, & pk2. inner) . is_ok( ) ) ;
130-
131- psbt. inputs [ 0 ]
132- . partial_sigs
133- . insert ( pk1, bitcoin:: ecdsa:: Signature { sig : sig1, hash_ty : hash_ty } ) ;
134-
135- println ! ( "{:#?}" , psbt) ;
136-
137- let serialized = psbt. serialize ( ) ;
138- println ! ( "{}" , base64:: encode( & serialized) ) ;
139-
140- psbt. finalize_mut ( & secp256k1) . unwrap ( ) ;
141- println ! ( "{:#?}" , psbt) ;
97+ . all_assets ( )
98+ . unwrap ( ) ;
14299
143- let tx = psbt. extract_tx ( ) ;
144- println ! ( "{}" , bitcoin:: consensus:: encode:: serialize_hex( & tx) ) ;
100+ for asset in all_assets {
101+ // Spend one input and spend one output for simplicity.
102+ let mut psbt = Psbt {
103+ unsigned_tx : spend_tx. clone ( ) ,
104+ unknown : BTreeMap :: new ( ) ,
105+ proprietary : BTreeMap :: new ( ) ,
106+ xpub : BTreeMap :: new ( ) ,
107+ version : 0 ,
108+ inputs : vec ! [ ] ,
109+ outputs : vec ! [ ] ,
110+ } ;
111+
112+ // Defining the Transaction Input
113+ let mut txin = TxIn :: default ( ) ;
114+ txin. previous_output = outpoint;
115+ txin. sequence = Sequence :: from_height ( 26 ) ; //Sequence::MAX; //
116+ psbt. unsigned_tx . input . push ( txin) ;
117+
118+ // Defining the Transaction Output
119+ psbt. unsigned_tx
120+ . output
121+ . push ( TxOut { script_pubkey : receiver. script_pubkey ( ) , value : amount / 5 - 500 } ) ;
122+
123+ psbt. unsigned_tx
124+ . output
125+ . push ( TxOut { script_pubkey : descriptor. script_pubkey ( ) , value : amount * 4 / 5 } ) ;
126+
127+ // Consider that out of all the keys required to sign the descriptor, we only have some handful of assets.
128+ // We can plan the PSBT with only few assets(keys or hashes) if that are enough for satisfying any policy.
129+ //
130+ // Here for example assume that we only have one key available i.e Key A(as seen from the descriptor above)
131+ // Key A is enough to satisfy the given descriptor because it is OR.
132+ // We have to add the key to `Asset` and then obtain plan with only available signature if the descriptor can be satisfied.
133+
134+ // Check the possible asset which we can use
135+ println ! ( "{:#?}" , asset) ;
136+
137+ // Obtain the Plan based on available Assets
138+ let plan = descriptor. clone ( ) . plan ( & asset) . unwrap ( ) ;
139+
140+ // Creating a PSBT Input
141+ let mut input = psbt:: Input :: default ( ) ;
142+
143+ // Update the PSBT input from the result which we have obtained from the Plan.
144+ plan. update_psbt_input ( & mut input) ;
145+ input. update_with_descriptor_unchecked ( & descriptor) . unwrap ( ) ;
146+ input. witness_utxo = Some ( witness_utxo. clone ( ) ) ;
147+
148+ // Push the PSBT Input and declare an PSBT Output Structure
149+ psbt. inputs . push ( input) ;
150+ psbt. outputs . push ( psbt:: Output :: default ( ) ) ;
151+
152+ let mut sighash_cache = SighashCache :: new ( & psbt. unsigned_tx ) ;
153+
154+ let msg = psbt
155+ . sighash_msg ( 0 , & mut sighash_cache, None )
156+ . unwrap ( )
157+ . to_secp_msg ( ) ;
158+
159+ // Fixme: Take a parameter
160+ let hash_ty = bitcoin:: sighash:: EcdsaSighashType :: All ;
161+
162+ let sk = backup1_private. inner ;
163+
164+ // Finally construct the signature and add to psbt
165+ let sig = secp256k1. sign_ecdsa ( & msg, & sk) ;
166+ let key_a = backup1_private. public_key ( & secp256k1) ;
167+ assert ! ( secp256k1. verify_ecdsa( & msg, & sig, & key_a. inner) . is_ok( ) ) ;
168+
169+ psbt. inputs [ 0 ]
170+ . partial_sigs
171+ . insert ( key_a, bitcoin:: ecdsa:: Signature { sig, hash_ty } ) ;
172+
173+ println ! ( "{:#?}" , psbt) ;
174+
175+ let serialized = psbt. serialize ( ) ;
176+ println ! ( "{}" , base64:: encode( & serialized) ) ;
177+
178+ psbt. finalize_mut ( & secp256k1) . unwrap ( ) ;
179+ println ! ( "{:#?}" , psbt) ;
180+
181+ let tx = psbt. extract_tx ( ) ;
182+ println ! ( "{}" , bitcoin:: consensus:: encode:: serialize_hex( & tx) ) ;
183+ }
145184}
146185
147186// Find the Outpoint by spk
0 commit comments