4
4
5
5
#![ allow( non_snake_case) ] // Test names intentionally use double underscore.
6
6
#![ allow( unused_imports) ] // Because of feature gated tests.
7
-
8
- use bitcoin:: hex:: FromHex as _;
9
- use bitcoin:: opcodes:: all:: * ;
10
- use bitcoin:: { absolute, transaction, consensus, script, Amount , TxOut , Transaction , ScriptBuf } ;
11
7
use integration_test:: { Node , NodeExt as _, Wallet } ;
12
8
use node:: { mtype, Input , Output } ;
13
9
use node:: vtype:: * ; // All the version specific types.
10
+ use bitcoin:: { hex:: FromHex as _,
11
+ absolute, transaction, consensus, Amount , TxOut , Transaction ,
12
+ Address , Network , ScriptBuf , script, hashes:: { hash160, sha256, Hash } ,
13
+ WPubkeyHash , WScriptHash , secp256k1,
14
+ PublicKey ,
15
+ script:: Builder ,
16
+ opcodes:: all:: * ,
17
+ key:: { Secp256k1 , XOnlyPublicKey } ,
18
+ address:: NetworkUnchecked ,
19
+ } ;
20
+ use rand:: Rng ;
21
+
14
22
15
23
#[ test]
16
24
#[ cfg( not( feature = "v17" ) ) ] // analyzepsbt was added in v0.18.
@@ -202,18 +210,63 @@ fn raw_transactions__decode_script__modelled() {
202
210
let node = Node :: with_wallet ( Wallet :: Default , & [ "-txindex" ] ) ;
203
211
node. fund_wallet ( ) ;
204
212
205
- let p2pkh = arbitrary_p2pkh_script ( ) ;
206
- let multi = arbitrary_multisig_script ( ) ;
207
-
208
- for script in & [ p2pkh, multi] {
213
+ let test_cases: Vec < ( & str , ScriptBuf , Option < & str > ) > = vec ! [
214
+ ( "p2pkh" , arbitrary_p2pkh_script( ) , Some ( "pubkeyhash" ) ) ,
215
+ ( "multisig" , arbitrary_multisig_script( ) , Some ( "multisig" ) ) ,
216
+ ( "p2sh" , arbitrary_p2sh_script( ) , Some ( "scripthash" ) ) ,
217
+ ( "bare" , arbitrary_bare_script( ) , Some ( "nonstandard" ) ) ,
218
+ ( "p2wpkh" , arbitrary_p2wpkh_script( ) , Some ( "witness_v0_keyhash" ) ) ,
219
+ ( "p2wsh" , arbitrary_p2wsh_script( ) , Some ( "witness_v0_scripthash" ) ) ,
220
+ ( "p2tr" , arbitrary_p2tr_script( ) , Some ( "witness_v1_taproot" ) ) ,
221
+ ] ;
222
+
223
+ for ( label, script, expected_type) in test_cases {
209
224
let hex = script. to_hex_string ( ) ;
210
225
211
226
let json: DecodeScript = node. client . decode_script ( & hex) . expect ( "decodescript" ) ;
212
227
let model: Result < mtype:: DecodeScript , DecodeScriptError > = json. into_model ( ) ;
213
- let _ = model. expect ( "DecodeScript into model" ) ;
228
+ let decoded = model. expect ( "DecodeScript into model" ) ;
229
+
230
+ println ! ( "Decoded script ({label}): {:?}" , decoded) ;
231
+
232
+ if let Some ( expected) = expected_type {
233
+ assert_eq ! ( decoded. type_, expected, "Unexpected script type for {label}" ) ;
234
+ } else {
235
+ println ! ( "Skipping type check for {}" , label) ;
236
+ }
237
+
238
+ // Address should be present for standard scripts
239
+ if expected_type != Some ( "nonstandard" ) {
240
+ let has_any_address = !decoded. addresses . is_empty ( ) || decoded. address . is_some ( ) ;
241
+ assert ! (
242
+ has_any_address,
243
+ "Expected at least one address for {label}"
244
+ ) ;
245
+ }
214
246
}
215
247
}
248
+ fn arbitrary_p2sh_script ( ) -> ScriptBuf {
249
+
250
+ let redeem_script = arbitrary_multisig_script ( ) ; // or arbitrary_p2pkh_script()
251
+ let redeem_script_hash = hash160:: Hash :: hash ( redeem_script. as_bytes ( ) ) ;
216
252
253
+ script:: Builder :: new ( )
254
+ . push_opcode ( bitcoin:: opcodes:: all:: OP_HASH160 )
255
+ . push_slice ( redeem_script_hash. as_byte_array ( ) ) // [u8; 20]
256
+ . push_opcode ( bitcoin:: opcodes:: all:: OP_EQUAL )
257
+ . into_script ( )
258
+ }
259
+ fn arbitrary_bare_script ( ) -> ScriptBuf {
260
+ script:: Builder :: new ( )
261
+ . push_opcode ( OP_RETURN )
262
+ . push_slice ( b"hello" )
263
+ . into_script ( )
264
+ }
265
+ fn arbitrary_pubkey ( ) -> PublicKey {
266
+ let secp = Secp256k1 :: new ( ) ;
267
+ let secret_key = secp256k1:: SecretKey :: from_slice ( & [ 1u8 ; 32 ] ) . unwrap ( ) ;
268
+ PublicKey :: new ( secp256k1:: PublicKey :: from_secret_key ( & secp, & secret_key) )
269
+ }
217
270
// Script builder code copied from rust-bitcoin script unit tests.
218
271
fn arbitrary_p2pkh_script ( ) -> ScriptBuf {
219
272
let pubkey_hash = <[ u8 ; 20 ] >:: from_hex ( "16e1ae70ff0fa102905d4af297f6912bda6cce19" ) . unwrap ( ) ;
@@ -226,7 +279,6 @@ fn arbitrary_p2pkh_script() -> ScriptBuf {
226
279
. push_opcode ( OP_CHECKSIG )
227
280
. into_script ( )
228
281
}
229
-
230
282
fn arbitrary_multisig_script ( ) -> ScriptBuf {
231
283
let pk1 =
232
284
<[ u8 ; 33 ] >:: from_hex ( "022afc20bf379bc96a2f4e9e63ffceb8652b2b6a097f63fbee6ecec2a49a48010e" )
@@ -245,6 +297,123 @@ fn arbitrary_multisig_script() -> ScriptBuf {
245
297
. push_opcode ( OP_CHECKMULTISIG )
246
298
. into_script ( )
247
299
}
300
+ fn arbitrary_p2wpkh_script ( ) -> ScriptBuf {
301
+ let pubkey = arbitrary_pubkey ( ) ;
302
+ let pubkey_hash = hash160:: Hash :: hash ( & pubkey. to_bytes ( ) ) ;
303
+
304
+ // P2WPKH: 0 <20-byte pubkey hash>
305
+ Builder :: new ( )
306
+ . push_int ( 0 )
307
+ . push_slice ( pubkey_hash. as_byte_array ( ) )
308
+ . into_script ( )
309
+ }
310
+
311
+ fn arbitrary_p2wsh_script ( ) -> ScriptBuf {
312
+ let redeem_script = arbitrary_multisig_script ( ) ; // any witness script
313
+ let script_hash = sha256:: Hash :: hash ( redeem_script. as_bytes ( ) ) ;
314
+
315
+ // P2WSH: 0 <32-byte script hash>
316
+ Builder :: new ( )
317
+ . push_int ( 0 )
318
+ . push_slice ( script_hash. as_byte_array ( ) )
319
+ . into_script ( )
320
+ }
321
+
322
+ fn arbitrary_p2tr_script ( ) -> ScriptBuf {
323
+ let secp = Secp256k1 :: new ( ) ;
324
+ let sk = secp256k1:: SecretKey :: from_slice ( & [ 2u8 ; 32 ] ) . unwrap ( ) ;
325
+ let internal_key = secp256k1:: PublicKey :: from_secret_key ( & secp, & sk) ;
326
+ let x_only = XOnlyPublicKey :: from ( internal_key) ;
327
+
328
+ // Taproot output script: OP_1 <x-only pubkey>
329
+ Builder :: new ( )
330
+ . push_int ( 1 )
331
+ . push_slice ( & x_only. serialize ( ) )
332
+ . into_script ( )
333
+ }
334
+
335
+ #[ test]
336
+ fn raw_transactions__decode_script_segwit__modelled ( ) {
337
+
338
+ let node = Node :: with_wallet ( Wallet :: Default , & [ "-txindex" ] ) ;
339
+ node. client . load_wallet ( "default" ) . ok ( ) ; // Ensure wallet is loaded
340
+ node. fund_wallet ( ) ;
341
+
342
+ // Get a new address and script
343
+ let address_unc = node
344
+ . client
345
+ . get_new_address ( None , None )
346
+ . expect ( "getnewaddress" )
347
+ . address ( )
348
+ . expect ( "valid address string" ) ;
349
+
350
+ let address = address_unc
351
+ . require_network ( Network :: Regtest )
352
+ . expect ( "must be regtest" ) ;
353
+
354
+ assert ! (
355
+ address. is_segwit( ) ,
356
+ "Expected SegWit address but got {:?}" ,
357
+ address
358
+ ) ;
359
+
360
+ let script = address. script_pubkey ( ) ;
361
+ let hex = script. to_hex_string ( ) ;
362
+
363
+ // Decode script
364
+ let json = node. client . decode_script ( & hex) . expect ( "decodescript" ) ;
365
+ let model: Result < mtype:: DecodeScript , DecodeScriptError > = json. into_model ( ) ;
366
+ let decoded = model. expect ( "DecodeScript into model" ) ;
367
+
368
+ let segwit = decoded
369
+ . segwit
370
+ . as_ref ( )
371
+ . expect ( "Expected segwit field to be present" ) ;
372
+
373
+ assert_eq ! (
374
+ segwit. hex, script,
375
+ "Segwit hex does not match script"
376
+ ) ;
377
+
378
+ // Extract the type field
379
+ let script_type = decoded
380
+ . segwit
381
+ . as_ref ( )
382
+ . map ( |s| s. type_ . as_str ( ) )
383
+ . unwrap_or_else ( || decoded. type_ . as_str ( ) ) ;
384
+
385
+ assert_eq ! (
386
+ script_type,
387
+ "witness_v0_keyhash" ,
388
+ "Expected script type to be witness_v0_keyhash"
389
+ ) ;
390
+
391
+ // Compare hex from segwit
392
+ let decoded_hex = decoded
393
+ . segwit
394
+ . as_ref ( )
395
+ . map ( |s| & s. hex )
396
+ . unwrap_or_else ( || {
397
+ panic ! ( "Expected segwit hex to be present" )
398
+ } ) ;
399
+
400
+ assert_eq ! ( * decoded_hex, script, "Script hex does not match" ) ;
401
+
402
+ // Compare addresses from segwit or fallback
403
+ let address_unc_check = address. into_unchecked ( ) ;
404
+ let segwit_addresses = decoded
405
+ . segwit
406
+ . as_ref ( )
407
+ . map ( |s| & s. addresses )
408
+ . unwrap_or ( & decoded. addresses ) ;
409
+
410
+ assert ! (
411
+ segwit_addresses. iter( ) . any( |a| a == & address_unc_check) ,
412
+ "Expected address {:?} in segwit.addresses or top-level addresses: {:?}" ,
413
+ address_unc_check,
414
+ segwit_addresses
415
+ ) ;
416
+ }
248
417
249
418
#[ test]
250
419
fn raw_transactions__finalize_psbt__modelled ( ) {
0 commit comments