@@ -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
)
@@ -696,3 +702,120 @@ func TestSimulatorVerifyAddressETH(t *testing.T) {
696
702
}
697
703
})
698
704
}
705
+
706
+ func computePaymentRequestSighash (paymentRequest * accounts.PaymentRequest , slip44 uint32 , outputValue uint64 , outputAddress string ) ([]byte , error ) {
707
+
708
+ hashDataLenPrefixed := func (hasher hash.Hash , data []byte ) {
709
+ _ = wire .WriteVarInt (hasher , 0 , uint64 (len (data )))
710
+ hasher .Write (data )
711
+ }
712
+
713
+ sighash := sha256 .New ()
714
+
715
+ // versionMagic
716
+ sighash .Write ([]byte ("SL\x00 \x24 " ))
717
+
718
+ // nonce
719
+ hashDataLenPrefixed (sighash , paymentRequest .Nonce )
720
+
721
+ // recipientName
722
+ hashDataLenPrefixed (sighash , []byte (paymentRequest .RecipientName ))
723
+
724
+ // memos
725
+ _ = wire .WriteVarInt (sighash , 0 , uint64 (len (paymentRequest .Memos )))
726
+ for _ , textMemo := range paymentRequest .Memos {
727
+ _ = binary .Write (sighash , binary .LittleEndian , uint32 (1 ))
728
+ hashDataLenPrefixed (sighash , []byte (textMemo .Note ))
729
+ }
730
+
731
+ // coinType
732
+ _ = binary .Write (sighash , binary .LittleEndian , slip44 )
733
+
734
+ // outputsHash (only one output for now)
735
+ outputHasher := sha256 .New ()
736
+ _ = binary .Write (outputHasher , binary .LittleEndian , outputValue )
737
+ hashDataLenPrefixed (outputHasher , []byte (outputAddress ))
738
+ sighash .Write (outputHasher .Sum (nil ))
739
+
740
+ return sighash .Sum (nil ), nil
741
+ }
742
+
743
+ func TestSimulatorSignBTCPaymentRequest (t * testing.T ) {
744
+ testInitializedSimulators (t , func (t * testing.T , device * Device , stdOut * bytes.Buffer ) {
745
+ t .Helper ()
746
+
747
+ if ! device .Version ().AtLeast (semver .NewSemVer (9 , 24 , 0 )) {
748
+ // While payment requests were added in v9.19.0, the simulator only enabled the
749
+ // test merchant in v9.24.0.
750
+ t .Skip ()
751
+ }
752
+
753
+ address , err := btcutil .DecodeAddress (
754
+ "bc1q2q0j6gmfxynj40p0kxsr9jkagcvgpuqv2zgq8j" ,
755
+ & chaincfg .MainNetParams )
756
+ require .NoError (t , err )
757
+ pkScript , err := util .PkScriptFromAddress (address )
758
+ require .NoError (t , err )
759
+ proposedTransaction := makeTx (t , device , maketx .NewOutputInfo (pkScript ))
760
+
761
+ txProposal := proposedTransaction .TXProposal
762
+ recipientOutput := txProposal .Transaction .TxOut [txProposal .OutIndex ]
763
+ value := uint64 (recipientOutput .Value )
764
+
765
+ paymentRequest := & accounts.PaymentRequest {
766
+ RecipientName : "Test Merchant" , // Hard-coded test merchant in simulator
767
+ Nonce : nil ,
768
+ TotalAmount : value ,
769
+ Memos : []accounts.TextMemo {
770
+ {
771
+ Note : "TextMemo line1\n TextMemo line2" ,
772
+ },
773
+ },
774
+ }
775
+
776
+ // Sign the payment request.
777
+ sighash , err := computePaymentRequestSighash (paymentRequest , 0 , value , address .String ())
778
+ require .NoError (t , err )
779
+ privKey , _ := btcec .PrivKeyFromBytes ([]byte ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" ))
780
+ require .NoError (t , err )
781
+ signature := ecdsa .SignCompact (privKey , sighash , true )
782
+ paymentRequest .Signature = signature [1 :]
783
+
784
+ proposedTransaction .TXProposal .PaymentRequest = paymentRequest
785
+
786
+ require .NoError (t , device .Keystore ().SignTransaction (proposedTransaction ))
787
+ require .NoError (t , proposedTransaction .Finalize ())
788
+ require .NoError (
789
+ t ,
790
+ btc .TxValidityCheck (
791
+ proposedTransaction .TXProposal .Transaction ,
792
+ proposedTransaction .TXProposal .PreviousOutputs ,
793
+ proposedTransaction .TXProposal .SigHashes ()))
794
+ require .Eventually (t ,
795
+ func () bool {
796
+ return strings .Contains (
797
+ stdOut .String (),
798
+ `CONFIRM TRANSACTION ADDRESS SCREEN START
799
+ AMOUNT: 2.50000000 BTC
800
+ ADDRESS: Test Merchant
801
+ CONFIRM TRANSACTION ADDRESS SCREEN END` ) &&
802
+ strings .Contains (
803
+ stdOut .String (),
804
+ `
805
+ BODY: Memo from
806
+
807
+ Test Merchant
808
+ CONFIRM SCREEN END
809
+ CONFIRM SCREEN START
810
+ TITLE: Memo 1/2
811
+ BODY: TextMemo line1
812
+ CONFIRM SCREEN END
813
+ CONFIRM SCREEN START
814
+ TITLE: Memo 2/2
815
+ BODY: TextMemo line2
816
+ CONFIRM SCREEN END` ) &&
817
+ ! strings .Contains (stdOut .String (), address .String ())
818
+ },
819
+ time .Second , 10 * time .Millisecond )
820
+ })
821
+ }
0 commit comments