Skip to content

Commit 2a63502

Browse files
Roasbeefguggero
authored andcommitted
channeldb: new custom blob nested TLV
In this commit, for each channel, we'll now start to store an optional custom blob. This can be used to store extra information for custom channels in an opauqe manner.
1 parent 0b56703 commit 2a63502

File tree

2 files changed

+157
-5
lines changed

2 files changed

+157
-5
lines changed

channeldb/channel.go

Lines changed: 150 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,10 @@ type chanAuxData struct {
251251
// tapscriptRoot is the optional Tapscript root the channel funding
252252
// output commits to.
253253
tapscriptRoot tlv.OptionalRecordT[tlv.TlvType5, [32]byte]
254+
255+
// customBlob is an optional TLV encoded blob of data representing
256+
// custom channel funding information.
257+
customBlob tlv.OptionalRecordT[tlv.TlvType6, tlv.Blob]
254258
}
255259

256260
// encode serializes the chanAuxData to the given io.Writer.
@@ -269,6 +273,9 @@ func (c *chanAuxData) encode(w io.Writer) error {
269273
tlvRecords = append(tlvRecords, root.Record())
270274
},
271275
)
276+
c.customBlob.WhenSome(func(blob tlv.RecordT[tlv.TlvType6, tlv.Blob]) {
277+
tlvRecords = append(tlvRecords, blob.Record())
278+
})
272279

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

287295
// Create the tlv stream.
288296
tlvStream, err := tlv.NewStream(
@@ -292,6 +300,7 @@ func (c *chanAuxData) decode(r io.Reader) error {
292300
c.realScid.Record(),
293301
memo.Record(),
294302
tapscriptRoot.Record(),
303+
blob.Record(),
295304
)
296305
if err != nil {
297306
return err
@@ -308,6 +317,9 @@ func (c *chanAuxData) decode(r io.Reader) error {
308317
if _, ok := tlvs[tapscriptRoot.TlvType()]; ok {
309318
c.tapscriptRoot = tlv.SomeRecordT(tapscriptRoot)
310319
}
320+
if _, ok := tlvs[c.customBlob.TlvType()]; ok {
321+
c.customBlob = tlv.SomeRecordT(blob)
322+
}
311323

312324
return nil
313325
}
@@ -325,6 +337,9 @@ func (c *chanAuxData) toOpenChan(o *OpenChannel) {
325337
c.tapscriptRoot.WhenSomeV(func(h [32]byte) {
326338
o.TapscriptRoot = fn.Some[chainhash.Hash](h)
327339
})
340+
c.customBlob.WhenSomeV(func(blob tlv.Blob) {
341+
o.CustomBlob = fn.Some(blob)
342+
})
328343
}
329344

330345
// newChanAuxDataFromChan creates a new chanAuxData from the given channel.
@@ -354,6 +369,11 @@ func newChanAuxDataFromChan(openChan *OpenChannel) *chanAuxData {
354369
tlv.NewPrimitiveRecord[tlv.TlvType5, [32]byte](h),
355370
)
356371
})
372+
openChan.CustomBlob.WhenSome(func(blob tlv.Blob) {
373+
c.customBlob = tlv.SomeRecordT(
374+
tlv.NewPrimitiveRecord[tlv.TlvType6](blob),
375+
)
376+
})
357377

358378
return c
359379
}
@@ -607,6 +627,74 @@ type ChannelConfig struct {
607627
HtlcBasePoint keychain.KeyDescriptor
608628
}
609629

