Skip to content
Merged
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
5 changes: 5 additions & 0 deletions chainreg/chainregistry.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/lightningnetwork/lnd/chainntnfs/neutrinonotify"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/channeldb/models"
"github.com/lightningnetwork/lnd/fn"
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/kvdb"
Expand Down Expand Up @@ -63,6 +64,10 @@ type Config struct {
// state.
ChanStateDB *channeldb.ChannelStateDB

// AuxLeafStore is an optional store that can be used to store auxiliary
// leaves for certain custom channel types.
AuxLeafStore fn.Option[lnwallet.AuxLeafStore]

// BlockCache is the main cache for storing block information.
BlockCache *blockcache.BlockCache

Expand Down
158 changes: 153 additions & 5 deletions channeldb/channel.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,10 @@ type chanAuxData struct {
// tapscriptRoot is the optional Tapscript root the channel funding
// output commits to.
tapscriptRoot tlv.OptionalRecordT[tlv.TlvType6, [32]byte]

// customBlob is an optional TLV encoded blob of data representing
// custom channel funding information.
customBlob tlv.OptionalRecordT[tlv.TlvType7, tlv.Blob]
}

// encode serializes the chanAuxData to the given io.Writer.
Expand All @@ -269,6 +273,9 @@ func (c *chanAuxData) encode(w io.Writer) error {
tlvRecords = append(tlvRecords, root.Record())
},
)
c.customBlob.WhenSome(func(blob tlv.RecordT[tlv.TlvType7, tlv.Blob]) {
tlvRecords = append(tlvRecords, blob.Record())
})

// Create the tlv stream.
tlvStream, err := tlv.NewStream(tlvRecords...)
Expand All @@ -283,6 +290,7 @@ func (c *chanAuxData) encode(w io.Writer) error {
func (c *chanAuxData) decode(r io.Reader) error {
memo := c.memo.Zero()
tapscriptRoot := c.tapscriptRoot.Zero()
blob := c.customBlob.Zero()

// Create the tlv stream.
tlvStream, err := tlv.NewStream(
Expand All @@ -292,6 +300,7 @@ func (c *chanAuxData) decode(r io.Reader) error {
c.realScid.Record(),
memo.Record(),
tapscriptRoot.Record(),
blob.Record(),
)
if err != nil {
return err
Expand All @@ -308,6 +317,9 @@ func (c *chanAuxData) decode(r io.Reader) error {
if _, ok := tlvs[tapscriptRoot.TlvType()]; ok {
c.tapscriptRoot = tlv.SomeRecordT(tapscriptRoot)
}
if _, ok := tlvs[c.customBlob.TlvType()]; ok {
c.customBlob = tlv.SomeRecordT(blob)
}

return nil
}
Expand All @@ -325,6 +337,9 @@ func (c *chanAuxData) toOpenChan(o *OpenChannel) {
c.tapscriptRoot.WhenSomeV(func(h [32]byte) {
o.TapscriptRoot = fn.Some[chainhash.Hash](h)
})
c.customBlob.WhenSomeV(func(blob tlv.Blob) {
o.CustomBlob = fn.Some(blob)
})
}

// newChanAuxDataFromChan creates a new chanAuxData from the given channel.
Expand Down Expand Up @@ -354,6 +369,11 @@ func newChanAuxDataFromChan(openChan *OpenChannel) *chanAuxData {
tlv.NewPrimitiveRecord[tlv.TlvType6, [32]byte](h),
)
})
openChan.CustomBlob.WhenSome(func(blob tlv.Blob) {
c.customBlob = tlv.SomeRecordT(
tlv.NewPrimitiveRecord[tlv.TlvType7](blob),
)
})

return c
}
Expand Down Expand Up @@ -607,6 +627,74 @@ type ChannelConfig struct {
HtlcBasePoint keychain.KeyDescriptor
}

// commitAuxData stores all the optional data that may be stored as a TLV stream
// at the _end_ of the normal serialized commit on disk.
type commitAuxData struct {
// customBlob is a custom blob that may store extra data for custom
// channels.
customBlob tlv.OptionalRecordT[tlv.TlvType1, tlv.Blob]
}

// encode encodes the aux data into the passed io.Writer.
func (c *commitAuxData) encode(w io.Writer) error {
var tlvRecords []tlv.Record
c.customBlob.WhenSome(func(blob tlv.RecordT[tlv.TlvType1, tlv.Blob]) {
tlvRecords = append(tlvRecords, blob.Record())
})

// Create the tlv stream.
tlvStream, err := tlv.NewStream(tlvRecords...)
if err != nil {
return err
}

return tlvStream.Encode(w)
}

// decode attempts to ecode the aux data from the passed io.Reader.
func (c *commitAuxData) decode(r io.Reader) error {
blob := c.customBlob.Zero()

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

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

if _, ok := tlvs[c.customBlob.TlvType()]; ok {
c.customBlob = tlv.SomeRecordT(blob)
}

return nil
}

// toChanCommit extracts the optional data stored in the commitAuxData struct
// and stores it in the ChannelCommitment.
func (c *commitAuxData) toChanCommit(commit *ChannelCommitment) {
c.customBlob.WhenSomeV(func(blob tlv.Blob) {
commit.CustomBlob = fn.Some(blob)
})
}

// newCommitAuxData creates an aux data struct from the normal chan commitment.
func newCommitAuxData(commit *ChannelCommitment) commitAuxData {
var c commitAuxData

commit.CustomBlob.WhenSome(func(blob tlv.Blob) {
c.customBlob = tlv.SomeRecordT(
tlv.NewPrimitiveRecord[tlv.TlvType1](blob),
)
})

return c
}

// ChannelCommitment is a snapshot of the commitment state at a particular
// point in the commitment chain. With each state transition, a snapshot of the
// current state along with all non-settled HTLCs are recorded. These snapshots
Expand Down Expand Up @@ -673,6 +761,11 @@ type ChannelCommitment struct {
// able by us.
CommitTx *wire.MsgTx

// CustomBlob is an optional blob that can be used to store information
// specific to a custom channel type. This may track some custom
// specific state for this given commitment.
CustomBlob fn.Option[tlv.Blob]

// CommitSig is one half of the signature required to fully complete
// the script for the commitment transaction above. This is the
// signature signed by the remote party for our version of the
Expand All @@ -682,9 +775,6 @@ type ChannelCommitment struct {
// Htlcs is the set of HTLC's that are pending at this particular
// commitment height.
Htlcs []HTLC

// TODO(roasbeef): pending commit pointer?
// * lets just walk through
}

// ChannelStatus is a bit vector used to indicate whether an OpenChannel is in
Expand Down Expand Up @@ -982,6 +1072,12 @@ type OpenChannel struct {
// funding output.
TapscriptRoot fn.Option[chainhash.Hash]

// CustomBlob is an optional blob that can be used to store information
// specific to a custom channel type. This information is only created
// at channel funding time, and after wards is to be considered
// immutable.
CustomBlob fn.Option[tlv.Blob]

// TODO(roasbeef): eww
Db *ChannelStateDB

Expand Down Expand Up @@ -2793,6 +2889,16 @@ func serializeCommitDiff(w io.Writer, diff *CommitDiff) error { // nolint: dupl
}
}

// We'll also encode the commit aux data stream here. We do this here
// rather than above (at the call to serializeChanCommit), to ensure
// backwards compat for reads to existing non-custom channels.
//
// TODO(roasbeef): migrate it after all?
auxData := newCommitAuxData(&diff.Commitment)
if err := auxData.encode(w); err != nil {
return fmt.Errorf("unable to write aux data: %w", err)
}

return nil
}

Expand Down Expand Up @@ -2853,6 +2959,17 @@ func deserializeCommitDiff(r io.Reader) (*CommitDiff, error) {
}
}

