Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Send to self #118

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading