lnwire: ensure we're able to decode legacy FailUnknownPaymentHash

In this commit, we modify the decoding of the FailUnknownPaymentHash
message to ensure we're able to fully decode the legacy serialization of
the onion error. We do this by catching the `io.EOF` error as it's
returned when _no_ bytes are read. If this is the case, then only the
error type was serialized and not also the optional amount.
This commit is contained in:
Olaoluwa Osuntokun 2019-01-31 22:20:41 -08:00
parent 5167b02312
commit 17fd5eef07
No known key found for this signature in database
GPG Key ID: CE58F7F8E20FD9A2
2 changed files with 42 additions and 2 deletions

@ -366,7 +366,20 @@ func (f FailUnknownPaymentHash) Error() string {
//
// NOTE: Part of the Serializable interface.
func (f *FailUnknownPaymentHash) Decode(r io.Reader, pver uint32) error {
return ReadElement(r, &f.amount)
err := ReadElement(r, &f.amount)
switch {
// This is an optional tack on that was added later in the protocol. As
// a result, older nodes may not include this value. We'll account for
// this by checking for io.EOF here which means that no bytes were read
// at all.
case err == io.EOF:
return nil
case err != nil:
return err
}
return nil
}
// Encode writes the failure in bytes stream.

@ -33,10 +33,10 @@ var onionFailures = []FailureMessage{
&FailPermanentChannelFailure{},
&FailRequiredChannelFeatureMissing{},
&FailUnknownNextPeer{},
&FailUnknownPaymentHash{},
&FailIncorrectPaymentAmount{},
&FailFinalExpiryTooSoon{},
NewFailUnknownPaymentHash(99),
NewInvalidOnionVersion(testOnionHash),
NewInvalidOnionHmac(testOnionHash),
NewInvalidOnionKey(testOnionHash),
@ -167,3 +167,30 @@ func TestWriteOnionErrorChanUpdate(t *testing.T) {
trueUpdateLength, encodedLen)
}
}
// TestFailUnknownPaymentHashOptionalAmount tests that we're able to decode an
// UnknownPaymentHash error that doesn't have the optional amount. This ensures
// we're able to decode FailUnknownPaymentHash messages from older nodes.
func TestFailUnknownPaymentHashOptionalAmount(t *testing.T) {
t.Parallel()
// Creation an error that is a non-pointer will allow us to skip the
// type assertion for the Serializable interface. As a result, the
// amount body won't be written.
onionError := FailUnknownPaymentHash{}
var b bytes.Buffer
if err := EncodeFailure(&b, onionError, 0); err != nil {
t.Fatalf("unable to encode failure: %v", err)
}
onionError2, err := DecodeFailure(bytes.NewReader(b.Bytes()), 0)
if err != nil {
t.Fatalf("unable to decode error: %v", err)
}
if !reflect.DeepEqual(onionError, onionError) {
t.Fatalf("expected %v, got %v", spew.Sdump(onionError),
spew.Sdump(onionError2))
}
}