Skip to content

Commit

Permalink
multi: add Tap address to VOutput
Browse files Browse the repository at this point in the history
  • Loading branch information
ffranr committed Aug 24, 2023
1 parent 5b689d8 commit 47f8522
Show file tree
Hide file tree
Showing 9 changed files with 161 additions and 9 deletions.
4 changes: 4 additions & 0 deletions address/mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ type TestVectors struct {
func NewTestFromAddress(t testing.TB, a *Tap) *TestAddress {
t.Helper()

if a == nil {
return nil
}

ta := &TestAddress{
ChainParamsHRP: a.ChainParams.TapHRP,
AssetVersion: uint8(a.AssetVersion),
Expand Down
4 changes: 4 additions & 0 deletions tapfreighter/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/btcsuite/btcd/btcutil/psbt"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
"github.com/lightninglabs/taproot-assets/address"
"github.com/lightninglabs/taproot-assets/asset"
"github.com/lightninglabs/taproot-assets/commitment"
"github.com/lightninglabs/taproot-assets/fn"
Expand Down Expand Up @@ -201,6 +202,9 @@ type TransferOutput struct {
// includes all the proof information other than the final chain
// information.
ProofSuffix []byte

// The Tap address that should be used to satisfy the transfer.
Addr address.Tap
}

// OutboundParcel represents the database level delta of an outbound Taproot
Expand Down
1 change: 1 addition & 0 deletions tappsbt/address.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ func FromAddresses(receiverAddrs []*address.Tap,
),
AnchorOutputInternalKey: &addr.InternalKey,
AnchorOutputTapscriptSibling: addr.TapscriptSibling,
Addr: addr,
})
}

Expand Down
31 changes: 31 additions & 0 deletions tappsbt/decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,9 @@ func (o *VOutput) decode(pOut psbt.POutput, txOut *wire.TxOut) error {
&o.AnchorOutputTapscriptSibling,
commitment.TapscriptPreimageDecoder,
),
}, {
key: PsbtKeyTypeOutputTapAddr,
decoder: addrDecoder(&o.Addr),
}}

for idx := range mapping {
Expand Down Expand Up @@ -319,6 +322,34 @@ func assetDecoder(a **asset.Asset) decoderFunc {
}
}

// addrDecoder returns a decoder function that can handle a Tap address.
func addrDecoder(a **address.Tap) decoderFunc {
return func(key, byteVal []byte) error {
if len(byteVal) == 0 {
return nil
}

// Decode variable length byte slice into address bytes.
var addrBytes []byte
err := tlvDecoder(&addrBytes, tlv.DVarBytes)(key, byteVal)
if err != nil {
return err
}

// Decode address bytes into address.
if *a == nil {
*a = &address.Tap{}
}

buf := bytes.NewBuffer(addrBytes)
if err := (*a).Decode(buf); err != nil {
return err
}

return nil
}
}

