@@ -23,14 +23,26 @@ import (
23
23
"github.com/BitBoxSwiss/bitbox02-api-go/util/semver"
24
24
"github.com/btcsuite/btcd/btcec/v2"
25
25
"github.com/btcsuite/btcd/btcec/v2/ecdsa"
26
+ "github.com/btcsuite/btcd/btcutil"
26
27
"github.com/btcsuite/btcd/btcutil/hdkeychain"
28
+ "github.com/btcsuite/btcd/chaincfg"
27
29
"github.com/btcsuite/btcd/chaincfg/chainhash"
30
+ "github.com/btcsuite/btcd/txscript"
31
+ "github.com/btcsuite/btcd/wire"
28
32
"github.com/stretchr/testify/require"
29
33
"google.golang.org/protobuf/proto"
30
34
)
31
35
32
36
const hardenedKeyStart = 0x80000000
33
37
38
+ func mustOutpoint (s string ) * wire.OutPoint {
39
+ outPoint , err := wire .NewOutPointFromString (s )
40
+ if err != nil {
41
+ panic (err )
42
+ }
43
+ return outPoint
44
+ }
45
+
34
46
func parseECDSASignature (t * testing.T , sig []byte ) * ecdsa.Signature {
35
47
t .Helper ()
36
48
require .Len (t , sig , 64 )
@@ -87,36 +99,32 @@ func TestBTCAddress(t *testing.T) {
87
99
})
88
100
}
89
101
90
- func parseXPub (t * testing.T , xpubStr string , keypath ... uint32 ) * hdkeychain. ExtendedKey {
102
+ func simulatorPub (t * testing.T , device * Device , keypath ... uint32 ) * btcec. PublicKey {
91
103
t .Helper ()
92
- xpub , err := hdkeychain .NewKeyFromString (xpubStr )
104
+
105
+ xpubStr , err := device .BTCXPub (messages .BTCCoin_BTC , keypath , messages .BTCPubRequest_XPUB , false )
93
106
require .NoError (t , err )
94
107
95
- for _ , child := range keypath {
96
- xpub , err = xpub . Derive ( child )
97
- require . NoError ( t , err )
98
- }
99
- return xpub
108
+ xpub , err := hdkeychain . NewKeyFromString ( xpubStr )
109
+ require . NoError ( t , err )
110
+ pubKey , err := xpub . ECPubKey ( )
111
+ require . NoError ( t , err )
112
+ return pubKey
100
113
}
101
114
102
115
func TestSimulatorBTCSignMessage (t * testing.T ) {
103
116
testInitializedSimulators (t , func (t * testing.T , device * Device ) {
104
117
t .Helper ()
105
118
coin := messages .BTCCoin_BTC
106
- accountKeypath := []uint32 {49 + hardenedKeyStart , 0 + hardenedKeyStart , 0 + hardenedKeyStart }
107
-
108
- xpubStr , err := device .BTCXPub (coin , accountKeypath , messages .BTCPubRequest_XPUB , false )
109
- require .NoError (t , err )
119
+ keypath := []uint32 {49 + hardenedKeyStart , 0 + hardenedKeyStart , 0 + hardenedKeyStart , 0 , 10 }
110
120
111
- xpub := parseXPub (t , xpubStr , 0 , 10 )
112
- pubKey , err := xpub .ECPubKey ()
113
- require .NoError (t , err )
121
+ pubKey := simulatorPub (t , device , keypath ... )
114
122
115
123
sig , _ , _ , err := device .BTCSignMessage (
116
124
coin ,
117
125
& messages.BTCScriptConfigWithKeypath {
118
126
ScriptConfig : NewBTCScriptConfigSimple (messages .BTCScriptConfig_P2WPKH_P2SH ),
119
- Keypath : append ( accountKeypath , 0 , 10 ) ,
127
+ Keypath : keypath ,
120
128
},
121
129
[]byte ("message" ),
122
130
)
@@ -321,3 +329,241 @@ func TestBTCSignMessage(t *testing.T) {
321
329
}
322
330
})
323
331
}
332
+
333
+ func makeTaprootOutput (t * testing.T , pubkey * btcec.PublicKey ) []byte {
334
+ t .Helper ()
335
+ outputKey := txscript .ComputeTaprootKeyNoScript (pubkey )
336
+ outputPkScript , err := txscript .PayToTaprootScript (outputKey )
337
+ require .NoError (t , err )
338
+ return outputPkScript
339
+ }
340
+
341
+ // Test signing; all inputs are BIP86 Taproot keyspends.
342
+ func TestSimulatorBTCSignTaprootKeySpend (t * testing.T ) {
343
+ testInitializedSimulators (t , func (t * testing.T , device * Device ) {
344
+ t .Helper ()
345
+ coin := messages .BTCCoin_BTC
346
+ accountKeypath := []uint32 {86 + hardenedKeyStart , 0 + hardenedKeyStart , 0 + hardenedKeyStart }
347
+ inputKeypath := []uint32 {86 + hardenedKeyStart , 0 + hardenedKeyStart , 0 + hardenedKeyStart , 0 , 0 }
348
+ input2Keypath := []uint32 {86 + hardenedKeyStart , 0 + hardenedKeyStart , 0 + hardenedKeyStart , 0 , 1 }
349
+ changeKeypath := []uint32 {86 + hardenedKeyStart , 0 + hardenedKeyStart , 0 + hardenedKeyStart , 1 , 0 }
350
+
351
+ input1PkScript := makeTaprootOutput (t , simulatorPub (t , device , inputKeypath ... ))
352
+ input2PkScript := makeTaprootOutput (t , simulatorPub (t , device , input2Keypath ... ))
353
+
354
+ prevTx := & wire.MsgTx {
355
+ Version : 2 ,
356
+ TxIn : []* wire.TxIn {
357
+ {
358
+ PreviousOutPoint : * mustOutpoint ("3131313131313131313131313131313131313131313131313131313131313131:0" ),
359
+ Sequence : 0xFFFFFFFF ,
360
+ },
361
+ },
362
+ TxOut : []* wire.TxOut {
363
+ {
364
+ Value : 60_000_000 ,
365
+ PkScript : input1PkScript ,
366
+ },
367
+ {
368
+ Value : 40_000_000 ,
369
+ PkScript : input2PkScript ,
370
+ },
371
+ },
372
+ LockTime : 0 ,
373
+ }
374
+
375
+ scriptConfigs := []* messages.BTCScriptConfigWithKeypath {
376
+ {
377
+ ScriptConfig : NewBTCScriptConfigSimple (messages .BTCScriptConfig_P2TR ),
378
+ Keypath : accountKeypath ,
379
+ },
380
+ }
381
+ require .False (t , BTCSignNeedsPrevTxs (scriptConfigs ))
382
+
383
+ prevTxHash := prevTx .TxHash ()
384
+ _ , err := device .BTCSign (
385
+ coin ,
386
+ scriptConfigs ,
387
+ & BTCTx {
388
+ Version : 2 ,
389
+ Inputs : []* BTCTxInput {
390
+ {
391
+ Input : & messages.BTCSignInputRequest {
392
+ PrevOutHash : prevTxHash [:],
393
+ PrevOutIndex : 0 ,
394
+ PrevOutValue : uint64 (prevTx .TxOut [0 ].Value ),
395
+ Sequence : 0xFFFFFFFF ,
396
+ Keypath : inputKeypath ,
397
+ ScriptConfigIndex : 0 ,
398
+ },
399
+ },
400
+ {
401
+ Input : & messages.BTCSignInputRequest {
402
+ PrevOutHash : prevTxHash [:],
403
+ PrevOutIndex : 1 ,
404
+ PrevOutValue : uint64 (prevTx .TxOut [1 ].Value ),
405
+ Sequence : 0xFFFFFFFF ,
406
+ Keypath : input2Keypath ,
407
+ ScriptConfigIndex : 0 ,
408
+ },
409
+ },
410
+ },
411
+ Outputs : []* messages.BTCSignOutputRequest {
412
+ {
413
+ Ours : true ,
414
+ Value : 70_000_000 ,
415
+ Keypath : changeKeypath ,
416
+ },
417
+ {
418
+ Value : 20_000_000 ,
419
+ Payload : []byte ("11111111111111111111111111111111" ),
420
+ Type : messages .BTCOutputType_P2WSH ,
421
+ },
422
+ },
423
+ Locktime : 0 ,
424
+ },
425
+ messages .BTCSignInitRequest_DEFAULT ,
426
+ )
427
+ require .NoError (t , err )
428
+ })
429
+ }
430
+
431
+ // Test signing; mixed input types (p2wpkh, p2wpkh-p2sh, p2tr)
432
+ func TestSimulatorBTCSignMixed (t * testing.T ) {
433
+ testInitializedSimulators (t , func (t * testing.T , device * Device ) {
434
+ t .Helper ()
435
+ coin := messages .BTCCoin_BTC
436
+ changeKeypath := []uint32 {86 + hardenedKeyStart , 0 + hardenedKeyStart , 0 + hardenedKeyStart , 1 , 0 }
437
+ input0Keypath := []uint32 {86 + hardenedKeyStart , 0 + hardenedKeyStart , 0 + hardenedKeyStart , 0 , 0 }
438
+ input1Keypath := []uint32 {84 + hardenedKeyStart , 0 + hardenedKeyStart , 0 + hardenedKeyStart , 0 , 0 }
439
+ input2Keypath := []uint32 {49 + hardenedKeyStart , 0 + hardenedKeyStart , 0 + hardenedKeyStart , 0 , 0 }
440
+
441
+ net := & chaincfg .MainNetParams
442
+
443
+ prevTx := & wire.MsgTx {
444
+ Version : 2 ,
445
+ TxIn : []* wire.TxIn {
446
+ {
447
+ PreviousOutPoint : * mustOutpoint ("3131313131313131313131313131313131313131313131313131313131313131:0" ),
448
+ Sequence : 0xFFFFFFFF ,
449
+ },
450
+ },
451
+ TxOut : []* wire.TxOut {
452
+ {
453
+ Value : 100_000_000 ,
454
+ PkScript : func () []byte {
455
+ return makeTaprootOutput (t , simulatorPub (t , device , input0Keypath ... ))
456
+ }(),
457
+ },
458
+ {
459
+ Value : 100_000_000 ,
460
+ PkScript : func () []byte {
461
+ inputPub := simulatorPub (t , device , input1Keypath ... )
462
+
463
+ publicKeyHash := btcutil .Hash160 (inputPub .SerializeCompressed ())
464
+ address , err := btcutil .NewAddressWitnessPubKeyHash (publicKeyHash , net )
465
+ require .NoError (t , err )
466
+ pkScript , err := txscript .PayToAddrScript (address )
467
+ require .NoError (t , err )
468
+ return pkScript
469
+ }(),
470
+ },
471
+ {
472
+ Value : 100_000_000 ,
473
+ PkScript : func () []byte {
474
+ inputPub := simulatorPub (t , device , input2Keypath ... )
475
+ publicKeyHash := btcutil .Hash160 (inputPub .SerializeCompressed ())
476
+
477
+ segwitAddress , err := btcutil .NewAddressWitnessPubKeyHash (publicKeyHash , net )
478
+ require .NoError (t , err )
479
+ redeemScript , err := txscript .PayToAddrScript (segwitAddress )
480
+ require .NoError (t , err )
481
+ address , err := btcutil .NewAddressScriptHash (redeemScript , net )
482
+ require .NoError (t , err )
483
+ pkScript , err := txscript .PayToAddrScript (address )
484
+ require .NoError (t , err )
485
+ return pkScript
486
+ }(),
487
+ },
488
+ },
489
+ LockTime : 0 ,
490
+ }
491
+ convertedPrevTx := NewBTCPrevTxFromBtcd (prevTx )
492
+
493
+ scriptConfigs := []* messages.BTCScriptConfigWithKeypath {
494
+ {
495
+ ScriptConfig : NewBTCScriptConfigSimple (messages .BTCScriptConfig_P2TR ),
496
+ Keypath : input0Keypath [:3 ],
497
+ },
498
+ {
499
+ ScriptConfig : NewBTCScriptConfigSimple (messages .BTCScriptConfig_P2WPKH ),
500
+ Keypath : input1Keypath [:3 ],
501
+ },
502
+
503
+ {
504
+ ScriptConfig : NewBTCScriptConfigSimple (messages .BTCScriptConfig_P2WPKH_P2SH ),
505
+ Keypath : input2Keypath [:3 ],
506
+ },
507
+ }
508
+ require .True (t , BTCSignNeedsPrevTxs (scriptConfigs ))
509
+
510
+ prevTxHash := prevTx .TxHash ()
511
+ _ , err := device .BTCSign (
512
+ coin ,
513
+ scriptConfigs ,
514
+ & BTCTx {
515
+ Version : 2 ,
516
+ Inputs : []* BTCTxInput {
517
+ {
518
+ Input : & messages.BTCSignInputRequest {
519
+ PrevOutHash : prevTxHash [:],
520
+ PrevOutIndex : 0 ,
521
+ PrevOutValue : uint64 (prevTx .TxOut [0 ].Value ),
522
+ Sequence : 0xFFFFFFFF ,
523
+ Keypath : input0Keypath ,
524
+ ScriptConfigIndex : 0 ,
525
+ },
526
+ PrevTx : convertedPrevTx ,
527
+ },
528
+ {
529
+ Input : & messages.BTCSignInputRequest {
530
+ PrevOutHash : prevTxHash [:],
531
+ PrevOutIndex : 1 ,
532
+ PrevOutValue : uint64 (prevTx .TxOut [1 ].Value ),
533
+ Sequence : 0xFFFFFFFF ,
534
+ Keypath : input1Keypath ,
535
+ ScriptConfigIndex : 1 ,
536
+ },
537
+ PrevTx : convertedPrevTx ,
538
+ },
539
+ {
540
+ Input : & messages.BTCSignInputRequest {
541
+ PrevOutHash : prevTxHash [:],
542
+ PrevOutIndex : 2 ,
543
+ PrevOutValue : uint64 (prevTx .TxOut [2 ].Value ),
544
+ Sequence : 0xFFFFFFFF ,
545
+ Keypath : input2Keypath ,
546
+ ScriptConfigIndex : 2 ,
547
+ },
548
+ PrevTx : convertedPrevTx ,
549
+ },
550
+ },
551
+ Outputs : []* messages.BTCSignOutputRequest {
552
+ {
553
+ Ours : true ,
554
+ Value : 270_000_000 ,
555
+ Keypath : changeKeypath ,
556
+ },
557
+ {
558
+ Value : 20_000_000 ,
559
+ Payload : []byte ("11111111111111111111111111111111" ),
560
+ Type : messages .BTCOutputType_P2WSH ,
561
+ },
562
+ },
563
+ Locktime : 0 ,
564
+ },
565
+ messages .BTCSignInitRequest_DEFAULT ,
566
+ )
567
+ require .NoError (t , err )
568
+ })
569
+ }
0 commit comments