Skip to content

Commit 8772d24

Browse files
committed
api/firmware/btc: add support for OP_RETURN
Added in v9.24.0
1 parent ba77092 commit 8772d24

File tree

5 files changed

+72
-19
lines changed

5 files changed

+72
-19
lines changed

api/firmware/btc.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"encoding/binary"
1919
"errors"
2020
"fmt"
21+
"slices"
2122
"strings"
2223

2324
"github.com/BitBoxSwiss/bitbox02-api-go/api/firmware/messages"
@@ -372,6 +373,14 @@ func (device *Device) nonAtomicBTCSign(
372373
}
373374
}
374375

376+
if !device.version.AtLeast(semver.NewSemVer(9, 24, 0)) {
377+
if slices.ContainsFunc(tx.Outputs, func(output *messages.BTCSignOutputRequest) bool {
378+
return output.Type == messages.BTCOutputType_OP_RETURN
379+
}) {
380+
return nil, UnsupportedError("9.24.0")
381+
}
382+
}
383+
375384
supportsAntiklepto := device.version.AtLeast(semver.NewSemVer(9, 4, 0))
376385

377386
containsSilentPaymentOutputs := false

api/firmware/messages/btc.pb.go

Lines changed: 18 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/firmware/messages/btc.proto

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ enum BTCOutputType {
175175
P2WPKH = 3;
176176
P2WSH = 4;
177177
P2TR = 5;
178+
OP_RETURN = 6;
178179
}
179180

180181
message BTCSignOutputRequest {

api/firmware/psbt.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,21 @@ func payloadFromPkScript(pkScript []byte) (messages.BTCOutputType, []byte, error
224224
outputType = messages.BTCOutputType_P2TR
225225
payload = pkScript[2:]
226226

227+
case len(pkScript) > 0 && pkScript[0] == txscript.OP_RETURN:
228+
outputType = messages.BTCOutputType_OP_RETURN
229+
230+
tokenizer := txscript.MakeScriptTokenizer(0, pkScript[1:])
231+
if !tokenizer.Next() {
232+
return 0, nil, errp.New("naked OP_RETURN is not supported")
233+
}
234+
payload = tokenizer.Data()
235+
// OP_0 is an empty data push
236+
if payload == nil && tokenizer.Opcode() != txscript.OP_0 {
237+
return 0, nil, errp.New("no data push found after OP_RETURN")
238+
}
239+
if !tokenizer.Done() {
240+
return 0, nil, errp.New("only one data push supported after OP_RETURN")
241+
}
227242
default:
228243
return 0, nil, errp.New("unrecognized output type")
229244
}

api/firmware/psbt_test.go

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ func TestPayloadFromPkScript(t *testing.T) {
8484
tests := []struct {
8585
name string
8686
address string
87+
pkScriptHex string // Used for OP_RETURN instead of address
8788
expectedType messages.BTCOutputType
8889
expectedPayload string
8990
}{
@@ -117,18 +118,41 @@ func TestPayloadFromPkScript(t *testing.T) {
117118
expectedPayload: "a60869f0dbcf1dc659c9cecbaf8050135ea9e8cdc487053f1dc6880949dc684c",
118119
expectedType: messages.BTCOutputType_P2TR,
119120
},
121+
{
122+
name: "OP_RETURN empty",
123+
pkScriptHex: "6a00",
124+
expectedType: messages.BTCOutputType_OP_RETURN,
125+
expectedPayload: "",
126+
},
127+
{
128+
name: "OP_RETURN 3 bytes",
129+
pkScriptHex: "6a03aabbcc",
130+
expectedType: messages.BTCOutputType_OP_RETURN,
131+
expectedPayload: "aabbcc",
132+
},
133+
{
134+
name: "OP_RETURN 80 bytes (OP_PUSHDATA1)",
135+
pkScriptHex: "6a4c50aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
136+
expectedType: messages.BTCOutputType_OP_RETURN,
137+
expectedPayload: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
138+
},
120139
}
121140

122141
for _, tt := range tests {
123142
t.Run(tt.name, func(t *testing.T) {
124-
addr, err := btcutil.DecodeAddress(tt.address, &chaincfg.MainNetParams)
125-
require.NoError(t, err)
143+
var pkScript []byte
144+
var err error
126145

127-
pkScript, err := txscript.PayToAddrScript(addr)
128-
require.NoError(t, err)
146+
if tt.pkScriptHex != "" {
147+
pkScript = unhex(tt.pkScriptHex)
148+
} else {
149+
addr, err := btcutil.DecodeAddress(tt.address, &chaincfg.MainNetParams)
150+
require.NoError(t, err)
151+
pkScript, err = txscript.PayToAddrScript(addr)
152+
require.NoError(t, err)
153+
}
129154

130155
outputType, payload, err := payloadFromPkScript(pkScript)
131-
132156
require.NoError(t, err)
133157
assert.Equal(t, tt.expectedType, outputType)
134158
assert.Equal(t, tt.expectedPayload, hex.EncodeToString(payload))

0 commit comments

Comments
 (0)