From d17b11862b15c1a8df2464895e43c5b2708eb4e4 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Sun, 16 Apr 2017 15:23:40 -0700 Subject: [PATCH] lnwire: convert ErrorGeneric to Error With this change we move one step closer to matching the wire protocol currently defined within the spec. --- lnwire/error.go | 123 +++++++++++++++++ lnwire/error_generic.go | 125 ------------------ .../{error_generic_test.go => error_test.go} | 17 ++- lnwire/lnwire.go | 21 +++ lnwire/message.go | 6 +- 5 files changed, 155 insertions(+), 137 deletions(-) create mode 100644 lnwire/error.go delete mode 100644 lnwire/error_generic.go rename lnwire/{error_generic_test.go => error_test.go} (56%) diff --git a/lnwire/error.go b/lnwire/error.go new file mode 100644 index 00000000..f8185cb5 --- /dev/null +++ b/lnwire/error.go @@ -0,0 +1,123 @@ +package lnwire + +import ( + "fmt" + "io" + "math" + + "google.golang.org/grpc/codes" +) + +// ErrorCode represents the short error code for each of the defined errors +// within the Lightning Network protocol spec. +type ErrorCode uint16 + +// 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 ( + // ErrMaxPendingChannels is returned by remote peer when the number of + // active pending channels exceeds their maximum policy limit. + ErrMaxPendingChannels ErrorCode = 1 + + // ErrSynchronizingChain is returned by a remote peer that receives a + // channel update or a funding request while their still syncing to the + // latest state of the blockchain. + ErrSynchronizingChain ErrorCode = 2 +) + +// ErrorData is a set of bytes associated with a particular sent error. A +// receiving node SHOULD only print out data verbatim if the string is composed +// solely of printable ASCII characters. For reference, the printable character +// set includes byte values 32 through 127 inclusive. +type ErrorData []byte + +// Error represents a generic error bound to an exact channel. The message +// 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 +// channel referenced by ChannelPoint. +type Error struct { + // ChanID references the active channel in which the error occurred + // within. If the ChanID is all zeroes, then this error applies to the + // entire established connection. + ChanID ChannelID + + // Code is the short error code that succinctly identifies the error + // code. This is similar field is similar to HTTP error codes. + // + // TODO(roasbeef): make PR to repo to add error codes, in addition to + // what's there atm + Code ErrorCode + + // Data is the attached error data that describes the exact failure + // which caused the error message to be sent. + Data ErrorData +} + +// NewError creates a new Error message. +func NewError() *Error { + return &Error{} +} + +// A compile time check to ensure Error implements the lnwire.Message +// interface. +var _ Message = (*Error)(nil) + +// Decode deserializes a serialized Error message stored in the passed +// io.Reader observing the specified protocol version. +// +// This is part of the lnwire.Message interface. +func (c *Error) Decode(r io.Reader, pver uint32) error { + return readElements(r, + &c.ChanID, + &c.Code, + &c.Data, + ) +} + +// Encode serializes the target Error into the passed io.Writer observing the +// protocol version specified. +// +// This is part of the lnwire.Message interface. +func (c *Error) Encode(w io.Writer, pver uint32) error { + return writeElements(w, + c.ChanID, + c.Code, + c.Data, + ) +} + +// Command returns the integer uniquely identifying an Error message on the +// wire. +// +// This is part of the lnwire.Message interface. +func (c *Error) Command() uint32 { + return CmdError +} + +// MaxPayloadLength returns the maximum allowed payload size for a Error +// complete message observing the specified protocol version. +// +// This is part of the lnwire.Message interface. +func (c *Error) MaxPayloadLength(uint32) uint32 { + // 32 + 2 + 655326 + return 65536 +} + +// Validate performs any necessary sanity checks to ensure all fields present +// on the Error are valid. +// +// This is part of the lnwire.Message interface. +func (c *Error) Validate() error { + if len(c.Data) > math.MaxUint16 { + return fmt.Errorf("problem string length too long") + } + + // We're good! + return nil +} diff --git a/lnwire/error_generic.go b/lnwire/error_generic.go deleted file mode 100644 index 4476f9f4..00000000 --- a/lnwire/error_generic.go +++ /dev/null @@ -1,125 +0,0 @@ -package lnwire - -import ( - "fmt" - "io" - - "github.com/roasbeef/btcd/wire" - "google.golang.org/grpc/codes" -) - -// ErrorCode represents the short error code for each of the defined errors -// within the Lightning Network protocol spec. -type ErrorCode uint16 - -// 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 ( - // ErrMaxPendingChannels is returned by remote peer when the number of - // active pending channels exceeds their maximum policy limit. - ErrMaxPendingChannels ErrorCode = 1 - - // ErrSynchronizingChain is returned by a remote peer that receives a - // channel update or a funding request while their still syncing to the - // latest state of the blockchain. - ErrSynchronizingChain ErrorCode = 2 -) - -// ErrorGeneric represents a generic error bound to an exact channel. The -// message format is purposefully general in order to allow expression of a wide -// array of possible errors. Each ErrorGeneric message is directed at a particular -// open channel referenced by ChannelPoint. -type ErrorGeneric struct { - // ChannelPoint references the active channel in which the error - // occurred within. A ChannelPoint of zeroHash:0 denotes this error - // applies to the entire established connection. - ChannelPoint wire.OutPoint - - // PendingChannelID allows peers communicate errors in the context of a - // particular pending channel. With this field, once a peer reads an - // ErrorGeneric message with the PendingChannelID field set, then they - // can forward the error to the fundingManager who can handle it - // properly. - PendingChannelID uint64 - - // Code is the short error ID which describes the nature of the error. - Code ErrorCode - - // Problem is a human-readable string further elaborating upon the - // nature of the exact error. The maximum allowed length of this - // message is 8192 bytes. - Problem string -} - -// NewErrorGeneric creates a new ErrorGeneric message. -func NewErrorGeneric() *ErrorGeneric { - return &ErrorGeneric{} -} - -// A compile time check to ensure ErrorGeneric implements the lnwire.Message -// interface. -var _ Message = (*ErrorGeneric)(nil) - -// Decode deserializes a serialized ErrorGeneric message stored in the -// passed io.Reader observing the specified protocol version. -// -// This is part of the lnwire.Message interface. -func (c *ErrorGeneric) Decode(r io.Reader, pver uint32) error { - // ChannelPoint(8) - // Problem - return readElements(r, - &c.ChannelPoint, - &c.Code, - &c.Problem, - &c.PendingChannelID, - ) -} - -// Encode serializes the target ErrorGeneric into the passed io.Writer -// observing the protocol version specified. -// -// This is part of the lnwire.Message interface. -func (c *ErrorGeneric) Encode(w io.Writer, pver uint32) error { - return writeElements(w, - c.ChannelPoint, - c.Code, - c.Problem, - c.PendingChannelID, - ) -} - -// Command returns the integer uniquely identifying an ErrorGeneric message on -// the wire. -// -// This is part of the lnwire.Message interface. -func (c *ErrorGeneric) Command() uint32 { - return CmdErrorGeneric -} - -// MaxPayloadLength returns the maximum allowed payload size for a -// ErrorGeneric complete message observing the specified protocol version. -// -// This is part of the lnwire.Message interface. -func (c *ErrorGeneric) MaxPayloadLength(uint32) uint32 { - // 8+8192 - return 8208 -} - -// Validate performs any necessary sanity checks to ensure all fields present -// on the ErrorGeneric are valid. -// -// This is part of the lnwire.Message interface. -func (c *ErrorGeneric) Validate() error { - if len(c.Problem) > 8192 { - return fmt.Errorf("problem string length too long") - } - - // We're good! - return nil -} diff --git a/lnwire/error_generic_test.go b/lnwire/error_test.go similarity index 56% rename from lnwire/error_generic_test.go rename to lnwire/error_test.go index 4ffb941f..57251268 100644 --- a/lnwire/error_generic_test.go +++ b/lnwire/error_test.go @@ -6,22 +6,21 @@ import ( "testing" ) -func TestErrorGenericEncodeDecode(t *testing.T) { - eg := &ErrorGeneric{ - ChannelPoint: *outpoint1, - PendingChannelID: 1, - Code: 99, - Problem: "Hello world!", +func TestErrorEncodeDecode(t *testing.T) { + eg := &Error{ + ChanID: ChannelID(revHash), + Code: 99, + Data: []byte{'k', 'e', 'k'}, } - // Next encode the EG message into an empty bytes buffer. + // Next encode the error message into an empty bytes buffer. var b bytes.Buffer if err := eg.Encode(&b, 0); err != nil { t.Fatalf("unable to encode ErrorGeneric: %v", err) } - // Deserialize the encoded EG message into a new empty struct. - eg2 := &ErrorGeneric{} + // Deserialize the encoded error message into a new empty struct. + eg2 := &Error{} if err := eg2.Decode(&b, 0); err != nil { t.Fatalf("unable to decode ErrorGeneric: %v", err) } diff --git a/lnwire/lnwire.go b/lnwire/lnwire.go index d37df071..21f7d82c 100644 --- a/lnwire/lnwire.go +++ b/lnwire/lnwire.go @@ -174,6 +174,16 @@ func writeElement(w io.Writer, element interface{}) error { if _, err := w.Write(b[:]); err != nil { return err } + case ErrorData: + var l [2]byte + binary.BigEndian.PutUint16(l[:], uint16(len(e))) + if _, err := w.Write(l[:]); err != nil { + return err + } + + if _, err := w.Write(e[:]); err != nil { + return err + } case OpaqueReason: var l [2]byte binary.BigEndian.PutUint16(l[:], uint16(len(e))) @@ -514,6 +524,17 @@ func readElement(r io.Reader, element interface{}) error { if _, err := io.ReadFull(r, *e); err != nil { return err } + case *ErrorData: + var l [2]byte + if _, err := io.ReadFull(r, l[:]); err != nil { + return err + } + errorLen := binary.BigEndian.Uint16(l[:]) + + *e = ErrorData(make([]byte, errorLen)) + if _, err := io.ReadFull(r, *e); err != nil { + return err + } case *[6]byte: if _, err := io.ReadFull(r, e[:]); err != nil { return err diff --git a/lnwire/message.go b/lnwire/message.go index 119def32..41167eaa 100644 --- a/lnwire/message.go +++ b/lnwire/message.go @@ -49,7 +49,7 @@ const ( CmdRevokeAndAck = uint32(2010) // Commands for reporting protocol errors. - CmdErrorGeneric = uint32(4000) + CmdError = uint32(4000) // Commands for discovery service. CmdChannelAnnouncement = uint32(5000) @@ -119,8 +119,8 @@ func makeEmptyMessage(command uint32) (Message, error) { msg = &CommitSig{} case CmdRevokeAndAck: msg = &RevokeAndAck{} - case CmdErrorGeneric: - msg = &ErrorGeneric{} + case CmdError: + msg = &Error{} case CmdChannelAnnouncement: msg = &ChannelAnnouncement{} case CmdChannelUpdateAnnouncement: