1
- // SPDX-License-Identifier: CC0-1.0
2
-
3
1
use std:: collections:: BTreeMap ;
4
2
use std:: str:: FromStr ;
5
3
4
+ use bitcoin:: sighash:: SighashCache ;
5
+ use bitcoin:: PrivateKey ;
6
6
use miniscript:: bitcoin:: consensus:: encode:: deserialize;
7
7
use 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 ;
10
9
use 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 ,
13
12
} ;
14
13
use miniscript:: psbt:: { PsbtExt , PsbtInputExt } ;
15
- use miniscript:: Descriptor ;
14
+ use miniscript:: { Descriptor , DescriptorPublicKey } ;
16
15
17
16
fn main ( ) {
17
+ // Defining the descriptor keys
18
18
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
27
42
) ;
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( ) ) ;
28
47
println ! (
29
- "Bridge address: {}" ,
30
- bridge_descriptor . address( Network :: Regtest ) . unwrap( )
48
+ "descriptor address: {}" ,
49
+ descriptor . address( Network :: Regtest ) . unwrap( )
31
50
) ;
32
51
println ! (
33
52
"Weight for witness satisfaction cost {}" ,
34
- bridge_descriptor . max_weight_to_satisfy( ) . unwrap( )
53
+ descriptor . max_weight_to_satisfy( ) . unwrap( )
35
54
) ;
36
55
37
56
let master_private_key_str = "cQhdvB3McbBJdx78VSSumqoHQiSXs75qwLptqwxSQBNBMDxafvaw" ;
@@ -69,24 +88,14 @@ fn main() {
69
88
_backup3_private. public_key( & secp256k1)
70
89
) ;
71
90
91
+ // Create a spending transaction
72
92
let spend_tx = Transaction {
73
93
version : 2 ,
74
94
lock_time : bitcoin:: absolute:: LockTime :: from_consensus ( 5000 ) ,
75
95
input : vec ! [ ] ,
76
96
output : vec ! [ ] ,
77
97
} ;
78
98
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
-
90
99
let hex_tx = "020000000001018ff27041f3d738f5f84fd5ee62f1c5b36afebfb15f6da0c9d1382ddd0eaaa23c0000000000feffffff02b3884703010000001600142ca3b4e53f17991582d47b15a053b3201891df5200e1f50500000000220020c0ebf552acd2a6f5dee4e067daaef17b3521e283aeaa44a475278617e3d2238a0247304402207b820860a9d425833f729775880b0ed59dd12b64b9a3d1ab677e27e4d6b370700220576003163f8420fe0b9dc8df726cff22cbc191104a2d4ae4f9dfedb087fcec72012103817e1da42a7701df4db94db8576f0e3605f3ab3701608b7e56f92321e4d8999100000000" ;
91
100
let depo_tx: Transaction = deserialize ( & Vec :: < u8 > :: from_hex ( hex_tx) . unwrap ( ) ) . unwrap ( ) ;
92
101
@@ -96,76 +105,99 @@ fn main() {
96
105
97
106
let amount = 100000000 ;
98
107
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 ( ) ) ;
118
109
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)
132
111
. 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 ( ) ;
166
114
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
+ }
169
201
}
170
202
171
203
// Find the Outpoint by spk
0 commit comments