Merge pull request #2485 from halseth/error-codes-dont-send

[lnwire+funding] Don't send ErrorCode on wire
This commit is contained in:
Olaoluwa Osuntokun 2019-09-23 17:50:27 -07:00 committed by GitHub
commit 9b1ecbd3fa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 64 additions and 95 deletions

@ -26,7 +26,6 @@ import (
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing" "github.com/lightningnetwork/lnd/routing"
"golang.org/x/crypto/salsa20" "golang.org/x/crypto/salsa20"
"google.golang.org/grpc"
) )
const ( const (
@ -858,19 +857,18 @@ func (f *fundingManager) failFundingFlow(peer lnpeer.Peer, tempChanID [32]byte,
} }
// We only send the exact error if it is part of out whitelisted set of // We only send the exact error if it is part of out whitelisted set of
// errors (lnwire.ErrorCode or lnwallet.ReservationError). // errors (lnwire.FundingError or lnwallet.ReservationError).
var msg lnwire.ErrorData var msg lnwire.ErrorData
switch e := fundingErr.(type) { switch e := fundingErr.(type) {
// Let the actual error message be sent to the remote. // Let the actual error message be sent to the remote for the
// whitelisted types.
case lnwallet.ReservationError: case lnwallet.ReservationError:
msg = lnwire.ErrorData(e.Error()) msg = lnwire.ErrorData(e.Error())
case lnwire.FundingError:
msg = lnwire.ErrorData(e.Error())
// Send the status code. // For all other error types we just send a generic error.
case lnwire.ErrorCode:
msg = lnwire.ErrorData{byte(e)}
// We just send a generic error.
default: default:
msg = lnwire.ErrorData("funding failed due to internal error") msg = lnwire.ErrorData("funding failed due to internal error")
} }
@ -2957,31 +2955,18 @@ func (f *fundingManager) handleErrorMsg(fmsg *fundingErrorMsg) {
resCtx, err := f.cancelReservationCtx(fmsg.peerKey, chanID) resCtx, err := f.cancelReservationCtx(fmsg.peerKey, chanID)
if err != nil { if err != nil {
fndgLog.Warnf("Received error for non-existent funding "+ fndgLog.Warnf("Received error for non-existent funding "+
"flow: %v (%v)", err, spew.Sdump(protocolErr)) "flow: %v (%v)", err, protocolErr.Error())
return return
} }
// If we did indeed find the funding workflow, then we'll return the // If we did indeed find the funding workflow, then we'll return the
// error back to the caller (if any), and cancel the workflow itself. // error back to the caller (if any), and cancel the workflow itself.
lnErr := lnwire.ErrorCode(protocolErr.Data[0]) fundingErr := fmt.Errorf("received funding error from %x: %v",
fndgLog.Errorf("Received funding error from %x: %v", fmsg.peerKey.SerializeCompressed(), protocolErr.Error(),
fmsg.peerKey.SerializeCompressed(), string(protocolErr.Data),
) )
fndgLog.Errorf(fundingErr.Error())
// If this isn't a simple error code, then we'll display the entire resCtx.err <- fundingErr
// thing.
if len(protocolErr.Data) > 1 {
err = grpc.Errorf(
lnErr.ToGrpcCode(), string(protocolErr.Data),
)
} else {
// Otherwise, we'll attempt to display just the error code
// itself.
err = grpc.Errorf(
lnErr.ToGrpcCode(), lnErr.String(),
)
}
resCtx.err <- err
} }
// pruneZombieReservations loops through all pending reservations and fails the // pruneZombieReservations loops through all pending reservations and fails the

