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

More custom channel bugfixes #939

Merged
merged 10 commits into from
Jun 9, 2024
39 changes: 23 additions & 16 deletions tapchannel/aux_closer.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,19 @@ func createCloseAlloc(isLocal, isInitiator bool, closeAsset *asset.Asset,
return nil, fmt.Errorf("no script key for asset %v", assetID)
}

var proofDeliveryUrl *url.URL
err := lfn.MapOptionZ(
shutdownMsg.ProofDeliveryAddr.ValOpt(), func(u []byte) error {
var err error
proofDeliveryUrl, err = url.Parse(string(u))
return err
},
)
if err != nil {
return nil, fmt.Errorf("unable to decode proof delivery "+
"address: %w", err)
}

return &Allocation{
Type: func() AllocationType {
if isLocal {
Expand All @@ -110,13 +123,14 @@ func createCloseAlloc(isLocal, isInitiator bool, closeAsset *asset.Asset,

return CommitAllocationToRemote
}(),
SplitRoot: isInitiator,
InternalKey: shutdownMsg.AssetInternalKey.Val,
ScriptKey: asset.NewScriptKey(&scriptKey),
Amount: closeAsset.Amount,
AssetVersion: asset.V0,
BtcAmount: tapsend.DummyAmtSats,
SortTaprootKeyBytes: sortKeyBytes,
SplitRoot: isInitiator,
InternalKey: shutdownMsg.AssetInternalKey.Val,
ScriptKey: asset.NewScriptKey(&scriptKey),
Amount: closeAsset.Amount,
AssetVersion: asset.V0,
BtcAmount: tapsend.DummyAmtSats,
SortTaprootKeyBytes: sortKeyBytes,
ProofDeliveryAddress: proofDeliveryUrl,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah existing tests assumed that they were all using the same courier.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before this commit the respondent to the shutdown process didn't actually know what proof courier the final proof would be uploaded to. Now the desired delivery address is sent along with the internal and script key, so the non-shutdown-initiator will know where to look for the proof.

}, nil
}

Expand Down Expand Up @@ -325,14 +339,6 @@ func (a *AuxChanCloser) AuxCloseOutputs(
return none, fmt.Errorf("unable to distribute coins: %w", err)
}

// For each vPkt, we'll also go ahead and add the default proof courier
// addr to them.
for _, vPacket := range vPackets {
for _, vOut := range vPacket.Outputs {
vOut.ProofDeliveryAddress = a.cfg.DefaultCourierAddr
}
}

// With the vPackets created we'll now prepare all the split
// information encoded in the vPackets.
fundingScriptTree := NewFundingScriptTree()
Expand Down Expand Up @@ -517,8 +523,9 @@ func (a *AuxChanCloser) ShutdownBlob(
// can send to lnd to have included.
shutdownRecord := tapchannelmsg.NewAuxShutdownMsg(
&btcInternalKey, newInternalKey.PubKey, scriptKeys,
a.cfg.DefaultCourierAddr,
)
records, err := tlv.RecordsToMap(shutdownRecord.Records())
records, err := tlv.RecordsToMap(shutdownRecord.EncodeRecords())
if err != nil {
return none, err
}
Expand Down
75 changes: 62 additions & 13 deletions tapchannelmsg/records.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"io"
"net/url"

"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/txscript"
Expand Down Expand Up @@ -69,6 +70,11 @@ type (
// ScriptKeysShutdownType is the type alias for the TLV type that is
// used to encode the script keys of the shutdown record on the wire.
ScriptKeysShutdownType = tlv.TlvType65541

// ProofDeliveryAddrShutdownType is the type alias for the TLV type that
// is used to encode the proof delivery address of the shutdown record
// on the wire.
ProofDeliveryAddrShutdownType = tlv.TlvType65542
)

// OpenChannel is a record that represents the capacity information related to
Expand Down Expand Up @@ -1795,12 +1801,31 @@ type AuxShutdownMsg struct {
// ScriptKeys maps asset IDs to script keys to be used to send the
// assets to the sending party in the co-op close transaction.
ScriptKeys tlv.RecordT[ScriptKeysShutdownType, ScriptKeyMap]

// ProofDeliveryAddr is an optional type that contains the delivery
// address for the proofs of the co-op close outputs of the local node.
ProofDeliveryAddr tlv.OptionalRecordT[
ProofDeliveryAddrShutdownType, []byte,
]
}

// NewAuxShutdownMsg creates a new AuxShutdownMsg with the given internal key
// and script key map.
func NewAuxShutdownMsg(btcInternalKey, assetInternalKey *btcec.PublicKey,
scriptKeys ScriptKeyMap) *AuxShutdownMsg {
scriptKeys ScriptKeyMap, proofDeliveryAddr *url.URL) *AuxShutdownMsg {

var deliveryAddr tlv.OptionalRecordT[
ProofDeliveryAddrShutdownType, []byte,
]
if proofDeliveryAddr != nil {
deliveryAddrBytes := []byte(proofDeliveryAddr.String())
rec := tlv.NewPrimitiveRecord[ProofDeliveryAddrShutdownType](
deliveryAddrBytes,
)
deliveryAddr = tlv.SomeRecordT[ProofDeliveryAddrShutdownType](
rec,
)
}

return &AuxShutdownMsg{
BtcInternalKey: tlv.NewPrimitiveRecord[BtcKeyShutdownType](
Expand All @@ -1812,12 +1837,31 @@ func NewAuxShutdownMsg(btcInternalKey, assetInternalKey *btcec.PublicKey,
ScriptKeys: tlv.NewRecordT[ScriptKeysShutdownType](
scriptKeys,
),
ProofDeliveryAddr: deliveryAddr,
}
}

// EncodeRecords returns the records that make up the AuxShutdownMsg for
// encoding.
func (a *AuxShutdownMsg) EncodeRecords() []tlv.Record {
records := []tlv.Record{
a.BtcInternalKey.Record(),
a.AssetInternalKey.Record(),
a.ScriptKeys.Record(),
}

a.ProofDeliveryAddr.WhenSome(
func(r tlv.RecordT[ProofDeliveryAddrShutdownType, []byte]) {
records = append(records, r.Record())
},
)

return records
}

// Encode serializes the AuxShutdownMsg to the given io.Writer.
func (a *AuxShutdownMsg) Encode(w io.Writer) error {
tlvStream, err := tlv.NewStream(a.Records()...)
tlvStream, err := tlv.NewStream(a.EncodeRecords()...)
if err != nil {
return err
}
Expand All @@ -1827,25 +1871,30 @@ func (a *AuxShutdownMsg) Encode(w io.Writer) error {

// Decode deserializes the AuxShutdownMsg from the given io.Reader.
func (a *AuxShutdownMsg) Decode(r io.Reader) error {
tlvStream, err := tlv.NewStream(a.Records()...)
deliveryAddr := a.ProofDeliveryAddr.Zero()

records := []tlv.Record{
a.BtcInternalKey.Record(),
a.AssetInternalKey.Record(),
a.ScriptKeys.Record(),
deliveryAddr.Record(),
}

tlvStream, err := tlv.NewStream(records...)
if err != nil {
return err
}

if _, err := tlvStream.DecodeWithParsedTypesP2P(r); err != nil {
tlvs, err := tlvStream.DecodeWithParsedTypesP2P(r)
if err != nil {
return err
}

return nil
}

// Records returns a slice of tlv.Record that represents the AuxShutdownMsg.
func (a *AuxShutdownMsg) Records() []tlv.Record {
return []tlv.Record{
a.BtcInternalKey.Record(),
a.AssetInternalKey.Record(),
a.ScriptKeys.Record(),
if _, ok := tlvs[deliveryAddr.TlvType()]; ok {
a.ProofDeliveryAddr = tlv.SomeRecordT(deliveryAddr)
}

return nil
}

// DecodeAuxShutdownMsg deserializes a AuxShutdownMsg from the given blob.
Expand Down
44 changes: 36 additions & 8 deletions tapchannelmsg/records_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package tapchannelmsg
import (
"bytes"
"encoding/hex"
"net/url"
"os"
"path/filepath"
"strings"
Expand Down Expand Up @@ -438,15 +439,42 @@ func TestAuxShutdownMsg(t *testing.T) {
testScriptKeys[[32]byte{byte(i)}] = *test.RandPubKey(t)
}

testShutdownMsg := NewAuxShutdownMsg(
testBtcInternalKey, testAssetInternalKey, testScriptKeys,
)
dummyURL, err := url.Parse("https://example.com")
require.NoError(t, err)

testCases := []struct {
name string
shutdown *AuxShutdownMsg
}{
{
name: "AuxShutdownMsg with no URL",
shutdown: NewAuxShutdownMsg(
testBtcInternalKey, testAssetInternalKey,
testScriptKeys, nil,
),
},
{
name: "AuxShutdownMsg with URL",
shutdown: NewAuxShutdownMsg(
testBtcInternalKey, testAssetInternalKey,
testScriptKeys, dummyURL,
),
},
}

var shutdownBuffer bytes.Buffer
require.NoError(t, testShutdownMsg.Encode(&shutdownBuffer))
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
// Serialize the AuxShutdownMsg and then deserialize it
// again.
var b bytes.Buffer
err := tc.shutdown.Encode(&b)
require.NoError(t, err)

var newShutdownMsg AuxShutdownMsg
require.NoError(t, newShutdownMsg.Decode(&shutdownBuffer))
newShutdownMsg := &AuxShutdownMsg{}
err = newShutdownMsg.Decode(&b)
require.NoError(t, err)

require.Equal(t, *testShutdownMsg, newShutdownMsg)
require.Equal(t, tc.shutdown, newShutdownMsg)
})
}
}