// booleanDecoder returns a function that decodes the given byte slice as a
// boolean.
func booleanDecoder(target *bool) decoderFunc {
Expand Down
17 changes: 17 additions & 0 deletions tappsbt/decode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,28 @@ func TestEncodingDecoding(t *testing.T) {
decoded, err := NewFromRawBytes(&buf, false)
require.NoError(t, err)

// Repopulate chain params in the VOutput address fields since they are
// not encoded/decoded.
for i := range pkg.Outputs {
if decoded.Outputs[i].Addr != nil {
decoded.Outputs[i].Addr.ChainParams = pkg.ChainParams
}
}

assertEqualPackets(t, pkg, decoded)

// Also make sure we can decode the packet from the base PSBT.
decoded, err = NewFromPsbt(packet)
require.NoError(t, err)

// Repopulate chain params in the VOutput address fields since they are
// not encoded/decoded.
for i := range pkg.Outputs {
if decoded.Outputs[i].Addr != nil {
decoded.Outputs[i].Addr.ChainParams = pkg.ChainParams
}
}

assertEqualPackets(t, pkg, decoded)
}

Expand All @@ -109,6 +125,7 @@ func TestEncodingDecoding(t *testing.T) {
require.NoError(t, err)
pkg.Outputs = append(pkg.Outputs, &VOutput{
ScriptKey: asset.RandScriptKey(t),
Addr: addr.Tap,
})

return pkg
Expand Down
33 changes: 33 additions & 0 deletions tappsbt/encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/btcsuite/btcd/btcutil/psbt"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/lightninglabs/taproot-assets/address"
"github.com/lightninglabs/taproot-assets/asset"
"github.com/lightninglabs/taproot-assets/commitment"
"github.com/lightninglabs/taproot-assets/fn"
Expand Down Expand Up @@ -213,6 +214,11 @@ func (o *VOutput) encode(coinType uint32) (psbt.POutput, *wire.TxOut, error) {
}

anchorOutputIndex := uint64(o.AnchorOutputIndex)
addrEncoderFunc, err := addrEncoder(o.Addr)
if err != nil {
return pOut, nil, fmt.Errorf("error encoding address: %w", err)
}

mapping := []encoderMapping{{
key: PsbtKeyTypeOutputTapType,
encoder: tlvEncoder(&o.Type, vOutputTypeEncoder),
Expand Down Expand Up @@ -244,6 +250,9 @@ func (o *VOutput) encode(coinType uint32) (psbt.POutput, *wire.TxOut, error) {
encoder: tapscriptPreimageEncoder(
o.AnchorOutputTapscriptSibling,
),
}, {
key: PsbtKeyTypeOutputTapAddr,
encoder: addrEncoderFunc,
}}

for idx := range mapping {
Expand Down Expand Up @@ -309,6 +318,30 @@ func assetEncoder(a *asset.Asset) encoderFunc {
return tlvEncoder(a, asset.LeafEncoder)
}

// addrEncoder returns an encoder function for encoding a Tap address.
func addrEncoder(addr *address.Tap) (encoderFunc, error) {
// Initially set the encoder function to a no-op. If the address is nil,
// or we encounter an error, we will return this function as the
// encoder.
encoder := func([]byte) ([]*customPsbtField, error) {
return nil, nil
}

if addr == nil {
return encoder, nil
}

// Encode address as a variable length byte slice.
var buf bytes.Buffer
if err := addr.Encode(&buf); err != nil {
return encoder, err
}
addrBytes := buf.Bytes()

encoder = tlvEncoder(&addrBytes, tlv.EVarBytes)
return encoder, nil
}

// booleanEncoder returns a function that encodes the given boolean value as a
// byte slice.
func booleanEncoder(val bool) encoderFunc {
Expand Down
6 changes: 6 additions & 0 deletions tappsbt/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ var (
PsbtKeyTypeOutputTapAsset = []byte{0x76}
PsbtKeyTypeOutputTapSplitAsset = []byte{0x77}
PsbtKeyTypeOutputTapAnchorTapscriptSibling = []byte{0x78}
PsbtKeyTypeOutputTapAddr = []byte{0x79}
)

// The following keys are used as custom fields on the BTC level anchor
Expand Down Expand Up @@ -493,6 +494,11 @@ type VOutput struct {
// serialized, this will be stored in the TaprootInternalKey and
// TaprootDerivationPath fields of the PSBT output.
ScriptKey asset.ScriptKey

// The Tap address that should be used to satisfy the transfer. Some
// outputs may not have a tap address, in which case this field will be
// nil.
Addr *address.Tap
}

// SplitLocator creates a split locator from the output. The asset ID is passed
Expand Down
11 changes: 11 additions & 0 deletions tappsbt/mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,13 @@ func RandPacket(t testing.TB) *VPacket {
txscript.NewTapBranch(leaf1, leaf1),
)

// Create two random Tap addresses, one for each virtual output.
proofCourierAddr1 := address.RandProofCourierAddr(t)
addr1, _, _ := address.RandAddr(t, testParams, proofCourierAddr1)

proofCourierAddr2 := address.RandProofCourierAddr(t)
addr2, _, _ := address.RandAddr(t, testParams, proofCourierAddr2)

vPacket := &VPacket{
Inputs: []*VInput{{
PrevID: asset.PrevID{
Expand Down Expand Up @@ -98,6 +105,7 @@ func RandPacket(t testing.TB) *VPacket {
ScriptKey: testOutputAsset.ScriptKey,
SplitAsset: testOutputAsset,
AnchorOutputTapscriptSibling: testPreimage1,
Addr: addr1.Tap,
}, {
Amount: 345,
Type: TypeSplitRoot,
Expand All @@ -110,6 +118,7 @@ func RandPacket(t testing.TB) *VPacket {
Asset: testOutputAsset,
ScriptKey: testOutputAsset.ScriptKey,
AnchorOutputTapscriptSibling: testPreimage2,
Addr: addr2.Tap,
}},
ChainParams: testParams,
}
Expand Down Expand Up @@ -440,6 +449,7 @@ func NewTestFromVOutput(t testing.TB, v *VOutput,
PkScript: hex.EncodeToString(test.ComputeTaprootScript(
t, v.ScriptKey.PubKey,
)),
Addr: address.NewTestFromAddress(t, v.Addr),
}

if v.Asset != nil {
Expand Down Expand Up @@ -524,6 +534,7 @@ type TestVOutput struct {
TrBip32Derivation []*TestTrBip32Derivation `json:"tr_bip32_derivation"`
TrInternalKey string `json:"tr_internal_key"`
TrMerkleRoot string `json:"tr_merkle_root"`
Addr *address.TestAddress `json:"address"`
}

func (to *TestVOutput) ToVOutput(t testing.TB) *VOutput {
Expand Down
Loading

0 comments on commit 47f8522

Please sign in to comment.