@ -602,7 +602,7 @@ func fundChannel(t *testing.T, alice, bob *testNode, localFundingAmt,
if gotError { if gotError {
t.Fatalf("expected OpenChannel to be sent "+ t.Fatalf("expected OpenChannel to be sent "+
"from bob, instead got error: %v", "from bob, instead got error: %v",
lnwire.ErrorCode(errorMsg.Data[0])) errorMsg.Error())
} }
t.Fatalf("expected OpenChannel to be sent from "+ t.Fatalf("expected OpenChannel to be sent from "+
"alice, instead got %T", aliceMsg) "alice, instead got %T", aliceMsg)
@ -728,7 +728,7 @@ func assertFundingMsgSent(t *testing.T, msgChan chan lnwire.Message,
errorMsg, gotError := msg.(*lnwire.Error) errorMsg, gotError := msg.(*lnwire.Error)
if gotError { if gotError {
t.Fatalf("expected %s to be sent, instead got error: %v", t.Fatalf("expected %s to be sent, instead got error: %v",
msgType, lnwire.ErrorCode(errorMsg.Data[0])) msgType, errorMsg.Error())
} }
_, _, line, _ := runtime.Caller(1) _, _, line, _ := runtime.Caller(1)
@ -1469,7 +1469,7 @@ func TestFundingManagerPeerTimeoutAfterInitFunding(t *testing.T) {
if gotError { if gotError {
t.Fatalf("expected OpenChannel to be sent "+ t.Fatalf("expected OpenChannel to be sent "+
"from bob, instead got error: %v", "from bob, instead got error: %v",
lnwire.ErrorCode(errorMsg.Data[0])) errorMsg.Error())
} }
t.Fatalf("expected OpenChannel to be sent from "+ t.Fatalf("expected OpenChannel to be sent from "+
"alice, instead got %T", aliceMsg) "alice, instead got %T", aliceMsg)
@ -1531,7 +1531,7 @@ func TestFundingManagerPeerTimeoutAfterFundingOpen(t *testing.T) {
if gotError { if gotError {
t.Fatalf("expected OpenChannel to be sent "+ t.Fatalf("expected OpenChannel to be sent "+
"from bob, instead got error: %v", "from bob, instead got error: %v",
lnwire.ErrorCode(errorMsg.Data[0])) errorMsg.Error())
} }
t.Fatalf("expected OpenChannel to be sent from "+ t.Fatalf("expected OpenChannel to be sent from "+
"alice, instead got %T", aliceMsg) "alice, instead got %T", aliceMsg)
@ -1602,7 +1602,7 @@ func TestFundingManagerPeerTimeoutAfterFundingAccept(t *testing.T) {
if gotError { if gotError {
t.Fatalf("expected OpenChannel to be sent "+ t.Fatalf("expected OpenChannel to be sent "+
"from bob, instead got error: %v", "from bob, instead got error: %v",
lnwire.ErrorCode(errorMsg.Data[0])) errorMsg.Error())
} }
t.Fatalf("expected OpenChannel to be sent from "+ t.Fatalf("expected OpenChannel to be sent from "+
"alice, instead got %T", aliceMsg) "alice, instead got %T", aliceMsg)
@ -2326,7 +2326,7 @@ func TestFundingManagerCustomChannelParameters(t *testing.T) {
if gotError { if gotError {
t.Fatalf("expected OpenChannel to be sent "+ t.Fatalf("expected OpenChannel to be sent "+
"from bob, instead got error: %v", "from bob, instead got error: %v",
lnwire.ErrorCode(errorMsg.Data[0])) errorMsg.Error())
} }
t.Fatalf("expected OpenChannel to be sent from "+ t.Fatalf("expected OpenChannel to be sent from "+
"alice, instead got %T", aliceMsg) "alice, instead got %T", aliceMsg)
@ -2561,7 +2561,7 @@ func TestFundingManagerMaxPendingChannels(t *testing.T) {
if gotError { if gotError {
t.Fatalf("expected OpenChannel to be sent "+ t.Fatalf("expected OpenChannel to be sent "+
"from bob, instead got error: %v", "from bob, instead got error: %v",
lnwire.ErrorCode(errorMsg.Data[0])) errorMsg.Error())
} }
t.Fatalf("expected OpenChannel to be sent from "+ t.Fatalf("expected OpenChannel to be sent from "+
"alice, instead got %T", aliceMsg) "alice, instead got %T", aliceMsg)
@ -2725,7 +2725,7 @@ func TestFundingManagerRejectPush(t *testing.T) {
if gotError { if gotError {
t.Fatalf("expected OpenChannel to be sent "+ t.Fatalf("expected OpenChannel to be sent "+
"from bob, instead got error: %v", "from bob, instead got error: %v",
lnwire.ErrorCode(errorMsg.Data[0])) errorMsg.Error())
} }
t.Fatalf("expected OpenChannel to be sent from "+ t.Fatalf("expected OpenChannel to be sent from "+
"alice, instead got %T", aliceMsg) "alice, instead got %T", aliceMsg)
@ -2736,9 +2736,9 @@ func TestFundingManagerRejectPush(t *testing.T) {
// Assert Bob responded with an ErrNonZeroPushAmount error. // Assert Bob responded with an ErrNonZeroPushAmount error.
err := assertFundingMsgSent(t, bob.msgChan, "Error").(*lnwire.Error) err := assertFundingMsgSent(t, bob.msgChan, "Error").(*lnwire.Error)
if "Non-zero push amounts are disabled" != string(err.Data) { if !strings.Contains(err.Error(), "Non-zero push amounts are disabled") {
t.Fatalf("expected ErrNonZeroPushAmount error, got \"%v\"", t.Fatalf("expected ErrNonZeroPushAmount error, got \"%v\"",
string(err.Data)) err.Error())
} }
} }
@ -2782,7 +2782,7 @@ func TestFundingManagerMaxConfs(t *testing.T) {
if gotError { if gotError {
t.Fatalf("expected OpenChannel to be sent "+ t.Fatalf("expected OpenChannel to be sent "+
"from bob, instead got error: %v", "from bob, instead got error: %v",
lnwire.ErrorCode(errorMsg.Data[0])) errorMsg.Error())
} }
t.Fatalf("expected OpenChannel to be sent from "+ t.Fatalf("expected OpenChannel to be sent from "+
"alice, instead got %T", aliceMsg) "alice, instead got %T", aliceMsg)
@ -2805,9 +2805,9 @@ func TestFundingManagerMaxConfs(t *testing.T) {
// Alice should respond back with an error indicating MinAcceptDepth is // Alice should respond back with an error indicating MinAcceptDepth is
// too large. // too large.
err := assertFundingMsgSent(t, alice.msgChan, "Error").(*lnwire.Error) err := assertFundingMsgSent(t, alice.msgChan, "Error").(*lnwire.Error)
if !strings.Contains(string(err.Data), "minimum depth") { if !strings.Contains(err.Error(), "minimum depth") {
t.Fatalf("expected ErrNumConfsTooLarge, got \"%v\"", t.Fatalf("expected ErrNumConfsTooLarge, got \"%v\"",
string(err.Data)) err.Error())
} }
} }

@ -1882,13 +1882,9 @@ func (l *channelLink) handleUpstreamMsg(msg lnwire.Message) {
// Error received from remote, MUST fail channel, but should // Error received from remote, MUST fail channel, but should
// only print the contents of the error message if all // only print the contents of the error message if all
// characters are printable ASCII. // characters are printable ASCII.
errMsg := "non-ascii data"
if isASCII(msg.Data) {
errMsg = string(msg.Data)
}
l.fail(LinkFailureError{code: ErrRemoteError}, l.fail(LinkFailureError{code: ErrRemoteError},
"ChannelPoint(%v): received error from peer: %v", "ChannelPoint(%v): received error from peer: %v",
l.channel.ChannelPoint(), errMsg) l.channel.ChannelPoint(), msg.Error())
default: default:
log.Warnf("ChannelPoint(%v): received unknown message of type %T", log.Warnf("ChannelPoint(%v): received unknown message of type %T",
l.channel.ChannelPoint(), msg) l.channel.ChannelPoint(), msg)
@ -3100,16 +3096,3 @@ func (l *channelLink) tracef(format string, a ...interface{}) {
msg := fmt.Sprintf(format, a...) msg := fmt.Sprintf(format, a...)
log.Tracef("ChannelLink(%s) %s", l.ShortChanID(), msg) log.Tracef("ChannelLink(%s) %s", l.ShortChanID(), msg)
} }
// isASCII is a helper method that checks whether all bytes in `data` would be
// printable ASCII characters if interpreted as a string.
func isASCII(data []byte) bool {
isASCII := true
for _, c := range data {
if c < 32 || c > 126 {
isASCII = false
break
}
}
return isASCII
}

@ -42,7 +42,6 @@ import (
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing" "github.com/lightningnetwork/lnd/routing"
"golang.org/x/net/context" "golang.org/x/net/context"
"google.golang.org/grpc"
) )
var ( var (
@ -6187,7 +6186,9 @@ func testMaxPendingChannels(net *lntest.NetworkHarness, t *harnessTest) {
if err == nil { if err == nil {
t.Fatalf("error wasn't received") t.Fatalf("error wasn't received")
} else if grpc.Code(err) != lnwire.ErrMaxPendingChannels.ToGrpcCode() { } else if !strings.Contains(
err.Error(), lnwire.ErrMaxPendingChannels.Error(),
) {
t.Fatalf("not expected error was received: %v", err) t.Fatalf("not expected error was received: %v", err)
} }

@ -1,41 +1,32 @@
package lnwire package lnwire
import ( import (
"fmt"
"io" "io"
"google.golang.org/grpc/codes"
) )
// ErrorCode represents the short error code for each of the defined errors // FundingError represents a set of errors that can be encountered and sent
// within the Lightning Network protocol spec. // during the funding workflow.
type ErrorCode uint8 type FundingError uint8
// ToGrpcCode is used to generate gRPC specific code which will be propagated
// to the ln rpc client. This code is used to have more detailed view of what
// goes wrong and also in order to have the ability pragmatically determine the
// error and take specific actions on the client side.
func (e ErrorCode) ToGrpcCode() codes.Code {
return (codes.Code)(e) + 100
}
const ( const (
// ErrMaxPendingChannels is returned by remote peer when the number of // ErrMaxPendingChannels is returned by remote peer when the number of
// active pending channels exceeds their maximum policy limit. // active pending channels exceeds their maximum policy limit.
ErrMaxPendingChannels ErrorCode = 1 ErrMaxPendingChannels FundingError = 1
// ErrSynchronizingChain is returned by a remote peer that receives a // ErrSynchronizingChain is returned by a remote peer that receives a
// channel update or a funding request while their still syncing to the // channel update or a funding request while their still syncing to the
// latest state of the blockchain. // latest state of the blockchain.
ErrSynchronizingChain ErrorCode = 2 ErrSynchronizingChain FundingError = 2
// ErrChanTooLarge is returned by a remote peer that receives a // ErrChanTooLarge is returned by a remote peer that receives a
// FundingOpen request for a channel that is above their current // FundingOpen request for a channel that is above their current
// soft-limit. // soft-limit.
ErrChanTooLarge ErrorCode = 3 ErrChanTooLarge FundingError = 3
) )
// String returns a human readable version of the target ErrorCode. // String returns a human readable version of the target FundingError.
func (e ErrorCode) String() string { func (e FundingError) String() string {
switch e { switch e {
case ErrMaxPendingChannels: case ErrMaxPendingChannels:
return "Number of pending channels exceed maximum" return "Number of pending channels exceed maximum"
@ -48,10 +39,10 @@ func (e ErrorCode) String() string {
} }
} }
// Error returns the human redable version of the target ErrorCode. // Error returns the human redable version of the target FundingError.
// //
// Satisfies the Error interface. // NOTE: Satisfies the Error interface.
func (e ErrorCode) Error() string { func (e FundingError) Error() string {
return e.String() return e.String()
} }
@ -65,8 +56,6 @@ type ErrorData []byte
// format is purposefully general in order to allow expression of a wide array // format is purposefully general in order to allow expression of a wide array
// of possible errors. Each Error message is directed at a particular open // of possible errors. Each Error message is directed at a particular open
// channel referenced by ChannelPoint. // channel referenced by ChannelPoint.
//
// TODO(roasbeef): remove the error code
type Error struct { type Error struct {
// ChanID references the active channel in which the error occurred // ChanID references the active channel in which the error occurred
// within. If the ChanID is all zeros, then this error applies to the // within. If the ChanID is all zeros, then this error applies to the
@ -87,6 +76,18 @@ func NewError() *Error {
// interface. // interface.
var _ Message = (*Error)(nil) var _ Message = (*Error)(nil)
// Error returns the string representation to Error.
//
// NOTE: Satisfies the error interface.
func (c *Error) Error() string {
errMsg := "non-ascii data"
if isASCII(c.Data) {
errMsg = string(c.Data)
}
return fmt.Sprintf("chan_id=%v, err=%v", c.ChanID, errMsg)
}
// Decode deserializes a serialized Error message stored in the passed // Decode deserializes a serialized Error message stored in the passed
// io.Reader observing the specified protocol version. // io.Reader observing the specified protocol version.
// //
@ -125,3 +126,14 @@ func (c *Error) MaxPayloadLength(uint32) uint32 {
// 32 + 2 + 65501 // 32 + 2 + 65501
return 65535 return 65535
} }
// isASCII is a helper method that checks whether all bytes in `data` would be
// printable ASCII characters if interpreted as a string.
func isASCII(data []byte) bool {
for _, c := range data {
if c < 32 || c > 126 {
return false
}
}
return true
}

@ -117,12 +117,6 @@ func WriteElement(w io.Writer, element interface{}) error {
if _, err := w.Write(b[:]); err != nil { if _, err := w.Write(b[:]); err != nil {
return err return err
} }
case ErrorCode:
var b [2]byte
binary.BigEndian.PutUint16(b[:], uint16(e))
if _, err := w.Write(b[:]); err != nil {
return err
}
case MilliSatoshi: case MilliSatoshi:
var b [8]byte var b [8]byte
binary.BigEndian.PutUint64(b[:], uint64(e)) binary.BigEndian.PutUint64(b[:], uint64(e))
@ -506,12 +500,6 @@ func ReadElement(r io.Reader, element interface{}) error {
return err return err
} }
*e = ChanUpdateChanFlags(b[0]) *e = ChanUpdateChanFlags(b[0])
case *ErrorCode:
var b [2]byte
if _, err := io.ReadFull(r, b[:]); err != nil {
return err
}
*e = ErrorCode(binary.BigEndian.Uint16(b[:]))
case *uint32: case *uint32:
var b [4]byte var b [4]byte
if _, err := io.ReadFull(r, b[:]); err != nil { if _, err := io.ReadFull(r, b[:]); err != nil {

@ -1250,7 +1250,7 @@ func messageSummary(msg lnwire.Message) string {
msg.ChanID, msg.ID, msg.FailureCode) msg.ChanID, msg.ID, msg.FailureCode)
case *lnwire.Error: case *lnwire.Error:
return fmt.Sprintf("chan_id=%v, err=%v", msg.ChanID, string(msg.Data)) return fmt.Sprintf("%v", msg.Error())
case *lnwire.AnnounceSignatures: case *lnwire.AnnounceSignatures:
return fmt.Sprintf("chan_id=%v, short_chan_id=%v", msg.ChannelID, return fmt.Sprintf("chan_id=%v, short_chan_id=%v", msg.ChannelID,