lnd/lnrpc/routerrpc/subscribe_events.go

246 lines
6.5 KiB
Go

package routerrpc
import (
"fmt"
"time"
"github.com/lightningnetwork/lnd/htlcswitch"
"github.com/lightningnetwork/lnd/invoices"
"github.com/lightningnetwork/lnd/lnrpc"
)
// rpcHtlcEvent returns a rpc htlc event from a htlcswitch event.
func rpcHtlcEvent(htlcEvent interface{}) (*HtlcEvent, error) {
var (
key htlcswitch.HtlcKey
timestamp time.Time
eventType htlcswitch.HtlcEventType
event isHtlcEvent_Event
)
switch e := htlcEvent.(type) {
case *htlcswitch.ForwardingEvent:
event = &HtlcEvent_ForwardEvent{
ForwardEvent: &ForwardEvent{
Info: rpcInfo(e.HtlcInfo),
},
}
key = e.HtlcKey
eventType = e.HtlcEventType
timestamp = e.Timestamp
case *htlcswitch.ForwardingFailEvent:
event = &HtlcEvent_ForwardFailEvent{
ForwardFailEvent: &ForwardFailEvent{},
}
key = e.HtlcKey
eventType = e.HtlcEventType
timestamp = e.Timestamp
case *htlcswitch.LinkFailEvent:
failureCode, failReason, err := rpcFailReason(
e.LinkError,
)
if err != nil {
return nil, err
}
event = &HtlcEvent_LinkFailEvent{
LinkFailEvent: &LinkFailEvent{
Info: rpcInfo(e.HtlcInfo),
WireFailure: failureCode,
FailureDetail: failReason,
FailureString: e.LinkError.Error(),
},
}
key = e.HtlcKey
eventType = e.HtlcEventType
timestamp = e.Timestamp
case *htlcswitch.SettleEvent:
event = &HtlcEvent_SettleEvent{
SettleEvent: &SettleEvent{},
}
key = e.HtlcKey
eventType = e.HtlcEventType
timestamp = e.Timestamp
default:
return nil, fmt.Errorf("unknown event type: %T", e)
}
rpcEvent := &HtlcEvent{
IncomingChannelId: key.IncomingCircuit.ChanID.ToUint64(),
OutgoingChannelId: key.OutgoingCircuit.ChanID.ToUint64(),
IncomingHtlcId: key.IncomingCircuit.HtlcID,
OutgoingHtlcId: key.OutgoingCircuit.HtlcID,
TimestampNs: uint64(timestamp.UnixNano()),
Event: event,
}
// Convert the htlc event type to a rpc event.
switch eventType {
case htlcswitch.HtlcEventTypeSend:
rpcEvent.EventType = HtlcEvent_SEND
case htlcswitch.HtlcEventTypeReceive:
rpcEvent.EventType = HtlcEvent_RECEIVE
case htlcswitch.HtlcEventTypeForward:
rpcEvent.EventType = HtlcEvent_FORWARD
default:
return nil, fmt.Errorf("unknown event type: %v", eventType)
}
return rpcEvent, nil
}
// rpcInfo returns a rpc struct containing the htlc information from the
// switch's htlc info struct.
func rpcInfo(info htlcswitch.HtlcInfo) *HtlcInfo {
return &HtlcInfo{
IncomingTimelock: info.IncomingTimeLock,
OutgoingTimelock: info.OutgoingTimeLock,
IncomingAmtMsat: uint64(info.IncomingAmt),
OutgoingAmtMsat: uint64(info.OutgoingAmt),
}
}
// rpcFailReason maps a lnwire failure message and failure detail to a rpc
// failure code and detail.
func rpcFailReason(linkErr *htlcswitch.LinkError) (lnrpc.Failure_FailureCode,
FailureDetail, error) {
wireErr, err := marshallError(linkErr)
if err != nil {
return 0, 0, err
}
wireCode := wireErr.GetCode()
// If the link has no failure detail, return with failure detail none.
if linkErr.FailureDetail == nil {
return wireCode, FailureDetail_NO_DETAIL, nil
}
switch failureDetail := linkErr.FailureDetail.(type) {
case invoices.FailResolutionResult:
fd, err := rpcFailureResolution(failureDetail)
return wireCode, fd, err
case htlcswitch.OutgoingFailure:
fd, err := rpcOutgoingFailure(failureDetail)
return wireCode, fd, err
default:
return 0, 0, fmt.Errorf("unknown failure "+
"detail type: %T", linkErr.FailureDetail)
}
}
// rpcFailureResolution maps an invoice failure resolution to a rpc failure
// detail. Invoice failures have no zero resolution results (every failure
// is accompanied with a result), so we error if we fail to match the result
// type.
func rpcFailureResolution(invoiceFailure invoices.FailResolutionResult) (
FailureDetail, error) {
switch invoiceFailure {
case invoices.ResultReplayToCanceled:
return FailureDetail_INVOICE_CANCELED, nil
case invoices.ResultInvoiceAlreadyCanceled:
return FailureDetail_INVOICE_CANCELED, nil
case invoices.ResultAmountTooLow:
return FailureDetail_INVOICE_UNDERPAID, nil
case invoices.ResultExpiryTooSoon:
return FailureDetail_INVOICE_EXPIRY_TOO_SOON, nil
case invoices.ResultCanceled:
return FailureDetail_INVOICE_CANCELED, nil
case invoices.ResultInvoiceNotOpen:
return FailureDetail_INVOICE_NOT_OPEN, nil
case invoices.ResultMppTimeout:
return FailureDetail_MPP_INVOICE_TIMEOUT, nil
case invoices.ResultAddressMismatch:
return FailureDetail_ADDRESS_MISMATCH, nil
case invoices.ResultHtlcSetTotalMismatch:
return FailureDetail_SET_TOTAL_MISMATCH, nil
case invoices.ResultHtlcSetTotalTooLow:
return FailureDetail_SET_TOTAL_TOO_LOW, nil
case invoices.ResultHtlcSetOverpayment:
return FailureDetail_SET_OVERPAID, nil
case invoices.ResultInvoiceNotFound:
return FailureDetail_UNKNOWN_INVOICE, nil
case invoices.ResultKeySendError:
return FailureDetail_INVALID_KEYSEND, nil
case invoices.ResultMppInProgress:
return FailureDetail_MPP_IN_PROGRESS, nil
default:
return 0, fmt.Errorf("unknown fail resolution: %v",
invoiceFailure.FailureString())
}
}
// rpcOutgoingFailure maps an outgoing failure to a rpc FailureDetail. If the
// failure detail is FailureDetailNone, which indicates that the failure was
// a wire message which required no further failure detail, we return a no
// detail failure detail to indicate that there was no additional information.
func rpcOutgoingFailure(failureDetail htlcswitch.OutgoingFailure) (
FailureDetail, error) {
switch failureDetail {
case htlcswitch.OutgoingFailureNone:
return FailureDetail_NO_DETAIL, nil
case htlcswitch.OutgoingFailureDecodeError:
return FailureDetail_ONION_DECODE, nil
case htlcswitch.OutgoingFailureLinkNotEligible:
return FailureDetail_LINK_NOT_ELIGIBLE, nil
case htlcswitch.OutgoingFailureOnChainTimeout:
return FailureDetail_ON_CHAIN_TIMEOUT, nil
case htlcswitch.OutgoingFailureHTLCExceedsMax:
return FailureDetail_HTLC_EXCEEDS_MAX, nil
case htlcswitch.OutgoingFailureInsufficientBalance:
return FailureDetail_INSUFFICIENT_BALANCE, nil
case htlcswitch.OutgoingFailureCircularRoute:
return FailureDetail_CIRCULAR_ROUTE, nil
case htlcswitch.OutgoingFailureIncompleteForward:
return FailureDetail_INCOMPLETE_FORWARD, nil
case htlcswitch.OutgoingFailureDownstreamHtlcAdd:
return FailureDetail_HTLC_ADD_FAILED, nil
case htlcswitch.OutgoingFailureForwardsDisabled:
return FailureDetail_FORWARDS_DISABLED, nil
default:
return 0, fmt.Errorf("unknown outgoing failure "+
"detail: %v", failureDetail.FailureString())
}
}