lnwire: convert ErrorGeneric to Error

With this change we move one step closer to matching the wire protocol
currently defined within the spec.
This commit is contained in:
Olaoluwa Osuntokun 2017-04-16 15:23:40 -07:00
parent c06894a2e6
commit d17b11862b
No known key found for this signature in database
GPG Key ID: 9CC5B105D03521A2
5 changed files with 155 additions and 137 deletions

123
lnwire/error.go Normal file

@ -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
}

@ -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
}

@ -6,22 +6,21 @@ import (
"testing" "testing"
) )
func TestErrorGenericEncodeDecode(t *testing.T) { func TestErrorEncodeDecode(t *testing.T) {
eg := &ErrorGeneric{ eg := &Error{
ChannelPoint: *outpoint1, ChanID: ChannelID(revHash),
PendingChannelID: 1,
Code: 99, Code: 99,
Problem: "Hello world!", 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 var b bytes.Buffer
if err := eg.Encode(&b, 0); err != nil { if err := eg.Encode(&b, 0); err != nil {
t.Fatalf("unable to encode ErrorGeneric: %v", err) t.Fatalf("unable to encode ErrorGeneric: %v", err)
} }
// Deserialize the encoded EG message into a new empty struct. // Deserialize the encoded error message into a new empty struct.
eg2 := &ErrorGeneric{} eg2 := &Error{}
if err := eg2.Decode(&b, 0); err != nil { if err := eg2.Decode(&b, 0); err != nil {
t.Fatalf("unable to decode ErrorGeneric: %v", err) t.Fatalf("unable to decode ErrorGeneric: %v", err)
} }

@ -174,6 +174,16 @@ 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 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: case OpaqueReason:
var l [2]byte var l [2]byte
binary.BigEndian.PutUint16(l[:], uint16(len(e))) 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 { if _, err := io.ReadFull(r, *e); err != nil {
return err 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: case *[6]byte:
if _, err := io.ReadFull(r, e[:]); err != nil { if _, err := io.ReadFull(r, e[:]); err != nil {
return err return err

@ -49,7 +49,7 @@ const (
CmdRevokeAndAck = uint32(2010) CmdRevokeAndAck = uint32(2010)
// Commands for reporting protocol errors. // Commands for reporting protocol errors.
CmdErrorGeneric = uint32(4000) CmdError = uint32(4000)
// Commands for discovery service. // Commands for discovery service.
CmdChannelAnnouncement = uint32(5000) CmdChannelAnnouncement = uint32(5000)
@ -119,8 +119,8 @@ func makeEmptyMessage(command uint32) (Message, error) {
msg = &CommitSig{} msg = &CommitSig{}
case CmdRevokeAndAck: case CmdRevokeAndAck:
msg = &RevokeAndAck{} msg = &RevokeAndAck{}
case CmdErrorGeneric: case CmdError:
msg = &ErrorGeneric{} msg = &Error{}
case CmdChannelAnnouncement: case CmdChannelAnnouncement:
msg = &ChannelAnnouncement{} msg = &ChannelAnnouncement{}
case CmdChannelUpdateAnnouncement: case CmdChannelUpdateAnnouncement: