Skip to content

Commit 17ab69c

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 433f10b commit 17ab69c

File tree

1 file changed

+123
-0
lines changed

1 file changed

+123
-0
lines changed

backend/devices/bitbox02/keystore_simulator_test.go

Lines changed: 123 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
)
@@ -696,3 +702,120 @@ func TestSimulatorVerifyAddressETH(t *testing.T) {
696702
}
697703
})
698704
}
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\nTextMemo 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

Comments
 (0)