Skip to content

Commit d5f595e

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 0bf2050 commit d5f595e

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
@@ -164,22 +164,32 @@ type HTLCEntry struct {
164164
//
165165
// NOTE: this field is the memory representation of the field amtUint.
166166
Amt tlv.RecordT[tlv.TlvType4, tlv.BigSizeT[btcutil.Amount]]
167+
168+
// CustomBlob is an optional blob that can be used to store information
169+
// specific to revocation handling for a custom channel type.
170+
CustomBlob tlv.OptionalRecordT[tlv.TlvType5, tlv.Blob]
167171
}
168172

169173
// toTlvStream converts an HTLCEntry record into a tlv representation.
170174
func (h *HTLCEntry) toTlvStream() (*tlv.Stream, error) {
171-
return tlv.NewStream(
175+
records := []tlv.Record{
172176
h.RHash.Record(),
173177
h.RefundTimeout.Record(),
174178
h.OutputIndex.Record(),
175179
h.Incoming.Record(),
176180
h.Amt.Record(),
177-
)
181+
}
182+
183+
h.CustomBlob.WhenSome(func(r tlv.RecordT[tlv.TlvType5, tlv.Blob]) {
184+
records = append(records, r.Record())
185+
})
186+
187+
return tlv.NewStream(records...)
178188
}
179189

180190
// NewHTLCEntryFromHTLC creates a new HTLCEntry from an HTLC.
181191
func NewHTLCEntryFromHTLC(htlc HTLC) *HTLCEntry {
182-
return &HTLCEntry{
192+
h := &HTLCEntry{
183193
RHash: tlv.NewRecordT[tlv.TlvType0](
184194
NewSparsePayHash(htlc.RHash),
185195
),
@@ -194,6 +204,16 @@ func NewHTLCEntryFromHTLC(htlc HTLC) *HTLCEntry {
194204
tlv.NewBigSizeT(htlc.Amt.ToSatoshis()),
195205
),
196206
}
207+
208+
if len(htlc.ExtraData) != 0 {
209+
h.CustomBlob = tlv.SomeRecordT(
210+
tlv.NewPrimitiveRecord[tlv.TlvType5, tlv.Blob](
211+
htlc.ExtraData,
212+
),
213+
)
214+
}
215+
216+
return h
197217
}
198218

199219
// RevocationLog stores the info needed to construct a breach retribution. Its
@@ -236,13 +256,19 @@ type RevocationLog struct {
236256
// this field, it could be the case that the field is not present for
237257
// all revocation logs.
238258
TheirBalance tlv.OptionalRecordT[tlv.TlvType4, BigSizeMilliSatoshi]
259+
260+
// CustomBlob is an optional blob that can be used to store information
261+
// specific to a custom channel type. This information is only created
262+
// at channel funding time, and after wards is to be considered
263+
// immutable.
264+
CustomBlob tlv.OptionalRecordT[tlv.TlvType5, tlv.Blob]
239265
}
240266

241267
// NewRevocationLog creates a new RevocationLog from the given parameters.
242268
func NewRevocationLog(ourOutputIndex uint16, theirOutputIndex uint16,
243269
commitHash [32]byte, ourBalance,
244-
theirBalance fn.Option[lnwire.MilliSatoshi],
245-
htlcs []*HTLCEntry) RevocationLog {
270+
theirBalance fn.Option[lnwire.MilliSatoshi], htlcs []*HTLCEntry,
271+
customBlob fn.Option[tlv.Blob]) RevocationLog {
246272

247273
rl := RevocationLog{
248274
OurOutputIndex: tlv.NewPrimitiveRecord[tlv.TlvType0](
@@ -267,6 +293,12 @@ func NewRevocationLog(ourOutputIndex uint16, theirOutputIndex uint16,
267293
))
268294
})
269295

296+
customBlob.WhenSome(func(blob tlv.Blob) {
297+
rl.CustomBlob = tlv.SomeRecordT(
298+
tlv.NewPrimitiveRecord[tlv.TlvType5, tlv.Blob](blob),
299+
)
300+
})
301+
270302
return rl
271303
}
272304