630+
// commitAuxData stores all the optional data that may be store as a TLV stream
631+
// at the _end_ of the normal serialized commit on disk.
632+
type commitAuxData struct {
633+
// customBlob is a custom blob that may store extra data for custom
634+
// channels.
635+
customBlob tlv.OptionalRecordT[tlv.TlvType1, tlv.Blob]
636+
}
637+
638+
// encode encodes the aux data into the passed io.Writer.
639+
func (c *commitAuxData) encode(w io.Writer) error {
640+
var tlvRecords []tlv.Record
641+
c.customBlob.WhenSome(func(blob tlv.RecordT[tlv.TlvType1, tlv.Blob]) {
642+
tlvRecords = append(tlvRecords, blob.Record())
643+
})
644+
645+
// Create the tlv stream.
646+
tlvStream, err := tlv.NewStream(tlvRecords...)
647+
if err != nil {
648+
return err
649+
}
650+
651+
return tlvStream.Encode(w)
652+
}
653+
654+
// decode attempts to ecode the aux data from the passed io.Reader.
655+
func (c *commitAuxData) decode(r io.Reader) error {
656+
blob := c.customBlob.Zero()
657+
658+
tlvStream, err := tlv.NewStream(
659+
blob.Record(),
660+
)
661+
if err != nil {
662+
return err
663+
}
664+
665+
tlvs, err := tlvStream.DecodeWithParsedTypes(r)
666+
if err != nil {
667+
return err
668+
}
669+
670+
if _, ok := tlvs[c.customBlob.TlvType()]; ok {
671+
c.customBlob = tlv.SomeRecordT(blob)
672+
}
673+
674+
return nil
675+
}
676+
677+
// toChanCommit extracts the optional data stored in the commitAuxData struct
678+
// and stores it in the ChannelCommitment.
679+
func (c *commitAuxData) toChanCommit(commit *ChannelCommitment) {
680+
c.customBlob.WhenSomeV(func(blob tlv.Blob) {
681+
commit.CustomBlob = fn.Some(blob)
682+
})
683+
}
684+
685+
// newCommitAuxData creates an aux data struct from the normal chan commitment.
686+
func newCommitAuxData(commit *ChannelCommitment) commitAuxData {
687+
var c commitAuxData
688+
689+
commit.CustomBlob.WhenSome(func(blob tlv.Blob) {
690+
c.customBlob = tlv.SomeRecordT(
691+
tlv.NewPrimitiveRecord[tlv.TlvType1](blob),
692+
)
693+
})
694+
695+
return c
696+
}
697+
610698
// ChannelCommitment is a snapshot of the commitment state at a particular
611699
// point in the commitment chain. With each state transition, a snapshot of the
612700
// current state along with all non-settled HTLCs are recorded. These snapshots
@@ -673,6 +761,11 @@ type ChannelCommitment struct {
673761
// able by us.
674762
CommitTx *wire.MsgTx
675763

764+
// CustomBlob is an optional blob that can be used to store information
765+
// specific to a custom channel type. This may track soem custom
766+
// specific state for this given commitment.
767+
CustomBlob fn.Option[tlv.Blob]
768+
676769
// CommitSig is one half of the signature required to fully complete
677770
// the script for the commitment transaction above. This is the
678771
// signature signed by the remote party for our version of the
@@ -682,9 +775,6 @@ type ChannelCommitment struct {
682775
// Htlcs is the set of HTLC's that are pending at this particular
683776
// commitment height.
684777
Htlcs []HTLC
685-
686-
// TODO(roasbeef): pending commit pointer?
687-
// * lets just walk through
688778
}
689779

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

1075+
// CustomBlob is an optional blob that can be used to store information
1076+
// specific to a custom channel type. This information is only created
1077+
// at channel funding time, and after wards is to be considered
1078+
// immutable.
1079+
CustomBlob fn.Option[tlv.Blob]
1080+
9851081
// TODO(roasbeef): eww
9861082
Db *ChannelStateDB
9871083

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

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

@@ -2853,6 +2959,18 @@ func deserializeCommitDiff(r io.Reader) (*CommitDiff, error) {
28532959
}
28542960
}
28552961

2962+
// As a final step, we'll read out any aux commit data that we have at
2963+
// the end of this byte stream. We do this here to ensure backward
2964+
// compatibility, as otherwise we risk erroneously reading into the
2965+
// wrong field.
2966+
var auxData commitAuxData
2967+
if err := auxData.decode(r); err != nil {
2968+
return nil, fmt.Errorf("unable to decode aux data: %w", err)
2969+
2970+
}
2971+
2972+
auxData.toChanCommit(&d.Commitment)
2973+
28562974
return &d, nil
28572975
}
28582976

@@ -3831,6 +3949,9 @@ func (c *OpenChannel) Snapshot() *ChannelSnapshot {
38313949
},
38323950
}
38333951

3952+
// TODO(roasbeef): fill in other info for the commitment above
3953+
// * also custom blob
3954+
38343955
// Copy over the current set of HTLCs to ensure the caller can't mutate
38353956
// our internal state.
38363957
snapshot.Htlcs = make([]HTLC, len(localCommit.Htlcs))
@@ -4222,6 +4343,12 @@ func putChanCommitment(chanBucket kvdb.RwBucket, c *ChannelCommitment,
42224343
return err
42234344
}
42244345

