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:
parent
c06894a2e6
commit
d17b11862b
123
lnwire/error.go
Normal file
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:
|
||||||
|
Loading…
Reference in New Issue
Block a user