Skip to content

Commit dc6b106

Browse files
committed
address+tappsbt: add ProofDeliveryAddress to VOutput
When interactively signing a transfer that goes to a TAP address, the participants might want to be able to check the proof courier delivery URL that will be used to transfer the proof. This information currently gets lost when converting a TAP address into a vPSBT and is transported in a different data structure inside of the freighter. For a flow that doesn't use the freighter (or just in the very end), there currently is no way to specify the proof courier address, which this commit now changes. A follow-up commit will refactor the freighter to use this new field.
1 parent 74cc217 commit dc6b106

File tree

7 files changed

+90
-14
lines changed

7 files changed

+90
-14
lines changed

address/encoding.go

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,17 +42,17 @@ func compressedPubKeyDecoder(r io.Reader, val any, buf *[8]byte, l uint64) error
4242
)
4343
}
4444

45-
// urlEncoder encodes a url.URL as a variable length byte slice.
46-
func urlEncoder(w io.Writer, val any, buf *[8]byte) error {
45+
// UrlEncoder encodes a url.URL as a variable length byte slice.
46+
func UrlEncoder(w io.Writer, val any, buf *[8]byte) error {
4747
if t, ok := val.(*url.URL); ok {
4848
addrBytes := []byte((*t).String())
4949
return tlv.EVarBytes(w, &addrBytes, buf)
5050
}
5151
return tlv.NewTypeForEncodingErr(val, "*url.URL")
5252
}
5353

54-
// urlDecoder decodes a variable length byte slice as an url.URL.
55-
func urlDecoder(r io.Reader, val any, buf *[8]byte, l uint64) error {
54+
// UrlDecoder decodes a variable length byte slice as an url.URL.
55+
func UrlDecoder(r io.Reader, val any, buf *[8]byte, l uint64) error {
5656
if t, ok := val.(*url.URL); ok {
5757
var addrBytes []byte
5858
err := tlv.DVarBytes(r, &addrBytes, buf, l)
@@ -68,9 +68,7 @@ func urlDecoder(r io.Reader, val any, buf *[8]byte, l uint64) error {
6868

6969
return nil
7070
}
71-
return tlv.NewTypeForDecodingErr(
72-
val, "*url.URL", l, l,
73-
)
71+
return tlv.NewTypeForDecodingErr(val, "*url.URL", l, l)
7472
}
7573

7674
func VersionEncoder(w io.Writer, val any, buf *[8]byte) error {

address/records.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,6 @@ func newProofCourierAddrRecord(addr *url.URL) tlv.Record {
115115

116116
return tlv.MakeDynamicRecord(
117117
addrProofCourierAddrType, addr, recordSize,
118-
urlEncoder, urlDecoder,
118+
UrlEncoder, UrlDecoder,
119119
)
120120
}

tappsbt/decode.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"errors"
66
"fmt"
77
"io"
8+
"net/url"
89

910
"github.com/btcsuite/btcd/btcec/v2"
1011
"github.com/btcsuite/btcd/btcec/v2/schnorr"
@@ -284,6 +285,10 @@ func (o *VOutput) decode(pOut psbt.POutput, txOut *wire.TxOut) error {
284285
&o.AssetVersion, vOutputAssetVersionDecoder,
285286
),
286287
},
288+
{
289+
key: PsbtKeyTypeOutputTapProofDeliveryAddress,
290+
decoder: urlDecoder(&o.ProofDeliveryAddress),
291+
},
287292
}
288293

289294
for idx := range mapping {
@@ -478,3 +483,17 @@ func vOutputAssetVersionDecoder(r io.Reader, val any, buf *[8]byte,
478483
}
479484
return tlv.NewTypeForDecodingErr(val, "VOutputAssetVersion", 8, l)
480485
}
486+
487+
// urlDecoder returns a decoder function that can handle nil URLs.
488+
func urlDecoder(u **url.URL) decoderFunc {
489+
return func(key, byteVal []byte) error {
490+
if len(byteVal) == 0 {
491+
return nil
492+
}
493+
494+
if *u == nil {
495+
*u = &url.URL{}
496+
}
497+
return tlvDecoder(*u, address.UrlDecoder)(key, byteVal)
498+
}
499+
}

tappsbt/encode.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@ import (
66
"fmt"
77
"io"
88
"math"
9+
"net/url"
910

1011
"github.com/btcsuite/btcd/btcec/v2"
1112
"github.com/btcsuite/btcd/btcec/v2/schnorr"
1213
"github.com/btcsuite/btcd/btcutil/psbt"
1314
"github.com/btcsuite/btcd/txscript"
1415
"github.com/btcsuite/btcd/wire"
16+
"github.com/lightninglabs/taproot-assets/address"
1517
"github.com/lightninglabs/taproot-assets/asset"
1618
"github.com/lightninglabs/taproot-assets/commitment"
1719
"github.com/lightninglabs/taproot-assets/fn"
@@ -283,6 +285,10 @@ func (o *VOutput) encode(coinType uint32) (psbt.POutput, *wire.TxOut, error) {
283285
&o.AssetVersion, vOutputAssetVersionEncoder,
284286
),
285287
},
288+
{
289+
key: PsbtKeyTypeOutputTapProofDeliveryAddress,
290+
encoder: urlEncoder(o.ProofDeliveryAddress),
291+
},
286292
}
287293

288294
for idx := range mapping {
@@ -481,3 +487,29 @@ func vOutputAssetVersionEncoder(w io.Writer, val any, buf *[8]byte) error {
481487
}
482488
return tlv.NewTypeForEncodingErr(val, "VOutputAssetVersion")
483489
}
490+
491+
// urlEncoder returns a function that encodes the given URL as a custom PSBT
492+
// field.
493+
func urlEncoder(val *url.URL) encoderFunc {
494+
return func(key []byte) ([]*customPsbtField, error) {
495+
if val == nil {
496+
return nil, nil
497+
}
498+
499+
var (
500+
b bytes.Buffer
501+
scratch [8]byte
502+
)
503+
if err := address.UrlEncoder(&b, val, &scratch); err != nil {
504+
return nil, fmt.Errorf("error encoding TLV record: %w",
505+
err)
506+
}
507+
508+
return []*customPsbtField{
509+
{
510+
Key: fn.CopySlice(key),
511+
Value: b.Bytes(),
512+
},
513+
}, nil
514+
}
515+
}

tappsbt/interface.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package tappsbt
33
import (
44
"bytes"
55
"fmt"
6+
"net/url"
67

78
"github.com/btcsuite/btcd/btcec/v2"
89
"github.com/btcsuite/btcd/btcutil"
@@ -52,6 +53,7 @@ var (
5253
PsbtKeyTypeOutputTapSplitAsset = []byte{0x77}
5354
PsbtKeyTypeOutputTapAnchorTapscriptSibling = []byte{0x78}
5455
PsbtKeyTypeOutputTapAssetVersion = []byte{0x79}
56+
PsbtKeyTypeOutputTapProofDeliveryAddress = []byte{0x7a}
5557
)
5658

5759
// The following keys are used as custom fields on the BTC level anchor
@@ -522,6 +524,10 @@ type VOutput struct {
522524
// serialized, this will be stored in the TaprootInternalKey and
523525
// TaprootDerivationPath fields of the PSBT output.
524526
ScriptKey asset.ScriptKey
527+
528+
// ProofDeliveryAddress is the address to which the proof of the asset
529+
// transfer should be delivered.
530+
ProofDeliveryAddress *url.URL
525531
}
526532

527533
// SplitLocator creates a split locator from the output. The asset ID is passed

tappsbt/mock.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package tappsbt
33
import (
44
"bytes"
55
"encoding/hex"
6+
"net/url"
67
"os"
78
"path/filepath"
89
"strings"
@@ -100,6 +101,9 @@ func RandPacket(t testing.TB) *VPacket {
100101
t, testAsset.Genesis, inputScriptKey.PubKey, oddTxBlock, 1, 0,
101102
)
102103

104+
courierAddress, err := url.Parse("https://example.com")
105+
require.NoError(t, err)
106+
103107
vPacket := &VPacket{
104108
Inputs: []*VInput{{
105109
PrevID: asset.PrevID{
@@ -136,6 +140,7 @@ func RandPacket(t testing.TB) *VPacket {
136140
ScriptKey: testOutputAsset.ScriptKey,
137141
SplitAsset: testOutputAsset,
138142
AnchorOutputTapscriptSibling: testPreimage1,
143+
ProofDeliveryAddress: courierAddress,
139144
}, {
140145
Amount: 345,
141146
AssetVersion: asset.Version(
@@ -493,6 +498,10 @@ func NewTestFromVOutput(t testing.TB, v *VOutput,
493498
vo.Asset = asset.NewTestFromAsset(t, v.Asset)
494499
}
495500

501+
if v.ProofDeliveryAddress != nil {
502+
vo.ProofDeliveryAddress = v.ProofDeliveryAddress.String()
503+
}
504+
496505
if v.ScriptKey.TweakedScriptKey != nil {
497506
bip32Derivation, trBip32Derivation := Bip32DerivationFromKeyDesc(
498507
v.ScriptKey.RawKey, coinType,
@@ -572,6 +581,7 @@ type TestVOutput struct {
572581
TrBip32Derivation []*TestTrBip32Derivation `json:"tr_bip32_derivation"`
573582
TrInternalKey string `json:"tr_internal_key"`
574583
TrMerkleRoot string `json:"tr_merkle_root"`
584+
ProofDeliveryAddress string `json:"proof_delivery_address"`
575585
}
576586

577587
func (to *TestVOutput) ToVOutput(t testing.TB) *VOutput {
@@ -609,6 +619,12 @@ func (to *TestVOutput) ToVOutput(t testing.TB) *VOutput {
609619
v.SplitAsset = to.SplitAsset.ToAsset(t)
610620
}
611621

622+
if to.ProofDeliveryAddress != "" {
623+
var err error
624+
v.ProofDeliveryAddress, err = url.Parse(to.ProofDeliveryAddress)
625+
require.NoError(t, err)
626+
}
627+
612628
if len(to.Bip32Derivation) > 0 && to.TrInternalKey != "" {
613629
firstDerivation := to.Bip32Derivation[0].ToBip32Derivation(t)
614630
keyDesc, err := KeyDescFromBip32Derivation(firstDerivation)

0 commit comments

Comments
 (0)