1
1
use {
2
- base64:: { engine:: general_purpose, Engine } ,
3
2
bitcoin:: {
4
- absolute:: LockTime ,
5
- blockdata:: script,
6
- consensus:: { Decodable , Encodable } ,
7
- key:: { Keypair , TapTweak } ,
8
- opcodes,
9
- psbt:: Psbt ,
10
- script:: PushBytes ,
11
- secp256k1:: { self , schnorr:: Signature , Message , Secp256k1 , XOnlyPublicKey } ,
12
- sighash:: { self , SighashCache , TapSighashType } ,
13
- transaction:: Version ,
14
- Address , Amount , Network , OutPoint , PrivateKey , PublicKey , ScriptBuf , Sequence , Transaction ,
15
- TxIn , TxOut , Witness ,
3
+ absolute:: LockTime , blockdata:: script, opcodes, psbt:: Psbt , script:: PushBytes ,
4
+ secp256k1:: Secp256k1 , transaction:: Version , Address , Amount , Network , OutPoint , PrivateKey ,
5
+ PublicKey , ScriptBuf , Sequence , Transaction , TxIn , TxOut , Witness ,
16
6
} ,
17
7
bitcoin_hashes:: { sha256, Hash } ,
18
- std:: { io:: Cursor , str} ,
8
+ } ;
9
+
10
+ mod sign;
11
+ mod verify;
12
+
13
+ pub use {
14
+ sign:: { full_sign, simple_sign} ,
15
+ verify:: { full_verify, simple_verify} ,
19
16
} ;
20
17
21
18
pub struct Wallet {
@@ -57,7 +54,7 @@ fn message_hash(message: &str) -> Vec<u8> {
57
54
. to_vec ( )
58
55
}
59
56
60
- fn to_spend ( address : & Address , message : & str ) -> Transaction {
57
+ fn create_to_spend ( address : & Address , message : & str ) -> Transaction {
61
58
Transaction {
62
59
version : Version ( 0 ) ,
63
60
lock_time : LockTime :: ZERO ,
@@ -82,153 +79,36 @@ fn to_spend(address: &Address, message: &str) -> Transaction {
82
79
}
83
80
}
84
81
85
- fn to_sign ( to_spend_tx : Transaction ) -> Transaction {
86
- Transaction {
82
+ fn create_to_sign ( to_spend : & Transaction ) -> Psbt {
83
+ let inputs = vec ! [ TxIn {
84
+ previous_output: OutPoint {
85
+ txid: to_spend. txid( ) ,
86
+ vout: 0 ,
87
+ } ,
88
+ script_sig: ScriptBuf :: new( ) ,
89
+ sequence: Sequence ( 0 ) ,
90
+ witness: Witness :: new( ) ,
91
+ } ] ;
92
+
93
+ let to_sign = Transaction {
87
94
version : Version ( 0 ) ,
88
95
lock_time : LockTime :: ZERO ,
89
- input : vec ! [ TxIn {
90
- previous_output: OutPoint {
91
- txid: to_spend_tx. txid( ) ,
92
- vout: 0 ,
93
- } ,
94
- script_sig: ScriptBuf :: new( ) ,
95
- sequence: Sequence ( 0 ) ,
96
- witness: Witness :: new( ) ,
97
- } ] ,
96
+ input : inputs,
98
97
output : vec ! [ TxOut {
99
98
value: Amount :: from_sat( 0 ) ,
100
99
script_pubkey: script:: Builder :: new( )
101
100
. push_opcode( opcodes:: all:: OP_RETURN )
102
101
. into_script( ) ,
103
102
} ] ,
104
- }
105
- }
103
+ } ;
106
104
107
- // #[allow(unused)]
108
- fn to_sign_psbt (
109
- to_spend_tx : Transaction ,
110
- to_sign_tx : Transaction ,
111
- ) -> Result < Psbt , bitcoin:: psbt:: Error > {
112
- let mut psbt = Psbt :: from_unsigned_tx ( to_sign_tx) ?;
105
+ let mut psbt = Psbt :: from_unsigned_tx ( to_sign) . unwrap ( ) ;
113
106
psbt. inputs [ 0 ] . witness_utxo = Some ( TxOut {
114
107
value : Amount :: from_sat ( 0 ) ,
115
- script_pubkey : to_spend_tx . output [ 0 ] . script_pubkey . clone ( ) ,
108
+ script_pubkey : to_spend . output [ 0 ] . script_pubkey . clone ( ) ,
116
109
} ) ;
117
110
118
- Ok ( psbt)
119
- }
120
-
121
- pub fn sign ( address : & Address , message : & str , wallet : & Wallet ) -> String {
122
- let to_spend_tx = to_spend ( address, message) ;
123
- let to_sign_tx = to_sign ( to_spend_tx. clone ( ) ) ;
124
- let mut psbt = to_sign_psbt ( to_spend_tx. clone ( ) , to_sign_tx) . unwrap ( ) ;
125
-
126
- let secp = Secp256k1 :: new ( ) ;
127
- let private_key = wallet. private_key ;
128
- let key_pair = Keypair :: from_secret_key ( & secp, & private_key. inner ) ;
129
- let ( x_only_public_key, _parity) = XOnlyPublicKey :: from_keypair ( & key_pair) ;
130
-
131
- psbt. inputs [ 0 ] . tap_internal_key = Some ( x_only_public_key) ;
132
-
133
- let sighash_type = TapSighashType :: All ;
134
-
135
- let mut sighash_cache = SighashCache :: new ( psbt. unsigned_tx . clone ( ) ) ;
136
-
137
- let sighash = sighash_cache
138
- . taproot_key_spend_signature_hash (
139
- 0 ,
140
- & sighash:: Prevouts :: All ( & [ TxOut {
141
- value : Amount :: from_sat ( 0 ) ,
142
- script_pubkey : to_spend_tx. output [ 0 ] . clone ( ) . script_pubkey ,
143
- } ] ) ,
144
- sighash_type,
145
- )
146
- . expect ( "signature hash should compute" ) ;
147
-
148
- let key_pair = key_pair
149
- . tap_tweak ( & secp, psbt. inputs [ 0 ] . tap_merkle_root )
150
- . to_inner ( ) ;
151
-
152
- let sig = secp. sign_schnorr_no_aux_rand (
153
- & secp256k1:: Message :: from_digest_slice ( sighash. as_ref ( ) )
154
- . expect ( "should be cryptographically secure hash" ) ,
155
- & key_pair,
156
- ) ;
157
-
158
- let witness = sighash_cache
159
- . witness_mut ( 0 )
160
- . expect ( "getting mutable witness reference should work" ) ;
161
-
162
- witness. push (
163
- bitcoin:: taproot:: Signature {
164
- sig,
165
- hash_ty : sighash_type,
166
- }
167
- . to_vec ( ) ,
168
- ) ;
169
-
170
- let mut buffer = Vec :: new ( ) ;
171
- witness. consensus_encode ( & mut buffer) . unwrap ( ) ;
172
-
173
- general_purpose:: STANDARD . encode ( buffer)
174
- }
175
-
176
- pub fn verify ( address : & Address , message : & str , signature : & str ) -> bool {
177
- let to_spend_tx = to_spend ( address, message) ;
178
- let to_sign_tx = to_sign ( to_spend_tx. clone ( ) ) ;
179
-
180
- let mut cursor = Cursor :: new ( general_purpose:: STANDARD . decode ( signature) . unwrap ( ) ) ;
181
-
182
- let witness = match Witness :: consensus_decode_from_finite_reader ( & mut cursor) {
183
- Ok ( witness) => witness,
184
- Err ( _) => return false ,
185
- } ;
186
-
187
- let encoded_signature = & witness. to_vec ( ) [ 0 ] ;
188
-
189
- let ( signature, sighash_type) = if encoded_signature. len ( ) == 65 {
190
- (
191
- Signature :: from_slice ( & encoded_signature. as_slice ( ) [ ..64 ] ) . unwrap ( ) ,
192
- TapSighashType :: from_consensus_u8 ( encoded_signature[ 64 ] ) . unwrap ( ) ,
193
- )
194
- } else if encoded_signature. len ( ) == 64 {
195
- (
196
- Signature :: from_slice ( encoded_signature. as_slice ( ) ) . unwrap ( ) ,
197
- TapSighashType :: Default ,
198
- )
199
- } else {
200
- return false ;
201
- } ;
202
-
203
- let pub_key =
204
- if let bitcoin:: address:: Payload :: WitnessProgram ( witness_program) = address. payload ( ) {
205
- if witness_program. version ( ) . to_num ( ) == 1 && witness_program. program ( ) . len ( ) == 32 {
206
- XOnlyPublicKey :: from_slice ( witness_program. program ( ) . as_bytes ( ) ) . unwrap ( )
207
- } else {
208
- return false ;
209
- }
210
- } else {
211
- return false ;
212
- } ;
213
-
214
- let mut sighash_cache = SighashCache :: new ( to_sign_tx) ;
215
-
216
- let sighash = sighash_cache
217
- . taproot_key_spend_signature_hash (
218
- 0 ,
219
- & sighash:: Prevouts :: All ( & [ TxOut {
220
- value : Amount :: from_sat ( 0 ) ,
221
- script_pubkey : to_spend_tx. output [ 0 ] . clone ( ) . script_pubkey ,
222
- } ] ) ,
223
- sighash_type,
224
- )
225
- . expect ( "signature hash should compute" ) ;
226
-
227
- let message = Message :: from_digest_slice ( sighash. as_ref ( ) ) . unwrap ( ) ;
228
-
229
- Secp256k1 :: verification_only ( )
230
- . verify_schnorr ( & signature, & message, & pub_key)
231
- . is_ok ( )
111
+ psbt
232
112
}
233
113
234
114
#[ cfg( test) ]
@@ -259,7 +139,7 @@ mod tests {
259
139
#[ test]
260
140
fn to_spend_txids_correct ( ) {
261
141
assert_eq ! (
262
- to_spend (
142
+ create_to_spend (
263
143
& Address :: from_str( SEGWIT_ADDRESS ) . unwrap( ) . assume_checked( ) ,
264
144
""
265
145
)
@@ -269,7 +149,7 @@ mod tests {
269
149
) ;
270
150
271
151
assert_eq ! (
272
- to_spend (
152
+ create_to_spend (
273
153
& Address :: from_str( SEGWIT_ADDRESS ) . unwrap( ) . assume_checked( ) ,
274
154
"Hello World"
275
155
)
@@ -281,47 +161,51 @@ mod tests {
281
161
282
162
#[ test]
283
163
fn to_sign_txids_correct ( ) {
164
+ let to_spend = create_to_spend (
165
+ & Address :: from_str ( SEGWIT_ADDRESS ) . unwrap ( ) . assume_checked ( ) ,
166
+ "" ,
167
+ ) ;
168
+ let to_sign = create_to_sign ( & to_spend) ;
284
169
assert_eq ! (
285
- to_sign( to_spend(
286
- & Address :: from_str( SEGWIT_ADDRESS ) . unwrap( ) . assume_checked( ) ,
287
- ""
288
- ) )
289
- . txid( )
290
- . to_string( ) ,
170
+ to_sign. unsigned_tx. txid( ) . to_string( ) ,
291
171
"1e9654e951a5ba44c8604c4de6c67fd78a27e81dcadcfe1edf638ba3aaebaed6"
292
172
) ;
293
173
174
+ let to_spend = create_to_spend (
175
+ & Address :: from_str ( SEGWIT_ADDRESS ) . unwrap ( ) . assume_checked ( ) ,
176
+ "Hello World" ,
177
+ ) ;
178
+ let to_sign = create_to_sign ( & to_spend) ;
294
179
assert_eq ! (
295
- to_sign( to_spend(
296
- & Address :: from_str( SEGWIT_ADDRESS ) . unwrap( ) . assume_checked( ) ,
297
- "Hello World"
298
- ) )
299
- . txid( )
300
- . to_string( ) ,
180
+ to_sign. unsigned_tx. txid( ) . to_string( ) ,
301
181
"88737ae86f2077145f93cc4b153ae9a1cb8d56afa511988c149c5c8c9d93bddf"
302
182
) ;
303
183
}
304
184
305
185
#[ test]
306
- fn verify_and_falsify_taproot ( ) {
307
- assert ! ( verify(
308
- & Address :: from_str( TAPROOT_ADDRESS ) . unwrap( ) . assume_checked( ) ,
309
- "Hello World" ,
310
- "AUHd69PrJQEv+oKTfZ8l+WROBHuy9HKrbFCJu7U1iK2iiEy1vMU5EfMtjc+VSHM7aU0SDbak5IUZRVno2P5mjSafAQ=="
311
- ) , ) ;
186
+ fn simple_verify_and_falsify_taproot ( ) {
187
+ assert ! (
188
+ simple_verify(
189
+ & Address :: from_str( TAPROOT_ADDRESS ) . unwrap( ) . assume_checked( ) ,
190
+ "Hello World" ,
191
+ "AUHd69PrJQEv+oKTfZ8l+WROBHuy9HKrbFCJu7U1iK2iiEy1vMU5EfMtjc+VSHM7aU0SDbak5IUZRVno2P5mjSafAQ=="
192
+ )
193
+ ) ;
312
194
313
- assert ! ( !verify(
314
- & Address :: from_str( TAPROOT_ADDRESS ) . unwrap( ) . assume_checked( ) ,
315
- "Hello World -- This should fail" ,
316
- "AUHd69PrJQEv+oKTfZ8l+WROBHuy9HKrbFCJu7U1iK2iiEy1vMU5EfMtjc+VSHM7aU0SDbak5IUZRVno2P5mjSafAQ=="
317
- ) , ) ;
195
+ assert ! (
196
+ !simple_verify(
197
+ & Address :: from_str( TAPROOT_ADDRESS ) . unwrap( ) . assume_checked( ) ,
198
+ "Hello World -- This should fail" ,
199
+ "AUHd69PrJQEv+oKTfZ8l+WROBHuy9HKrbFCJu7U1iK2iiEy1vMU5EfMtjc+VSHM7aU0SDbak5IUZRVno2P5mjSafAQ=="
200
+ )
201
+ ) ;
318
202
}
319
203
320
204
#[ test]
321
- fn sign_taproot ( ) {
205
+ fn simple_sign_taproot ( ) {
322
206
let wallet = Wallet :: new ( WIF_PRIVATE_KEY ) ;
323
207
324
- let signature = sign (
208
+ let signature = simple_sign (
325
209
& Address :: from_str ( TAPROOT_ADDRESS ) . unwrap ( ) . assume_checked ( ) ,
326
210
"Hello World" ,
327
211
& wallet,
@@ -334,13 +218,28 @@ mod tests {
334
218
}
335
219
336
220
#[ test]
337
- fn roundtrip_taproot ( ) {
221
+ fn roundtrip_taproot_simple ( ) {
222
+ let wallet = Wallet :: new ( WIF_PRIVATE_KEY ) ;
223
+
224
+ assert ! ( simple_verify(
225
+ & Address :: from_str( TAPROOT_ADDRESS ) . unwrap( ) . assume_checked( ) ,
226
+ "Hello World" ,
227
+ & simple_sign(
228
+ & Address :: from_str( TAPROOT_ADDRESS ) . unwrap( ) . assume_checked( ) ,
229
+ "Hello World" ,
230
+ & wallet
231
+ )
232
+ ) ) ;
233
+ }
234
+
235
+ #[ test]
236
+ fn roundtrip_taproot_full ( ) {
338
237
let wallet = Wallet :: new ( WIF_PRIVATE_KEY ) ;
339
238
340
- assert ! ( verify (
239
+ assert ! ( full_verify (
341
240
& Address :: from_str( TAPROOT_ADDRESS ) . unwrap( ) . assume_checked( ) ,
342
241
"Hello World" ,
343
- & sign (
242
+ & full_sign (
344
243
& Address :: from_str( TAPROOT_ADDRESS ) . unwrap( ) . assume_checked( ) ,
345
244
"Hello World" ,
346
245
& wallet
0 commit comments