mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-09-02 02:56:35 +02:00
channeldb: use BigSize to encode htlc amount
This commit uses bigsize record to encode the htlc amount, which could save us 3 more bytes if the encoded value is no greater than roughly 0.043 bitcoin. The uint test has been updated with a more realistic values to reflect the actual gain.
This commit is contained in:
@@ -8,6 +8,7 @@ import (
|
|||||||
|
|
||||||
"github.com/btcsuite/btcd/btcutil"
|
"github.com/btcsuite/btcd/btcutil"
|
||||||
"github.com/lightningnetwork/lnd/kvdb"
|
"github.com/lightningnetwork/lnd/kvdb"
|
||||||
|
"github.com/lightningnetwork/lnd/lntypes"
|
||||||
"github.com/lightningnetwork/lnd/tlv"
|
"github.com/lightningnetwork/lnd/tlv"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -43,6 +44,16 @@ var (
|
|||||||
// HTLCEntry specifies the minimal info needed to be stored on disk for ALL the
|
// HTLCEntry specifies the minimal info needed to be stored on disk for ALL the
|
||||||
// historical HTLCs, which is useful for constructing RevocationLog when a
|
// historical HTLCs, which is useful for constructing RevocationLog when a
|
||||||
// breach is detected.
|
// breach is detected.
|
||||||
|
// The actual size of each HTLCEntry varies based on its RHash and Amt(sat),
|
||||||
|
// summarized as follows,
|
||||||
|
//
|
||||||
|
// | RHash empty | Amt<=252 | Amt<=65,535 | Amt<=4,294,967,295 | otherwise |
|
||||||
|
// |:-----------:|:--------:|:-----------:|:------------------:|:---------:|
|
||||||
|
// | true | 19 | 21 | 23 | 26 |
|
||||||
|
// | false | 51 | 53 | 55 | 58 |
|
||||||
|
//
|
||||||
|
// So the size varies from 19 bytes to 58 bytes, where most likely to be 23 or
|
||||||
|
// 55 bytes.
|
||||||
//
|
//
|
||||||
// NOTE: all the fields saved to disk use the primitive go types so they can be
|
// NOTE: all the fields saved to disk use the primitive go types so they can be
|
||||||
// made into tlv records without further conversion.
|
// made into tlv records without further conversion.
|
||||||
@@ -86,6 +97,46 @@ type HTLCEntry struct {
|
|||||||
incomingTlv uint8
|
incomingTlv uint8
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RHashLen is used by MakeDynamicRecord to return the size of the RHash.
|
||||||
|
//
|
||||||
|
// NOTE: for zero hash, we return a length 0.
|
||||||
|
func (h *HTLCEntry) RHashLen() uint64 {
|
||||||
|
if h.RHash == lntypes.ZeroHash {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return 32
|
||||||
|
}
|
||||||
|
|
||||||
|
// RHashEncoder is the customized encoder which skips encoding the empty hash.
|
||||||
|
func RHashEncoder(w io.Writer, val interface{}, buf *[8]byte) error {
|
||||||
|
v, ok := val.(*[32]byte)
|
||||||
|
if !ok {
|
||||||
|
return tlv.NewTypeForEncodingErr(val, "RHash")
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the value is an empty hash, we will skip encoding it.
|
||||||
|
if *v == lntypes.ZeroHash {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return tlv.EBytes32(w, v, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RHashDecoder is the customized decoder which skips decoding the empty hash.
|
||||||
|
func RHashDecoder(r io.Reader, val interface{}, buf *[8]byte, l uint64) error {
|
||||||
|
v, ok := val.(*[32]byte)
|
||||||
|
if !ok {
|
||||||
|
return tlv.NewTypeForEncodingErr(val, "RHash")
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the length is zero, we will skip encoding the empty hash.
|
||||||
|
if l == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return tlv.DBytes32(r, v, buf, 32)
|
||||||
|
}
|
||||||
|
|
||||||
// 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) {
|
||||||
const (
|
const (
|
||||||
@@ -103,7 +154,10 @@ func (h *HTLCEntry) toTlvStream() (*tlv.Stream, error) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
return tlv.NewStream(
|
return tlv.NewStream(
|
||||||
tlv.MakePrimitiveRecord(rHashType, &h.RHash),
|
tlv.MakeDynamicRecord(
|
||||||
|
rHashType, &h.RHash, h.RHashLen,
|
||||||
|
RHashEncoder, RHashDecoder,
|
||||||
|
),
|
||||||
tlv.MakePrimitiveRecord(
|
tlv.MakePrimitiveRecord(
|
||||||
refundTimeoutType, &h.RefundTimeout,
|
refundTimeoutType, &h.RefundTimeout,
|
||||||
),
|
),
|
||||||
@@ -111,7 +165,9 @@ func (h *HTLCEntry) toTlvStream() (*tlv.Stream, error) {
|
|||||||
outputIndexType, &h.OutputIndex,
|
outputIndexType, &h.OutputIndex,
|
||||||
),
|
),
|
||||||
tlv.MakePrimitiveRecord(incomingType, &h.incomingTlv),
|
tlv.MakePrimitiveRecord(incomingType, &h.incomingTlv),
|
||||||
tlv.MakePrimitiveRecord(amtType, &h.amtTlv),
|
// We will save 3 bytes if the amount is less or equal to
|
||||||
|
// 4,294,967,295 msat, or roughly 0.043 bitcoin.
|
||||||
|
tlv.MakeBigSizeRecord(amtType, &h.amtTlv),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -34,30 +34,26 @@ var (
|
|||||||
}
|
}
|
||||||
|
|
||||||
testHTLCEntry = HTLCEntry{
|
testHTLCEntry = HTLCEntry{
|
||||||
RefundTimeout: 100,
|
RefundTimeout: 740_000,
|
||||||
OutputIndex: 10,
|
OutputIndex: 10,
|
||||||
Incoming: true,
|
Incoming: true,
|
||||||
Amt: 255,
|
Amt: 1000_000,
|
||||||
amtTlv: 255,
|
amtTlv: 1000_000,
|
||||||
incomingTlv: 1,
|
incomingTlv: 1,
|
||||||
}
|
}
|
||||||
testHTLCEntryBytes = []byte{
|
testHTLCEntryBytes = []byte{
|
||||||
// Body length 58.
|
// Body length 23.
|
||||||
0x39,
|
0x16,
|
||||||
// Rhash tlv.
|
// Rhash tlv.
|
||||||
0x0, 0x20,
|
0x0, 0x0,
|
||||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
|
||||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
|
||||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
|
||||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
|
||||||
// RefundTimeout tlv.
|
// RefundTimeout tlv.
|
||||||
0x1, 0x4, 0x0, 0x0, 0x0, 0x64,
|
0x1, 0x4, 0x0, 0xb, 0x4a, 0xa0,
|
||||||
// OutputIndex tlv.
|
// OutputIndex tlv.
|
||||||
0x2, 0x2, 0x0, 0xa,
|
0x2, 0x2, 0x0, 0xa,
|
||||||
// Incoming tlv.
|
// Incoming tlv.
|
||||||
0x3, 0x1, 0x1,
|
0x3, 0x1, 0x1,
|
||||||
// Amt tlv.
|
// Amt tlv.
|
||||||
0x4, 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff,
|
0x4, 0x5, 0xfe, 0x0, 0xf, 0x42, 0x40,
|
||||||
}
|
}
|
||||||
|
|
||||||
testChannelCommit = ChannelCommitment{
|
testChannelCommit = ChannelCommitment{
|
||||||
@@ -161,7 +157,7 @@ func TestReadTLVStreamErr(t *testing.T) {
|
|||||||
require.Zero(t, valueRead)
|
require.Zero(t, valueRead)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSerializeHTLCEntries(t *testing.T) {
|
func TestSerializeHTLCEntriesEmptyRHash(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
// Copy the testHTLCEntry.
|
// Copy the testHTLCEntry.
|
||||||
@@ -181,6 +177,37 @@ func TestSerializeHTLCEntries(t *testing.T) {
|
|||||||
require.Equal(t, testHTLCEntryBytes, buf.Bytes())
|
require.Equal(t, testHTLCEntryBytes, buf.Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSerializeHTLCEntries(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Copy the testHTLCEntry.
|
||||||
|
entry := testHTLCEntry
|
||||||
|
|
||||||
|
// Create a fake rHash.
|
||||||
|
rHashBytes := bytes.Repeat([]byte{10}, 32)
|
||||||
|
copy(entry.RHash[:], rHashBytes)
|
||||||
|
|
||||||
|
// Construct the serialized bytes.
|
||||||
|
//
|
||||||
|
// Exclude the first 3 bytes, which are total length, RHash type and
|
||||||
|
// RHash length(0).
|
||||||
|
partialBytes := testHTLCEntryBytes[3:]
|
||||||
|
|
||||||
|
// Write the total length and RHash tlv.
|
||||||
|
expectedBytes := []byte{0x36, 0x0, 0x20}
|
||||||
|
expectedBytes = append(expectedBytes, rHashBytes...)
|
||||||
|
|
||||||
|
// Append the rest.
|
||||||
|
expectedBytes = append(expectedBytes, partialBytes...)
|
||||||
|
|
||||||
|
buf := bytes.NewBuffer([]byte{})
|
||||||
|
err := serializeHTLCEntries(buf, []*HTLCEntry{&entry})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Check the bytes are read as expected.
|
||||||
|
require.Equal(t, expectedBytes, buf.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
func TestSerializeRevocationLog(t *testing.T) {
|
func TestSerializeRevocationLog(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@@ -197,7 +224,7 @@ func TestSerializeRevocationLog(t *testing.T) {
|
|||||||
require.Equal(t, testRevocationLogBytes, buf.Bytes()[:bodyIndex])
|
require.Equal(t, testRevocationLogBytes, buf.Bytes()[:bodyIndex])
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDerializeHTLCEntries(t *testing.T) {
|
func TestDerializeHTLCEntriesEmptyRHash(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
// Read the tlv stream.
|
// Read the tlv stream.
|
||||||
@@ -210,6 +237,38 @@ func TestDerializeHTLCEntries(t *testing.T) {
|
|||||||
require.Equal(t, &testHTLCEntry, htlcs[0])
|
require.Equal(t, &testHTLCEntry, htlcs[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDerializeHTLCEntries(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Copy the testHTLCEntry.
|
||||||
|
entry := testHTLCEntry
|
||||||
|
|
||||||
|
// Create a fake rHash.
|
||||||
|
rHashBytes := bytes.Repeat([]byte{10}, 32)
|
||||||
|
copy(entry.RHash[:], rHashBytes)
|
||||||
|
|
||||||
|
// Construct the serialized bytes.
|
||||||
|
//
|
||||||
|
// Exclude the first 3 bytes, which are total length, RHash type and
|
||||||
|
// RHash length(0).
|
||||||
|
partialBytes := testHTLCEntryBytes[3:]
|
||||||
|
|
||||||
|
// Write the total length and RHash tlv.
|
||||||
|
testBytes := append([]byte{0x36, 0x0, 0x20}, rHashBytes...)
|
||||||
|
|
||||||
|
// Append the rest.
|
||||||
|
testBytes = append(testBytes, partialBytes...)
|
||||||
|
|
||||||
|
// Read the tlv stream.
|
||||||
|
buf := bytes.NewBuffer(testBytes)
|
||||||
|
htlcs, err := deserializeHTLCEntries(buf)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Check the bytes are read as expected.
|
||||||
|
require.Len(t, htlcs, 1)
|
||||||
|
require.Equal(t, &entry, htlcs[0])
|
||||||
|
}
|
||||||
|
|
||||||
func TestDerializeRevocationLog(t *testing.T) {
|
func TestDerializeRevocationLog(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user