lnwire: add HtlcMaximumMsat field to ChannelUpdate

In this commit, we add a field to the ChannelUpdate
denoting the maximum HTLC we support sending over
this channel, a field which was recently added to the
spec.

This field serves multiple purposes. In the short
term, it enables nodes to signal the largest HTLC
they're willing to carry, allows light clients who
don't verify channel existence to have some guidance
when routing HTLCs, and finally may allow nodes to
preserve a portion of bandwidth at all times.

In the long term, this field can be used by
implementations of AMP to guide payment splitting,
as it becomes apparent to a node the largest possible
HTLC one can route over a particular channel.

This PR was made possible by the merge of #1825,
which enables older nodes to properly retain and
verify signatures on updates that include new fields
(like this new max HTLC field) that they haven't yet
been updated to recognize.

In addition, the new ChannelUpdate fields are added to
the lnwire fuzzing tests.

Co-authored-by: Johan T. Halseth <johanth@gmail.com>
This commit is contained in:
Valentine Wallace 2019-01-12 18:59:43 +01:00 committed by Johan T. Halseth
parent f0ba4b454c
commit b49637fbe9
No known key found for this signature in database
GPG Key ID: 15BAADA29DA20D26
2 changed files with 58 additions and 4 deletions

@ -13,6 +13,12 @@ import (
// present in the ChannelUpdate.
type ChanUpdateMsgFlags uint8
const (
// ChanUpdateOptionMaxHtlc is a bit that indicates whether the
// optional htlc_maximum_msat field is present in this ChannelUpdate.
ChanUpdateOptionMaxHtlc ChanUpdateMsgFlags = 1 << iota
)
// String returns the bitfield flags as a string.
func (c ChanUpdateMsgFlags) String() string {
return fmt.Sprintf("%08b", c)
@ -95,6 +101,9 @@ type ChannelUpdate struct {
// satoshi.
FeeRate uint32
// HtlcMaximumMsat is the maximum HTLC value which will be accepted.
HtlcMaximumMsat MilliSatoshi
// ExtraOpaqueData is the set of data that was appended to this
// message, some of which we may not actually know how to iterate or
// parse. By holding onto this data, we ensure that we're able to
@ -129,6 +138,13 @@ func (a *ChannelUpdate) Decode(r io.Reader, pver uint32) error {
return err
}
// Now check whether the max HTLC field is present and read it if so.
if a.MessageFlags&ChanUpdateOptionMaxHtlc != 0 {
if err := ReadElements(r, &a.HtlcMaximumMsat); err != nil {
return err
}
}
// Now that we've read out all the fields that we explicitly know of,
// we'll collect the remainder into the ExtraOpaqueData field. If there
// aren't any bytes, then we'll snip off the slice to avoid carrying
@ -149,7 +165,7 @@ func (a *ChannelUpdate) Decode(r io.Reader, pver uint32) error {
//
// This is part of the lnwire.Message interface.
func (a *ChannelUpdate) Encode(w io.Writer, pver uint32) error {
return WriteElements(w,
err := WriteElements(w,
a.Signature,
a.ChainHash[:],
a.ShortChannelID,
@ -160,8 +176,21 @@ func (a *ChannelUpdate) Encode(w io.Writer, pver uint32) error {
a.HtlcMinimumMsat,
a.BaseFee,
a.FeeRate,
a.ExtraOpaqueData,
)
if err != nil {
return err
}
// Now append optional fields if they are set. Currently, the only
// optional field is max HTLC.
if a.MessageFlags&ChanUpdateOptionMaxHtlc != 0 {
if err := WriteElements(w, a.HtlcMaximumMsat); err != nil {
return err
}
}
// Finally, append any extra opaque data.
return WriteElements(w, a.ExtraOpaqueData)
}
// MsgType returns the integer uniquely identifying this message type on the
@ -196,11 +225,23 @@ func (a *ChannelUpdate) DataToSign() ([]byte, error) {
a.HtlcMinimumMsat,
a.BaseFee,
a.FeeRate,
a.ExtraOpaqueData,
)
if err != nil {
return nil, err
}
// Now append optional fields if they are set. Currently, the only
// optional field is max HTLC.
if a.MessageFlags&ChanUpdateOptionMaxHtlc != 0 {
if err := WriteElements(&w, a.HtlcMaximumMsat); err != nil {
return nil, err
}
}
// Finally, append any extra opaque data.
if err := WriteElements(&w, a.ExtraOpaqueData); err != nil {
return nil, err
}
return w.Bytes(), nil
}

@ -649,13 +649,26 @@ func TestLightningWireProtocol(t *testing.T) {
},
MsgChannelUpdate: func(v []reflect.Value, r *rand.Rand) {
var err error
msgFlags := ChanUpdateMsgFlags(r.Int31())
maxHtlc := MilliSatoshi(r.Int63())
// We make the max_htlc field zero if it is not flagged
// as being part of the ChannelUpdate, to pass
// serialization tests, as it will be ignored if the bit
// is not set.
if msgFlags&ChanUpdateOptionMaxHtlc == 0 {
maxHtlc = 0
}
req := ChannelUpdate{
ShortChannelID: NewShortChanIDFromInt(uint64(r.Int63())),
Timestamp: uint32(r.Int31()),
MessageFlags: ChanUpdateMsgFlags(r.Int31()),
MessageFlags: msgFlags,
ChannelFlags: ChanUpdateChanFlags(r.Int31()),
TimeLockDelta: uint16(r.Int31()),
HtlcMinimumMsat: MilliSatoshi(r.Int63()),
HtlcMaximumMsat: maxHtlc,
BaseFee: uint32(r.Int31()),
FeeRate: uint32(r.Int31()),
}