Skip to content

Commit

Permalink
Merge branch 'send-to-self'
Browse files Browse the repository at this point in the history
  • Loading branch information
benma committed Feb 26, 2025
2 parents 2b90fad + 57eb1c0 commit 4f738f4
Show file tree
Hide file tree
Showing 19 changed files with 1,049 additions and 525 deletions.
3 changes: 2 additions & 1 deletion api/firmware/backup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package firmware

import (
"bytes"
"testing"

"github.com/stretchr/testify/require"
Expand All @@ -23,7 +24,7 @@ import (
func TestSimulatorBackups(t *testing.T) {
const seedLen = 32
const testName = "test wallet name"
testSimulatorsAfterPairing(t, func(t *testing.T, device *Device) {
testSimulatorsAfterPairing(t, func(t *testing.T, device *Device, stdOut *bytes.Buffer) {
t.Helper()
require.NoError(t, device.SetDeviceName(testName))

Expand Down
3 changes: 2 additions & 1 deletion api/firmware/bip85_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package firmware

import (
"bytes"
"encoding/hex"
"testing"

Expand All @@ -27,7 +28,7 @@ func TestSimulatorBIP85AppBip39(t *testing.T) {
}

func TestSimulatorBIP85AppLN(t *testing.T) {
testInitializedSimulators(t, func(t *testing.T, device *Device) {
testInitializedSimulators(t, func(t *testing.T, device *Device, stdOut *bytes.Buffer) {
t.Helper()
entropy, err := device.BIP85AppLN()
require.NoError(t, err)
Expand Down
6 changes: 6 additions & 0 deletions api/firmware/btc.go
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,7 @@ type BTCTx struct {
func (device *Device) BTCSign(
coin messages.BTCCoin,
scriptConfigs []*messages.BTCScriptConfigWithKeypath,
outputScriptConfigs []*messages.BTCScriptConfigWithKeypath,
tx *BTCTx,
formatUnit messages.BTCSignInitRequest_FormatUnit,
) ([][]byte, map[int][]byte, error) {
Expand All @@ -310,6 +311,10 @@ func (device *Device) BTCSign(
return nil, nil, UnsupportedError("9.21.0")
}

if len(outputScriptConfigs) > 0 && !device.version.AtLeast(semver.NewSemVer(9, 22, 0)) {
return nil, nil, UnsupportedError("9.22.0")
}

signatures := make([][]byte, len(tx.Inputs))
next, err := device.queryBtcSign(&messages.Request{
Request: &messages.Request_BtcSignInit{
Expand All @@ -322,6 +327,7 @@ func (device *Device) BTCSign(
Locktime: tx.Locktime,
FormatUnit: formatUnit,
ContainsSilentPaymentOutputs: containsSilentPaymentOutputs,
OutputScriptConfigs: outputScriptConfigs,
}}})
if err != nil {
return nil, nil, err
Expand Down
236 changes: 228 additions & 8 deletions api/firmware/btc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package firmware

import (
"bytes"
"errors"
"testing"

Expand All @@ -36,6 +37,7 @@ import (

const hardenedKeyStart = 0x80000000

//nolint:unparam
func mustOutpoint(s string) *wire.OutPoint {
outPoint, err := wire.NewOutPointFromString(s)
if err != nil {
Expand Down Expand Up @@ -68,7 +70,7 @@ func TestNewXPub(t *testing.T) {
}

func TestBTCXpub(t *testing.T) {
testInitializedSimulators(t, func(t *testing.T, device *Device) {
testInitializedSimulators(t, func(t *testing.T, device *Device, stdOut *bytes.Buffer) {
t.Helper()
xpub, err := device.BTCXPub(messages.BTCCoin_TBTC, []uint32{
49 + hardenedKeyStart,
Expand All @@ -80,9 +82,10 @@ func TestBTCXpub(t *testing.T) {
})
}

func TestBTCAddress(t *testing.T) {
testInitializedSimulators(t, func(t *testing.T, device *Device) {
func TestSimulatorBTCAddress(t *testing.T) {
testInitializedSimulators(t, func(t *testing.T, device *Device, stdOut *bytes.Buffer) {
t.Helper()
// TBTC, P2WPKH
address, err := device.BTCAddress(
messages.BTCCoin_TBTC,
[]uint32{
Expand All @@ -97,6 +100,43 @@ func TestBTCAddress(t *testing.T) {
)
require.NoError(t, err)
require.Equal(t, "tb1qq064dxjgl9h9wzgsmzy6t6306qew42w9ka02u3", address)

// BTC, P2WPKH
address, err = device.BTCAddress(
messages.BTCCoin_BTC,
[]uint32{
84 + hardenedKeyStart,
0 + hardenedKeyStart,
0 + hardenedKeyStart,
1,
10,
},
NewBTCScriptConfigSimple(messages.BTCScriptConfig_P2WPKH),
false,
)
require.NoError(t, err)
require.Equal(t, "bc1qcq0ceq9vs24g4tnkkx3k2rry9j44r74huc3d7s", address)

// RBTC, P2WPKH
address, err = device.BTCAddress(
messages.BTCCoin_RBTC,
[]uint32{
84 + hardenedKeyStart,
1 + hardenedKeyStart,
0 + hardenedKeyStart,
1,
10,
},
NewBTCScriptConfigSimple(messages.BTCScriptConfig_P2WPKH),
false,
)
// Regtest (RBTC) support added in v9.21.0
if device.Version().AtLeast(semver.NewSemVer(9, 21, 0)) {
require.NoError(t, err)
require.Equal(t, "bcrt1qq064dxjgl9h9wzgsmzy6t6306qew42w955k8tc", address)
} else {
require.Error(t, err)
}
})
}

Expand All @@ -114,7 +154,7 @@ func simulatorPub(t *testing.T, device *Device, keypath ...uint32) *btcec.Public
}

func TestSimulatorBTCSignMessage(t *testing.T) {
testInitializedSimulators(t, func(t *testing.T, device *Device) {
testInitializedSimulators(t, func(t *testing.T, device *Device, stdOut *bytes.Buffer) {
t.Helper()
coin := messages.BTCCoin_BTC
keypath := []uint32{49 + hardenedKeyStart, 0 + hardenedKeyStart, 0 + hardenedKeyStart, 0, 10}
Expand Down Expand Up @@ -206,7 +246,7 @@ func TestSimulatorBTCXPub(t *testing.T) {
})
}

func TestSimulatorBTCAddress(t *testing.T) {
func TestBTCAddress(t *testing.T) {
testConfigurations(t, func(t *testing.T, env *testEnv) {
t.Helper()
expected := "mocked-address"
Expand Down Expand Up @@ -341,7 +381,7 @@ func makeTaprootOutput(t *testing.T, pubkey *btcec.PublicKey) (*btcec.PublicKey,

// Test signing; all inputs are BIP86 Taproot keyspends.
func TestSimulatorBTCSignTaprootKeySpend(t *testing.T) {
testInitializedSimulators(t, func(t *testing.T, device *Device) {
testInitializedSimulators(t, func(t *testing.T, device *Device, stdOut *bytes.Buffer) {
t.Helper()
coin := messages.BTCCoin_BTC
accountKeypath := []uint32{86 + hardenedKeyStart, 0 + hardenedKeyStart, 0 + hardenedKeyStart}
Expand Down Expand Up @@ -385,6 +425,7 @@ func TestSimulatorBTCSignTaprootKeySpend(t *testing.T) {
_, _, err := device.BTCSign(
coin,
scriptConfigs,
nil,
&BTCTx{
Version: 2,
Inputs: []*BTCTxInput{
Expand Down Expand Up @@ -431,7 +472,7 @@ func TestSimulatorBTCSignTaprootKeySpend(t *testing.T) {

// Test signing; mixed input types (p2wpkh, p2wpkh-p2sh, p2tr)
func TestSimulatorBTCSignMixed(t *testing.T) {
testInitializedSimulators(t, func(t *testing.T, device *Device) {
testInitializedSimulators(t, func(t *testing.T, device *Device, stdOut *bytes.Buffer) {
t.Helper()
coin := messages.BTCCoin_BTC
changeKeypath := []uint32{86 + hardenedKeyStart, 0 + hardenedKeyStart, 0 + hardenedKeyStart, 1, 0}
Expand Down Expand Up @@ -513,6 +554,7 @@ func TestSimulatorBTCSignMixed(t *testing.T) {
_, _, err := device.BTCSign(
coin,
scriptConfigs,
nil,
&BTCTx{
Version: 2,
Inputs: []*BTCTxInput{
Expand Down Expand Up @@ -573,7 +615,7 @@ func TestSimulatorBTCSignMixed(t *testing.T) {
// Test that we can send to a silent payment output (generated by the BitBox) and verify the
// corresponding DLEQ proof on the host that the output was generated correctly.
func TestSimulatorBTCSignSilentPayment(t *testing.T) {
testInitializedSimulators(t, func(t *testing.T, device *Device) {
testInitializedSimulators(t, func(t *testing.T, device *Device, stdOut *bytes.Buffer) {
t.Helper()
coin := messages.BTCCoin_BTC
accountKeypath := []uint32{86 + hardenedKeyStart, 0 + hardenedKeyStart, 0 + hardenedKeyStart}
Expand Down Expand Up @@ -614,6 +656,7 @@ func TestSimulatorBTCSignSilentPayment(t *testing.T) {
Keypath: accountKeypath,
},
},
nil,
&BTCTx{
Version: 2,
Inputs: []*BTCTxInput{
Expand Down Expand Up @@ -671,3 +714,180 @@ func TestSimulatorBTCSignSilentPayment(t *testing.T) {
}
})
}

// Tests that the BitBox displays the output as being of the same account in a self-send.
func TestSimulatorSignBTCTransactionSendSelfSameAccount(t *testing.T) {
testInitializedSimulators(t, func(t *testing.T, device *Device, stdOut *bytes.Buffer) {
t.Helper()
coin := messages.BTCCoin_BTC

input0Keypath := []uint32{86 + hardenedKeyStart, 0 + hardenedKeyStart, 0 + hardenedKeyStart, 0, 0}
input1Keypath := []uint32{86 + hardenedKeyStart, 0 + hardenedKeyStart, 0 + hardenedKeyStart, 0, 1}

prevTx := &wire.MsgTx{
Version: 2,
TxIn: []*wire.TxIn{
{
PreviousOutPoint: *mustOutpoint("3131313131313131313131313131313131313131313131313131313131313131:0"),
Sequence: 0xFFFFFFFF,
},
},
TxOut: []*wire.TxOut{
{
Value: 100_000_000,
PkScript: func() []byte {
_, script := makeTaprootOutput(t, simulatorPub(t, device, input0Keypath...))
return script
}(),
},
},
LockTime: 0,
}
convertedPrevTx := NewBTCPrevTxFromBtcd(prevTx)

scriptConfigs := []*messages.BTCScriptConfigWithKeypath{
{
ScriptConfig: NewBTCScriptConfigSimple(messages.BTCScriptConfig_P2TR),
Keypath: input0Keypath[:3],
},
}

prevTxHash := prevTx.TxHash()
_, _, err := device.BTCSign(
coin,
scriptConfigs,
nil,
&BTCTx{
Version: 2,
Inputs: []*BTCTxInput{
{
Input: &messages.BTCSignInputRequest{
PrevOutHash: prevTxHash[:],
PrevOutIndex: 0,
PrevOutValue: uint64(prevTx.TxOut[0].Value),
Sequence: 0xFFFFFFFF,
Keypath: input0Keypath,
ScriptConfigIndex: 0,
},
PrevTx: convertedPrevTx,
},
},
Outputs: []*messages.BTCSignOutputRequest{
{
Ours: true,
Value: 70_000_000,
Keypath: input1Keypath,
},
},
Locktime: 0,
},
messages.BTCSignInitRequest_DEFAULT,
)
require.NoError(t, err)

switch {
// Display changed in v9.22.0.
case device.Version().AtLeast(semver.NewSemVer(9, 22, 0)):
require.Contains(t,
stdOut.String(),
"This BitBox (same account): bc1psz0tsdr9sgnukfcx4gtwpp5exyeqdycfqjvm2jw6tvsj3k3eavts20yuag",
)
case device.Version().AtLeast(semver.NewSemVer(9, 20, 0)):
require.Contains(t,
stdOut.String(),
"This BitBox02: bc1psz0tsdr9sgnukfcx4gtwpp5exyeqdycfqjvm2jw6tvsj3k3eavts20yuag",
)
}
// Before simulator v9.20, address confirmation data was not written to stdout.
})
}

// Tests that the BitBox displays the output as being of the same keystore, but different account.
func TestSimulatorSignBTCTransactionSendSelfDifferentAccount(t *testing.T) {
testInitializedSimulators(t, func(t *testing.T, device *Device, stdOut *bytes.Buffer) {
t.Helper()
coin := messages.BTCCoin_BTC

input0Keypath := []uint32{86 + hardenedKeyStart, 0 + hardenedKeyStart, 0 + hardenedKeyStart, 0, 0}
input1Keypath := []uint32{86 + hardenedKeyStart, 0 + hardenedKeyStart, 1 + hardenedKeyStart, 0, 0}

prevTx := &wire.MsgTx{
Version: 2,
TxIn: []*wire.TxIn{
{
PreviousOutPoint: *mustOutpoint("3131313131313131313131313131313131313131313131313131313131313131:0"),
Sequence: 0xFFFFFFFF,
},
},
TxOut: []*wire.TxOut{
{
Value: 100_000_000,
PkScript: func() []byte {
_, script := makeTaprootOutput(t, simulatorPub(t, device, input0Keypath...))
return script
}(),
},
},
LockTime: 0,
}
convertedPrevTx := NewBTCPrevTxFromBtcd(prevTx)

scriptConfigs := []*messages.BTCScriptConfigWithKeypath{
{
ScriptConfig: NewBTCScriptConfigSimple(messages.BTCScriptConfig_P2TR),
Keypath: input0Keypath[:3],
},
}
outputScriptConfigs := []*messages.BTCScriptConfigWithKeypath{
{
ScriptConfig: NewBTCScriptConfigSimple(messages.BTCScriptConfig_P2TR),
Keypath: input1Keypath[:3],
},
}
outputScriptConfigIndex := uint32(0)

prevTxHash := prevTx.TxHash()
_, _, err := device.BTCSign(
coin,
scriptConfigs,
outputScriptConfigs,
&BTCTx{
Version: 2,
Inputs: []*BTCTxInput{
{
Input: &messages.BTCSignInputRequest{
PrevOutHash: prevTxHash[:],
PrevOutIndex: 0,
PrevOutValue: uint64(prevTx.TxOut[0].Value),
Sequence: 0xFFFFFFFF,
Keypath: input0Keypath,
ScriptConfigIndex: 0,
},
PrevTx: convertedPrevTx,
},
},
Outputs: []*messages.BTCSignOutputRequest{
{
Ours: true,
Value: 70_000_000,
Keypath: input1Keypath,
OutputScriptConfigIndex: &outputScriptConfigIndex,
},
},
Locktime: 0,
},
messages.BTCSignInitRequest_DEFAULT,
)

// Introduced in v9.22.0.
if !device.Version().AtLeast(semver.NewSemVer(9, 22, 0)) {
require.EqualError(t, err, UnsupportedError("9.22.0").Error())
return
}
require.NoError(t, err)
require.Contains(t,
stdOut.String(),
"This BitBox (account #2): bc1pzeyhtmk2d5jrjunam30dus0p34095m622dq7trm7r0g8pwac2gvqxh8d47",
)
})
}
Loading

0 comments on commit 4f738f4

Please sign in to comment.