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-
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 ! (
25- "Bridge pubkey script: {}" ,
26- bridge_descriptor. script_pubkey( )
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
2742 ) ;
43+ let descriptor = Descriptor :: from_str ( & s) . expect ( "parse descriptor string" ) ;
44+
45+ assert ! ( descriptor. sanity_check( ) . is_ok( ) ) ;
46+ println ! ( "descriptor pubkey script: {}" , descriptor. script_pubkey( ) ) ;
2847 println ! (
29- "Bridge address: {}" ,
30- bridge_descriptor . address( Network :: Regtest ) . unwrap( )
48+ "descriptor address: {}" ,
49+ descriptor . address( Network :: Regtest ) . unwrap( )
3150 ) ;
3251 println ! (
3352 "Weight for witness satisfaction cost {}" ,
34- bridge_descriptor . max_weight_to_satisfy( ) . unwrap( )
53+ descriptor . max_weight_to_satisfy( ) . unwrap( )
3554 ) ;
3655
3756 let master_private_key_str = "cQhdvB3McbBJdx78VSSumqoHQiSXs75qwLptqwxSQBNBMDxafvaw" ;
@@ -69,24 +88,14 @@ fn main() {
6988 _backup3_private. public_key( & secp256k1)
7089 ) ;
7190
91+ // Create a spending transaction
7292 let spend_tx = Transaction {
7393 version : 2 ,
7494 lock_time : bitcoin:: absolute:: LockTime :: from_consensus ( 5000 ) ,
7595 input : vec ! [ ] ,
7696 output : vec ! [ ] ,
7797 } ;
7898
79- // Spend one input and spend one output for simplicity.
80- let mut psbt = Psbt {
81- unsigned_tx : spend_tx,
82- unknown : BTreeMap :: new ( ) ,
83- proprietary : BTreeMap :: new ( ) ,
84- xpub : BTreeMap :: new ( ) ,
85- version : 0 ,
86- inputs : vec ! [ ] ,
87- outputs : vec ! [ ] ,
88- } ;
89-
9099 let hex_tx = "020000000001018ff27041f3d738f5f84fd5ee62f1c5b36afebfb15f6da0c9d1382ddd0eaaa23c0000000000feffffff02b3884703010000001600142ca3b4e53f17991582d47b15a053b3201891df5200e1f50500000000220020c0ebf552acd2a6f5dee4e067daaef17b3521e283aeaa44a475278617e3d2238a0247304402207b820860a9d425833f729775880b0ed59dd12b64b9a3d1ab677e27e4d6b370700220576003163f8420fe0b9dc8df726cff22cbc191104a2d4ae4f9dfedb087fcec72012103817e1da42a7701df4db94db8576f0e3605f3ab3701608b7e56f92321e4d8999100000000" ;
91100 let depo_tx: Transaction = deserialize ( & Vec :: < u8 > :: from_hex ( hex_tx) . unwrap ( ) ) . unwrap ( ) ;
92101
@@ -96,76 +105,99 @@ fn main() {
96105
97106 let amount = 100000000 ;
98107
99- let ( outpoint, witness_utxo) = get_vout ( & depo_tx, & bridge_descriptor. script_pubkey ( ) ) ;
100-
101- let mut txin = TxIn :: default ( ) ;
102- txin. previous_output = outpoint;
103-
104- txin. sequence = Sequence :: from_height ( 26 ) ; //Sequence::MAX; //
105- psbt. unsigned_tx . input . push ( txin) ;
106-
107- psbt. unsigned_tx . output . push ( TxOut {
108- script_pubkey : receiver. script_pubkey ( ) ,
109- value : amount / 5 - 500 ,
110- } ) ;
111-
112- psbt. unsigned_tx . output . push ( TxOut {
113- script_pubkey : bridge_descriptor. script_pubkey ( ) ,
114- value : amount * 4 / 5 ,
115- } ) ;
116-
117- // Generating signatures & witness data
108+ let ( outpoint, witness_utxo) = get_vout ( & depo_tx, & descriptor. script_pubkey ( ) ) ;
118109
119- let mut input = psbt:: Input :: default ( ) ;
120- input
121- . update_with_descriptor_unchecked ( & bridge_descriptor)
122- . unwrap ( ) ;
123-
124- input. witness_utxo = Some ( witness_utxo. clone ( ) ) ;
125- psbt. inputs . push ( input) ;
126- psbt. outputs . push ( psbt:: Output :: default ( ) ) ;
127-
128- let mut sighash_cache = SighashCache :: new ( & psbt. unsigned_tx ) ;
129-
130- let msg = psbt
131- . sighash_msg ( 0 , & mut sighash_cache, None )
110+ let all_assets = Descriptor :: < DescriptorPublicKey > :: from_str ( & s)
132111 . unwrap ( )
133- . to_secp_msg ( ) ;
134-
135- // Fixme: Take a parameter
136- let hash_ty = bitcoin:: sighash:: EcdsaSighashType :: All ;
137-
138- let sk1 = backup1_private. inner ;
139- let sk2 = backup2_private. inner ;
140-
141- // Finally construct the signature and add to psbt
142- let sig1 = secp256k1. sign_ecdsa ( & msg, & sk1) ;
143- let pk1 = backup1_private. public_key ( & secp256k1) ;
144- assert ! ( secp256k1. verify_ecdsa( & msg, & sig1, & pk1. inner) . is_ok( ) ) ;
145-
146- // Second key just in case
147- let sig2 = secp256k1. sign_ecdsa ( & msg, & sk2) ;
148- let pk2 = backup2_private. public_key ( & secp256k1) ;
149- assert ! ( secp256k1. verify_ecdsa( & msg, & sig2, & pk2. inner) . is_ok( ) ) ;
150-
151- psbt. inputs [ 0 ] . partial_sigs . insert (
152- pk1,
153- bitcoin:: ecdsa:: Signature {
154- sig : sig1,
155- hash_ty : hash_ty,
156- } ,
157- ) ;
158-
159- println ! ( "{:#?}" , psbt) ;
160-
161- let serialized = psbt. serialize ( ) ;
162- println ! ( "{}" , base64:: encode( & serialized) ) ;
163-
164- psbt. finalize_mut ( & secp256k1) . unwrap ( ) ;
165- println ! ( "{:#?}" , psbt) ;
112+ . all_assets ( )
113+ . unwrap ( ) ;
166114
167- let tx = psbt. extract_tx ( ) ;
168- println ! ( "{}" , bitcoin:: consensus:: encode:: serialize_hex( & tx) ) ;
115+ for asset in all_assets {
116+ // Spend one input and spend one output for simplicity.
117+ let mut psbt = Psbt {
118+ unsigned_tx : spend_tx. clone ( ) ,
119+ unknown : BTreeMap :: new ( ) ,
120+ proprietary : BTreeMap :: new ( ) ,
121+ xpub : BTreeMap :: new ( ) ,
122+ version : 0 ,
123+ inputs : vec ! [ ] ,
124+ outputs : vec ! [ ] ,
125+ } ;
126+
127+ // Defining the Transaction Input
128+ let mut txin = TxIn :: default ( ) ;
129+ txin. previous_output = outpoint;
130+ txin. sequence = Sequence :: from_height ( 26 ) ; //Sequence::MAX; //
131+ psbt. unsigned_tx . input . push ( txin) ;
132+
133+ // Defining the Transaction Output
134+ psbt. unsigned_tx . output . push ( TxOut {
135+ script_pubkey : receiver. script_pubkey ( ) ,
136+ value : amount / 5 - 500 ,
137+ } ) ;
138+
139+ psbt. unsigned_tx . output . push ( TxOut {
140+ script_pubkey : descriptor. script_pubkey ( ) ,
141+ value : amount * 4 / 5 ,
142+ } ) ;
143+
144+ // Consider that out of all the keys required to sign the descriptor, we only have some handful of assets.
145+ // We can plan the PSBT with only few assets(keys or hashes) if that are enough for satisfying any policy.
146+ //
147+ // Here for example assume that we only have one key available i.e Key A(as seen from the descriptor above)
148+ // Key A is enough to satisfy the given descriptor because it is OR.
149+ // We have to add the key to `Asset` and then obtain plan with only available signature if the descriptor can be satisfied.
150+
151+ // Check the possible asset which we can use
152+ println ! ( "{:#?}" , asset) ;
153+
154+ // Obtain the Plan based on available Assets
155+ let plan = descriptor. clone ( ) . plan ( & asset) . unwrap ( ) ;
156+
157+ // Creating a PSBT Input
158+ let mut input = psbt:: Input :: default ( ) ;
159+
160+ // Update the PSBT input from the result which we have obtained from the Plan.
161+ plan. update_psbt_input ( & mut input) ;
162+ input. update_with_descriptor_unchecked ( & descriptor) . unwrap ( ) ;
163+ input. witness_utxo = Some ( witness_utxo. clone ( ) ) ;
164+
165+ // Push the PSBT Input and declare an PSBT Output Structure
166+ psbt. inputs . push ( input) ;
167+ psbt. outputs . push ( psbt:: Output :: default ( ) ) ;
168+
169+ let mut sighash_cache = SighashCache :: new ( & psbt. unsigned_tx ) ;
170+
171+ let msg = psbt
172+ . sighash_msg ( 0 , & mut sighash_cache, None )
173+ . unwrap ( )
174+ . to_secp_msg ( ) ;
175+
176+ // Fixme: Take a parameter
177+ let hash_ty = bitcoin:: sighash:: EcdsaSighashType :: All ;
178+
179+ let sk = backup1_private. inner ;
180+
181+ // Finally construct the signature and add to psbt
182+ let sig = secp256k1. sign_ecdsa ( & msg, & sk) ;
183+ let key_a = backup1_private. public_key ( & secp256k1) ;
184+ assert ! ( secp256k1. verify_ecdsa( & msg, & sig, & key_a. inner) . is_ok( ) ) ;
185+
186+ psbt. inputs [ 0 ]
187+ . partial_sigs
188+ . insert ( key_a, bitcoin:: ecdsa:: Signature { sig, hash_ty } ) ;
189+
190+ println ! ( "{:#?}" , psbt) ;
191+
192+ let serialized = psbt. serialize ( ) ;
193+ println ! ( "{}" , base64:: encode( & serialized) ) ;
194+
195+ psbt. finalize_mut ( & secp256k1) . unwrap ( ) ;
196+ println ! ( "{:#?}" , psbt) ;
197+
198+ let tx = psbt. extract_tx ( ) ;
199+ println ! ( "{}" , bitcoin:: consensus:: encode:: serialize_hex( & tx) ) ;
200+ }
169201}
170202
171203// Find the Outpoint by spk
0 commit comments