lnwire: add cancellation reason to htlc cancel messages

This commit adds a new field to the CancelHTLC message which describes
the event that led to an HTLC being cancelled up stream.

A new enum has been added which describers the “why” concerning the
cancellation of the HTLC. Currently the encoding and back propagation
of the errors aren’t properly implemented as defined within the spec.
As a result the current error types provide to privacy as the error are
in plain-site rather doing being properly encrypted.
This commit is contained in:
Olaoluwa Osuntokun 2017-01-05 21:43:25 -08:00
parent fd0c0574e6
commit d079c88702
No known key found for this signature in database
GPG Key ID: 9CC5B105D03521A2
3 changed files with 82 additions and 4 deletions

@ -7,10 +7,68 @@ import (
"github.com/roasbeef/btcd/wire"
)
// CancelReason specifies the precise reason that an upstream HTLC was
// cancelled. Each CancelHTLC message carries a CancelReason which is to be
// passed back unaltered to the source of the HTLC within the route.
//
// TODO(roasbeef): implement proper encrypted error messages as defined in spec
// * these errors as it stands reveal the error cause to all links in the
// route and are horrible for privacy
type CancelReason uint16
const (
// InsufficientCapacity indicates that a payment failed due to a link
// in the ultimate route not having enough satoshi flow to successfully
// carry the payment.
InsufficientCapacity = 0
// UpstreamTimeout indicates that an upstream link had to enforce the
// absolute HTLC timeout, removing the HTLC.
UpstreamTimeout = 1
// UnkownPaymentHash indicates that the destination did not recognize
// the payment hash.
UnkownPaymentHash = 2
// UnkownDestination indicates that the specified next hop within the
// Sphinx packet at a point in the route contained an unknown or
// invalid "next hop".
UnkownDestination = 3
// SphinxParseError indicates that an intermediate node was unable
// properly parse the HTLC.
SphinxParseError = 4
)
// String returns a human-readable version of the CancelReason type.
func (c CancelReason) String() string {
switch c {
case InsufficientCapacity:
return "InsufficientCapacity: next hop had insufficient " +
"capacity for payment"
case UpstreamTimeout:
return "UpstreamTimeout: HTLC has timed out upstream"
case UnkownPaymentHash:
return "UnkownPaymentHash: the destination did not know the " +
"preimage"
case UnkownDestination:
return "UnkownDestination: next hop unknown"
case SphinxParseError:
return "SphinxParseError: unable to parse sphinx packet"
default:
return "unknown reason"
}
}
// CancelHTLC is sent by Alice to Bob in order to remove a previously added
// HTLC. Upon receipt of an CancelHTLC the HTLC should be removed from the next
// commitment transaction, with the CancelHTLC propgated backwards in the route
// to fully un-clear the HTLC.
// commitment transaction, with the CancelHTLC propagated backwards in the
// route to fully un-clear the HTLC.
type CancelHTLC struct {
// ChannelPoint is the particular active channel that this CancelHTLC
// is binded to.
@ -19,6 +77,10 @@ type CancelHTLC struct {
// HTLCKey references which HTLC on the remote node's commitment
// transaction has timed out.
HTLCKey HTLCKey
// Reason described the event that caused the HTLC to be cancelled
// within the route.
Reason CancelReason
}
// Decode deserializes a serialized CancelHTLC message stored in the passed
@ -31,6 +93,7 @@ func (c *CancelHTLC) Decode(r io.Reader, pver uint32) error {
err := readElements(r,
&c.ChannelPoint,
&c.HTLCKey,
&c.Reason,
)
if err != nil {
return err
@ -56,6 +119,7 @@ func (c *CancelHTLC) Encode(w io.Writer, pver uint32) error {
err := writeElements(w,
c.ChannelPoint,
c.HTLCKey,
c.Reason,
)
if err != nil {
return err
@ -77,8 +141,8 @@ func (c *CancelHTLC) Command() uint32 {
//
// This is part of the lnwire.Message interface.
func (c *CancelHTLC) MaxPayloadLength(uint32) uint32 {
// 36 + 8
return 44
// 36 + 8 + 2
return 46
}
// Validate performs any necessary sanity checks to ensure all fields present
@ -96,5 +160,6 @@ func (c *CancelHTLC) String() string {
return fmt.Sprintf("\n--- Begin CancelHTLC ---\n") +
fmt.Sprintf("ChannelPoint:\t%d\n", c.ChannelPoint) +
fmt.Sprintf("HTLCKey:\t%d\n", c.HTLCKey) +
fmt.Sprintf("Reason:\t%v\n", c.Reason) +
fmt.Sprintf("--- End CancelHTLC ---\n")
}

@ -11,6 +11,7 @@ func TestCancelHTLCEncodeDecode(t *testing.T) {
cancelMsg := &CancelHTLC{
ChannelPoint: outpoint1,
HTLCKey: 22,
Reason: UpstreamTimeout,
}
// Next encode the HTLCTR message into an empty bytes buffer.

@ -88,6 +88,12 @@ func writeElement(w io.Writer, element interface{}) error {
if _, err := w.Write(b[:]); err != nil {
return err
}
case CancelReason:
var b [2]byte
binary.BigEndian.PutUint16(b[:], uint16(e))
if _, err := w.Write(b[:]); err != nil {
return err
}
case ErrorCode:
var b [2]byte
binary.BigEndian.PutUint16(b[:], uint16(e))
@ -384,6 +390,12 @@ func readElement(r io.Reader, element interface{}) error {
return err
}
*e = b[0]
case *CancelReason:
var b [2]byte
if _, err := io.ReadFull(r, b[:]); err != nil {
return err
}
*e = CancelReason(binary.BigEndian.Uint16(b[:]))
case *uint16:
var b [2]byte
if _, err := io.ReadFull(r, b[:]); err != nil {