Merge pull request #1822 from Roasbeef/chan-update-compat
lnwire: add new compatibility parsing more for onion error chan updates
This commit is contained in:
commit
450cabf0d8
@ -1,6 +1,7 @@
|
||||
package lnwire
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
@ -478,6 +479,44 @@ func (f FailInvalidOnionKey) Error() string {
|
||||
return fmt.Sprintf("InvalidOnionKey(onion_sha=%x)", f.OnionSHA256[:])
|
||||
}
|
||||
|
||||
// parseChannelUpdateCompatabilityMode will attempt to parse a channel updated
|
||||
// encoded into an onion error payload in two ways. First, we'll try the
|
||||
// compatibility oriented version wherein we'll _skip_ the length prefixing on
|
||||
// the channel update message. Older versions of c-lighting do this so we'll
|
||||
// attempt to parse these messages in order to retain compatibility. If we're
|
||||
// unable to pull out a fully valid version, then we'll fall back to the
|
||||
// regular parsing mechanism which includes the length prefix an NO type byte.
|
||||
func parseChannelUpdateCompatabilityMode(r *bufio.Reader,
|
||||
chanUpdate *ChannelUpdate, pver uint32) error {
|
||||
|
||||
// We'll peek out two bytes from the buffer without advancing the
|
||||
// buffer so we can decide how to parse the remainder of it.
|
||||
maybeTypeBytes, err := r.Peek(2)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Some nodes well prefix an additional set of bytes in front of their
|
||||
// channel updates. These bytes will _almost_ always be 258 or the type
|
||||
// of the ChannelUpdate message.
|
||||
typeInt := binary.BigEndian.Uint16(maybeTypeBytes)
|
||||
if typeInt == MsgChannelUpdate {
|
||||
// At this point it's likely the case that this is a channel
|
||||
// update message with its type prefixed, so we'll snip off the
|
||||
// first two bytes and parse it as normal.
|
||||
var throwAwayTypeBytes [2]byte
|
||||
_, err := r.Read(throwAwayTypeBytes[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// At this pint, we've either decided to keep the entire thing, or snip
|
||||
// off the first two bytes. In either case, we can just read it as
|
||||
// normal.
|
||||
return chanUpdate.Decode(r, pver)
|
||||
}
|
||||
|
||||
// FailTemporaryChannelFailure is if an otherwise unspecified transient error
|
||||
// occurs for the outgoing channel (eg. channel capacity reached, too many
|
||||
// in-flight htlcs)
|
||||
@ -491,9 +530,6 @@ type FailTemporaryChannelFailure struct {
|
||||
Update *ChannelUpdate
|
||||
}
|
||||
|
||||
// TODO(roasbeef): any error with an Update needs to include the edge+vertex of
|
||||
// failure
|
||||
|
||||
// NewTemporaryChannelFailure creates new instance of the FailTemporaryChannelFailure.
|
||||
func NewTemporaryChannelFailure(update *ChannelUpdate) *FailTemporaryChannelFailure {
|
||||
return &FailTemporaryChannelFailure{Update: update}
|
||||
@ -530,7 +566,9 @@ func (f *FailTemporaryChannelFailure) Decode(r io.Reader, pver uint32) error {
|
||||
|
||||
if length != 0 {
|
||||
f.Update = &ChannelUpdate{}
|
||||
return f.Update.Decode(r, pver)
|
||||
return parseChannelUpdateCompatabilityMode(
|
||||
bufio.NewReader(r), f.Update, pver,
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -610,7 +648,9 @@ func (f *FailAmountBelowMinimum) Decode(r io.Reader, pver uint32) error {
|
||||
}
|
||||
|
||||
f.Update = ChannelUpdate{}
|
||||
return f.Update.Decode(r, pver)
|
||||
return parseChannelUpdateCompatabilityMode(
|
||||
bufio.NewReader(r), &f.Update, pver,
|
||||
)
|
||||
}
|
||||
|
||||
// Encode writes the failure in bytes stream.
|
||||
@ -681,7 +721,9 @@ func (f *FailFeeInsufficient) Decode(r io.Reader, pver uint32) error {
|
||||
}
|
||||
|
||||
f.Update = ChannelUpdate{}
|
||||
return f.Update.Decode(r, pver)
|
||||
return parseChannelUpdateCompatabilityMode(
|
||||
bufio.NewReader(r), &f.Update, pver,
|
||||
)
|
||||
}
|
||||
|
||||
// Encode writes the failure in bytes stream.
|
||||
@ -752,7 +794,9 @@ func (f *FailIncorrectCltvExpiry) Decode(r io.Reader, pver uint32) error {
|
||||
}
|
||||
|
||||
f.Update = ChannelUpdate{}
|
||||
return f.Update.Decode(r, pver)
|
||||
return parseChannelUpdateCompatabilityMode(
|
||||
bufio.NewReader(r), &f.Update, pver,
|
||||
)
|
||||
}
|
||||
|
||||
// Encode writes the failure in bytes stream.
|
||||
@ -812,7 +856,9 @@ func (f *FailExpiryTooSoon) Decode(r io.Reader, pver uint32) error {
|
||||
}
|
||||
|
||||
f.Update = ChannelUpdate{}
|
||||
return f.Update.Decode(r, pver)
|
||||
return parseChannelUpdateCompatabilityMode(
|
||||
bufio.NewReader(r), &f.Update, pver,
|
||||
)
|
||||
}
|
||||
|
||||
// Encode writes the failure in bytes stream.
|
||||
@ -879,7 +925,9 @@ func (f *FailChannelDisabled) Decode(r io.Reader, pver uint32) error {
|
||||
}
|
||||
|
||||
f.Update = ChannelUpdate{}
|
||||
return f.Update.Decode(r, pver)
|
||||
return parseChannelUpdateCompatabilityMode(
|
||||
bufio.NewReader(r), &f.Update, pver,
|
||||
)
|
||||
}
|
||||
|
||||
// Encode writes the failure in bytes stream.
|
||||
|
@ -1,7 +1,9 @@
|
||||
package lnwire
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
@ -69,3 +71,61 @@ func TestEncodeDecodeCode(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestChannelUpdateCompatabilityParsing tests that we're able to properly read
|
||||
// out channel update messages encoded in an onion error payload that was
|
||||
// written in the legacy (type prefixed) format.
|
||||
func TestChannelUpdateCompatabilityParsing(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// We'll start by taking out test channel update, and encoding it into
|
||||
// a set of raw bytes.
|
||||
var b bytes.Buffer
|
||||
if err := testChannelUpdate.Encode(&b, 0); err != nil {
|
||||
t.Fatalf("unable to encode chan update: %v", err)
|
||||
}
|
||||
|
||||
// Now that we have the set of bytes encoded, we'll ensure that we're
|
||||
// able to decode it using our compatibility method, as it's a regular
|
||||
// encoded channel update message.
|
||||
var newChanUpdate ChannelUpdate
|
||||
err := parseChannelUpdateCompatabilityMode(
|
||||
bufio.NewReader(&b), &newChanUpdate, 0,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to parse channel update: %v", err)
|
||||
}
|
||||
|
||||
// At this point, we'll ensure that we get the exact same failure out
|
||||
// on the other side.
|
||||
if !reflect.DeepEqual(testChannelUpdate, newChanUpdate) {
|
||||
t.Fatalf("mismatched channel updates: %v", err)
|
||||
}
|
||||
|
||||
// We'll now reset then re-encoded the same channel update to try it in
|
||||
// the proper compatible mode.
|
||||
b.Reset()
|
||||
|
||||
// Before we encode the update itself, we'll also write out the 2-byte
|
||||
// type in order to simulate the compat mode.
|
||||
var tByte [2]byte
|
||||
binary.BigEndian.PutUint16(tByte[:], MsgChannelUpdate)
|
||||
b.Write(tByte[:])
|
||||
if err := testChannelUpdate.Encode(&b, 0); err != nil {
|
||||
t.Fatalf("unable to encode chan update: %v", err)
|
||||
}
|
||||
|
||||
// We should be able to properly parse the encoded channel update
|
||||
// message even with the extra two bytes.
|
||||
var newChanUpdate2 ChannelUpdate
|
||||
err = parseChannelUpdateCompatabilityMode(
|
||||
bufio.NewReader(&b), &newChanUpdate2, 0,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to parse channel update: %v", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(newChanUpdate2, newChanUpdate) {
|
||||
t.Fatalf("mismatched channel updates: %v", err)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user