4346+
// Before we write to disk, we'll also write our aux data as well.
4347+
auxData := newCommitAuxData(c)
4348+
if err := auxData.encode(&b); err != nil {
4349+
return fmt.Errorf("unable to write aux data: %w", err)
4350+
}
4351+
42254352
return chanBucket.Put(commitKey, b.Bytes())
42264353
}
42274354

@@ -4367,7 +4494,9 @@ func deserializeChanCommit(r io.Reader) (ChannelCommitment, error) {
43674494
return c, nil
43684495
}
43694496

4370-
func fetchChanCommitment(chanBucket kvdb.RBucket, local bool) (ChannelCommitment, error) {
4497+
func fetchChanCommitment(chanBucket kvdb.RBucket,
4498+
local bool) (ChannelCommitment, error) {
4499+
43714500
var commitKey []byte
43724501
if local {
43734502
commitKey = append(chanCommitmentKey, byte(0x00))
@@ -4381,7 +4510,23 @@ func fetchChanCommitment(chanBucket kvdb.RBucket, local bool) (ChannelCommitment
43814510
}
43824511

43834512
r := bytes.NewReader(commitBytes)
4384-
return deserializeChanCommit(r)
4513+
chanCommit, err := deserializeChanCommit(r)
4514+
if err != nil {
4515+
return ChannelCommitment{}, fmt.Errorf("unable to decode "+
4516+
"chan commit: %w", err)
4517+
}
4518+
4519+
// We'll also check to see if we have any aux data stored as the end of
4520+
// the stream.
4521+
var auxData commitAuxData
4522+
if err := auxData.decode(r); err != nil {
4523+
return ChannelCommitment{}, fmt.Errorf("unable to decode "+
4524+
"chan aux data: %w", err)
4525+
}
4526+
4527+
auxData.toChanCommit(&chanCommit)
4528+
4529+
return chanCommit, nil
43854530
}
43864531

43874532
func fetchChanCommitments(chanBucket kvdb.RBucket, channel *OpenChannel) error {

channeldb/channel_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,7 @@ func createTestChannelState(t *testing.T, cdb *ChannelStateDB) *OpenChannel {
337337
FeePerKw: btcutil.Amount(5000),
338338
CommitTx: channels.TestFundingTx,
339339
CommitSig: bytes.Repeat([]byte{1}, 71),
340+
CustomBlob: fn.Some([]byte{1, 2, 3}),
340341
},
341342
RemoteCommitment: ChannelCommitment{
342343
CommitHeight: 0,
@@ -346,6 +347,7 @@ func createTestChannelState(t *testing.T, cdb *ChannelStateDB) *OpenChannel {
346347
FeePerKw: btcutil.Amount(5000),
347348
CommitTx: channels.TestFundingTx,
348349
CommitSig: bytes.Repeat([]byte{1}, 71),
350+
CustomBlob: fn.Some([]byte{4, 5, 6}),
349351
},
350352
NumConfsRequired: 4,
351353
RemoteCurrentRevocation: privKey.PubKey(),
@@ -360,6 +362,7 @@ func createTestChannelState(t *testing.T, cdb *ChannelStateDB) *OpenChannel {
360362
InitialRemoteBalance: lnwire.MilliSatoshi(3000),
361363
Memo: []byte("test"),
362364
TapscriptRoot: fn.Some(tapscriptRoot),
365+
CustomBlob: fn.Some([]byte{1, 2, 3}),
363366
}
364367
}
365368

@@ -649,6 +652,7 @@ func TestChannelStateTransition(t *testing.T) {
649652
CommitTx: newTx,
650653
CommitSig: newSig,
651654
Htlcs: htlcs,
655+
CustomBlob: fn.Some([]byte{4, 5, 6}),
652656
}
653657

654658
// First update the local node's broadcastable state and also add a
@@ -686,9 +690,12 @@ func TestChannelStateTransition(t *testing.T) {
686690
// have been updated.
687691
updatedChannel, err := cdb.FetchOpenChannels(channel.IdentityPub)
688692
require.NoError(t, err, "unable to fetch updated channel")
693+
689694
assertCommitmentEqual(t, &commitment, &updatedChannel[0].LocalCommitment)
695+
690696
numDiskUpdates, err := updatedChannel[0].CommitmentHeight()
691697
require.NoError(t, err, "unable to read commitment height from disk")
698+
692699
if numDiskUpdates != uint64(commitment.CommitHeight) {
693700
t.Fatalf("num disk updates doesn't match: %v vs %v",
694701
numDiskUpdates, commitment.CommitHeight)

0 commit comments

Comments
 (0)