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.
This commit is contained in:
1 parent
61f276856a
commit
669740c84e
4 files changed
+120
-21
No files matched your search
@@ -164,22 +164,32 @@ type HTLCEntry struct {
|
|||||||
//
|
//
|
||||||
// NOTE: this field is the memory representation of the field amtUint.
|
// NOTE: this field is the memory representation of the field amtUint.
|
||||||
Amt tlv.RecordT[tlv.TlvType4, tlv.BigSizeT[btcutil.Amount]]
|
Amt tlv.RecordT[tlv.TlvType4, tlv.BigSizeT[btcutil.Amount]]
|
||||||
|
|
||||||
|
// CustomBlob is an optional blob that can be used to store information
|
||||||
|
// specific to revocation handling for a custom channel type.
|
||||||
|
CustomBlob tlv.OptionalRecordT[tlv.TlvType5, tlv.Blob]
|
||||||
}
|
}
|
||||||
|
|
||||||
// toTlvStream converts an HTLCEntry record into a tlv representation.
|
// toTlvStream converts an HTLCEntry record into a tlv representation.
|
||||||
func (h *HTLCEntry) toTlvStream() (*tlv.Stream, error) {
|
func (h *HTLCEntry) toTlvStream() (*tlv.Stream, error) {
|
||||||
return tlv.NewStream(
|
records := []tlv.Record{
|
||||||
h.RHash.Record(),
|
h.RHash.Record(),
|
||||||
h.RefundTimeout.Record(),
|
h.RefundTimeout.Record(),
|
||||||
h.OutputIndex.Record(),
|
h.OutputIndex.Record(),
|
||||||
h.Incoming.Record(),
|
h.Incoming.Record(),
|
||||||
h.Amt.Record(),
|
h.Amt.Record(),
|
||||||
)
|
}
|
||||||
|
|
||||||
|
h.CustomBlob.WhenSome(func(r tlv.RecordT[tlv.TlvType5, tlv.Blob]) {
|
||||||
|
records = append(records, r.Record())
|
||||||
|
})
|
||||||
|
|
||||||
|
return tlv.NewStream(records...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewHTLCEntryFromHTLC creates a new HTLCEntry from an HTLC.
|
// NewHTLCEntryFromHTLC creates a new HTLCEntry from an HTLC.
|
||||||
func NewHTLCEntryFromHTLC(htlc HTLC) *HTLCEntry {
|
func NewHTLCEntryFromHTLC(htlc HTLC) (*HTLCEntry, error) {
|
||||||
return &HTLCEntry{
|
h := &HTLCEntry{
|
||||||
RHash: tlv.NewRecordT[tlv.TlvType0](
|
RHash: tlv.NewRecordT[tlv.TlvType0](
|
||||||
NewSparsePayHash(htlc.RHash),
|
NewSparsePayHash(htlc.RHash),
|
||||||
),
|
),
|
||||||
@@ -194,6 +204,19 @@ func NewHTLCEntryFromHTLC(htlc HTLC) *HTLCEntry {
|
|||||||
tlv.NewBigSizeT(htlc.Amt.ToSatoshis()),
|
tlv.NewBigSizeT(htlc.Amt.ToSatoshis()),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(htlc.CustomRecords) != 0 {
|
||||||
|
blob, err := htlc.CustomRecords.Serialize()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
h.CustomBlob = tlv.SomeRecordT(
|
||||||
|
tlv.NewPrimitiveRecord[tlv.TlvType5, tlv.Blob](blob),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return h, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RevocationLog stores the info needed to construct a breach retribution. Its
|
// RevocationLog stores the info needed to construct a breach retribution. Its
|
||||||
@@ -236,13 +259,19 @@ type RevocationLog struct {
|
|||||||
// this field, it could be the case that the field is not present for
|
// this field, it could be the case that the field is not present for
|
||||||
// all revocation logs.
|
// all revocation logs.
|
||||||
TheirBalance tlv.OptionalRecordT[tlv.TlvType4, BigSizeMilliSatoshi]
|
TheirBalance tlv.OptionalRecordT[tlv.TlvType4, BigSizeMilliSatoshi]
|
||||||
|
|
||||||
|
// 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 tlv.OptionalRecordT[tlv.TlvType5, tlv.Blob]
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRevocationLog creates a new RevocationLog from the given parameters.
|
// NewRevocationLog creates a new RevocationLog from the given parameters.
|
||||||
func NewRevocationLog(ourOutputIndex uint16, theirOutputIndex uint16,
|
func NewRevocationLog(ourOutputIndex uint16, theirOutputIndex uint16,
|
||||||
commitHash [32]byte, ourBalance,
|
commitHash [32]byte, ourBalance,
|
||||||
theirBalance fn.Option[lnwire.MilliSatoshi],
|
theirBalance fn.Option[lnwire.MilliSatoshi], htlcs []*HTLCEntry,
|
||||||
htlcs []*HTLCEntry) RevocationLog {
|
customBlob fn.Option[tlv.Blob]) RevocationLog {
|
||||||
|
|
||||||
rl := RevocationLog{
|
rl := RevocationLog{
|
||||||
OurOutputIndex: tlv.NewPrimitiveRecord[tlv.TlvType0](
|
OurOutputIndex: tlv.NewPrimitiveRecord[tlv.TlvType0](
|
||||||
@@ -267,6 +296,12 @@ func NewRevocationLog(ourOutputIndex uint16, theirOutputIndex uint16,
|
|||||||
))
|
))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
customBlob.WhenSome(func(blob tlv.Blob) {
|
||||||
|
rl.CustomBlob = tlv.SomeRecordT(
|
||||||
|
tlv.NewPrimitiveRecord[tlv.TlvType5, tlv.Blob](blob),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
return rl
|
return rl
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,6 +333,12 @@ func putRevocationLog(bucket kvdb.RwBucket, commit *ChannelCommitment,
|
|||||||
HTLCEntries: make([]*HTLCEntry, 0, len(commit.Htlcs)),
|
HTLCEntries: make([]*HTLCEntry, 0, len(commit.Htlcs)),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
commit.CustomBlob.WhenSome(func(blob tlv.Blob) {
|
||||||
|
rl.CustomBlob = tlv.SomeRecordT(
|
||||||
|
tlv.NewPrimitiveRecord[tlv.TlvType5, tlv.Blob](blob),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
if !noAmtData {
|
if !noAmtData {
|
||||||
rl.OurBalance = tlv.SomeRecordT(tlv.NewRecordT[tlv.TlvType3](
|
rl.OurBalance = tlv.SomeRecordT(tlv.NewRecordT[tlv.TlvType3](
|
||||||
tlv.NewBigSizeT(commit.LocalBalance),
|
tlv.NewBigSizeT(commit.LocalBalance),
|
||||||
@@ -320,7 +361,10 @@ func putRevocationLog(bucket kvdb.RwBucket, commit *ChannelCommitment,
|
|||||||
return ErrOutputIndexTooBig
|
return ErrOutputIndexTooBig
|
||||||
}
|
}
|
||||||
|
|
||||||
entry := NewHTLCEntryFromHTLC(htlc)
|
entry, err := NewHTLCEntryFromHTLC(htlc)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
rl.HTLCEntries = append(rl.HTLCEntries, entry)
|
rl.HTLCEntries = append(rl.HTLCEntries, entry)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -373,6 +417,10 @@ func serializeRevocationLog(w io.Writer, rl *RevocationLog) error {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
rl.CustomBlob.WhenSome(func(r tlv.RecordT[tlv.TlvType5, tlv.Blob]) {
|
||||||
|
records = append(records, r.Record())
|
||||||
|
})
|
||||||
|
|
||||||
// Create the tlv stream.
|
// Create the tlv stream.
|
||||||
tlvStream, err := tlv.NewStream(records...)
|
tlvStream, err := tlv.NewStream(records...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -413,6 +461,7 @@ func deserializeRevocationLog(r io.Reader) (RevocationLog, error) {
|
|||||||
|
|
||||||
ourBalance := rl.OurBalance.Zero()
|
ourBalance := rl.OurBalance.Zero()
|
||||||
theirBalance := rl.TheirBalance.Zero()
|
theirBalance := rl.TheirBalance.Zero()
|
||||||
|
customBlob := rl.CustomBlob.Zero()
|
||||||
|
|
||||||
// Create the tlv stream.
|
// Create the tlv stream.
|
||||||
tlvStream, err := tlv.NewStream(
|
tlvStream, err := tlv.NewStream(
|
||||||
@@ -421,6 +470,7 @@ func deserializeRevocationLog(r io.Reader) (RevocationLog, error) {
|
|||||||
rl.CommitTxHash.Record(),
|
rl.CommitTxHash.Record(),
|
||||||
ourBalance.Record(),
|
ourBalance.Record(),
|
||||||
theirBalance.Record(),
|
theirBalance.Record(),
|
||||||
|
customBlob.Record(),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return rl, err
|
return rl, err
|
||||||
@@ -440,6 +490,10 @@ func deserializeRevocationLog(r io.Reader) (RevocationLog, error) {
|
|||||||
rl.TheirBalance = tlv.SomeRecordT(theirBalance)
|
rl.TheirBalance = tlv.SomeRecordT(theirBalance)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if t, ok := parsedTypes[customBlob.TlvType()]; ok && t == nil {
|
||||||
|
rl.CustomBlob = tlv.SomeRecordT(customBlob)
|
||||||
|
}
|
||||||
|
|
||||||
// Read the HTLC entries.
|
// Read the HTLC entries.
|
||||||
rl.HTLCEntries, err = deserializeHTLCEntries(r)
|
rl.HTLCEntries, err = deserializeHTLCEntries(r)
|
||||||
|
|
||||||
@@ -454,14 +508,26 @@ func deserializeHTLCEntries(r io.Reader) ([]*HTLCEntry, error) {
|
|||||||
for {
|
for {
|
||||||
var htlc HTLCEntry
|
var htlc HTLCEntry
|
||||||
|
|
||||||
|
customBlob := htlc.CustomBlob.Zero()
|
||||||
|
|
||||||
// Create the tlv stream.
|
// Create the tlv stream.
|
||||||
tlvStream, err := htlc.toTlvStream()
|
records := []tlv.Record{
|
||||||
|
htlc.RHash.Record(),
|
||||||
|
htlc.RefundTimeout.Record(),
|
||||||
|
htlc.OutputIndex.Record(),
|
||||||
|
htlc.Incoming.Record(),
|
||||||
|
htlc.Amt.Record(),
|
||||||
|
customBlob.Record(),
|
||||||
|
}
|
||||||
|
|
||||||
|
tlvStream, err := tlv.NewStream(records...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read the HTLC entry.
|
// Read the HTLC entry.
|
||||||
if _, err := readTlvStream(r, tlvStream); err != nil {
|
parsedTypes, err := readTlvStream(r, tlvStream)
|
||||||
|
if err != nil {
|
||||||
// We've reached the end when hitting an EOF.
|
// We've reached the end when hitting an EOF.
|
||||||
if err == io.ErrUnexpectedEOF {
|
if err == io.ErrUnexpectedEOF {
|
||||||
break
|
break
|
||||||
@@ -469,6 +535,10 @@ func deserializeHTLCEntries(r io.Reader) ([]*HTLCEntry, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if t, ok := parsedTypes[customBlob.TlvType()]; ok && t == nil {
|
||||||
|
htlc.CustomBlob = tlv.SomeRecordT(customBlob)
|
||||||
|
}
|
||||||
|
|
||||||
// Append the entry.
|
// Append the entry.
|
||||||
htlcs = append(htlcs, &htlc)
|
htlcs = append(htlcs, &htlc)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,17 @@ var (
|
|||||||
0xff, // value = 255
|
0xff, // value = 255
|
||||||
}
|
}
|
||||||
|
|
||||||
|
customRecords = lnwire.CustomRecords{
|
||||||
|
lnwire.MinCustomRecordsTlvType + 1: []byte("custom data"),
|
||||||
|
}
|
||||||
|
|
||||||
|
blobBytes = []byte{
|
||||||
|
// Corresponds to the encoded version of the above custom
|
||||||
|
// records.
|
||||||
|
0xfe, 0x00, 0x01, 0x00, 0x01, 0x0b, 0x63, 0x75, 0x73, 0x74,
|
||||||
|
0x6f, 0x6d, 0x20, 0x64, 0x61, 0x74, 0x61,
|
||||||
|
}
|
||||||
|
|
||||||
testHTLCEntry = HTLCEntry{
|
testHTLCEntry = HTLCEntry{
|
||||||
RefundTimeout: tlv.NewPrimitiveRecord[tlv.TlvType1, uint32](
|
RefundTimeout: tlv.NewPrimitiveRecord[tlv.TlvType1, uint32](
|
||||||
740_000,
|
740_000,
|
||||||
@@ -45,10 +56,13 @@ var (
|
|||||||
Amt: tlv.NewRecordT[tlv.TlvType4](
|
Amt: tlv.NewRecordT[tlv.TlvType4](
|
||||||
tlv.NewBigSizeT(btcutil.Amount(1_000_000)),
|
tlv.NewBigSizeT(btcutil.Amount(1_000_000)),
|
||||||
),
|
),
|
||||||
|
CustomBlob: tlv.SomeRecordT(
|
||||||
|
tlv.NewPrimitiveRecord[tlv.TlvType5](blobBytes),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
testHTLCEntryBytes = []byte{
|
testHTLCEntryBytes = []byte{
|
||||||
// Body length 23.
|
// Body length 41.
|
||||||
0x16,
|
0x29,
|
||||||
// Rhash tlv.
|
// Rhash tlv.
|
||||||
0x0, 0x0,
|
0x0, 0x0,
|
||||||
// RefundTimeout tlv.
|
// RefundTimeout tlv.
|
||||||
@@ -59,6 +73,9 @@ var (
|
|||||||
0x3, 0x1, 0x1,
|
0x3, 0x1, 0x1,
|
||||||
// Amt tlv.
|
// Amt tlv.
|
||||||
0x4, 0x5, 0xfe, 0x0, 0xf, 0x42, 0x40,
|
0x4, 0x5, 0xfe, 0x0, 0xf, 0x42, 0x40,
|
||||||
|
// Custom blob tlv.
|
||||||
|
0x5, 0x11, 0xfe, 0x00, 0x01, 0x00, 0x01, 0x0b, 0x63, 0x75, 0x73,
|
||||||
|
0x74, 0x6f, 0x6d, 0x20, 0x64, 0x61, 0x74, 0x61,
|
||||||
}
|
}
|
||||||
|
|
||||||
testHTLCEntryHash = HTLCEntry{
|
testHTLCEntryHash = HTLCEntry{
|
||||||
@@ -113,17 +130,19 @@ var (
|
|||||||
Amt: lnwire.NewMSatFromSatoshis(
|
Amt: lnwire.NewMSatFromSatoshis(
|
||||||
testHTLCEntry.Amt.Val.Int(),
|
testHTLCEntry.Amt.Val.Int(),
|
||||||
),
|
),
|
||||||
|
CustomRecords: customRecords,
|
||||||
}},
|
}},
|
||||||
|
CustomBlob: fn.Some(blobBytes),
|
||||||
}
|
}
|
||||||
|
|
||||||
testRevocationLogNoAmts = NewRevocationLog(
|
testRevocationLogNoAmts = NewRevocationLog(
|
||||||
0, 1, testChannelCommit.CommitTx.TxHash(),
|
0, 1, testChannelCommit.CommitTx.TxHash(),
|
||||||
fn.None[lnwire.MilliSatoshi](), fn.None[lnwire.MilliSatoshi](),
|
fn.None[lnwire.MilliSatoshi](), fn.None[lnwire.MilliSatoshi](),
|
||||||
[]*HTLCEntry{&testHTLCEntry},
|
[]*HTLCEntry{&testHTLCEntry}, fn.Some(blobBytes),
|
||||||
)
|
)
|
||||||
testRevocationLogNoAmtsBytes = []byte{
|
testRevocationLogNoAmtsBytes = []byte{
|
||||||
// Body length 42.
|
// Body length 61.
|
||||||
0x2a,
|
0x3d,
|
||||||
// OurOutputIndex tlv.
|
// OurOutputIndex tlv.
|
||||||
0x0, 0x2, 0x0, 0x0,
|
0x0, 0x2, 0x0, 0x0,
|
||||||
// TheirOutputIndex tlv.
|
// TheirOutputIndex tlv.
|
||||||
@@ -134,16 +153,19 @@ var (
|
|||||||
0x6e, 0x60, 0x29, 0x23, 0x1d, 0x5e, 0xc5, 0xe6,
|
0x6e, 0x60, 0x29, 0x23, 0x1d, 0x5e, 0xc5, 0xe6,
|
||||||
0xbd, 0xf7, 0xd3, 0x9b, 0x16, 0x7d, 0x0, 0xff,
|
0xbd, 0xf7, 0xd3, 0x9b, 0x16, 0x7d, 0x0, 0xff,
|
||||||
0xc8, 0x22, 0x51, 0xb1, 0x5b, 0xa0, 0xbf, 0xd,
|
0xc8, 0x22, 0x51, 0xb1, 0x5b, 0xa0, 0xbf, 0xd,
|
||||||
|
// Custom blob tlv.
|
||||||
|
0x5, 0x11, 0xfe, 0x00, 0x01, 0x00, 0x01, 0x0b, 0x63, 0x75, 0x73,
|
||||||
|
0x74, 0x6f, 0x6d, 0x20, 0x64, 0x61, 0x74, 0x61,
|
||||||
}
|
}
|
||||||
|
|
||||||
testRevocationLogWithAmts = NewRevocationLog(
|
testRevocationLogWithAmts = NewRevocationLog(
|
||||||
0, 1, testChannelCommit.CommitTx.TxHash(),
|
0, 1, testChannelCommit.CommitTx.TxHash(),
|
||||||
fn.Some(localBalance), fn.Some(remoteBalance),
|
fn.Some(localBalance), fn.Some(remoteBalance),
|
||||||
[]*HTLCEntry{&testHTLCEntry},
|
[]*HTLCEntry{&testHTLCEntry}, fn.Some(blobBytes),
|
||||||
)
|
)
|
||||||
testRevocationLogWithAmtsBytes = []byte{
|
testRevocationLogWithAmtsBytes = []byte{
|
||||||
// Body length 52.
|
// Body length 71.
|
||||||
0x34,
|
0x47,
|
||||||
// OurOutputIndex tlv.
|
// OurOutputIndex tlv.
|
||||||
0x0, 0x2, 0x0, 0x0,
|
0x0, 0x2, 0x0, 0x0,
|
||||||
// TheirOutputIndex tlv.
|
// TheirOutputIndex tlv.
|
||||||
@@ -158,6 +180,9 @@ var (
|
|||||||
0x3, 0x3, 0xfd, 0x23, 0x28,
|
0x3, 0x3, 0xfd, 0x23, 0x28,
|
||||||
// Remote Balance.
|
// Remote Balance.
|
||||||
0x4, 0x3, 0xfd, 0x0b, 0xb8,
|
0x4, 0x3, 0xfd, 0x0b, 0xb8,
|
||||||
|
// Custom blob tlv.
|
||||||
|
0x5, 0x11, 0xfe, 0x00, 0x01, 0x00, 0x01, 0x0b, 0x63, 0x75, 0x73,
|
||||||
|
0x74, 0x6f, 0x6d, 0x20, 0x64, 0x61, 0x74, 0x61,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -269,7 +294,7 @@ func TestSerializeHTLCEntries(t *testing.T) {
|
|||||||
partialBytes := testHTLCEntryBytes[3:]
|
partialBytes := testHTLCEntryBytes[3:]
|
||||||
|
|
||||||
// Write the total length and RHash tlv.
|
// Write the total length and RHash tlv.
|
||||||
expectedBytes := []byte{0x36, 0x0, 0x20}
|
expectedBytes := []byte{0x49, 0x0, 0x20}
|
||||||
expectedBytes = append(expectedBytes, rHashBytes...)
|
expectedBytes = append(expectedBytes, rHashBytes...)
|
||||||
|
|
||||||
// Append the rest.
|
// Append the rest.
|
||||||
@@ -384,7 +409,7 @@ func TestDeserializeHTLCEntries(t *testing.T) {
|
|||||||
partialBytes := testHTLCEntryBytes[3:]
|
partialBytes := testHTLCEntryBytes[3:]
|
||||||
|
|
||||||
// Write the total length and RHash tlv.
|
// Write the total length and RHash tlv.
|
||||||
testBytes := append([]byte{0x36, 0x0, 0x20}, rHashBytes...)
|
testBytes := append([]byte{0x4d, 0x0, 0x20}, rHashBytes...)
|
||||||
|
|
||||||
// Append the rest.
|
// Append the rest.
|
||||||
testBytes = append(testBytes, partialBytes...)
|
testBytes = append(testBytes, partialBytes...)
|
||||||
|
|||||||
+5
-1
@@ -2420,7 +2420,11 @@ func createBreachRetributionLegacy(revokedLog *channeldb.ChannelCommitment,
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
entry := channeldb.NewHTLCEntryFromHTLC(htlc)
|
entry, err := channeldb.NewHTLCEntryFromHTLC(htlc)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
hr, err := createHtlcRetribution(
|
hr, err := createHtlcRetribution(
|
||||||
chanState, keyRing, commitHash,
|
chanState, keyRing, commitHash,
|
||||||
commitmentSecret, leaseExpiry, entry,
|
commitmentSecret, leaseExpiry, entry,
|
||||||
|
|||||||
@@ -10031,7 +10031,7 @@ func TestCreateBreachRetribution(t *testing.T) {
|
|||||||
revokedLog := channeldb.NewRevocationLog(
|
revokedLog := channeldb.NewRevocationLog(
|
||||||
uint16(localIndex), uint16(remoteIndex), commitHash,
|
uint16(localIndex), uint16(remoteIndex), commitHash,
|
||||||
fn.Some(ourAmtMsat), fn.Some(theirAmtMsat),
|
fn.Some(ourAmtMsat), fn.Some(theirAmtMsat),
|
||||||
[]*channeldb.HTLCEntry{htlc},
|
[]*channeldb.HTLCEntry{htlc}, fn.None[tlv.Blob](),
|
||||||
)
|
)
|
||||||
|
|
||||||
// Create a log with an empty local output index.
|
// Create a log with an empty local output index.
|
||||||
|
|||||||
Reference in new issue
Block a user