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:
Olaoluwa Osuntokun 2018-09-03 19:24:40 -07:00 committed by GitHub
commit 450cabf0d8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 117 additions and 9 deletions

View File

@ -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.

View File

@ -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)
}
}