Skip to content

Commit

Permalink
Merge pull request #8684 from lightningnetwork/aux-leaf-fetcher
Browse files Browse the repository at this point in the history
[2/?]: lnwallet+channeldb: add new AuxLeafStore for dynamic aux leaves
  • Loading branch information
guggero authored May 1, 2024
2 parents bb44793 + 72f7b80 commit 81970ea
Show file tree
Hide file tree
Showing 31 changed files with 1,796 additions and 596 deletions.
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

0 comments on commit 81970ea

Please sign in to comment.