tlv+record+routing: add payload size calculation

This commit is contained in:
Joost Jager 2019-12-16 21:34:29 +01:00
parent 72a6383975
commit e8fd05e8e3
No known key found for this signature in database
GPG Key ID: A61B9D4C393C59C7
4 changed files with 121 additions and 1 deletions

@ -98,6 +98,11 @@ func (r *MPP) Record() tlv.Record {
) )
} }
// PayloadSize returns the size this record takes up in encoded form.
func (r *MPP) PayloadSize() uint64 {
return 32 + tlv.SizeTUint64(uint64(r.totalMsat))
}
// String returns a human-readable representation of the mpp payload field. // String returns a human-readable representation of the mpp payload field.
func (r *MPP) String() string { func (r *MPP) String() string {
return fmt.Sprintf("total=%v, addr=%x", r.totalMsat, r.paymentAddr) return fmt.Sprintf("total=%v, addr=%x", r.totalMsat, r.paymentAddr)

@ -184,6 +184,53 @@ func (h *Hop) PackHopPayload(w io.Writer, nextChanID uint64) error {
return tlvStream.Encode(w) return tlvStream.Encode(w)
} }
// Size returns the total size this hop's payload would take up in the onion
// packet.
func (h *Hop) PayloadSize(nextChanID uint64) uint64 {
if h.LegacyPayload {
return sphinx.LegacyHopDataSize
}
var payloadSize uint64
addRecord := func(tlvType tlv.Type, length uint64) {
payloadSize += tlv.VarIntSize(uint64(tlvType)) +
tlv.VarIntSize(length) + length
}
// Add amount size.
addRecord(record.AmtOnionType, tlv.SizeTUint64(uint64(h.AmtToForward)))
// Add lock time size.
addRecord(
record.LockTimeOnionType,
tlv.SizeTUint64(uint64(h.OutgoingTimeLock)),
)
// Add next hop if present.
if nextChanID != 0 {
addRecord(record.NextHopOnionType, 8)
}
// Add mpp if present.
if h.MPP != nil {
addRecord(record.MPPOnionType, h.MPP.PayloadSize())
}
// Add custom records.
for k, v := range h.CustomRecords {
addRecord(tlv.Type(k), uint64(len(v)))
}
// Add the size required to encode the payload length.
payloadSize += tlv.VarIntSize(payloadSize)
// Add HMAC.
payloadSize += sphinx.HMACSize
return payloadSize
}
// Route represents a path through the channel graph which runs over one or // Route represents a path through the channel graph which runs over one or
// more channels in succession. This struct carries all the information // more channels in succession. This struct carries all the information
// required to craft the Sphinx onion packet, and send the payment along the // required to craft the Sphinx onion packet, and send the payment along the

@ -2,12 +2,20 @@ package route
import ( import (
"bytes" "bytes"
"encoding/hex"
"testing" "testing"
"github.com/btcsuite/btcd/btcec"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/record" "github.com/lightningnetwork/lnd/record"
) )
var (
testPrivKeyBytes, _ = hex.DecodeString("e126f68f7eafcc8b74f54d269fe206be715000f94dac067d1c04a8ca3b2db734")
_, testPubKey = btcec.PrivKeyFromBytes(btcec.S256(), testPrivKeyBytes)
testPubKeyBytes, _ = NewVertexFromBytes(testPubKey.SerializeCompressed())
)
// TestRouteTotalFees checks that a route reports the expected total fee. // TestRouteTotalFees checks that a route reports the expected total fee.
func TestRouteTotalFees(t *testing.T) { func TestRouteTotalFees(t *testing.T) {
t.Parallel() t.Parallel()
@ -56,7 +64,6 @@ func TestRouteTotalFees(t *testing.T) {
if r.TotalFees() != fee { if r.TotalFees() != fee {
t.Fatalf("expected %v fees, got %v", fee, r.TotalFees()) t.Fatalf("expected %v fees, got %v", fee, r.TotalFees())
} }
} }
var ( var (
@ -93,3 +100,57 @@ func TestMPPHop(t *testing.T) {
t.Fatalf("expected err: %v, got: %v", nil, err) t.Fatalf("expected err: %v, got: %v", nil, err)
} }
} }
// TestPayloadSize tests the payload size calculation that is provided by Hop
// structs.
func TestPayloadSize(t *testing.T) {
hops := []*Hop{
{
PubKeyBytes: testPubKeyBytes,
AmtToForward: 1000,
OutgoingTimeLock: 600000,
ChannelID: 3432483437438,
LegacyPayload: true,
},
{
PubKeyBytes: testPubKeyBytes,
AmtToForward: 1200,
OutgoingTimeLock: 700000,
ChannelID: 63584534844,
},
{
PubKeyBytes: testPubKeyBytes,
AmtToForward: 1200,
OutgoingTimeLock: 700000,
MPP: record.NewMPP(500, [32]byte{}),
CustomRecords: map[uint64][]byte{
100000: {1, 2, 3},
1000000: {4, 5},
},
},
}
rt := Route{
Hops: hops,
}
path, err := rt.ToSphinxPath()
if err != nil {
t.Fatal(err)
}
for i, onionHop := range path[:path.TrueRouteLength()] {
hop := hops[i]
var nextChan uint64
if i < len(hops)-1 {
nextChan = hops[i+1].ChannelID
}
expected := uint64(onionHop.HopPayload.NumBytes())
actual := hop.PayloadSize(nextChan)
if expected != actual {
t.Fatalf("unexpected payload size at hop %v: "+
"expected %v, got %v",
i, expected, actual)
}
}
}

@ -4,6 +4,8 @@ import (
"encoding/binary" "encoding/binary"
"errors" "errors"
"io" "io"
"github.com/btcsuite/btcd/wire"
) )
// ErrVarIntNotCanonical signals that the decoded varint was not minimally encoded. // ErrVarIntNotCanonical signals that the decoded varint was not minimally encoded.
@ -107,3 +109,8 @@ func WriteVarInt(w io.Writer, val uint64, buf *[8]byte) error {
_, err := w.Write(buf[:length]) _, err := w.Write(buf[:length])
return err return err
} }
// VarIntSize returns the required number of bytes to encode a var int.
func VarIntSize(val uint64) uint64 {
return uint64(wire.VarIntSerializeSize(val))
}