lnwire+htlcswitch: report height for invalid payment details failure

Extends the invalid payment details failure with the new accept height
field. This allows sender to distinguish between a genuine invalid
details situation and a delay caused by intermediate nodes.
This commit is contained in:
Joost Jager committed 2019-09-16 10:10:20 +02:00
1 parent 1de08c4780
commit f60e4b1e14
9 files changed
+212 -118

No files matched your search

+27 -4
View File
@@ -333,13 +333,19 @@ func (f *FailIncorrectPaymentAmount) Error() string {
type FailIncorrectDetails struct {
// amount is the value of the extended HTLC.
amount MilliSatoshi
// height is the block height when the htlc was received.
height uint32
}
// NewFailIncorrectDetails makes a new instance of the FailIncorrectDetails
// error bound to the specified HTLC amount.
func NewFailIncorrectDetails(amt MilliSatoshi) *FailIncorrectDetails {
// error bound to the specified HTLC amount and acceptance height.
func NewFailIncorrectDetails(amt MilliSatoshi,
height uint32) *FailIncorrectDetails {
return &FailIncorrectDetails{
amount: amt,
height: height,
}
}
@@ -348,6 +354,11 @@ func (f *FailIncorrectDetails) Amount() MilliSatoshi {
return f.amount
}
// Height is the block height when the htlc was received.
func (f *FailIncorrectDetails) Height() uint32 {
return f.height
}
// Code returns the failure unique code.
//
// NOTE: Part of the FailureMessage interface.
@@ -360,7 +371,8 @@ func (f *FailIncorrectDetails) Code() FailCode {
// NOTE: Implements the error interface.
func (f *FailIncorrectDetails) Error() string {
return fmt.Sprintf(
"%v(amt=%v)", CodeIncorrectOrUnknownPaymentDetails, f.amount,
"%v(amt=%v, height=%v)", CodeIncorrectOrUnknownPaymentDetails,
f.amount, f.height,
)
}
@@ -381,6 +393,17 @@ func (f *FailIncorrectDetails) Decode(r io.Reader, pver uint32) error {
return err
}
// At a later stage, the height field was also tacked on. We need to
// check for io.EOF here as well.
err = ReadElement(r, &f.height)
switch {
case err == io.EOF:
return nil
case err != nil:
return err
}
return nil
}
@@ -388,7 +411,7 @@ func (f *FailIncorrectDetails) Decode(r io.Reader, pver uint32) error {
//
// NOTE: Part of the Serializable interface.
func (f *FailIncorrectDetails) Encode(w io.Writer, pver uint32) error {
return WriteElement(w, f.amount)
return WriteElements(w, f.amount, f.height)
}
// FailFinalExpiryTooSoon is returned if the cltv_expiry is too low, the final
+58 -1
View File
@@ -37,7 +37,7 @@ var onionFailures = []FailureMessage{
&FailIncorrectPaymentAmount{},
&FailFinalExpiryTooSoon{},
NewFailIncorrectDetails(99),
NewFailIncorrectDetails(99, 100),
NewInvalidOnionVersion(testOnionHash),
NewInvalidOnionHmac(testOnionHash),
NewInvalidOnionKey(testOnionHash),
@@ -196,6 +196,9 @@ func TestFailIncorrectDetailsOptionalAmount(t *testing.T) {
if invalidDetailsErr.amount != 0 {
t.Fatalf("expected amount to be zero")
}
if invalidDetailsErr.height != 0 {
t.Fatalf("height incorrect")
}
}
type mockFailIncorrectDetailsNoAmt struct {
@@ -216,3 +219,57 @@ func (f *mockFailIncorrectDetailsNoAmt) Decode(r io.Reader, pver uint32) error {
func (f *mockFailIncorrectDetailsNoAmt) Encode(w io.Writer, pver uint32) error {
return nil
}
// TestFailIncorrectDetailsOptionalHeight tests that we're able to decode an
// FailIncorrectDetails error that doesn't have the optional height. This
// ensures we're able to decode FailIncorrectDetails messages from older nodes.
func TestFailIncorrectDetailsOptionalHeight(t *testing.T) {
t.Parallel()
onionError := &mockFailIncorrectDetailsNoHeight{
amount: uint64(123),
}
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)
}
invalidDetailsErr, ok := onionError2.(*FailIncorrectDetails)
if !ok {
t.Fatalf("expected FailIncorrectDetails, but got %T",
onionError2)
}
if invalidDetailsErr.amount != 123 {
t.Fatalf("amount incorrect")
}
if invalidDetailsErr.height != 0 {
t.Fatalf("height incorrect")
}
}
type mockFailIncorrectDetailsNoHeight struct {
amount uint64
}
func (f *mockFailIncorrectDetailsNoHeight) Code() FailCode {
return CodeIncorrectOrUnknownPaymentDetails
}
func (f *mockFailIncorrectDetailsNoHeight) Error() string {
return ""
}
func (f *mockFailIncorrectDetailsNoHeight) Decode(r io.Reader, pver uint32) error {
return nil
}
func (f *mockFailIncorrectDetailsNoHeight) Encode(w io.Writer, pver uint32) error {
return WriteElement(w, f.amount)
}