Skip to content

Commit ceaab9f

Browse files
Roasbeefguggero
authored andcommitted
channeldb: add custom blobs to RevocationLog+HTLCEntry
This'll be useful for custom channel types that want to store extra information that'll be useful to help handle channel revocation cases.
1 parent 9d1926b commit ceaab9f

File tree

3 files changed

+97
-18
lines changed

3 files changed

+97
-18
lines changed

channeldb/revocation_log.go

Lines changed: 71 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -172,22 +172,32 @@ type HTLCEntry struct {
172172
//
173173
// NOTE: this field is the memory representation of the field amtUint.
174174
Amt tlv.RecordT[tlv.TlvType4, tlv.BigSizeT[btcutil.Amount]]
175+
176+
// CustomBlob is an optional blob that can be used to store information
177+
// specific to revocation handling for a custom channel type.
178+
CustomBlob tlv.OptionalRecordT[tlv.TlvType5, tlv.Blob]
175179
}
176180

177181
// toTlvStream converts an HTLCEntry record into a tlv representation.
178182
func (h *HTLCEntry) toTlvStream() (*tlv.Stream, error) {
179-
return tlv.NewStream(
183+
records := []tlv.Record{
180184
h.RHash.Record(),
181185
h.RefundTimeout.Record(),
182186
h.OutputIndex.Record(),
183187
h.Incoming.Record(),
184188
h.Amt.Record(),
185-
)
189+
}
190+
191+
h.CustomBlob.WhenSome(func(r tlv.RecordT[tlv.TlvType5, tlv.Blob]) {
192+
records = append(records, r.Record())
193+
})
194+
195+
return tlv.NewStream(records...)
186196
}
187197

188198
// NewHTLCEntryFromHTLC creates a new HTLCEntry from an HTLC.
189199
func NewHTLCEntryFromHTLC(htlc HTLC) *HTLCEntry {
190-
return &HTLCEntry{
200+
h := &HTLCEntry{
191201
RHash: tlv.NewRecordT[tlv.TlvType0](
192202
NewSparsePayHash(htlc.RHash),
193203
),
@@ -202,6 +212,16 @@ func NewHTLCEntryFromHTLC(htlc HTLC) *HTLCEntry {
202212
tlv.NewBigSizeT(htlc.Amt.ToSatoshis()),
203213
),
204214
}
215+
216+
if len(htlc.ExtraData) != 0 {
217+
h.CustomBlob = tlv.SomeRecordT(
218+
tlv.NewPrimitiveRecord[tlv.TlvType5, tlv.Blob](
219+
htlc.ExtraData,
220+
),
221+
)
222+
}
223+
224+
return h
205225
}
206226

207227
// RevocationLog stores the info needed to construct a breach retribution. Its
@@ -244,13 +264,19 @@ type RevocationLog struct {
244264
// this field, it could be the case that the field is not present for
245265
// all revocation logs.
246266
TheirBalance tlv.OptionalRecordT[tlv.TlvType4, BigSizeMilliSatoshi]
267+
268+
// CustomBlob is an optional blob that can be used to store information
269+
// specific to a custom channel type. This information is only created
270+
// at channel funding time, and after wards is to be considered
271+
// immutable.
272+
CustomBlob tlv.OptionalRecordT[tlv.TlvType5, tlv.Blob]
247273
}
248274

249275
// NewRevocationLog creates a new RevocationLog from the given parameters.
250276
func NewRevocationLog(ourOutputIndex uint16, theirOutputIndex uint16,
251277
commitHash [32]byte, ourBalance,
252-
theirBalance fn.Option[lnwire.MilliSatoshi],
253-
htlcs []*HTLCEntry) RevocationLog {
278+
theirBalance fn.Option[lnwire.MilliSatoshi], htlcs []*HTLCEntry,
279+
customBlob fn.Option[tlv.Blob]) RevocationLog {
254280

255281
rl := RevocationLog{
256282
OurOutputIndex: tlv.NewPrimitiveRecord[tlv.TlvType0](
@@ -275,6 +301,12 @@ func NewRevocationLog(ourOutputIndex uint16, theirOutputIndex uint16,
275301
))
276302
})
277303

304+
customBlob.WhenSome(func(blob tlv.Blob) {
305+
rl.CustomBlob = tlv.SomeRecordT(
306+
tlv.NewPrimitiveRecord[tlv.TlvType5, tlv.Blob](blob),
307+
)
308+
})
309+
278310
return rl
279311
}
280312

@@ -306,6 +338,12 @@ func putRevocationLog(bucket kvdb.RwBucket, commit *ChannelCommitment,
306338
HTLCEntries: make([]*HTLCEntry, 0, len(commit.Htlcs)),
307339
}
308340

