github.com/decred/dcrlnd@v0.7.6/lnrpc/routerrpc/subscribe_events.go (about) 1 package routerrpc 2 3 import ( 4 "fmt" 5 "time" 6 7 "github.com/decred/dcrlnd/htlcswitch" 8 "github.com/decred/dcrlnd/invoices" 9 "github.com/decred/dcrlnd/lnrpc" 10 ) 11 12 // rpcHtlcEvent returns a rpc htlc event from a htlcswitch event. 13 func rpcHtlcEvent(htlcEvent interface{}) (*HtlcEvent, error) { 14 var ( 15 key htlcswitch.HtlcKey 16 timestamp time.Time 17 eventType htlcswitch.HtlcEventType 18 event isHtlcEvent_Event 19 ) 20 21 switch e := htlcEvent.(type) { 22 case *htlcswitch.ForwardingEvent: 23 event = &HtlcEvent_ForwardEvent{ 24 ForwardEvent: &ForwardEvent{ 25 Info: rpcInfo(e.HtlcInfo), 26 }, 27 } 28 29 key = e.HtlcKey 30 eventType = e.HtlcEventType 31 timestamp = e.Timestamp 32 33 case *htlcswitch.ForwardingFailEvent: 34 event = &HtlcEvent_ForwardFailEvent{ 35 ForwardFailEvent: &ForwardFailEvent{}, 36 } 37 38 key = e.HtlcKey 39 eventType = e.HtlcEventType 40 timestamp = e.Timestamp 41 42 case *htlcswitch.LinkFailEvent: 43 failureCode, failReason, err := rpcFailReason( 44 e.LinkError, 45 ) 46 if err != nil { 47 return nil, err 48 } 49 50 event = &HtlcEvent_LinkFailEvent{ 51 LinkFailEvent: &LinkFailEvent{ 52 Info: rpcInfo(e.HtlcInfo), 53 WireFailure: failureCode, 54 FailureDetail: failReason, 55 FailureString: e.LinkError.Error(), 56 }, 57 } 58 59 key = e.HtlcKey 60 eventType = e.HtlcEventType 61 timestamp = e.Timestamp 62 63 case *htlcswitch.SettleEvent: 64 event = &HtlcEvent_SettleEvent{ 65 SettleEvent: &SettleEvent{ 66 Preimage: e.Preimage[:], 67 }, 68 } 69 70 key = e.HtlcKey 71 eventType = e.HtlcEventType 72 timestamp = e.Timestamp 73 74 default: 75 return nil, fmt.Errorf("unknown event type: %T", e) 76 } 77 78 rpcEvent := &HtlcEvent{ 79 IncomingChannelId: key.IncomingCircuit.ChanID.ToUint64(), 80 OutgoingChannelId: key.OutgoingCircuit.ChanID.ToUint64(), 81 IncomingHtlcId: key.IncomingCircuit.HtlcID, 82 OutgoingHtlcId: key.OutgoingCircuit.HtlcID, 83 TimestampNs: uint64(timestamp.UnixNano()), 84 Event: event, 85 } 86 87 // Convert the htlc event type to a rpc event. 88 switch eventType { 89 case htlcswitch.HtlcEventTypeSend: 90 rpcEvent.EventType = HtlcEvent_SEND 91 92 case htlcswitch.HtlcEventTypeReceive: 93 rpcEvent.EventType = HtlcEvent_RECEIVE 94 95 case htlcswitch.HtlcEventTypeForward: 96 rpcEvent.EventType = HtlcEvent_FORWARD 97 98 default: 99 return nil, fmt.Errorf("unknown event type: %v", eventType) 100 } 101 102 return rpcEvent, nil 103 } 104 105 // rpcInfo returns a rpc struct containing the htlc information from the 106 // switch's htlc info struct. 107 func rpcInfo(info htlcswitch.HtlcInfo) *HtlcInfo { 108 return &HtlcInfo{ 109 IncomingTimelock: info.IncomingTimeLock, 110 OutgoingTimelock: info.OutgoingTimeLock, 111 IncomingAmtMAtoms: uint64(info.IncomingAmt), 112 OutgoingAmtMAtoms: uint64(info.OutgoingAmt), 113 } 114 } 115 116 // rpcFailReason maps a lnwire failure message and failure detail to a rpc 117 // failure code and detail. 118 func rpcFailReason(linkErr *htlcswitch.LinkError) (lnrpc.Failure_FailureCode, 119 FailureDetail, error) { 120 121 wireErr, err := marshallError(linkErr) 122 if err != nil { 123 return 0, 0, err 124 } 125 wireCode := wireErr.GetCode() 126 127 // If the link has no failure detail, return with failure detail none. 128 if linkErr.FailureDetail == nil { 129 return wireCode, FailureDetail_NO_DETAIL, nil 130 } 131 132 switch failureDetail := linkErr.FailureDetail.(type) { 133 case invoices.FailResolutionResult: 134 fd, err := rpcFailureResolution(failureDetail) 135 return wireCode, fd, err 136 137 case htlcswitch.OutgoingFailure: 138 fd, err := rpcOutgoingFailure(failureDetail) 139 return wireCode, fd, err 140 141 default: 142 return 0, 0, fmt.Errorf("unknown failure "+ 143 "detail type: %T", linkErr.FailureDetail) 144 145 } 146 147 } 148 149 // rpcFailureResolution maps an invoice failure resolution to a rpc failure 150 // detail. Invoice failures have no zero resolution results (every failure 151 // is accompanied with a result), so we error if we fail to match the result 152 // type. 153 func rpcFailureResolution(invoiceFailure invoices.FailResolutionResult) ( 154 FailureDetail, error) { 155 156 switch invoiceFailure { 157 case invoices.ResultReplayToCanceled: 158 return FailureDetail_INVOICE_CANCELED, nil 159 160 case invoices.ResultInvoiceAlreadyCanceled: 161 return FailureDetail_INVOICE_CANCELED, nil 162 163 case invoices.ResultAmountTooLow: 164 return FailureDetail_INVOICE_UNDERPAID, nil 165 166 case invoices.ResultExpiryTooSoon: 167 return FailureDetail_INVOICE_EXPIRY_TOO_SOON, nil 168 169 case invoices.ResultCanceled: 170 return FailureDetail_INVOICE_CANCELED, nil 171 172 case invoices.ResultInvoiceNotOpen: 173 return FailureDetail_INVOICE_NOT_OPEN, nil 174 175 case invoices.ResultMppTimeout: 176 return FailureDetail_MPP_INVOICE_TIMEOUT, nil 177 178 case invoices.ResultAddressMismatch: 179 return FailureDetail_ADDRESS_MISMATCH, nil 180 181 case invoices.ResultHtlcSetTotalMismatch: 182 return FailureDetail_SET_TOTAL_MISMATCH, nil 183 184 case invoices.ResultHtlcSetTotalTooLow: 185 return FailureDetail_SET_TOTAL_TOO_LOW, nil 186 187 case invoices.ResultHtlcSetOverpayment: 188 return FailureDetail_SET_OVERPAID, nil 189 190 case invoices.ResultInvoiceNotFound: 191 return FailureDetail_UNKNOWN_INVOICE, nil 192 193 case invoices.ResultKeySendError: 194 return FailureDetail_INVALID_KEYSEND, nil 195 196 case invoices.ResultMppInProgress: 197 return FailureDetail_MPP_IN_PROGRESS, nil 198 199 default: 200 return 0, fmt.Errorf("unknown fail resolution: %v", 201 invoiceFailure.FailureString()) 202 } 203 } 204 205 // rpcOutgoingFailure maps an outgoing failure to a rpc FailureDetail. If the 206 // failure detail is FailureDetailNone, which indicates that the failure was 207 // a wire message which required no further failure detail, we return a no 208 // detail failure detail to indicate that there was no additional information. 209 func rpcOutgoingFailure(failureDetail htlcswitch.OutgoingFailure) ( 210 FailureDetail, error) { 211 212 switch failureDetail { 213 case htlcswitch.OutgoingFailureNone: 214 return FailureDetail_NO_DETAIL, nil 215 216 case htlcswitch.OutgoingFailureDecodeError: 217 return FailureDetail_ONION_DECODE, nil 218 219 case htlcswitch.OutgoingFailureLinkNotEligible: 220 return FailureDetail_LINK_NOT_ELIGIBLE, nil 221 222 case htlcswitch.OutgoingFailureOnChainTimeout: 223 return FailureDetail_ON_CHAIN_TIMEOUT, nil 224 225 case htlcswitch.OutgoingFailureHTLCExceedsMax: 226 return FailureDetail_HTLC_EXCEEDS_MAX, nil 227 228 case htlcswitch.OutgoingFailureInsufficientBalance: 229 return FailureDetail_INSUFFICIENT_BALANCE, nil 230 231 case htlcswitch.OutgoingFailureCircularRoute: 232 return FailureDetail_CIRCULAR_ROUTE, nil 233 234 case htlcswitch.OutgoingFailureIncompleteForward: 235 return FailureDetail_INCOMPLETE_FORWARD, nil 236 237 case htlcswitch.OutgoingFailureDownstreamHtlcAdd: 238 return FailureDetail_HTLC_ADD_FAILED, nil 239 240 case htlcswitch.OutgoingFailureForwardsDisabled: 241 return FailureDetail_FORWARDS_DISABLED, nil 242 243 default: 244 return 0, fmt.Errorf("unknown outgoing failure "+ 245 "detail: %v", failureDetail.FailureString()) 246 } 247 }