Skip to content
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
9 changes: 9 additions & 0 deletions api/firmware/btc.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"encoding/binary"
"errors"
"fmt"
"slices"
"strings"

"github.com/BitBoxSwiss/bitbox02-api-go/api/firmware/messages"
Expand Down Expand Up @@ -372,6 +373,14 @@ func (device *Device) nonAtomicBTCSign(
}
}

if !device.version.AtLeast(semver.NewSemVer(9, 24, 0)) {
if slices.ContainsFunc(tx.Outputs, func(output *messages.BTCSignOutputRequest) bool {
return output.Type == messages.BTCOutputType_OP_RETURN
}) {
return nil, UnsupportedError("9.24.0")
}
}

supportsAntiklepto := device.version.AtLeast(semver.NewSemVer(9, 4, 0))

containsSilentPaymentOutputs := false
Expand Down
32 changes: 18 additions & 14 deletions api/firmware/messages/btc.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions api/firmware/messages/btc.proto
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ enum BTCOutputType {
P2WPKH = 3;
P2WSH = 4;
P2TR = 5;
OP_RETURN = 6;
}

message BTCSignOutputRequest {
Expand Down
15 changes: 15 additions & 0 deletions api/firmware/psbt.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,21 @@ func payloadFromPkScript(pkScript []byte) (messages.BTCOutputType, []byte, error
outputType = messages.BTCOutputType_P2TR
payload = pkScript[2:]

case len(pkScript) > 0 && pkScript[0] == txscript.OP_RETURN:
outputType = messages.BTCOutputType_OP_RETURN

tokenizer := txscript.MakeScriptTokenizer(0, pkScript[1:])
if !tokenizer.Next() {
return 0, nil, errp.New("naked OP_RETURN is not supported")
}
payload = tokenizer.Data()
// OP_0 is an empty data push
if payload == nil && tokenizer.Opcode() != txscript.OP_0 {
return 0, nil, errp.New("no data push found after OP_RETURN")
}
if !tokenizer.Done() {
return 0, nil, errp.New("only one data push supported after OP_RETURN")
}
default:
return 0, nil, errp.New("unrecognized output type")
}
Expand Down
34 changes: 29 additions & 5 deletions api/firmware/psbt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ func TestPayloadFromPkScript(t *testing.T) {
tests := []struct {
name string
address string
pkScriptHex string // Used for OP_RETURN instead of address
expectedType messages.BTCOutputType
expectedPayload string
}{
Expand Down Expand Up @@ -117,18 +118,41 @@ func TestPayloadFromPkScript(t *testing.T) {
expectedPayload: "a60869f0dbcf1dc659c9cecbaf8050135ea9e8cdc487053f1dc6880949dc684c",
expectedType: messages.BTCOutputType_P2TR,
},
{
name: "OP_RETURN empty",
pkScriptHex: "6a00",
expectedType: messages.BTCOutputType_OP_RETURN,
expectedPayload: "",
},
{
name: "OP_RETURN 3 bytes",
pkScriptHex: "6a03aabbcc",
expectedType: messages.BTCOutputType_OP_RETURN,
expectedPayload: "aabbcc",
},
{
name: "OP_RETURN 80 bytes (OP_PUSHDATA1)",
pkScriptHex: "6a4c50aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
expectedType: messages.BTCOutputType_OP_RETURN,
expectedPayload: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
addr, err := btcutil.DecodeAddress(tt.address, &chaincfg.MainNetParams)
require.NoError(t, err)
var pkScript []byte
var err error

pkScript, err := txscript.PayToAddrScript(addr)
require.NoError(t, err)
if tt.pkScriptHex != "" {
pkScript = unhex(tt.pkScriptHex)
} else {
addr, err := btcutil.DecodeAddress(tt.address, &chaincfg.MainNetParams)
require.NoError(t, err)
pkScript, err = txscript.PayToAddrScript(addr)
require.NoError(t, err)
}

outputType, payload, err := payloadFromPkScript(pkScript)

require.NoError(t, err)
assert.Equal(t, tt.expectedType, outputType)
assert.Equal(t, tt.expectedPayload, hex.EncodeToString(payload))
Expand Down
Loading