Skip to content

Commit 6a70b4f

Browse files
committed
backend/bitbox02: add integration test for payment requests
So refactorings related to BitBox02 keystore transactions are sure not to break payment requests.
1 parent 9ea6a4b commit 6a70b4f

File tree

2 files changed

+126
-1
lines changed

2 files changed

+126
-1
lines changed

backend/devices/bitbox02/keystore_simulator_test.go

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@ import (
2121
"bufio"
2222
"bytes"
2323
"crypto/sha256"
24+
"encoding/binary"
2425
"encoding/hex"
2526
"encoding/json"
2627
"fmt"
28+
"hash"
2729
"io"
2830
"net"
2931
"net/http"
@@ -39,11 +41,13 @@ import (
3941
"testing"
4042
"time"
4143

44+
"github.com/BitBoxSwiss/bitbox-wallet-app/backend/accounts"
4245
"github.com/BitBoxSwiss/bitbox-wallet-app/backend/coins/btc"
4346
"github.com/BitBoxSwiss/bitbox-wallet-app/backend/coins/btc/addresses"
4447
"github.com/BitBoxSwiss/bitbox-wallet-app/backend/coins/btc/blockchain"
4548
"github.com/BitBoxSwiss/bitbox-wallet-app/backend/coins/btc/maketx"
4649
"github.com/BitBoxSwiss/bitbox-wallet-app/backend/coins/btc/types"
50+
"github.com/BitBoxSwiss/bitbox-wallet-app/backend/coins/btc/util"
4751
coinpkg "github.com/BitBoxSwiss/bitbox-wallet-app/backend/coins/coin"
4852
"github.com/BitBoxSwiss/bitbox-wallet-app/backend/coins/eth"
4953
"github.com/BitBoxSwiss/bitbox-wallet-app/backend/coins/ltc"
@@ -59,11 +63,13 @@ import (
5963
"github.com/BitBoxSwiss/bitbox02-api-go/api/firmware/mocks"
6064
"github.com/BitBoxSwiss/bitbox02-api-go/communication/u2fhid"
6165
"github.com/BitBoxSwiss/bitbox02-api-go/util/semver"
66+
"github.com/btcsuite/btcd/btcec/v2"
6267
"github.com/btcsuite/btcd/btcutil"
6368
"github.com/btcsuite/btcd/btcutil/hdkeychain"
6469
"github.com/btcsuite/btcd/chaincfg"
6570
"github.com/btcsuite/btcd/chaincfg/chainhash"
6671
"github.com/btcsuite/btcd/wire"
72+
"github.com/decred/dcrd/dcrec/secp256k1/v4/ecdsa"
6773
"github.com/ethereum/go-ethereum/params"
6874
"github.com/stretchr/testify/require"
6975
)
@@ -698,3 +704,122 @@ func TestSimulatorVerifyAddressETH(t *testing.T) {
698704
}
699705
})
700706
}
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\nTextMemo 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+
}

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ require (
99
github.com/btcsuite/btcd/btcec/v2 v2.3.4
1010
github.com/btcsuite/btcd/btcutil v1.1.6
1111
github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0
12+
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0
1213
github.com/ethereum/go-ethereum v1.14.13
1314
github.com/flynn/noise v1.1.0
1415
github.com/gorilla/mux v1.8.1
@@ -39,7 +40,6 @@ require (
3940
github.com/davecgh/go-spew v1.1.1 // indirect
4041
github.com/deckarep/golang-set/v2 v2.6.0 // indirect
4142
github.com/decred/dcrd/crypto/blake256 v1.0.1 // indirect
42-
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect
4343
github.com/ethereum/c-kzg-4844 v1.0.3 // indirect
4444
github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9 // indirect
4545
github.com/fsnotify/fsnotify v1.7.0 // indirect

0 commit comments

Comments
 (0)