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
|
package lnwire
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -478,6 +479,44 @@ func (f FailInvalidOnionKey) Error() string {
|
|||||||
return fmt.Sprintf("InvalidOnionKey(onion_sha=%x)", f.OnionSHA256[:])
|
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
|
// FailTemporaryChannelFailure is if an otherwise unspecified transient error
|
||||||
// occurs for the outgoing channel (eg. channel capacity reached, too many
|
// occurs for the outgoing channel (eg. channel capacity reached, too many
|
||||||
// in-flight htlcs)
|
// in-flight htlcs)
|
||||||
@ -491,9 +530,6 @@ type FailTemporaryChannelFailure struct {
|
|||||||
Update *ChannelUpdate
|
Update *ChannelUpdate
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(roasbeef): any error with an Update needs to include the edge+vertex of
|
|
||||||
// failure
|
|
||||||
|
|
||||||
// NewTemporaryChannelFailure creates new instance of the FailTemporaryChannelFailure.
|
// NewTemporaryChannelFailure creates new instance of the FailTemporaryChannelFailure.
|
||||||
func NewTemporaryChannelFailure(update *ChannelUpdate) *FailTemporaryChannelFailure {
|
func NewTemporaryChannelFailure(update *ChannelUpdate) *FailTemporaryChannelFailure {
|
||||||
return &FailTemporaryChannelFailure{Update: update}
|
return &FailTemporaryChannelFailure{Update: update}
|
||||||
@ -530,7 +566,9 @@ func (f *FailTemporaryChannelFailure) Decode(r io.Reader, pver uint32) error {
|
|||||||
|
|
||||||
if length != 0 {
|
if length != 0 {
|
||||||
f.Update = &ChannelUpdate{}
|
f.Update = &ChannelUpdate{}
|
||||||
return f.Update.Decode(r, pver)
|
return parseChannelUpdateCompatabilityMode(
|
||||||
|
bufio.NewReader(r), f.Update, pver,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -610,7 +648,9 @@ func (f *FailAmountBelowMinimum) Decode(r io.Reader, pver uint32) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
f.Update = ChannelUpdate{}
|
f.Update = ChannelUpdate{}
|
||||||
return f.Update.Decode(r, pver)
|
return parseChannelUpdateCompatabilityMode(
|
||||||
|
bufio.NewReader(r), &f.Update, pver,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encode writes the failure in bytes stream.
|
// Encode writes the failure in bytes stream.
|
||||||
@ -681,7 +721,9 @@ func (f *FailFeeInsufficient) Decode(r io.Reader, pver uint32) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
f.Update = ChannelUpdate{}
|
f.Update = ChannelUpdate{}
|
||||||
return f.Update.Decode(r, pver)
|
return parseChannelUpdateCompatabilityMode(
|
||||||
|
bufio.NewReader(r), &f.Update, pver,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encode writes the failure in bytes stream.
|
// Encode writes the failure in bytes stream.
|
||||||
@ -752,7 +794,9 @@ func (f *FailIncorrectCltvExpiry) Decode(r io.Reader, pver uint32) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
f.Update = ChannelUpdate{}
|
f.Update = ChannelUpdate{}
|
||||||
return f.Update.Decode(r, pver)
|
return parseChannelUpdateCompatabilityMode(
|
||||||
|
bufio.NewReader(r), &f.Update, pver,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encode writes the failure in bytes stream.
|
// Encode writes the failure in bytes stream.
|
||||||
@ -812,7 +856,9 @@ func (f *FailExpiryTooSoon) Decode(r io.Reader, pver uint32) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
f.Update = ChannelUpdate{}
|
f.Update = ChannelUpdate{}
|
||||||
return f.Update.Decode(r, pver)
|
return parseChannelUpdateCompatabilityMode(
|
||||||
|
bufio.NewReader(r), &f.Update, pver,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encode writes the failure in bytes stream.
|
// Encode writes the failure in bytes stream.
|
||||||
@ -879,7 +925,9 @@ func (f *FailChannelDisabled) Decode(r io.Reader, pver uint32) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
f.Update = ChannelUpdate{}
|
f.Update = ChannelUpdate{}
|
||||||
return f.Update.Decode(r, pver)
|
return parseChannelUpdateCompatabilityMode(
|
||||||
|
bufio.NewReader(r), &f.Update, pver,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encode writes the failure in bytes stream.
|
// Encode writes the failure in bytes stream.
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
package lnwire
|
package lnwire
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"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