@@ -21,9 +21,11 @@ import (
21
21
"bufio"
22
22
"bytes"
23
23
"crypto/sha256"
24
+ "encoding/binary"
24
25
"encoding/hex"
25
26
"encoding/json"
26
27
"fmt"
28
+ "hash"
27
29
"io"
28
30
"net"
29
31
"net/http"
@@ -39,11 +41,13 @@ import (
39
41
"testing"
40
42
"time"
41
43
44
+ "github.com/BitBoxSwiss/bitbox-wallet-app/backend/accounts"
42
45
"github.com/BitBoxSwiss/bitbox-wallet-app/backend/coins/btc"
43
46
"github.com/BitBoxSwiss/bitbox-wallet-app/backend/coins/btc/addresses"
44
47
"github.com/BitBoxSwiss/bitbox-wallet-app/backend/coins/btc/blockchain"
45
48
"github.com/BitBoxSwiss/bitbox-wallet-app/backend/coins/btc/maketx"
46
49
"github.com/BitBoxSwiss/bitbox-wallet-app/backend/coins/btc/types"
50
+ "github.com/BitBoxSwiss/bitbox-wallet-app/backend/coins/btc/util"
47
51
coinpkg "github.com/BitBoxSwiss/bitbox-wallet-app/backend/coins/coin"
48
52
"github.com/BitBoxSwiss/bitbox-wallet-app/backend/coins/eth"
49
53
"github.com/BitBoxSwiss/bitbox-wallet-app/backend/coins/ltc"
@@ -59,11 +63,13 @@ import (
59
63
"github.com/BitBoxSwiss/bitbox02-api-go/api/firmware/mocks"
60
64
"github.com/BitBoxSwiss/bitbox02-api-go/communication/u2fhid"
61
65
"github.com/BitBoxSwiss/bitbox02-api-go/util/semver"
66
+ "github.com/btcsuite/btcd/btcec/v2"
62
67
"github.com/btcsuite/btcd/btcutil"
63
68
"github.com/btcsuite/btcd/btcutil/hdkeychain"
64
69
"github.com/btcsuite/btcd/chaincfg"
65
70
"github.com/btcsuite/btcd/chaincfg/chainhash"
66
71
"github.com/btcsuite/btcd/wire"
72
+ "github.com/decred/dcrd/dcrec/secp256k1/v4/ecdsa"
67
73
"github.com/ethereum/go-ethereum/params"
68
74
"github.com/stretchr/testify/require"
69
75
)
@@ -698,3 +704,122 @@ func TestSimulatorVerifyAddressETH(t *testing.T) {
698
704
}
699
705
})
700
706
}
707
+
708
+ func computePaymentRequestSighash (paymentRequest * accounts.PaymentRequest , slip44 uint32 , outputValue uint64 , outputAddress string ) ([]byte , error ) {
709
+
710
+ hashDataLenPrefixed := func (hasher hash.Hash , data []byte ) {
711
+ _ = wire .WriteVarInt (hasher , 0 , uint64 (len (data )))
712
+ hasher .Write (data )
713
+ }
714
+
715
+ sighash := sha256 .New ()
716
+
717
+ // versionMagic
718
+ sighash .Write ([]byte ("SL\x00 \x24 " ))
719
+
720
+ // nonce
721
+ hashDataLenPrefixed (sighash , paymentRequest .Nonce )
722
+
723
+ // recipientName
724
+ hashDataLenPrefixed (sighash , []byte (paymentRequest .RecipientName ))
725
+
726
+ // memos
727
+ _ = wire .WriteVarInt (sighash , 0 , uint64 (len (paymentRequest .Memos )))
728
+ for _ , textMemo := range paymentRequest .Memos {
729
+ _ = binary .Write (sighash , binary .LittleEndian , uint32 (1 ))
730
+ hashDataLenPrefixed (sighash , []byte (textMemo .Note ))
731
+ }
732
+
733
+ // coinType
734
+ _ = binary .Write (sighash , binary .LittleEndian , slip44 )
735
+
736
+ // outputsHash (only one output for now)
737
+ outputHasher := sha256 .New ()
738
+ _ = binary .Write (outputHasher , binary .LittleEndian , outputValue )
739
+ hashDataLenPrefixed (outputHasher , []byte (outputAddress ))
740
+ sighash .Write (outputHasher .Sum (nil ))
741
+
742
+ return sighash .Sum (nil ), nil
743
+ }
744
+
745
+ func TestSimulatorSignBTCPaymentRequest (t * testing.T ) {
746
+ testInitializedSimulators (t , func (t * testing.T , device * Device , stdOut * bytes.Buffer ) {
747
+ t .Helper ()
748
+
749
+ if ! device .Version ().AtLeast (semver .NewSemVer (9 , 24 , 0 )) {
750
+ // While payment requests were added in v9.19.0, the simulator only enabled the
751
+ // test merchant in v9.24.0.
752
+ t .Skip ()
753
+ }
754
+
755
+ address , err := btcutil .DecodeAddress (
756
+ "bc1q2q0j6gmfxynj40p0kxsr9jkagcvgpuqv2zgq8j" ,
757
+ & chaincfg .MainNetParams )
758
+ require .NoError (t , err )
759
+ pkScript , err := util .PkScriptFromAddress (address )
760
+ require .NoError (t , err )
761
+ proposedTransaction := makeTx (t , device , maketx .NewOutputInfo (pkScript ))
762
+
763
+ txProposal := proposedTransaction .TXProposal
764
+ recipientOutput := txProposal .Transaction .TxOut [txProposal .OutIndex ]
765
+ value := uint64 (recipientOutput .Value )
766
+
767
+ paymentRequest := & accounts.PaymentRequest {
768
+ RecipientName : "Test Merchant" , // Hard-coded test merchant in simulator
769
+ Nonce : nil ,
770
+ TotalAmount : value ,
771
+ Memos : []accounts.TextMemo {
772
+ {
773
+ Note : "TextMemo line1\n TextMemo line2" ,
774
+ },
775
+ },
776
+ }
777
+
778
+ // Sign the payment request.
779
+ sighash , err := computePaymentRequestSighash (paymentRequest , 0 , value , address .String ())
780
+ require .NoError (t , err )
781
+ privKey , _ := btcec .PrivKeyFromBytes ([]byte ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" ))
782
+ require .NoError (t , err )
783
+ signature := ecdsa .SignCompact (privKey , sighash , true )
784
+ paymentRequest .Signature = signature [1 :]
785
+
786
+ proposedTransaction .TXProposal .PaymentRequest = paymentRequest
787
+
788
+ require .NoError (t , device .Keystore ().SignTransaction (proposedTransaction ))
789
+ require .NoError (t , proposedTransaction .Finalize ())
790
+ require .NoError (
791
+ t ,
792
+ btc .TxValidityCheck (
793
+ proposedTransaction .TXProposal .Transaction ,
794
+ proposedTransaction .TXProposal .PreviousOutputs ,
795
+ proposedTransaction .TXProposal .SigHashes ()))
796
+
797
+ const expected1 = `CONFIRM TRANSACTION ADDRESS SCREEN START
798
+ AMOUNT: 2.50000000 BTC
799
+ ADDRESS: Test Merchant
800
+ CONFIRM TRANSACTION ADDRESS SCREEN END`
801
+
802
+ const expected2 = `BODY: Memo from
803
+
804
+ Test Merchant
805
+ CONFIRM SCREEN END
806
+ CONFIRM SCREEN START
807
+ TITLE: Memo 1/2
808
+ BODY: TextMemo line1
809
+ CONFIRM SCREEN END
810
+ CONFIRM SCREEN START
811
+ TITLE: Memo 2/2
812
+ BODY: TextMemo line2
813
+ CONFIRM SCREEN END`
814
+
815
+ require .Eventually (t ,
816
+ func () bool {
817
+ return strings .Contains (
818
+ stdOut .String (), expected1 ) &&
819
+ strings .Contains (
820
+ stdOut .String (), expected2 ) &&
821
+ ! strings .Contains (stdOut .String (), address .String ())
822
+ },
823
+ time .Second , 10 * time .Millisecond )
824
+ })
825
+ }
0 commit comments