From b49637fbe99d676086d319cfa42b31cd373cc994 Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Sat, 12 Jan 2019 18:59:43 +0100 Subject: [PATCH] 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 --- lnwire/channel_update.go | 47 +++++++++++++++++++++++++++++++++++++--- lnwire/lnwire_test.go | 15 ++++++++++++- 2 files changed, 58 insertions(+), 4 deletions(-) diff --git a/lnwire/channel_update.go b/lnwire/channel_update.go index 02dfa5db..1788cdc7 100644 --- a/lnwire/channel_update.go +++ b/lnwire/channel_update.go @@ -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 } diff --git a/lnwire/lnwire_test.go b/lnwire/lnwire_test.go index 91de511b..3046c2f3 100644 --- a/lnwire/lnwire_test.go +++ b/lnwire/lnwire_test.go @@ -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()), }