channeldb: add customized encoding for HtlcIndex

This commit is contained in:
yyforyongyu
2025-05-23 22:37:33 +08:00
committed by Olaoluwa Osuntokun
parent 938d78a28b
commit 6947e0a87a
2 changed files with 130 additions and 3 deletions

View File

@@ -2,6 +2,7 @@ package channeldb
import (
"bytes"
"encoding/binary"
"errors"
"io"
"math"
@@ -512,13 +513,22 @@ func deserializeRevocationLog(r io.Reader) (RevocationLog, error) {
// deserializeHTLCEntries deserializes a list of HTLC entries based on tlv
// format.
func deserializeHTLCEntries(r io.Reader) ([]*HTLCEntry, error) {
var htlcs []*HTLCEntry
var (
htlcs []*HTLCEntry
// htlcIndexBlob defines the tlv record type to be used when
// decoding from the disk. We use it instead of the one defined
// in `HTLCEntry.HtlcIndex` as previously this field was encoded
// using `uint16`, thus we will read it as raw bytes and
// deserialize it further below.
htlcIndexBlob tlv.OptionalRecordT[tlv.TlvType6, tlv.Blob]
)
for {
var htlc HTLCEntry
customBlob := htlc.CustomBlob.Zero()
htlcIndex := htlc.HtlcIndex.Zero()
htlcIndex := htlcIndexBlob.Zero()
// Create the tlv stream.
records := []tlv.Record{
@@ -551,7 +561,14 @@ func deserializeHTLCEntries(r io.Reader) ([]*HTLCEntry, error) {
}
if t, ok := parsedTypes[htlcIndex.TlvType()]; ok && t == nil {
htlc.HtlcIndex = tlv.SomeRecordT(htlcIndex)
record, err := deserializeHtlcIndexCompatible(
htlcIndex.Val,
)
if err != nil {
return nil, err
}
htlc.HtlcIndex = record
}
// Append the entry.
@@ -561,6 +578,55 @@ func deserializeHTLCEntries(r io.Reader) ([]*HTLCEntry, error) {
return htlcs, nil
}
// deserializeHtlcIndexCompatible takes raw bytes and decodes it into an
// optional record that's assigned to the entry's HtlcIndex.
//
// NOTE: previously this `HtlcIndex` was a tlv record that used `uint16` to
// encode its value. Given now its value is encoded using BigSizeT, and for any
// BigSizeT, its possible length values are 1, 3, 5, and 8. This means if the
// tlv record has a length of 2, we know for sure it must be an old record
// whose value was encoded using uint16.
func deserializeHtlcIndexCompatible(rawBytes []byte) (
tlv.OptionalRecordT[tlv.TlvType6, tlv.BigSizeT[uint64]], error) {
var (
// record defines the record that's used by the HtlcIndex in the
// entry.
record tlv.OptionalRecordT[
tlv.TlvType6, tlv.BigSizeT[uint64],
]
// htlcIndexVal is the decoded uint64 value.
htlcIndexVal uint64
)
// If the length of the tlv record is 2, it must be encoded using uint16
// as the BigSizeT encoding cannot have this length.
if len(rawBytes) == 2 {
// Decode the raw bytes into uint16 and convert it into uint64.
htlcIndexVal = uint64(binary.BigEndian.Uint16(rawBytes))
} else {
// This value is encoded using BigSizeT, we now use the decoder
// to deserialize the raw bytes.
r := bytes.NewBuffer(rawBytes)
// Create a buffer to be used in the decoding process.
buf := [8]byte{}
// Use the BigSizeT's decoder.
err := tlv.DBigSize(r, &htlcIndexVal, &buf, 8)
if err != nil {
return record, err
}
}
record = tlv.SomeRecordT(tlv.NewRecordT[tlv.TlvType6](
tlv.NewBigSizeT(htlcIndexVal),
))
return record, nil
}
// writeTlvStream is a helper function that encodes the tlv stream into the
// writer.
func writeTlvStream(w io.Writer, s *tlv.Stream) error {

View File

@@ -2,6 +2,7 @@ package channeldb
import (
"bytes"
"encoding/binary"
"io"
"math"
"math/rand"
@@ -785,3 +786,63 @@ func createTestRevocationLogBuckets(tx kvdb.RwTx) (kvdb.RwBucket,
return chanBucket, logBucket, nil
}
// TestDeserializeHTLCEntriesLegacy checks that the legacy encoding of the
// HtlcIndex can be correctly read as BigSizeT. The field `HtlcIndex` was
// encoded using `uint16` and should now be deserialized into BigSizeT
// on-the-fly.
func TestDeserializeHTLCEntriesLegacy(t *testing.T) {
t.Parallel()
// rawBytes defines the bytes read from the disk for the testing HTLC
// entry.
rawBytes := []byte{
// Body length 45.
0x2d,
// Rhash tlv.
0x0, 0x0,
// RefundTimeout tlv.
0x1, 0x4, 0x0, 0xb, 0x4a, 0xa0,
// OutputIndex tlv.
0x2, 0x2, 0x0, 0xa,
// Incoming tlv.
0x3, 0x1, 0x1,
// Amt tlv.
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,
// HTLC index tlv.
//
// NOTE: We are missing two bytes in the end, which is appended
// below.
0x6, 0x2,
}
// Iterate through all possible values encoded using uint16. They should
// be correctly read as uint16 and converted to BigSizeT.
for i := range math.MaxUint16 + 1 {
// Encode the index using two bytes.
rawHtlcIndex := make([]byte, 2)
binary.BigEndian.PutUint16(rawHtlcIndex, uint16(i))
// Copy the raw bytes and append the htlc index.
rawEntry := bytes.Clone(rawBytes)
rawEntry = append(rawEntry, rawHtlcIndex...)
// Read the tlv stream.
buf := bytes.NewBuffer(rawEntry)
htlcs, err := deserializeHTLCEntries(buf)
require.NoError(t, err)
// Check the bytes are read as expected.
require.Len(t, htlcs, 1)
// Assert that the uint16 is converted to BigSizeT.
record := tlv.SomeRecordT(tlv.NewRecordT[tlv.TlvType6](
tlv.NewBigSizeT(uint64(i)),
))
require.Equal(t, record, htlcs[0].HtlcIndex)
}
}