@@ -298,6 +330,12 @@ func putRevocationLog(bucket kvdb.RwBucket, commit *ChannelCommitment,
298330
HTLCEntries: make([]*HTLCEntry, 0, len(commit.Htlcs)),
299331
}
300332

333+
commit.CustomBlob.WhenSome(func(blob tlv.Blob) {
334+
rl.CustomBlob = tlv.SomeRecordT(
335+
tlv.NewPrimitiveRecord[tlv.TlvType5, tlv.Blob](blob),
336+
)
337+
})
338+
301339
if !noAmtData {
302340
rl.OurBalance = tlv.SomeRecordT(tlv.NewRecordT[tlv.TlvType3](
303341
tlv.NewBigSizeT(commit.LocalBalance),
@@ -373,6 +411,10 @@ func serializeRevocationLog(w io.Writer, rl *RevocationLog) error {
373411
},
374412
)
375413

414+
rl.CustomBlob.WhenSome(func(r tlv.RecordT[tlv.TlvType5, tlv.Blob]) {
415+
records = append(records, r.Record())
416+
})
417+
376418
// Create the tlv stream.
377419
tlvStream, err := tlv.NewStream(records...)
378420
if err != nil {
@@ -413,6 +455,7 @@ func deserializeRevocationLog(r io.Reader) (RevocationLog, error) {
413455

414456
ourBalance := rl.OurBalance.Zero()
415457
theirBalance := rl.TheirBalance.Zero()
458+
customBlob := rl.CustomBlob.Zero()
416459

417460
// Create the tlv stream.
418461
tlvStream, err := tlv.NewStream(
@@ -421,6 +464,7 @@ func deserializeRevocationLog(r io.Reader) (RevocationLog, error) {
421464
rl.CommitTxHash.Record(),
422465
ourBalance.Record(),
423466
theirBalance.Record(),
467+
customBlob.Record(),
424468
)
425469
if err != nil {
426470
return rl, err
@@ -440,6 +484,10 @@ func deserializeRevocationLog(r io.Reader) (RevocationLog, error) {
440484
rl.TheirBalance = tlv.SomeRecordT(theirBalance)
441485
}
442486

487+
if t, ok := parsedTypes[customBlob.TlvType()]; ok && t == nil {
488+
rl.CustomBlob = tlv.SomeRecordT(customBlob)
489+
}
490+
443491
// Read the HTLC entries.
444492
rl.HTLCEntries, err = deserializeHTLCEntries(r)
445493

@@ -454,21 +502,37 @@ func deserializeHTLCEntries(r io.Reader) ([]*HTLCEntry, error) {
454502
for {
455503
var htlc HTLCEntry
456504

505+
customBlob := htlc.CustomBlob.Zero()
506+
457507
// Create the tlv stream.
458-
tlvStream, err := htlc.toTlvStream()
508+
records := []tlv.Record{
509+
htlc.RHash.Record(),
510+
htlc.RefundTimeout.Record(),
511+
htlc.OutputIndex.Record(),
512+
htlc.Incoming.Record(),
513+
htlc.Amt.Record(),
514+
customBlob.Record(),
515+
}
516+
517+
tlvStream, err := tlv.NewStream(records...)
459518
if err != nil {
460519
return nil, err
461520
}
462521

463522
// Read the HTLC entry.
464-
if _, err := readTlvStream(r, tlvStream); err != nil {
523+
parsedTypes, err := readTlvStream(r, tlvStream)
524+
if err != nil {
465525
// We've reached the end when hitting an EOF.
466526
if err == io.ErrUnexpectedEOF {
467527
break
468528
}
469529
return nil, err
470530
}
471531

532+
if t, ok := parsedTypes[customBlob.TlvType()]; ok && t == nil {
533+
htlc.CustomBlob = tlv.SomeRecordT(customBlob)
534+
}
535+
472536
// Append the entry.
473537
htlcs = append(htlcs, &htlc)
474538
}

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
testHTLCEntryHash = HTLCEntry{
@@ -113,17 +122,19 @@ var (
113122
Amt: lnwire.NewMSatFromSatoshis(
114123
testHTLCEntry.Amt.Val.Int(),
115124
),
125+
ExtraData: blobBytes,
116126
}},
127+
CustomBlob: fn.Some(blobBytes),
117128
}
118129

119130
testRevocationLogNoAmts = NewRevocationLog(
120131
0, 1, testChannelCommit.CommitTx.TxHash(),
121132
fn.None[lnwire.MilliSatoshi](), fn.None[lnwire.MilliSatoshi](),
122-
[]*HTLCEntry{&testHTLCEntry},
133+
[]*HTLCEntry{&testHTLCEntry}, fn.Some(blobBytes),
123134
)
124135
testRevocationLogNoAmtsBytes = []byte{
125-
// Body length 42.
126-
0x2a,
136+
// Body length 48.
137+
0x30,
127138
// OurOutputIndex tlv.
128139
0x0, 0x2, 0x0, 0x0,
129140
// TheirOutputIndex tlv.
@@ -134,16 +145,18 @@ var (
134145
0x6e, 0x60, 0x29, 0x23, 0x1d, 0x5e, 0xc5, 0xe6,
135146
0xbd, 0xf7, 0xd3, 0x9b, 0x16, 0x7d, 0x0, 0xff,
136147
0xc8, 0x22, 0x51, 0xb1, 0x5b, 0xa0, 0xbf, 0xd,
148+
// Custom blob tlv.
149+
0x5, 0x4, 0x1, 0x2, 0x3, 0x4,
137150
}
138151

139152
testRevocationLogWithAmts = NewRevocationLog(
140153
0, 1, testChannelCommit.CommitTx.TxHash(),
141154
fn.Some(localBalance), fn.Some(remoteBalance),
142-
[]*HTLCEntry{&testHTLCEntry},
155+
[]*HTLCEntry{&testHTLCEntry}, fn.Some(blobBytes),
143156
)
144157
testRevocationLogWithAmtsBytes = []byte{
145-
// Body length 52.
146-
0x34,
158+
// Body length 58.
159+
0x3a,
147160
// OurOutputIndex tlv.
148161
0x0, 0x2, 0x0, 0x0,
149162
// TheirOutputIndex tlv.
@@ -158,6 +171,8 @@ var (
158171
0x3, 0x3, 0xfd, 0x23, 0x28,
159172
// Remote Balance.
160173
0x4, 0x3, 0xfd, 0x0b, 0xb8,
174+
// Custom blob tlv.
175+
0x5, 0x4, 0x1, 0x2, 0x3, 0x4,
161176
}
162177
)
163178

@@ -269,7 +284,7 @@ func TestSerializeHTLCEntries(t *testing.T) {
269284
partialBytes := testHTLCEntryBytes[3:]
270285

271286
// Write the total length and RHash tlv.
272-
expectedBytes := []byte{0x36, 0x0, 0x20}
287+
expectedBytes := []byte{0x3c, 0x0, 0x20}
273288
expectedBytes = append(expectedBytes, rHashBytes...)
274289

275290
// Append the rest.
@@ -384,7 +399,7 @@ func TestDeserializeHTLCEntries(t *testing.T) {
384399
partialBytes := testHTLCEntryBytes[3:]
385400

386401
// Write the total length and RHash tlv.
387-
testBytes := append([]byte{0x36, 0x0, 0x20}, rHashBytes...)
402+
testBytes := append([]byte{0x3c, 0x0, 0x20}, rHashBytes...)
388403

389404
// Append the rest.
390405
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)