// As a final step, we'll read out any aux commit data that we have at
// the end of this byte stream. We do this here to ensure backward
// compatibility, as otherwise we risk erroneously reading into the
// wrong field.
var auxData commitAuxData
if err := auxData.decode(r); err != nil {
return nil, fmt.Errorf("unable to decode aux data: %w", err)
}

auxData.toChanCommit(&d.Commitment)

return &d, nil
}

Expand Down Expand Up @@ -3831,6 +3948,13 @@ func (c *OpenChannel) Snapshot() *ChannelSnapshot {
},
}

localCommit.CustomBlob.WhenSome(func(blob tlv.Blob) {
blobCopy := make([]byte, len(blob))
copy(blobCopy, blob)

snapshot.ChannelCommitment.CustomBlob = fn.Some(blobCopy)
})

// Copy over the current set of HTLCs to ensure the caller can't mutate
// our internal state.
snapshot.Htlcs = make([]HTLC, len(localCommit.Htlcs))
Expand Down Expand Up @@ -4222,6 +4346,12 @@ func putChanCommitment(chanBucket kvdb.RwBucket, c *ChannelCommitment,
return err
}

// Before we write to disk, we'll also write our aux data as well.
auxData := newCommitAuxData(c)
if err := auxData.encode(&b); err != nil {
return fmt.Errorf("unable to write aux data: %w", err)
}

return chanBucket.Put(commitKey, b.Bytes())
}

Expand Down Expand Up @@ -4367,7 +4497,9 @@ func deserializeChanCommit(r io.Reader) (ChannelCommitment, error) {
return c, nil
}

func fetchChanCommitment(chanBucket kvdb.RBucket, local bool) (ChannelCommitment, error) {
func fetchChanCommitment(chanBucket kvdb.RBucket,
local bool) (ChannelCommitment, error) {

var commitKey []byte
if local {
commitKey = append(chanCommitmentKey, byte(0x00))
Expand All @@ -4381,7 +4513,23 @@ func fetchChanCommitment(chanBucket kvdb.RBucket, local bool) (ChannelCommitment
}

r := bytes.NewReader(commitBytes)
return deserializeChanCommit(r)
chanCommit, err := deserializeChanCommit(r)
if err != nil {
return ChannelCommitment{}, fmt.Errorf("unable to decode "+
"chan commit: %w", err)
}

// We'll also check to see if we have any aux data stored as the end of
// the stream.
var auxData commitAuxData
if err := auxData.decode(r); err != nil {
return ChannelCommitment{}, fmt.Errorf("unable to decode "+
"chan aux data: %w", err)
}

auxData.toChanCommit(&chanCommit)

return chanCommit, nil
}

func fetchChanCommitments(chanBucket kvdb.RBucket, channel *OpenChannel) error {
Expand Down
Loading