github.com/decred/dcrlnd@v0.7.6/htlcswitch/htlcnotifier.go (about) 1 package htlcswitch 2 3 import ( 4 "fmt" 5 "strings" 6 "sync" 7 "time" 8 9 "github.com/decred/dcrlnd/channeldb" 10 "github.com/decred/dcrlnd/htlcswitch/hop" 11 "github.com/decred/dcrlnd/lntypes" 12 "github.com/decred/dcrlnd/lnwire" 13 "github.com/decred/dcrlnd/subscribe" 14 ) 15 16 // HtlcNotifier notifies clients of htlc forwards, failures and settles for 17 // htlcs that the switch handles. It takes subscriptions for its events and 18 // notifies them when htlc events occur. These are served on a best-effort 19 // basis; events are not persisted, delivery is not guaranteed (in the event 20 // of a crash in the switch, forward events may be lost) and some events may 21 // be replayed upon restart. Events consumed from this package should be 22 // de-duplicated by the htlc's unique combination of incoming+outgoing circuit 23 // and not relied upon for critical operations. 24 // 25 // The htlc notifier sends the following kinds of events: 26 // Forwarding Event: 27 // - Represents a htlc which is forwarded onward from our node. 28 // - Present for htlc forwards through our node and local sends. 29 // 30 // Link Failure Event: 31 // - Indicates that a htlc has failed on our incoming or outgoing link, 32 // with an incoming boolean which indicates where the failure occurred. 33 // - Incoming link failures are present for failed attempts to pay one of 34 // our invoices (insufficient amount or mpp timeout, for example) and for 35 // forwards that we cannot decode to forward onwards. 36 // - Outgoing link failures are present for forwards or local payments that 37 // do not meet our outgoing link's policy (insufficient fees, for example) 38 // and when we fail to forward the payment on (insufficient outgoing 39 // capacity, or an unknown outgoing link). 40 // 41 // Forwarding Failure Event: 42 // - Forwarding failures indicate that a htlc we forwarded has failed at 43 // another node down the route. 44 // - Present for local sends and htlc forwards which fail after they left 45 // our node. 46 // 47 // Settle event: 48 // - Settle events are present when a htlc which we added is settled through 49 // the release of a preimage. 50 // - Present for local receives, and successful local sends or forwards. 51 // 52 // Each htlc is identified by its incoming and outgoing circuit key. Htlcs, 53 // and their subsequent settles or fails, can be identified by the combination 54 // of incoming and outgoing circuits. Note that receives to our node will 55 // have a zero outgoing circuit key because the htlc terminates at our 56 // node, and sends from our node will have a zero incoming circuit key because 57 // the send originates at our node. 58 type HtlcNotifier struct { 59 started sync.Once 60 stopped sync.Once 61 62 // now returns the current time, it is set in the htlcnotifier to allow 63 // for timestamp mocking in tests. 64 now func() time.Time 65 66 ntfnServer *subscribe.Server 67 } 68 69 // NewHtlcNotifier creates a new HtlcNotifier which gets htlc forwarded, 70 // failed and settled events from links our node has established with peers 71 // and sends notifications to subscribing clients. 72 func NewHtlcNotifier(now func() time.Time) *HtlcNotifier { 73 return &HtlcNotifier{ 74 now: now, 75 ntfnServer: subscribe.NewServer(), 76 } 77 } 78 79 // Start starts the HtlcNotifier and all goroutines it needs to consume 80 // events and provide subscriptions to clients. 81 func (h *HtlcNotifier) Start() error { 82 var err error 83 h.started.Do(func() { 84 log.Trace("HtlcNotifier starting") 85 err = h.ntfnServer.Start() 86 }) 87 return err 88 } 89 90 // Stop signals the notifier for a graceful shutdown. 91 func (h *HtlcNotifier) Stop() error { 92 var err error 93 h.stopped.Do(func() { 94 log.Info("HtlcNotifier shutting down") 95 if err = h.ntfnServer.Stop(); err != nil { 96 log.Warnf("error stopping htlc notifier: %v", err) 97 } 98 }) 99 return err 100 } 101 102 // SubscribeHtlcEvents returns a subscribe.Client that will receive updates 103 // any time the server is made aware of a new event. 104 func (h *HtlcNotifier) SubscribeHtlcEvents() (*subscribe.Client, error) { 105 return h.ntfnServer.Subscribe() 106 } 107 108 // HtlcKey uniquely identifies the htlc. 109 type HtlcKey struct { 110 // IncomingCircuit is the channel an htlc id of the incoming htlc. 111 IncomingCircuit channeldb.CircuitKey 112 113 // OutgoingCircuit is the channel and htlc id of the outgoing htlc. 114 OutgoingCircuit channeldb.CircuitKey 115 } 116 117 // String returns a string representation of a htlc key. 118 func (k HtlcKey) String() string { 119 switch { 120 case k.IncomingCircuit.ChanID == hop.Source: 121 return k.OutgoingCircuit.String() 122 123 case k.OutgoingCircuit.ChanID == hop.Exit: 124 return k.IncomingCircuit.String() 125 126 default: 127 return fmt.Sprintf("%v -> %v", k.IncomingCircuit, 128 k.OutgoingCircuit) 129 } 130 } 131 132 // HtlcInfo provides the details of a htlc that our node has processed. For 133 // forwards, incoming and outgoing values are set, whereas sends and receives 134 // will only have outgoing or incoming details set. 135 type HtlcInfo struct { 136 // IncomingTimelock is the time lock of the htlc on our incoming 137 // channel. 138 IncomingTimeLock uint32 139 140 // OutgoingTimelock is the time lock the htlc on our outgoing channel. 141 OutgoingTimeLock uint32 142 143 // IncomingAmt is the amount of the htlc on our incoming channel. 144 IncomingAmt lnwire.MilliAtom 145 146 // OutgoingAmt is the amount of the htlc on our outgoing channel. 147 OutgoingAmt lnwire.MilliAtom 148 } 149 150 // String returns a string representation of a htlc. 151 func (h HtlcInfo) String() string { 152 var details []string 153 154 // If the incoming information is not zero, as is the case for a send, 155 // we include the incoming amount and timelock. 156 if h.IncomingAmt != 0 || h.IncomingTimeLock != 0 { 157 str := fmt.Sprintf("incoming amount: %v, "+ 158 "incoming timelock: %v", h.IncomingAmt, 159 h.IncomingTimeLock) 160 161 details = append(details, str) 162 } 163 164 // If the outgoing information is not zero, as is the case for a 165 // receive, we include the outgoing amount and timelock. 166 if h.OutgoingAmt != 0 || h.OutgoingTimeLock != 0 { 167 str := fmt.Sprintf("outgoing amount: %v, "+ 168 "outgoing timelock: %v", h.OutgoingAmt, 169 h.OutgoingTimeLock) 170 171 details = append(details, str) 172 } 173 174 return strings.Join(details, ", ") 175 } 176 177 // HtlcEventType represents the type of event that a htlc was part of. 178 type HtlcEventType int 179 180 const ( 181 // HtlcEventTypeSend represents a htlc that was part of a send from 182 // our node. 183 HtlcEventTypeSend HtlcEventType = iota 184 185 // HtlcEventTypeReceive represents a htlc that was part of a receive 186 // to our node. 187 HtlcEventTypeReceive 188 189 // HtlcEventTypeForward represents a htlc that was forwarded through 190 // our node. 191 HtlcEventTypeForward 192 ) 193 194 // String returns a string representation of a htlc event type. 195 func (h HtlcEventType) String() string { 196 switch h { 197 case HtlcEventTypeSend: 198 return "send" 199 200 case HtlcEventTypeReceive: 201 return "receive" 202 203 case HtlcEventTypeForward: 204 return "forward" 205 206 default: 207 return "unknown" 208 } 209 } 210 211 // ForwardingEvent represents a htlc that was forwarded onwards from our node. 212 // Sends which originate from our node will report forward events with zero 213 // incoming circuits in their htlc key. 214 type ForwardingEvent struct { 215 // HtlcKey uniquely identifies the htlc, and can be used to match the 216 // forwarding event with subsequent settle/fail events. 217 HtlcKey 218 219 // HtlcInfo contains details about the htlc. 220 HtlcInfo 221 222 // HtlcEventType classifies the event as part of a local send or 223 // receive, or as part of a forward. 224 HtlcEventType 225 226 // Timestamp is the time when this htlc was forwarded. 227 Timestamp time.Time 228 } 229 230 // LinkFailEvent describes a htlc that failed on our incoming or outgoing 231 // link. The incoming bool is true for failures on incoming links, and false 232 // for failures on outgoing links. The failure reason is provided by a lnwire 233 // failure message which is enriched with a failure detail in the cases where 234 // the wire failure message does not contain full information about the 235 // failure. 236 type LinkFailEvent struct { 237 // HtlcKey uniquely identifies the htlc. 238 HtlcKey 239 240 // HtlcInfo contains details about the htlc. 241 HtlcInfo 242 243 // HtlcEventType classifies the event as part of a local send or 244 // receive, or as part of a forward. 245 HtlcEventType 246 247 // LinkError is the reason that we failed the htlc. 248 LinkError *LinkError 249 250 // Incoming is true if the htlc was failed on an incoming link. 251 // If it failed on the outgoing link, it is false. 252 Incoming bool 253 254 // Timestamp is the time when the link failure occurred. 255 Timestamp time.Time 256 } 257 258 // ForwardingFailEvent represents a htlc failure which occurred down the line 259 // after we forwarded a htlc onwards. An error is not included in this event 260 // because errors returned down the route are encrypted. HtlcInfo is not 261 // reliably available for forwarding failures, so it is omitted. These events 262 // should be matched with their corresponding forward event to obtain this 263 // information. 264 type ForwardingFailEvent struct { 265 // HtlcKey uniquely identifies the htlc, and can be used to match the 266 // htlc with its corresponding forwarding event. 267 HtlcKey 268 269 // HtlcEventType classifies the event as part of a local send or 270 // receive, or as part of a forward. 271 HtlcEventType 272 273 // Timestamp is the time when the forwarding failure was received. 274 Timestamp time.Time 275 } 276 277 // SettleEvent represents a htlc that was settled. HtlcInfo is not reliably 278 // available for forwarding failures, so it is omitted. These events should 279 // be matched with corresponding forward events or invoices (for receives) 280 // to obtain additional information about the htlc. 281 type SettleEvent struct { 282 // HtlcKey uniquely identifies the htlc, and can be used to match 283 // forwards with their corresponding forwarding event. 284 HtlcKey 285 286 // Preimage that was released for settling the htlc. 287 Preimage lntypes.Preimage 288 289 // HtlcEventType classifies the event as part of a local send or 290 // receive, or as part of a forward. 291 HtlcEventType 292 293 // Timestamp is the time when this htlc was settled. 294 Timestamp time.Time 295 } 296 297 // NotifyForwardingEvent notifies the HtlcNotifier than a htlc has been 298 // forwarded. 299 // 300 // Note this is part of the htlcNotifier interface. 301 func (h *HtlcNotifier) NotifyForwardingEvent(key HtlcKey, info HtlcInfo, 302 eventType HtlcEventType) { 303 304 event := &ForwardingEvent{ 305 HtlcKey: key, 306 HtlcInfo: info, 307 HtlcEventType: eventType, 308 Timestamp: h.now(), 309 } 310 311 log.Tracef("Notifying forward event: %v over %v, %v", eventType, key, 312 info) 313 314 if err := h.ntfnServer.SendUpdate(event); err != nil { 315 log.Warnf("Unable to send forwarding event: %v", err) 316 } 317 } 318 319 // NotifyLinkFailEvent notifies that a htlc has failed on our incoming 320 // or outgoing link. 321 // 322 // Note this is part of the htlcNotifier interface. 323 func (h *HtlcNotifier) NotifyLinkFailEvent(key HtlcKey, info HtlcInfo, 324 eventType HtlcEventType, linkErr *LinkError, incoming bool) { 325 326 event := &LinkFailEvent{ 327 HtlcKey: key, 328 HtlcInfo: info, 329 HtlcEventType: eventType, 330 LinkError: linkErr, 331 Incoming: incoming, 332 Timestamp: h.now(), 333 } 334 335 log.Tracef("Notifying link failure event: %v over %v, %v", eventType, 336 key, info) 337 338 if err := h.ntfnServer.SendUpdate(event); err != nil { 339 log.Warnf("Unable to send link fail event: %v", err) 340 } 341 } 342 343 // NotifyForwardingFailEvent notifies the HtlcNotifier that a htlc we 344 // forwarded has failed down the line. 345 // 346 // Note this is part of the htlcNotifier interface. 347 func (h *HtlcNotifier) NotifyForwardingFailEvent(key HtlcKey, 348 eventType HtlcEventType) { 349 350 event := &ForwardingFailEvent{ 351 HtlcKey: key, 352 HtlcEventType: eventType, 353 Timestamp: h.now(), 354 } 355 356 log.Tracef("Notifying forwarding failure event: %v over %v", eventType, 357 key) 358 359 if err := h.ntfnServer.SendUpdate(event); err != nil { 360 log.Warnf("Unable to send forwarding fail event: %v", err) 361 } 362 } 363 364 // NotifySettleEvent notifies the HtlcNotifier that a htlc that we committed 365 // to as part of a forward or a receive to our node has been settled. 366 // 367 // Note this is part of the htlcNotifier interface. 368 func (h *HtlcNotifier) NotifySettleEvent(key HtlcKey, 369 preimage lntypes.Preimage, eventType HtlcEventType) { 370 371 event := &SettleEvent{ 372 HtlcKey: key, 373 Preimage: preimage, 374 HtlcEventType: eventType, 375 Timestamp: h.now(), 376 } 377 378 log.Tracef("Notifying settle event: %v over %v", eventType, key) 379 380 if err := h.ntfnServer.SendUpdate(event); err != nil { 381 log.Warnf("Unable to send settle event: %v", err) 382 } 383 } 384 385 // newHtlc key returns a htlc key for the packet provided. If the packet 386 // has a zero incoming channel ID, the packet is for one of our own sends, 387 // which has the payment id stashed in the incoming htlc id. If this is the 388 // case, we replace the incoming htlc id with zero so that the notifier 389 // consistently reports zero circuit keys for events that terminate or 390 // originate at our node. 391 func newHtlcKey(pkt *htlcPacket) HtlcKey { 392 htlcKey := HtlcKey{ 393 IncomingCircuit: channeldb.CircuitKey{ 394 ChanID: pkt.incomingChanID, 395 HtlcID: pkt.incomingHTLCID, 396 }, 397 OutgoingCircuit: CircuitKey{ 398 ChanID: pkt.outgoingChanID, 399 HtlcID: pkt.outgoingHTLCID, 400 }, 401 } 402 403 // If the packet has a zero incoming channel ID, it is a send that was 404 // initiated at our node. If this is the case, our internal pid is in 405 // the incoming htlc ID, so we overwrite it with 0 for notification 406 // purposes. 407 if pkt.incomingChanID == hop.Source { 408 htlcKey.IncomingCircuit.HtlcID = 0 409 } 410 411 return htlcKey 412 } 413 414 // newHtlcInfo returns HtlcInfo for the packet provided. 415 func newHtlcInfo(pkt *htlcPacket) HtlcInfo { 416 return HtlcInfo{ 417 IncomingTimeLock: pkt.incomingTimeout, 418 OutgoingTimeLock: pkt.outgoingTimeout, 419 IncomingAmt: pkt.incomingAmount, 420 OutgoingAmt: pkt.amount, 421 } 422 } 423 424 // getEventType returns the htlc type based on the fields set in the htlc 425 // packet. Sends that originate at our node have the source (zero) incoming 426 // channel ID. Receives to our node have the exit (zero) outgoing channel ID 427 // and forwards have both fields set. 428 func getEventType(pkt *htlcPacket) HtlcEventType { 429 switch { 430 case pkt.incomingChanID == hop.Source: 431 return HtlcEventTypeSend 432 433 case pkt.outgoingChanID == hop.Exit: 434 return HtlcEventTypeReceive 435 436 default: 437 return HtlcEventTypeForward 438 } 439 }