341+
commit.CustomBlob.WhenSome(func(blob tlv.Blob) {
342+
rl.CustomBlob = tlv.SomeRecordT(
343+
tlv.NewPrimitiveRecord[tlv.TlvType5, tlv.Blob](blob),
344+
)
345+
})
346+
309347
if !noAmtData {
310348
rl.OurBalance = tlv.SomeRecordT(tlv.NewRecordT[tlv.TlvType3](
311349
tlv.NewBigSizeT(commit.LocalBalance),
@@ -381,6 +419,10 @@ func serializeRevocationLog(w io.Writer, rl *RevocationLog) error {
381419
},
382420
)
383421

422+
rl.CustomBlob.WhenSome(func(r tlv.RecordT[tlv.TlvType5, tlv.Blob]) {
423+
records = append(records, r.Record())
424+
})
425+
384426
// Create the tlv stream.
385427
tlvStream, err := tlv.NewStream(records...)
386428
if err != nil {
@@ -421,6 +463,7 @@ func deserializeRevocationLog(r io.Reader) (RevocationLog, error) {
421463

422464
ourBalance := rl.OurBalance.Zero()
423465
theirBalance := rl.TheirBalance.Zero()
466+
customBlob := rl.CustomBlob.Zero()
424467

425468
// Create the tlv stream.
426469
tlvStream, err := tlv.NewStream(
@@ -429,6 +472,7 @@ func deserializeRevocationLog(r io.Reader) (RevocationLog, error) {
429472
rl.CommitTxHash.Record(),
430473
ourBalance.Record(),
431474
theirBalance.Record(),
475+
customBlob.Record(),
432476
)
433477
if err != nil {
434478
return rl, err
@@ -448,6 +492,10 @@ func deserializeRevocationLog(r io.Reader) (RevocationLog, error) {
448492
rl.TheirBalance = tlv.SomeRecordT(theirBalance)
449493
}
450494

495+
if t, ok := parsedTypes[customBlob.TlvType()]; ok && t == nil {
496+
rl.CustomBlob = tlv.SomeRecordT(customBlob)
497+
}
498+
451499
// Read the HTLC entries.
452500
rl.HTLCEntries, err = deserializeHTLCEntries(r)
453501

@@ -462,21 +510,37 @@ func deserializeHTLCEntries(r io.Reader) ([]*HTLCEntry, error) {
462510
for {
463511
var htlc HTLCEntry
464512

513+
customBlob := htlc.CustomBlob.Zero()
514+
465515
// Create the tlv stream.
466-
tlvStream, err := htlc.toTlvStream()
516+
records := []tlv.Record{
517+
htlc.RHash.Record(),
518+
htlc.RefundTimeout.Record(),
519+
htlc.OutputIndex.Record(),
520+
htlc.Incoming.Record(),
521+
htlc.Amt.Record(),
522+
customBlob.Record(),
523+
}
524+
525+
tlvStream, err := tlv.NewStream(records...)
467526
if err != nil {
468527
return nil, err
469528
}
470529

471530
// Read the HTLC entry.
472-
if _, err := readTlvStream(r, tlvStream); err != nil {
531+
parsedTypes, err := readTlvStream(r, tlvStream)
532+
if err != nil {
473533
// We've reached the end when hitting an EOF.
474534
if err == io.ErrUnexpectedEOF {
475535
break
476536
}
477537
return nil, err
478538
}
479539

540+
if t, ok := parsedTypes[customBlob.TlvType()]; ok && t == nil {
541+
htlc.CustomBlob = tlv.SomeRecordT(customBlob)
542+
}
543+
480544
// Append the entry.
481545
htlcs = append(htlcs, &htlc)
482546
}

channeldb/revocation_log_test.go

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ var (
3434
0xff, // value = 255
3535
}
3636

37+
blobBytes = tlv.Blob{
38+
0x01, 0x02, 0x03, 0x04,
39+
}
40+
3741
testHTLCEntry = HTLCEntry{
3842
RefundTimeout: tlv.NewPrimitiveRecord[tlv.TlvType1, uint32](
3943
740_000,
@@ -45,10 +49,13 @@ var (
4549
Amt: tlv.NewRecordT[tlv.TlvType4](
4650
tlv.NewBigSizeT(btcutil.Amount(1_000_000)),
4751
),
52+
CustomBlob: tlv.SomeRecordT(
53+
tlv.NewPrimitiveRecord[tlv.TlvType5](blobBytes),
54+
),
4855
}
4956
testHTLCEntryBytes = []byte{
50-
// Body length 23.
51-
0x16,
57+
// Body length 28.
58+
0x1c,
5259
// Rhash tlv.
5360
0x0, 0x0,
5461
// RefundTimeout tlv.
@@ -59,6 +66,8 @@ var (
5966
0x3, 0x1, 0x1,
6067
// Amt tlv.
6168
0x4, 0x5, 0xfe, 0x0, 0xf, 0x42, 0x40,
69+
// Custom blob tlv.
70+
0x5, 0x4, 0x1, 0x2, 0x3, 0x4,
6271
}
6372

6473
localBalance = lnwire.MilliSatoshi(9000)
@@ -79,17 +88,19 @@ var (
7988
Amt: lnwire.NewMSatFromSatoshis(
8089
testHTLCEntry.Amt.Val.Int(),
8190
),
91+
ExtraData: blobBytes,
8292
}},
93+
CustomBlob: fn.Some(blobBytes),
8394
}
8495

8596
testRevocationLogNoAmts = NewRevocationLog(
8697
0, 1, testChannelCommit.CommitTx.TxHash(),
8798
fn.None[lnwire.MilliSatoshi](), fn.None[lnwire.MilliSatoshi](),
88-
[]*HTLCEntry{&testHTLCEntry},
99+
[]*HTLCEntry{&testHTLCEntry}, fn.Some(blobBytes),
89100
)
90101
testRevocationLogNoAmtsBytes = []byte{
91-
// Body length 42.
92-
0x2a,
102+
// Body length 48.
103+
0x30,
93104
// OurOutputIndex tlv.
94105
0x0, 0x2, 0x0, 0x0,
95106
// TheirOutputIndex tlv.
@@ -100,16 +111,18 @@ var (
100111
0x6e, 0x60, 0x29, 0x23, 0x1d, 0x5e, 0xc5, 0xe6,
101112
0xbd, 0xf7, 0xd3, 0x9b, 0x16, 0x7d, 0x0, 0xff,
102113
0xc8, 0x22, 0x51, 0xb1, 0x5b, 0xa0, 0xbf, 0xd,
114+
// Custom blob tlv.
115+
0x5, 0x4, 0x1, 0x2, 0x3, 0x4,
103116
}
104117

105118
testRevocationLogWithAmts = NewRevocationLog(
106119
0, 1, testChannelCommit.CommitTx.TxHash(),
107120
fn.Some(localBalance), fn.Some(remoteBalance),
108-
[]*HTLCEntry{&testHTLCEntry},
121+
[]*HTLCEntry{&testHTLCEntry}, fn.Some(blobBytes),
109122
)
110123
testRevocationLogWithAmtsBytes = []byte{
111-
// Body length 52.
112-
0x34,
124+
// Body length 58.
125+
0x3a,
113126
// OurOutputIndex tlv.
114127
0x0, 0x2, 0x0, 0x0,
115128
// TheirOutputIndex tlv.
@@ -124,6 +137,8 @@ var (
124137
0x3, 0x3, 0xfd, 0x23, 0x28,
125138
// Remote Balance.
126139
0x4, 0x3, 0xfd, 0x0b, 0xb8,
140+
// Custom blob tlv.
141+
0x5, 0x4, 0x1, 0x2, 0x3, 0x4,
127142
}
128143
)
129144

@@ -220,7 +235,7 @@ func TestSerializeHTLCEntries(t *testing.T) {
220235
partialBytes := testHTLCEntryBytes[3:]
221236

222237
// Write the total length and RHash tlv.
223-
expectedBytes := []byte{0x36, 0x0, 0x20}
238+
expectedBytes := []byte{0x3c, 0x0, 0x20}
224239
expectedBytes = append(expectedBytes, rHashBytes...)
225240

226241
// Append the rest.
@@ -335,7 +350,7 @@ func TestDerializeHTLCEntries(t *testing.T) {
335350
partialBytes := testHTLCEntryBytes[3:]
336351

337352
// Write the total length and RHash tlv.
338-
testBytes := append([]byte{0x36, 0x0, 0x20}, rHashBytes...)
353+
testBytes := append([]byte{0x3c, 0x0, 0x20}, rHashBytes...)
339354

340355
// Append the rest.
341356
testBytes = append(testBytes, partialBytes...)

lnwallet/channel_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10025,7 +10025,7 @@ func TestCreateBreachRetribution(t *testing.T) {
1002510025
revokedLog := channeldb.NewRevocationLog(
1002610026
uint16(localIndex), uint16(remoteIndex), commitHash,
1002710027
fn.Some(ourAmtMsat), fn.Some(theirAmtMsat),
10028-
[]*channeldb.HTLCEntry{htlc},
10028+
[]*channeldb.HTLCEntry{htlc}, fn.None[tlv.Blob](),
1002910029
)
1003010030

1003110031
// Create a log with an empty local output index.

0 commit comments

Comments
 (0)