lnd.xprv/lnwire/onion_error_test.go
Olaoluwa Osuntokun f83fde8483
lnwire: ensure we precisely encode the length for onion errors w/ chan updates
In this commit we fix a compatibility issue with other implementations.
Before this commit, when writing out an onion error that includes a
`ChannelUpdate` we would use the `MaxPayloadLength` to get the length to
encode. However, a recent update has modified that to be the max
`brontide` payload length as it's possible to pad out the message with
optional fields we're unaware of. As a result, we would always write out
a length of 65KB or so. This didn't effect our parser as we ignore the
length and decode the channel update directly as we don't need the
length to do that. However, other implementations depended on the length
rather than just reading the channel update, meaning that they weren't
able to decode our onion errors that had channel updates.

In this commit we fix that by introducing a new
`writeOnionErrorChanUpdate` which will write out the precise length
instead of using the max payload size.

Fixes #2450.
2019-01-11 14:21:48 -08:00

169 lines
4.9 KiB
Go

package lnwire
import (
"bufio"
"bytes"
"encoding/binary"
"reflect"
"testing"
"github.com/davecgh/go-spew/spew"
)
var (
testOnionHash = []byte{}
testAmount = MilliSatoshi(1)
testCtlvExpiry = uint32(2)
testFlags = uint16(2)
sig, _ = NewSigFromSignature(testSig)
testChannelUpdate = ChannelUpdate{
Signature: sig,
ShortChannelID: NewShortChanIDFromInt(1),
Timestamp: 1,
Flags: 1,
}
)
var onionFailures = []FailureMessage{
&FailInvalidRealm{},
&FailTemporaryNodeFailure{},
&FailPermanentNodeFailure{},
&FailRequiredNodeFeatureMissing{},
&FailPermanentChannelFailure{},
&FailRequiredChannelFeatureMissing{},
&FailUnknownNextPeer{},
&FailUnknownPaymentHash{},
&FailIncorrectPaymentAmount{},
&FailFinalExpiryTooSoon{},
NewInvalidOnionVersion(testOnionHash),
NewInvalidOnionHmac(testOnionHash),
NewInvalidOnionKey(testOnionHash),
NewTemporaryChannelFailure(&testChannelUpdate),
NewTemporaryChannelFailure(nil),
NewAmountBelowMinimum(testAmount, testChannelUpdate),
NewFeeInsufficient(testAmount, testChannelUpdate),
NewIncorrectCltvExpiry(testCtlvExpiry, testChannelUpdate),
NewExpiryTooSoon(testChannelUpdate),
NewChannelDisabled(testFlags, testChannelUpdate),
NewFinalIncorrectCltvExpiry(testCtlvExpiry),
NewFinalIncorrectHtlcAmount(testAmount),
}
// TestEncodeDecodeCode tests the ability of onion errors to be properly encoded
// and decoded.
func TestEncodeDecodeCode(t *testing.T) {
for _, failure1 := range onionFailures {
var b bytes.Buffer
if err := EncodeFailure(&b, failure1, 0); err != nil {
t.Fatalf("unable to encode failure code(%v): %v",
failure1.Code(), err)
}
failure2, err := DecodeFailure(&b, 0)
if err != nil {
t.Fatalf("unable to decode failure code(%v): %v",
failure1.Code(), err)
}
if !reflect.DeepEqual(failure1, failure2) {
t.Fatalf("expected %v, got %v", spew.Sdump(failure1),
spew.Sdump(failure2))
}
}
}
// 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)
}
}
// TestWriteOnionErrorChanUpdate tests that we write an exact size for the
// channel update in order to be more compliant with the parsers of other
// implementations.
func TestWriteOnionErrorChanUpdate(t *testing.T) {
t.Parallel()
// First, we'll write out the raw channel update so we can obtain the
// raw serialized length.
var b bytes.Buffer
update := testChannelUpdate
if err := update.Encode(&b, 0); err != nil {
t.Fatalf("unable to write update: %v", err)
}
trueUpdateLength := b.Len()
// Next, we'll use the function to encode the update as we would in a
// onion error message.
var errorBuf bytes.Buffer
err := writeOnionErrorChanUpdate(&errorBuf, &update, 0)
if err != nil {
t.Fatalf("unable to encode onion error: %v", err)
}
// Finally, read the length encoded and ensure that it matches the raw
// length.
var encodedLen uint16
if err := ReadElement(&errorBuf, &encodedLen); err != nil {
t.Fatalf("unable to read len: %v", err)
}
if uint16(trueUpdateLength) != encodedLen {
t.Fatalf("wrong length written: expected %v, got %v",
trueUpdateLength, encodedLen)
}
}