github.com/decred/dcrlnd@v0.7.6/lnwire/onion_error_test.go (about)

     1  package lnwire
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"encoding/binary"
     7  	"io"
     8  	"reflect"
     9  	"testing"
    10  
    11  	"github.com/davecgh/go-spew/spew"
    12  )
    13  
    14  var (
    15  	testOnionHash     = []byte{}
    16  	testAmount        = MilliAtom(1)
    17  	testCtlvExpiry    = uint32(2)
    18  	testFlags         = uint16(2)
    19  	testType          = uint64(3)
    20  	testOffset        = uint16(24)
    21  	sig, _            = NewSigFromSignature(testSig)
    22  	testChannelUpdate = ChannelUpdate{
    23  		Signature:       sig,
    24  		ShortChannelID:  NewShortChanIDFromInt(1),
    25  		Timestamp:       1,
    26  		MessageFlags:    0,
    27  		ChannelFlags:    1,
    28  		ExtraOpaqueData: make([]byte, 0),
    29  	}
    30  )
    31  
    32  var onionFailures = []FailureMessage{
    33  	&FailInvalidRealm{},
    34  	&FailTemporaryNodeFailure{},
    35  	&FailPermanentNodeFailure{},
    36  	&FailRequiredNodeFeatureMissing{},
    37  	&FailPermanentChannelFailure{},
    38  	&FailRequiredChannelFeatureMissing{},
    39  	&FailUnknownNextPeer{},
    40  	&FailIncorrectPaymentAmount{},
    41  	&FailFinalExpiryTooSoon{},
    42  	&FailMPPTimeout{},
    43  
    44  	NewFailIncorrectDetails(99, 100),
    45  	NewInvalidOnionVersion(testOnionHash),
    46  	NewInvalidOnionHmac(testOnionHash),
    47  	NewInvalidOnionKey(testOnionHash),
    48  	NewTemporaryChannelFailure(&testChannelUpdate),
    49  	NewTemporaryChannelFailure(nil),
    50  	NewAmountBelowMinimum(testAmount, testChannelUpdate),
    51  	NewFeeInsufficient(testAmount, testChannelUpdate),
    52  	NewIncorrectCltvExpiry(testCtlvExpiry, testChannelUpdate),
    53  	NewExpiryTooSoon(testChannelUpdate),
    54  	NewChannelDisabled(testFlags, testChannelUpdate),
    55  	NewFinalIncorrectCltvExpiry(testCtlvExpiry),
    56  	NewFinalIncorrectHtlcAmount(testAmount),
    57  	NewInvalidOnionPayload(testType, testOffset),
    58  }
    59  
    60  // TestEncodeDecodeCode tests the ability of onion errors to be properly encoded
    61  // and decoded.
    62  func TestEncodeDecodeCode(t *testing.T) {
    63  	for _, failure1 := range onionFailures {
    64  		var b bytes.Buffer
    65  
    66  		if err := EncodeFailure(&b, failure1, 0); err != nil {
    67  			t.Fatalf("unable to encode failure code(%v): %v",
    68  				failure1.Code(), err)
    69  		}
    70  
    71  		failure2, err := DecodeFailure(&b, 0)
    72  		if err != nil {
    73  			t.Fatalf("unable to decode failure code(%v): %v",
    74  				failure1.Code(), err)
    75  		}
    76  
    77  		if !reflect.DeepEqual(failure1, failure2) {
    78  			t.Fatalf("expected %v, got %v", spew.Sdump(failure1),
    79  				spew.Sdump(failure2))
    80  		}
    81  	}
    82  }
    83  
    84  // TestChannelUpdateCompatabilityParsing tests that we're able to properly read
    85  // out channel update messages encoded in an onion error payload that was
    86  // written in the legacy (type prefixed) format.
    87  func TestChannelUpdateCompatabilityParsing(t *testing.T) {
    88  	t.Parallel()
    89  
    90  	// We'll start by taking out test channel update, and encoding it into
    91  	// a set of raw bytes.
    92  	var b bytes.Buffer
    93  	if err := testChannelUpdate.Encode(&b, 0); err != nil {
    94  		t.Fatalf("unable to encode chan update: %v", err)
    95  	}
    96  
    97  	// Now that we have the set of bytes encoded, we'll ensure that we're
    98  	// able to decode it using our compatibility method, as it's a regular
    99  	// encoded channel update message.
   100  	var newChanUpdate ChannelUpdate
   101  	err := parseChannelUpdateCompatabilityMode(
   102  		bufio.NewReader(&b), &newChanUpdate, 0,
   103  	)
   104  	if err != nil {
   105  		t.Fatalf("unable to parse channel update: %v", err)
   106  	}
   107  
   108  	// At this point, we'll ensure that we get the exact same failure out
   109  	// on the other side.
   110  	if !reflect.DeepEqual(testChannelUpdate, newChanUpdate) {
   111  		t.Fatalf("mismatched channel updates: %v", err)
   112  	}
   113  
   114  	// We'll now reset then re-encoded the same channel update to try it in
   115  	// the proper compatible mode.
   116  	b.Reset()
   117  
   118  	// Before we encode the update itself, we'll also write out the 2-byte
   119  	// type in order to simulate the compat mode.
   120  	var tByte [2]byte
   121  	binary.BigEndian.PutUint16(tByte[:], MsgChannelUpdate)
   122  	b.Write(tByte[:])
   123  	if err := testChannelUpdate.Encode(&b, 0); err != nil {
   124  		t.Fatalf("unable to encode chan update: %v", err)
   125  	}
   126  
   127  	// We should be able to properly parse the encoded channel update
   128  	// message even with the extra two bytes.
   129  	var newChanUpdate2 ChannelUpdate
   130  	err = parseChannelUpdateCompatabilityMode(
   131  		bufio.NewReader(&b), &newChanUpdate2, 0,
   132  	)
   133  	if err != nil {
   134  		t.Fatalf("unable to parse channel update: %v", err)
   135  	}
   136  
   137  	if !reflect.DeepEqual(newChanUpdate2, newChanUpdate) {
   138  		t.Fatalf("mismatched channel updates: %v", err)
   139  	}
   140  }
   141  
   142  // TestWriteOnionErrorChanUpdate tests that we write an exact size for the
   143  // channel update in order to be more compliant with the parsers of other
   144  // implementations.
   145  func TestWriteOnionErrorChanUpdate(t *testing.T) {
   146  	t.Parallel()
   147  
   148  	// First, we'll write out the raw channel update so we can obtain the
   149  	// raw serialized length.
   150  	var b bytes.Buffer
   151  	update := testChannelUpdate
   152  	if err := update.Encode(&b, 0); err != nil {
   153  		t.Fatalf("unable to write update: %v", err)
   154  	}
   155  	trueUpdateLength := b.Len()
   156  
   157  	// Next, we'll use the function to encode the update as we would in a
   158  	// onion error message.
   159  	var errorBuf bytes.Buffer
   160  	err := writeOnionErrorChanUpdate(&errorBuf, &update, 0)
   161  	if err != nil {
   162  		t.Fatalf("unable to encode onion error: %v", err)
   163  	}
   164  
   165  	// Finally, read the length encoded and ensure that it matches the raw
   166  	// length.
   167  	var encodedLen uint16
   168  	if err := ReadElement(&errorBuf, &encodedLen); err != nil {
   169  		t.Fatalf("unable to read len: %v", err)
   170  	}
   171  	if uint16(trueUpdateLength) != encodedLen {
   172  		t.Fatalf("wrong length written: expected %v, got %v",
   173  			trueUpdateLength, encodedLen)
   174  	}
   175  }
   176  
   177  // TestFailIncorrectDetailsOptionalAmount tests that we're able to decode an
   178  // FailIncorrectDetails error that doesn't have the optional amount. This
   179  // ensures we're able to decode FailIncorrectDetails messages from older nodes.
   180  func TestFailIncorrectDetailsOptionalAmount(t *testing.T) {
   181  	t.Parallel()
   182  
   183  	onionError := &mockFailIncorrectDetailsNoAmt{}
   184  
   185  	var b bytes.Buffer
   186  	if err := EncodeFailure(&b, onionError, 0); err != nil {
   187  		t.Fatalf("unable to encode failure: %v", err)
   188  	}
   189  
   190  	onionError2, err := DecodeFailure(bytes.NewReader(b.Bytes()), 0)
   191  	if err != nil {
   192  		t.Fatalf("unable to decode error: %v", err)
   193  	}
   194  
   195  	invalidDetailsErr, ok := onionError2.(*FailIncorrectDetails)
   196  	if !ok {
   197  		t.Fatalf("expected FailIncorrectDetails, but got %T",
   198  			onionError2)
   199  	}
   200  
   201  	if invalidDetailsErr.amount != 0 {
   202  		t.Fatalf("expected amount to be zero")
   203  	}
   204  	if invalidDetailsErr.height != 0 {
   205  		t.Fatalf("height incorrect")
   206  	}
   207  }
   208  
   209  type mockFailIncorrectDetailsNoAmt struct {
   210  }
   211  
   212  func (f *mockFailIncorrectDetailsNoAmt) Code() FailCode {
   213  	return CodeIncorrectOrUnknownPaymentDetails
   214  }
   215  
   216  func (f *mockFailIncorrectDetailsNoAmt) Error() string {
   217  	return ""
   218  }
   219  
   220  func (f *mockFailIncorrectDetailsNoAmt) Decode(r io.Reader, pver uint32) error {
   221  	return nil
   222  }
   223  
   224  func (f *mockFailIncorrectDetailsNoAmt) Encode(w io.Writer, pver uint32) error {
   225  	return nil
   226  }
   227  
   228  // TestFailIncorrectDetailsOptionalHeight tests that we're able to decode an
   229  // FailIncorrectDetails error that doesn't have the optional height. This
   230  // ensures we're able to decode FailIncorrectDetails messages from older nodes.
   231  func TestFailIncorrectDetailsOptionalHeight(t *testing.T) {
   232  	t.Parallel()
   233  
   234  	onionError := &mockFailIncorrectDetailsNoHeight{
   235  		amount: uint64(123),
   236  	}
   237  
   238  	var b bytes.Buffer
   239  	if err := EncodeFailure(&b, onionError, 0); err != nil {
   240  		t.Fatalf("unable to encode failure: %v", err)
   241  	}
   242  
   243  	onionError2, err := DecodeFailure(bytes.NewReader(b.Bytes()), 0)
   244  	if err != nil {
   245  		t.Fatalf("unable to decode error: %v", err)
   246  	}
   247  
   248  	invalidDetailsErr, ok := onionError2.(*FailIncorrectDetails)
   249  	if !ok {
   250  		t.Fatalf("expected FailIncorrectDetails, but got %T",
   251  			onionError2)
   252  	}
   253  
   254  	if invalidDetailsErr.amount != 123 {
   255  		t.Fatalf("amount incorrect")
   256  	}
   257  	if invalidDetailsErr.height != 0 {
   258  		t.Fatalf("height incorrect")
   259  	}
   260  }
   261  
   262  type mockFailIncorrectDetailsNoHeight struct {
   263  	amount uint64
   264  }
   265  
   266  func (f *mockFailIncorrectDetailsNoHeight) Code() FailCode {
   267  	return CodeIncorrectOrUnknownPaymentDetails
   268  }
   269  
   270  func (f *mockFailIncorrectDetailsNoHeight) Error() string {
   271  	return ""
   272  }
   273  
   274  func (f *mockFailIncorrectDetailsNoHeight) Decode(r io.Reader, pver uint32) error {
   275  	return nil
   276  }
   277  
   278  func (f *mockFailIncorrectDetailsNoHeight) Encode(w *bytes.Buffer,
   279  	pver uint32) error {
   280  
   281  	return WriteUint64(w, f.amount)
   282  }