mirror of
https://github.com/lightningnetwork/lnd.git
synced 2024-11-19 01:43:16 +01:00
multi: return route role from HopPayload
We need to know what role we're playing to be able to handle errors correctly, but the information that we need for this is held by our iterator: - Whether we had a blinding point in update add (blinding kit) - Whether we had a blinding point in payload As we're now going to use the route role return value even when our err!=nil, we rename the error to signal that we're using less canonical golang here. An alternative to this approach is to attach a RouteRole to our ErrInvalidPayload. The downside of that approach is: - Propagate context through parsing (whether we had updateAddHtlc) - Clumsy handling for errors that are not of type ErrInvalidPayload
This commit is contained in:
parent
b81a6f3d2f
commit
776c889267
@ -543,7 +543,7 @@ func (h *htlcIncomingContestResolver) decodePayload() (*hop.Payload,
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
payload, err := iterator.HopPayload()
|
||||
payload, _, err := iterator.HopPayload()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@ -263,7 +263,7 @@ type mockHopIterator struct {
|
||||
hop.Iterator
|
||||
}
|
||||
|
||||
func (h *mockHopIterator) HopPayload() (*hop.Payload, error) {
|
||||
func (h *mockHopIterator) HopPayload() (*hop.Payload, hop.RouteRole, error) {
|
||||
var nextAddress [8]byte
|
||||
if !h.isExit {
|
||||
nextAddress = [8]byte{0x01}
|
||||
@ -275,7 +275,7 @@ func (h *mockHopIterator) HopPayload() (*hop.Payload, error) {
|
||||
ForwardAmount: 100,
|
||||
OutgoingCltv: 40,
|
||||
ExtraBytes: [12]byte{},
|
||||
}), nil
|
||||
}), hop.RouteRoleCleartext, nil
|
||||
}
|
||||
|
||||
func (h *mockHopIterator) EncodeNextHop(w io.Writer) error {
|
||||
|
@ -24,6 +24,62 @@ var (
|
||||
"blinded hop")
|
||||
)
|
||||
|
||||
// RouteRole represents the different types of roles a node can have as a
|
||||
// recipient of a HTLC.
|
||||
type RouteRole uint8
|
||||
|
||||
const (
|
||||
// RouteRoleCleartext represents a regular route hop.
|
||||
RouteRoleCleartext RouteRole = iota
|
||||
|
||||
// RouteRoleIntroduction represents an introduction node in a blinded
|
||||
// path, characterized by a blinding point in the onion payload.
|
||||
RouteRoleIntroduction
|
||||
|
||||
// RouteRoleRelaying represents a relaying node in a blinded path,
|
||||
// characterized by a blinding point in update_add_htlc.
|
||||
RouteRoleRelaying
|
||||
)
|
||||
|
||||
// String representation of a role in a route.
|
||||
func (h RouteRole) String() string {
|
||||
switch h {
|
||||
case RouteRoleCleartext:
|
||||
return "cleartext"
|
||||
|
||||
case RouteRoleRelaying:
|
||||
return "blinded relay"
|
||||
|
||||
case RouteRoleIntroduction:
|
||||
return "introduction node"
|
||||
|
||||
default:
|
||||
return fmt.Sprintf("unknown route role: %d", h)
|
||||
}
|
||||
}
|
||||
|
||||
// NewRouteRole returns the role we're playing in a route depending on the
|
||||
// blinding points set (or not). If we are in the situation where we received
|
||||
// blinding points in both the update add message and the payload:
|
||||
// - We must have had a valid update add blinding point, because we were able
|
||||
// to decrypt our onion to get the payload blinding point.
|
||||
// - We return a relaying node role, because an introduction node (by
|
||||
// definition) does not receive a blinding point in update add.
|
||||
// - We assume the sending node to be buggy (including a payload blinding
|
||||
// where it shouldn't), and rely on validation elsewhere to handle this.
|
||||
func NewRouteRole(updateAddBlinding, payloadBlinding bool) RouteRole {
|
||||
switch {
|
||||
case updateAddBlinding:
|
||||
return RouteRoleRelaying
|
||||
|
||||
case payloadBlinding:
|
||||
return RouteRoleIntroduction
|
||||
|
||||
default:
|
||||
return RouteRoleCleartext
|
||||
}
|
||||
}
|
||||
|
||||
// Iterator is an interface that abstracts away the routing information
|
||||
// included in HTLC's which includes the entirety of the payment path of an
|
||||
// HTLC. This interface provides two basic method which carry out: how to
|
||||
@ -35,8 +91,11 @@ type Iterator interface {
|
||||
// information encoded within the returned ForwardingInfo is to be used
|
||||
// by each hop to authenticate the information given to it by the prior
|
||||
// hop. The payload will also contain any additional TLV fields provided
|
||||
// by the sender.
|
||||
HopPayload() (*Payload, error)
|
||||
// by the sender. The role that this hop plays in the context of
|
||||
// route blinding (regular, introduction or relaying) is returned
|
||||
// whenever the payload is successfully parsed, even if we subsequently
|
||||
// face a validation error.
|
||||
HopPayload() (*Payload, RouteRole, error)
|
||||
|
||||
// EncodeNextHop encodes the onion packet destined for the next hop
|
||||
// into the passed io.Writer.
|
||||
@ -95,18 +154,21 @@ func (r *sphinxHopIterator) EncodeNextHop(w io.Writer) error {
|
||||
// HopPayload returns the set of fields that detail exactly _how_ this hop
|
||||
// should forward the HTLC to the next hop. Additionally, the information
|
||||
// encoded within the returned ForwardingInfo is to be used by each hop to
|
||||
// authenticate the information given to it by the prior hop. The payload will
|
||||
// also contain any additional TLV fields provided by the sender.
|
||||
// authenticate the information given to it by the prior hop. The role that
|
||||
// this hop plays in the context of route blinding (regular, introduction or
|
||||
// relaying) is returned whenever the payload is successfully parsed, even if
|
||||
// we subsequently face a validation error. The payload will also contain any
|
||||
// additional TLV fields provided by the sender.
|
||||
//
|
||||
// NOTE: Part of the HopIterator interface.
|
||||
func (r *sphinxHopIterator) HopPayload() (*Payload, error) {
|
||||
func (r *sphinxHopIterator) HopPayload() (*Payload, RouteRole, error) {
|
||||
switch r.processedPacket.Payload.Type {
|
||||
|
||||
// If this is the legacy payload, then we'll extract the information
|
||||
// directly from the pre-populated ForwardingInstructions field.
|
||||
case sphinx.PayloadLegacy:
|
||||
fwdInst := r.processedPacket.ForwardingInstructions
|
||||
return NewLegacyPayload(fwdInst), nil
|
||||
return NewLegacyPayload(fwdInst), RouteRoleCleartext, nil
|
||||
|
||||
// Otherwise, if this is the TLV payload, then we'll make a new stream
|
||||
// to decode only what we need to make routing decisions.
|
||||
@ -116,14 +178,32 @@ func (r *sphinxHopIterator) HopPayload() (*Payload, error) {
|
||||
bytes.NewReader(r.processedPacket.Payload.Payload),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
// If we couldn't even parse our payload then we do
|
||||
// a best-effort of determining our role in a blinded
|
||||
// route, accepting that we can't know whether we
|
||||
// were the introduction node (as the payload
|
||||
// is not parseable).
|
||||
routeRole := RouteRoleCleartext
|
||||
if r.blindingKit.UpdateAddBlinding.IsSome() {
|
||||
routeRole = RouteRoleRelaying
|
||||
}
|
||||
|
||||
return nil, routeRole, err
|
||||
}
|
||||
|
||||
// Now that we've parsed our payload we can determine which
|
||||
// role we're playing in the route.
|
||||
_, payloadBlinding := parsed[record.BlindingPointOnionType]
|
||||
routeRole := NewRouteRole(
|
||||
r.blindingKit.UpdateAddBlinding.IsSome(),
|
||||
payloadBlinding,
|
||||
)
|
||||
|
||||
if err := ValidateTLVPayload(
|
||||
parsed, isFinal,
|
||||
r.blindingKit.UpdateAddBlinding.IsSome(),
|
||||
); err != nil {
|
||||
return nil, err
|
||||
return nil, routeRole, err
|
||||
}
|
||||
|
||||
// If we had an encrypted data payload present, pull out our
|
||||
@ -133,17 +213,18 @@ func (r *sphinxHopIterator) HopPayload() (*Payload, error) {
|
||||
payload, isFinal, parsed,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, routeRole, err
|
||||
}
|
||||
|
||||
payload.FwdInfo = *fwdInfo
|
||||
}
|
||||
|
||||
return payload, err
|
||||
return payload, routeRole, nil
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown sphinx payload type: %v",
|
||||
r.processedPacket.Payload.Type)
|
||||
return nil, RouteRoleCleartext,
|
||||
fmt.Errorf("unknown sphinx payload type: %v",
|
||||
r.processedPacket.Payload.Type)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -88,10 +88,10 @@ func TestSphinxHopIteratorForwardingInstructions(t *testing.T) {
|
||||
for i, testCase := range testCases {
|
||||
iterator.processedPacket = testCase.sphinxPacket
|
||||
|
||||
pld, err := iterator.HopPayload()
|
||||
if err != nil {
|
||||
pld, _, pldErr := iterator.HopPayload()
|
||||
if pldErr != nil {
|
||||
t.Fatalf("#%v: unable to extract forwarding "+
|
||||
"instructions: %v", i, err)
|
||||
"instructions: %v", i, pldErr)
|
||||
}
|
||||
|
||||
fwdInfo := pld.ForwardingInfo()
|
||||
|
@ -3293,14 +3293,18 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg,
|
||||
|
||||
heightNow := l.cfg.BestHeight()
|
||||
|
||||
pld, err := chanIterator.HopPayload()
|
||||
if err != nil {
|
||||
pld, _, pldErr := chanIterator.HopPayload()
|
||||
if pldErr != nil {
|
||||
// If we're unable to process the onion payload, or we
|
||||
// received invalid onion payload failure, then we
|
||||
// should send an error back to the caller so the HTLC
|
||||
// can be canceled.
|
||||
var failedType uint64
|
||||
if e, ok := err.(hop.ErrInvalidPayload); ok {
|
||||
|
||||
// We need to get the underlying error value, so we
|
||||
// can't use errors.As as suggested by the linter.
|
||||
//nolint:errorlint
|
||||
if e, ok := pldErr.(hop.ErrInvalidPayload); ok {
|
||||
failedType = uint64(e.Type)
|
||||
}
|
||||
|
||||
@ -3316,7 +3320,8 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg,
|
||||
)
|
||||
|
||||
l.log.Errorf("unable to decode forwarding "+
|
||||
"instructions: %v", err)
|
||||
"instructions: %v", pldErr)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -330,10 +330,10 @@ func newMockHopIterator(hops ...*hop.Payload) hop.Iterator {
|
||||
return &mockHopIterator{hops: hops}
|
||||
}
|
||||
|
||||
func (r *mockHopIterator) HopPayload() (*hop.Payload, error) {
|
||||
func (r *mockHopIterator) HopPayload() (*hop.Payload, hop.RouteRole, error) {
|
||||
h := r.hops[0]
|
||||
r.hops = r.hops[1:]
|
||||
return h, nil
|
||||
return h, hop.RouteRoleCleartext, nil
|
||||
}
|
||||
|
||||
func (r *mockHopIterator) ExtraOnionBlob() []byte {
|
||||
|
Loading…
Reference in New Issue
Block a user