github.com/decred/dcrlnd@v0.7.6/htlcswitch/circuit_map.go (about) 1 package htlcswitch 2 3 import ( 4 "bytes" 5 "fmt" 6 "sync" 7 8 "github.com/davecgh/go-spew/spew" 9 "github.com/decred/dcrlnd/channeldb" 10 "github.com/decred/dcrlnd/htlcswitch/hop" 11 "github.com/decred/dcrlnd/kvdb" 12 "github.com/decred/dcrlnd/lnwire" 13 "github.com/go-errors/errors" 14 ) 15 16 var ( 17 // ErrCorruptedCircuitMap indicates that the on-disk bucketing structure 18 // has altered since the circuit map instance was initialized. 19 ErrCorruptedCircuitMap = errors.New("circuit map has been corrupted") 20 21 // ErrCircuitNotInHashIndex indicates that a particular circuit did not 22 // appear in the in-memory hash index. 23 ErrCircuitNotInHashIndex = errors.New("payment circuit not found in " + 24 "hash index") 25 26 // ErrUnknownCircuit signals that circuit could not be removed from the 27 // map because it was not found. 28 ErrUnknownCircuit = errors.New("unknown payment circuit") 29 30 // ErrCircuitClosing signals that an htlc has already closed this 31 // circuit in-memory. 32 ErrCircuitClosing = errors.New("circuit has already been closed") 33 34 // ErrDuplicateCircuit signals that this circuit was previously 35 // added. 36 ErrDuplicateCircuit = errors.New("duplicate circuit add") 37 38 // ErrUnknownKeystone signals that no circuit was found using the 39 // outgoing circuit key. 40 ErrUnknownKeystone = errors.New("unknown circuit keystone") 41 42 // ErrDuplicateKeystone signals that this circuit was previously 43 // assigned a keystone. 44 ErrDuplicateKeystone = errors.New("cannot add duplicate keystone") 45 ) 46 47 // CircuitModifier is a common interface used by channel links to modify the 48 // contents of the circuit map maintained by the switch. 49 type CircuitModifier interface { 50 // OpenCircuits preemptively records a batch keystones that will mark 51 // currently pending circuits as open. These changes can be rolled back 52 // on restart if the outgoing Adds do not make it into a commitment 53 // txn. 54 OpenCircuits(...Keystone) error 55 56 // TrimOpenCircuits removes a channel's open channels with htlc indexes 57 // above `start`. 58 TrimOpenCircuits(chanID lnwire.ShortChannelID, start uint64) error 59 60 // DeleteCircuits removes the incoming circuit key to remove all 61 // persistent references to a circuit. Returns a ErrUnknownCircuit if 62 // any of the incoming keys are not known. 63 DeleteCircuits(inKeys ...CircuitKey) error 64 } 65 66 // CircuitLookup is a common interface used to lookup information that is stored 67 // in the circuit map. 68 type CircuitLookup interface { 69 // LookupCircuit queries the circuit map for the circuit identified by 70 // inKey. 71 LookupCircuit(inKey CircuitKey) *PaymentCircuit 72 73 // LookupOpenCircuit queries the circuit map for a circuit identified 74 // by its outgoing circuit key. 75 LookupOpenCircuit(outKey CircuitKey) *PaymentCircuit 76 } 77 78 // CircuitFwdActions represents the forwarding decision made by the circuit 79 // map, and is returned from CommitCircuits. The sequence of circuits provided 80 // to CommitCircuits is split into three sub-sequences, allowing the caller to 81 // do an in-order scan, comparing the head of each subsequence, to determine 82 // the decision made by the circuit map. 83 type CircuitFwdActions struct { 84 // Adds is the subsequence of circuits that were successfully committed 85 // in the circuit map. 86 Adds []*PaymentCircuit 87 88 // Drops is the subsequence of circuits for which no action should be 89 // done. 90 Drops []*PaymentCircuit 91 92 // Fails is the subsequence of circuits that should be failed back by 93 // the calling link. 94 Fails []*PaymentCircuit 95 } 96 97 // CircuitMap is an interface for managing the construction and teardown of 98 // payment circuits used by the switch. 99 type CircuitMap interface { 100 CircuitModifier 101 102 CircuitLookup 103 104 // CommitCircuits attempts to add the given circuits to the circuit 105 // map. The list of circuits is split into three distinct 106 // sub-sequences, corresponding to adds, drops, and fails. Adds should 107 // be forwarded to the switch, while fails should be failed back 108 // locally within the calling link. 109 CommitCircuits(circuit ...*PaymentCircuit) (*CircuitFwdActions, error) 110 111 // CloseCircuit marks the circuit identified by `outKey` as closing 112 // in-memory, which prevents duplicate settles/fails from completing an 113 // open circuit twice. 114 CloseCircuit(outKey CircuitKey) (*PaymentCircuit, error) 115 116 // FailCircuit is used by locally failed HTLCs to mark the circuit 117 // identified by `inKey` as closing in-memory, which prevents duplicate 118 // settles/fails from being accepted for the same circuit. 119 FailCircuit(inKey CircuitKey) (*PaymentCircuit, error) 120 121 // LookupByPaymentHash queries the circuit map and returns all open 122 // circuits that use the given payment hash. 123 LookupByPaymentHash(hash [32]byte) []*PaymentCircuit 124 125 // NumPending returns the total number of active circuits added by 126 // CommitCircuits. 127 NumPending() int 128 129 // NumOpen returns the number of circuits with HTLCs that have been 130 // forwarded via an outgoing link. 131 NumOpen() int 132 } 133 134 var ( 135 // circuitAddKey is the key used to retrieve the bucket containing 136 // payment circuits. A circuit records information about how to return 137 // a packet to the source link, potentially including an error 138 // encrypter for applying this hop's encryption to the payload in the 139 // reverse direction. 140 // 141 // Bucket hierarchy: 142 // 143 // circuitAddKey(root-bucket) 144 // | 145 // |-- <incoming-circuit-key>: <encoded bytes of PaymentCircuit> 146 // |-- <incoming-circuit-key>: <encoded bytes of PaymentCircuit> 147 // | 148 // ... 149 // 150 circuitAddKey = []byte("circuit-adds") 151 152 // circuitKeystoneKey is used to retrieve the bucket containing circuit 153 // keystones, which are set in place once a forwarded packet is 154 // assigned an index on an outgoing commitment txn. 155 // 156 // Bucket hierarchy: 157 // 158 // circuitKeystoneKey(root-bucket) 159 // | 160 // |-- <outgoing-circuit-key>: <incoming-circuit-key> 161 // |-- <outgoing-circuit-key>: <incoming-circuit-key> 162 // | 163 // ... 164 // 165 circuitKeystoneKey = []byte("circuit-keystones") 166 ) 167 168 // circuitMap is a data structure that implements thread safe, persistent 169 // storage of circuit routing information. The switch consults a circuit map to 170 // determine where to forward returning HTLC update messages. Circuits are 171 // always identifiable by their incoming CircuitKey, in addition to their 172 // outgoing CircuitKey if the circuit is fully-opened. 173 type circuitMap struct { 174 cfg *CircuitMapConfig 175 176 mtx sync.RWMutex 177 178 // pending is an in-memory mapping of all half payment circuits, and is 179 // kept in sync with the on-disk contents of the circuit map. 180 pending map[CircuitKey]*PaymentCircuit 181 182 // opened is an in-memory mapping of all full payment circuits, which 183 // is also synchronized with the persistent state of the circuit map. 184 opened map[CircuitKey]*PaymentCircuit 185 186 // closed is an in-memory set of circuits for which the switch has 187 // received a settle or fail. This precedes the actual deletion of a 188 // circuit from disk. 189 closed map[CircuitKey]struct{} 190 191 // hashIndex is a volatile index that facilitates fast queries by 192 // payment hash against the contents of circuits. This index can be 193 // reconstructed entirely from the set of persisted full circuits on 194 // startup. 195 hashIndex map[[32]byte]map[CircuitKey]struct{} 196 } 197 198 // CircuitMapConfig houses the critical interfaces and references necessary to 199 // parameterize an instance of circuitMap. 200 type CircuitMapConfig struct { 201 // DB provides the persistent storage engine for the circuit map. 202 DB kvdb.Backend 203 204 // FetchAllOpenChannels is a function that fetches all currently open 205 // channels from the channel database. 206 FetchAllOpenChannels func() ([]*channeldb.OpenChannel, error) 207 208 // FetchClosedChannels is a function that fetches all closed channels 209 // from the channel database. 210 FetchClosedChannels func( 211 pendingOnly bool) ([]*channeldb.ChannelCloseSummary, error) 212 213 // ExtractErrorEncrypter derives the shared secret used to encrypt 214 // errors from the obfuscator's ephemeral public key. 215 ExtractErrorEncrypter hop.ErrorEncrypterExtracter 216 } 217 218 // NewCircuitMap creates a new instance of the circuitMap. 219 func NewCircuitMap(cfg *CircuitMapConfig) (CircuitMap, error) { 220 cm := &circuitMap{ 221 cfg: cfg, 222 } 223 224 // Initialize the on-disk buckets used by the circuit map. 225 if err := cm.initBuckets(); err != nil { 226 return nil, err 227 } 228 229 // Delete old circuits and keystones of closed channels. 230 if err := cm.cleanClosedChannels(); err != nil { 231 return nil, err 232 } 233 234 // Load any previously persisted circuit into back into memory. 235 if err := cm.restoreMemState(); err != nil { 236 return nil, err 237 } 238 239 // Trim any keystones that were not committed in an outgoing commit txn. 240 // 241 // NOTE: This operation will be applied to the persistent state of all 242 // active channels. Therefore, it must be called before any links are 243 // created to avoid interfering with normal operation. 244 if err := cm.trimAllOpenCircuits(); err != nil { 245 return nil, err 246 } 247 248 return cm, nil 249 } 250 251 // initBuckets ensures that the primary buckets used by the circuit are 252 // initialized so that we can assume their existence after startup. 253 func (cm *circuitMap) initBuckets() error { 254 return kvdb.Update(cm.cfg.DB, func(tx kvdb.RwTx) error { 255 _, err := tx.CreateTopLevelBucket(circuitKeystoneKey) 256 if err != nil { 257 return err 258 } 259 260 _, err = tx.CreateTopLevelBucket(circuitAddKey) 261 return err 262 }, func() {}) 263 } 264 265 // cleanClosedChannels deletes all circuits and keystones related to closed 266 // channels. It first reads all the closed channels and caches the ShortChanIDs 267 // into a map for fast lookup. Then it iterates the circuit bucket and keystone 268 // bucket and deletes items whose ChanID matches the ShortChanID. 269 // 270 // NOTE: this operation can also be built into restoreMemState since the latter 271 // already opens and iterates the two root buckets, circuitAddKey and 272 // circuitKeystoneKey. Depending on the size of the buckets, this marginal gain 273 // may be worth investigating. Atm, for clarity, this operation is wrapped into 274 // its own function. 275 func (cm *circuitMap) cleanClosedChannels() error { 276 log.Infof("Cleaning circuits from disk for closed channels") 277 278 // closedChanIDSet stores the short channel IDs for closed channels. 279 closedChanIDSet := make(map[lnwire.ShortChannelID]struct{}) 280 281 // circuitKeySet stores the incoming circuit keys of the payment 282 // circuits that need to be deleted. 283 circuitKeySet := make(map[CircuitKey]struct{}) 284 285 // keystoneKeySet stores the outgoing keys of the keystones that need 286 // to be deleted. 287 keystoneKeySet := make(map[CircuitKey]struct{}) 288 289 // isClosedChannel is a helper closure that returns a bool indicating 290 // the chanID belongs to a closed channel. 291 isClosedChannel := func(chanID lnwire.ShortChannelID) bool { 292 // Skip if the channel ID is zero value. This has the effect 293 // that a zero value incoming or outgoing key will never be 294 // matched and its corresponding circuits or keystones are not 295 // deleted. 296 if chanID.ToUint64() == 0 { 297 return false 298 } 299 300 _, ok := closedChanIDSet[chanID] 301 return ok 302 } 303 304 // Find closed channels and cache their ShortChannelIDs into a map. 305 // This map will be used for looking up relative circuits and keystones. 306 closedChannels, err := cm.cfg.FetchClosedChannels(false) 307 if err != nil { 308 return err 309 } 310 311 for _, closedChannel := range closedChannels { 312 // Skip if the channel close is pending. 313 if closedChannel.IsPending { 314 continue 315 } 316 317 closedChanIDSet[closedChannel.ShortChanID] = struct{}{} 318 } 319 320 log.Debugf("Found %v closed channels", len(closedChanIDSet)) 321 322 // Exit early if there are no closed channels. 323 if len(closedChanIDSet) == 0 { 324 log.Infof("Finished cleaning: no closed channels found, " + 325 "no actions taken.", 326 ) 327 return nil 328 } 329 330 // Find the payment circuits and keystones that need to be deleted. 331 if err := kvdb.View(cm.cfg.DB, func(tx kvdb.RTx) error { 332 circuitBkt := tx.ReadBucket(circuitAddKey) 333 if circuitBkt == nil { 334 return ErrCorruptedCircuitMap 335 } 336 keystoneBkt := tx.ReadBucket(circuitKeystoneKey) 337 if keystoneBkt == nil { 338 return ErrCorruptedCircuitMap 339 } 340 341 // If a circuit's incoming/outgoing key prefix matches the 342 // ShortChanID, it will be deleted. However, if the ShortChanID 343 // of the incoming key is zero, the circuit will be kept as it 344 // indicates a locally initiated payment. 345 if err := circuitBkt.ForEach(func(_, v []byte) error { 346 circuit, err := cm.decodeCircuit(v) 347 if err != nil { 348 return err 349 } 350 351 // Check if the incoming channel ID can be found in the 352 // closed channel ID map. 353 if !isClosedChannel(circuit.Incoming.ChanID) { 354 return nil 355 } 356 357 circuitKeySet[circuit.Incoming] = struct{}{} 358 359 return nil 360 }); err != nil { 361 return err 362 } 363 364 // If a keystone's InKey or OutKey matches the short channel id 365 // in the closed channel ID map, it will be deleted. 366 err := keystoneBkt.ForEach(func(k, v []byte) error { 367 var ( 368 inKey CircuitKey 369 outKey CircuitKey 370 ) 371 372 // Decode the incoming and outgoing circuit keys. 373 if err := inKey.SetBytes(v); err != nil { 374 return err 375 } 376 if err := outKey.SetBytes(k); err != nil { 377 return err 378 } 379 380 // Check if the incoming channel ID can be found in the 381 // closed channel ID map. 382 if isClosedChannel(inKey.ChanID) { 383 // If the incoming channel is closed, we can 384 // skip checking on outgoing channel ID because 385 // this keystone will be deleted. 386 keystoneKeySet[outKey] = struct{}{} 387 388 // Technically the incoming keys found in 389 // keystone bucket should be a subset of 390 // circuit bucket. So a previous loop should 391 // have this inKey put inside circuitAddKey map 392 // already. We do this again to be sure the 393 // circuits are properly cleaned. Even this 394 // inKey doesn't exist in circuit bucket, we 395 // are fine as db deletion is a noop. 396 circuitKeySet[inKey] = struct{}{} 397 return nil 398 } 399 400 // Check if the outgoing channel ID can be found in the 401 // closed channel ID map. Notice that we need to store 402 // the outgoing key because it's used for db query. 403 if isClosedChannel(outKey.ChanID) { 404 keystoneKeySet[outKey] = struct{}{} 405 406 // Also update circuitKeySet to mark the 407 // payment circuit needs to be deleted. 408 circuitKeySet[inKey] = struct{}{} 409 } 410 411 return nil 412 }) 413 return err 414 415 }, func() { 416 // Reset the sets. 417 circuitKeySet = make(map[CircuitKey]struct{}) 418 keystoneKeySet = make(map[CircuitKey]struct{}) 419 }); err != nil { 420 return err 421 } 422 423 log.Debugf("To be deleted: num_circuits=%v, num_keystones=%v", 424 len(circuitKeySet), len(keystoneKeySet), 425 ) 426 427 numCircuitsDeleted := 0 428 numKeystonesDeleted := 0 429 430 // Delete all the circuits and keystones for closed channels. 431 if err := kvdb.Update(cm.cfg.DB, func(tx kvdb.RwTx) error { 432 circuitBkt := tx.ReadWriteBucket(circuitAddKey) 433 if circuitBkt == nil { 434 return ErrCorruptedCircuitMap 435 } 436 keystoneBkt := tx.ReadWriteBucket(circuitKeystoneKey) 437 if keystoneBkt == nil { 438 return ErrCorruptedCircuitMap 439 } 440 441 // Delete the ciruit. 442 for inKey := range circuitKeySet { 443 if err := circuitBkt.Delete(inKey.Bytes()); err != nil { 444 return err 445 } 446 447 numCircuitsDeleted++ 448 } 449 450 // Delete the keystone using the outgoing key. 451 for outKey := range keystoneKeySet { 452 err := keystoneBkt.Delete(outKey.Bytes()) 453 if err != nil { 454 return err 455 } 456 457 numKeystonesDeleted++ 458 } 459 460 return nil 461 }, func() {}); err != nil { 462 numCircuitsDeleted = 0 463 numKeystonesDeleted = 0 464 return err 465 } 466 467 log.Infof("Finished cleaning: num_closed_channel=%v, "+ 468 "num_circuits=%v, num_keystone=%v", 469 len(closedChannels), numCircuitsDeleted, numKeystonesDeleted, 470 ) 471 472 return nil 473 } 474 475 // restoreMemState loads the contents of the half circuit and full circuit 476 // buckets from disk and reconstructs the in-memory representation of the 477 // circuit map. Afterwards, the state of the hash index is reconstructed using 478 // the recovered set of full circuits. This method will also remove any stray 479 // keystones, which are those that appear fully-opened, but have no pending 480 // circuit related to the intended incoming link. 481 func (cm *circuitMap) restoreMemState() error { 482 log.Infof("Restoring in-memory circuit state from disk") 483 484 var ( 485 opened map[CircuitKey]*PaymentCircuit 486 pending map[CircuitKey]*PaymentCircuit 487 ) 488 489 if err := kvdb.Update(cm.cfg.DB, func(tx kvdb.RwTx) error { 490 // Restore any of the circuits persisted in the circuit bucket 491 // back into memory. 492 circuitBkt := tx.ReadWriteBucket(circuitAddKey) 493 if circuitBkt == nil { 494 return ErrCorruptedCircuitMap 495 } 496 497 if err := circuitBkt.ForEach(func(_, v []byte) error { 498 circuit, err := cm.decodeCircuit(v) 499 if err != nil { 500 return err 501 } 502 503 circuit.LoadedFromDisk = true 504 pending[circuit.Incoming] = circuit 505 506 return nil 507 }); err != nil { 508 return err 509 } 510 511 // Furthermore, load the keystone bucket and resurrect the 512 // keystones used in any open circuits. 513 keystoneBkt := tx.ReadWriteBucket(circuitKeystoneKey) 514 if keystoneBkt == nil { 515 return ErrCorruptedCircuitMap 516 } 517 518 var strayKeystones []Keystone 519 if err := keystoneBkt.ForEach(func(k, v []byte) error { 520 var ( 521 inKey CircuitKey 522 outKey = &CircuitKey{} 523 ) 524 525 // Decode the incoming and outgoing circuit keys. 526 if err := inKey.SetBytes(v); err != nil { 527 return err 528 } 529 if err := outKey.SetBytes(k); err != nil { 530 return err 531 } 532 533 // Retrieve the pending circuit, set its keystone, then 534 // add it to the opened map. 535 circuit, ok := pending[inKey] 536 if ok { 537 circuit.Outgoing = outKey 538 opened[*outKey] = circuit 539 } else { 540 strayKeystones = append(strayKeystones, Keystone{ 541 InKey: inKey, 542 OutKey: *outKey, 543 }) 544 } 545 546 return nil 547 }); err != nil { 548 return err 549 } 550 551 // If any stray keystones were found, we'll proceed to prune 552 // them from the circuit map's persistent storage. This may 553 // manifest on older nodes that had updated channels before 554 // their short channel id was set properly. We believe this 555 // issue has been fixed, though this will allow older nodes to 556 // recover without additional intervention. 557 for _, strayKeystone := range strayKeystones { 558 // As a precaution, we will only cleanup keystones 559 // related to locally-initiated payments. If a 560 // documented case of stray keystones emerges for 561 // forwarded payments, this check should be removed, but 562 // with extreme caution. 563 if strayKeystone.OutKey.ChanID != hop.Source { 564 continue 565 } 566 567 log.Infof("Removing stray keystone: %v", strayKeystone) 568 err := keystoneBkt.Delete(strayKeystone.OutKey.Bytes()) 569 if err != nil { 570 return err 571 } 572 } 573 574 return nil 575 576 }, func() { 577 opened = make(map[CircuitKey]*PaymentCircuit) 578 pending = make(map[CircuitKey]*PaymentCircuit) 579 }); err != nil { 580 return err 581 } 582 583 cm.pending = pending 584 cm.opened = opened 585 cm.closed = make(map[CircuitKey]struct{}) 586 587 log.Infof("Payment circuits loaded: num_pending=%v, num_open=%v", 588 len(pending), len(opened)) 589 590 // Finally, reconstruct the hash index by running through our set of 591 // open circuits. 592 cm.hashIndex = make(map[[32]byte]map[CircuitKey]struct{}) 593 for _, circuit := range opened { 594 cm.addCircuitToHashIndex(circuit) 595 } 596 597 return nil 598 } 599 600 // decodeCircuit reconstructs an in-memory payment circuit from a byte slice. 601 // The byte slice is assumed to have been generated by the circuit's Encode 602 // method. If the decoding is successful, the onion obfuscator will be 603 // reextracted, since it is not stored in plaintext on disk. 604 func (cm *circuitMap) decodeCircuit(v []byte) (*PaymentCircuit, error) { 605 var circuit = &PaymentCircuit{} 606 607 circuitReader := bytes.NewReader(v) 608 if err := circuit.Decode(circuitReader); err != nil { 609 return nil, err 610 } 611 612 // If the error encrypter is nil, this is locally-source payment so 613 // there is no encrypter. 614 if circuit.ErrorEncrypter == nil { 615 return circuit, nil 616 } 617 618 // Otherwise, we need to reextract the encrypter, so that the shared 619 // secret is rederived from what was decoded. 620 err := circuit.ErrorEncrypter.Reextract( 621 cm.cfg.ExtractErrorEncrypter, 622 ) 623 if err != nil { 624 return nil, err 625 } 626 627 return circuit, nil 628 } 629 630 // trimAllOpenCircuits reads the set of active channels from disk and trims 631 // keystones for any non-pending channels using the next unallocated htlc index. 632 // This method is intended to be called on startup. Each link will also trim 633 // it's own circuits upon startup. 634 // 635 // NOTE: This operation will be applied to the persistent state of all active 636 // channels. Therefore, it must be called before any links are created to avoid 637 // interfering with normal operation. 638 func (cm *circuitMap) trimAllOpenCircuits() error { 639 activeChannels, err := cm.cfg.FetchAllOpenChannels() 640 if err != nil { 641 return err 642 } 643 644 for _, activeChannel := range activeChannels { 645 if activeChannel.IsPending { 646 continue 647 } 648 649 // First, skip any channels that have not been assigned their 650 // final channel identifier, otherwise we would try to trim 651 // htlcs belonging to the all-zero, hop.Source ID. 652 chanID := activeChannel.ShortChanID() 653 if chanID == hop.Source { 654 continue 655 } 656 657 // Next, retrieve the next unallocated htlc index, which bounds 658 // the cutoff of confirmed htlc indexes. 659 start, err := activeChannel.NextLocalHtlcIndex() 660 if err != nil { 661 return err 662 } 663 664 // Finally, remove all pending circuits above at or above the 665 // next unallocated local htlc indexes. This has the effect of 666 // reverting any circuits that have either not been locked in, 667 // or had not been included in a pending commitment. 668 err = cm.TrimOpenCircuits(chanID, start) 669 if err != nil { 670 return err 671 } 672 } 673 674 return nil 675 } 676 677 // TrimOpenCircuits removes a channel's keystones above the short chan id's 678 // highest committed htlc index. This has the effect of returning those 679 // circuits to a half-open state. Since opening of circuits is done in advance 680 // of actually committing the Add htlcs into a commitment txn, this allows 681 // circuits to be opened preemptively, since we can roll them back after any 682 // failures. 683 func (cm *circuitMap) TrimOpenCircuits(chanID lnwire.ShortChannelID, 684 start uint64) error { 685 686 log.Infof("Trimming open circuits for chan_id=%v, start_htlc_id=%v", 687 chanID, start) 688 689 var trimmedOutKeys []CircuitKey 690 691 // Scan forward from the last unacked htlc id, stopping as soon as we 692 // don't find any more. Outgoing htlc id's must be assigned in order, 693 // so there should never be disjoint segments of keystones to trim. 694 cm.mtx.Lock() 695 for i := start; ; i++ { 696 outKey := CircuitKey{ 697 ChanID: chanID, 698 HtlcID: i, 699 } 700 701 circuit, ok := cm.opened[outKey] 702 if !ok { 703 break 704 } 705 706 circuit.Outgoing = nil 707 delete(cm.opened, outKey) 708 trimmedOutKeys = append(trimmedOutKeys, outKey) 709 cm.removeCircuitFromHashIndex(circuit) 710 } 711 cm.mtx.Unlock() 712 713 if len(trimmedOutKeys) == 0 { 714 return nil 715 } 716 717 return kvdb.Update(cm.cfg.DB, func(tx kvdb.RwTx) error { 718 keystoneBkt := tx.ReadWriteBucket(circuitKeystoneKey) 719 if keystoneBkt == nil { 720 return ErrCorruptedCircuitMap 721 } 722 723 for _, outKey := range trimmedOutKeys { 724 err := keystoneBkt.Delete(outKey.Bytes()) 725 if err != nil { 726 return err 727 } 728 } 729 730 return nil 731 }, func() {}) 732 } 733 734 // LookupCircuit queries the circuit map for the circuit identified by its 735 // incoming circuit key. Returns nil if there is no such circuit. 736 func (cm *circuitMap) LookupCircuit(inKey CircuitKey) *PaymentCircuit { 737 cm.mtx.RLock() 738 defer cm.mtx.RUnlock() 739 740 return cm.pending[inKey] 741 } 742 743 // LookupOpenCircuit searches for the circuit identified by its outgoing circuit 744 // key. 745 func (cm *circuitMap) LookupOpenCircuit(outKey CircuitKey) *PaymentCircuit { 746 cm.mtx.RLock() 747 defer cm.mtx.RUnlock() 748 749 return cm.opened[outKey] 750 } 751 752 // LookupByPaymentHash looks up and returns any payment circuits with a given 753 // payment hash. 754 func (cm *circuitMap) LookupByPaymentHash(hash [32]byte) []*PaymentCircuit { 755 cm.mtx.RLock() 756 defer cm.mtx.RUnlock() 757 758 var circuits []*PaymentCircuit 759 if circuitSet, ok := cm.hashIndex[hash]; ok { 760 // Iterate over the outgoing circuit keys found with this hash, 761 // and retrieve the circuit from the opened map. 762 circuits = make([]*PaymentCircuit, 0, len(circuitSet)) 763 for key := range circuitSet { 764 if circuit, ok := cm.opened[key]; ok { 765 circuits = append(circuits, circuit) 766 } 767 } 768 } 769 770 return circuits 771 } 772 773 // CommitCircuits accepts any number of circuits and persistently adds them to 774 // the switch's circuit map. The method returns a list of circuits that had not 775 // been seen prior by the switch. A link should only forward HTLCs corresponding 776 // to the returned circuits to the switch. 777 // 778 // NOTE: This method uses batched writes to improve performance, gains will only 779 // be realized if it is called concurrently from separate goroutines. 780 func (cm *circuitMap) CommitCircuits(circuits ...*PaymentCircuit) ( 781 *CircuitFwdActions, error) { 782 783 inKeys := make([]CircuitKey, 0, len(circuits)) 784 for _, circuit := range circuits { 785 inKeys = append(inKeys, circuit.Incoming) 786 } 787 788 log.Tracef("Committing fresh circuits: %v", newLogClosure(func() string { 789 return spew.Sdump(inKeys) 790 })) 791 792 actions := &CircuitFwdActions{} 793 794 // If an empty list was passed, return early to avoid grabbing the lock. 795 if len(circuits) == 0 { 796 return actions, nil 797 } 798 799 // First, we reconcile the provided circuits with our set of pending 800 // circuits to construct a set of new circuits that need to be written 801 // to disk. The circuit's pointer is stored so that we only permit this 802 // exact circuit to be forwarded through the switch. If a circuit is 803 // already pending, the htlc will be reforwarded by the switch. 804 // 805 // NOTE: We track an additional addFails subsequence, which permits us 806 // to fail back all packets that weren't dropped if we encounter an 807 // error when committing the circuits. 808 cm.mtx.Lock() 809 var adds, drops, fails, addFails []*PaymentCircuit 810 for _, circuit := range circuits { 811 inKey := circuit.InKey() 812 if foundCircuit, ok := cm.pending[inKey]; ok { 813 switch { 814 815 // This circuit has a keystone, it's waiting for a 816 // response from the remote peer on the outgoing link. 817 // Drop it like it's hot, ensure duplicates get caught. 818 case foundCircuit.HasKeystone(): 819 drops = append(drops, circuit) 820 821 // If no keystone is set and the switch has not been 822 // restarted, the corresponding packet should still be 823 // in the outgoing link's mailbox. It will be delivered 824 // if it comes online before the switch goes down. 825 // 826 // NOTE: Dropping here prevents a flapping, incoming 827 // link from failing a duplicate add while it is still 828 // in the server's memory mailboxes. 829 case !foundCircuit.LoadedFromDisk: 830 drops = append(drops, circuit) 831 832 // Otherwise, the in-mem packet has been lost due to a 833 // restart. It is now safe to send back a failure along 834 // the incoming link. The incoming link should be able 835 // detect and ignore duplicate packets of this type. 836 default: 837 fails = append(fails, circuit) 838 addFails = append(addFails, circuit) 839 } 840 841 continue 842 } 843 844 cm.pending[inKey] = circuit 845 adds = append(adds, circuit) 846 addFails = append(addFails, circuit) 847 } 848 cm.mtx.Unlock() 849 850 // If all circuits are dropped or failed, we are done. 851 if len(adds) == 0 { 852 actions.Drops = drops 853 actions.Fails = fails 854 return actions, nil 855 } 856 857 // Now, optimistically serialize the circuits to add. 858 var bs = make([]bytes.Buffer, len(adds)) 859 for i, circuit := range adds { 860 if err := circuit.Encode(&bs[i]); err != nil { 861 actions.Drops = drops 862 actions.Fails = addFails 863 return actions, err 864 } 865 } 866 867 // Write the entire batch of circuits to the persistent circuit bucket 868 // using bbolt's Batch write. This method must be called from multiple, 869 // distinct goroutines to have any impact on performance. 870 err := kvdb.Batch(cm.cfg.DB, func(tx kvdb.RwTx) error { 871 circuitBkt := tx.ReadWriteBucket(circuitAddKey) 872 if circuitBkt == nil { 873 return ErrCorruptedCircuitMap 874 } 875 876 for i, circuit := range adds { 877 inKeyBytes := circuit.InKey().Bytes() 878 circuitBytes := bs[i].Bytes() 879 880 err := circuitBkt.Put(inKeyBytes, circuitBytes) 881 if err != nil { 882 return err 883 } 884 } 885 886 return nil 887 }) 888 889 // Return if the write succeeded. 890 if err == nil { 891 actions.Adds = adds 892 actions.Drops = drops 893 actions.Fails = fails 894 return actions, nil 895 } 896 897 // Otherwise, rollback the circuits added to the pending set if the 898 // write failed. 899 cm.mtx.Lock() 900 for _, circuit := range adds { 901 delete(cm.pending, circuit.InKey()) 902 } 903 cm.mtx.Unlock() 904 905 // Since our write failed, we will return the dropped packets and mark 906 // all other circuits as failed. 907 actions.Drops = drops 908 actions.Fails = addFails 909 910 return actions, err 911 } 912 913 // Keystone is a tuple binding an incoming and outgoing CircuitKey. Keystones 914 // are preemptively written by an outgoing link before signing a new commitment 915 // state, and cements which HTLCs we are awaiting a response from a remote 916 // peer. 917 type Keystone struct { 918 InKey CircuitKey 919 OutKey CircuitKey 920 } 921 922 // String returns a human readable description of the Keystone. 923 func (k *Keystone) String() string { 924 return fmt.Sprintf("%s --> %s", k.InKey, k.OutKey) 925 } 926 927 // OpenCircuits sets the outgoing circuit key for the circuit identified by 928 // inKey, persistently marking the circuit as opened. After the changes have 929 // been persisted, the circuit map's in-memory indexes are updated so that this 930 // circuit can be queried using LookupByKeystone or LookupByPaymentHash. 931 func (cm *circuitMap) OpenCircuits(keystones ...Keystone) error { 932 if len(keystones) == 0 { 933 return nil 934 } 935 936 log.Tracef("Opening finalized circuits: %v", newLogClosure(func() string { 937 return spew.Sdump(keystones) 938 })) 939 940 // Check that all keystones correspond to committed-but-unopened 941 // circuits. 942 cm.mtx.RLock() 943 openedCircuits := make([]*PaymentCircuit, 0, len(keystones)) 944 for _, ks := range keystones { 945 if _, ok := cm.opened[ks.OutKey]; ok { 946 cm.mtx.RUnlock() 947 return ErrDuplicateKeystone 948 } 949 950 circuit, ok := cm.pending[ks.InKey] 951 if !ok { 952 cm.mtx.RUnlock() 953 return ErrUnknownCircuit 954 } 955 956 openedCircuits = append(openedCircuits, circuit) 957 } 958 cm.mtx.RUnlock() 959 960 err := kvdb.Update(cm.cfg.DB, func(tx kvdb.RwTx) error { 961 // Now, load the circuit bucket to which we will write the 962 // already serialized circuit. 963 keystoneBkt := tx.ReadWriteBucket(circuitKeystoneKey) 964 if keystoneBkt == nil { 965 return ErrCorruptedCircuitMap 966 } 967 968 for _, ks := range keystones { 969 outBytes := ks.OutKey.Bytes() 970 inBytes := ks.InKey.Bytes() 971 err := keystoneBkt.Put(outBytes, inBytes) 972 if err != nil { 973 return err 974 } 975 } 976 977 return nil 978 }, func() {}) 979 980 if err != nil { 981 return err 982 } 983 984 cm.mtx.Lock() 985 for i, circuit := range openedCircuits { 986 ks := keystones[i] 987 988 // Since our persistent operation was successful, we can now 989 // modify the in memory representations. Set the outgoing 990 // circuit key on our pending circuit, add the same circuit to 991 // set of opened circuits, and add this circuit to the hash 992 // index. 993 circuit.Outgoing = &CircuitKey{} 994 *circuit.Outgoing = ks.OutKey 995 996 cm.opened[ks.OutKey] = circuit 997 cm.addCircuitToHashIndex(circuit) 998 } 999 cm.mtx.Unlock() 1000 1001 return nil 1002 } 1003 1004 // addCirciutToHashIndex inserts a circuit into the circuit map's hash index, so 1005 // that it can be queried using LookupByPaymentHash. 1006 func (cm *circuitMap) addCircuitToHashIndex(c *PaymentCircuit) { 1007 if _, ok := cm.hashIndex[c.PaymentHash]; !ok { 1008 cm.hashIndex[c.PaymentHash] = make(map[CircuitKey]struct{}) 1009 } 1010 cm.hashIndex[c.PaymentHash][c.OutKey()] = struct{}{} 1011 } 1012 1013 // FailCircuit marks the circuit identified by `inKey` as closing in-memory, 1014 // which prevents duplicate settles/fails from completing an open circuit twice. 1015 func (cm *circuitMap) FailCircuit(inKey CircuitKey) (*PaymentCircuit, error) { 1016 1017 cm.mtx.Lock() 1018 defer cm.mtx.Unlock() 1019 1020 circuit, ok := cm.pending[inKey] 1021 if !ok { 1022 return nil, ErrUnknownCircuit 1023 } 1024 1025 _, ok = cm.closed[inKey] 1026 if ok { 1027 return nil, ErrCircuitClosing 1028 } 1029 1030 cm.closed[inKey] = struct{}{} 1031 1032 return circuit, nil 1033 } 1034 1035 // CloseCircuit marks the circuit identified by `outKey` as closing in-memory, 1036 // which prevents duplicate settles/fails from completing an open 1037 // circuit twice. 1038 func (cm *circuitMap) CloseCircuit(outKey CircuitKey) (*PaymentCircuit, error) { 1039 1040 cm.mtx.Lock() 1041 defer cm.mtx.Unlock() 1042 1043 circuit, ok := cm.opened[outKey] 1044 if !ok { 1045 return nil, ErrUnknownCircuit 1046 } 1047 1048 _, ok = cm.closed[circuit.Incoming] 1049 if ok { 1050 return nil, ErrCircuitClosing 1051 } 1052 1053 cm.closed[circuit.Incoming] = struct{}{} 1054 1055 return circuit, nil 1056 } 1057 1058 // DeleteCircuits destroys the target circuits by removing them from the circuit 1059 // map, additionally removing the circuits' keystones if any HTLCs were 1060 // forwarded through an outgoing link. The circuits should be identified by its 1061 // incoming circuit key. If a given circuit is not found in the circuit map, it 1062 // will be ignored from the query. This would typically indicate that the 1063 // circuit was already cleaned up at a different point in time. 1064 func (cm *circuitMap) DeleteCircuits(inKeys ...CircuitKey) error { 1065 1066 log.Tracef("Deleting resolved circuits: %v", newLogClosure(func() string { 1067 return spew.Sdump(inKeys) 1068 })) 1069 1070 var ( 1071 closingCircuits = make(map[CircuitKey]struct{}) 1072 removedCircuits = make(map[CircuitKey]*PaymentCircuit) 1073 ) 1074 1075 cm.mtx.Lock() 1076 // Remove any references to the circuits from memory, keeping track of 1077 // which circuits were removed, and which ones had been marked closed. 1078 // This can be used to restore these entries later if the persistent 1079 // removal fails. 1080 for _, inKey := range inKeys { 1081 circuit, ok := cm.pending[inKey] 1082 if !ok { 1083 continue 1084 } 1085 delete(cm.pending, inKey) 1086 1087 if _, ok := cm.closed[inKey]; ok { 1088 closingCircuits[inKey] = struct{}{} 1089 delete(cm.closed, inKey) 1090 } 1091 1092 if circuit.HasKeystone() { 1093 delete(cm.opened, circuit.OutKey()) 1094 cm.removeCircuitFromHashIndex(circuit) 1095 } 1096 1097 removedCircuits[inKey] = circuit 1098 } 1099 cm.mtx.Unlock() 1100 1101 err := kvdb.Batch(cm.cfg.DB, func(tx kvdb.RwTx) error { 1102 for _, circuit := range removedCircuits { 1103 // If this htlc made it to an outgoing link, load the 1104 // keystone bucket from which we will remove the 1105 // outgoing circuit key. 1106 if circuit.HasKeystone() { 1107 keystoneBkt := tx.ReadWriteBucket(circuitKeystoneKey) 1108 if keystoneBkt == nil { 1109 return ErrCorruptedCircuitMap 1110 } 1111 1112 outKey := circuit.OutKey() 1113 1114 err := keystoneBkt.Delete(outKey.Bytes()) 1115 if err != nil { 1116 return err 1117 } 1118 } 1119 1120 // Remove the circuit itself based on the incoming 1121 // circuit key. 1122 circuitBkt := tx.ReadWriteBucket(circuitAddKey) 1123 if circuitBkt == nil { 1124 return ErrCorruptedCircuitMap 1125 } 1126 1127 inKey := circuit.InKey() 1128 if err := circuitBkt.Delete(inKey.Bytes()); err != nil { 1129 return err 1130 } 1131 } 1132 1133 return nil 1134 }) 1135 1136 // Return if the write succeeded. 1137 if err == nil { 1138 return nil 1139 } 1140 1141 // If the persistent changes failed, restore the circuit map to it's 1142 // previous state. 1143 cm.mtx.Lock() 1144 for inKey, circuit := range removedCircuits { 1145 cm.pending[inKey] = circuit 1146 1147 if _, ok := closingCircuits[inKey]; ok { 1148 cm.closed[inKey] = struct{}{} 1149 } 1150 1151 if circuit.HasKeystone() { 1152 cm.opened[circuit.OutKey()] = circuit 1153 cm.addCircuitToHashIndex(circuit) 1154 } 1155 } 1156 cm.mtx.Unlock() 1157 1158 return err 1159 } 1160 1161 // removeCircuitFromHashIndex removes the given circuit from the hash index, 1162 // pruning any unnecessary memory optimistically. 1163 func (cm *circuitMap) removeCircuitFromHashIndex(c *PaymentCircuit) { 1164 // Locate bucket containing this circuit's payment hashes. 1165 circuitsWithHash, ok := cm.hashIndex[c.PaymentHash] 1166 if !ok { 1167 return 1168 } 1169 1170 outKey := c.OutKey() 1171 1172 // Remove this circuit from the set of circuitsWithHash. 1173 delete(circuitsWithHash, outKey) 1174 1175 // Prune the payment hash bucket if no other entries remain. 1176 if len(circuitsWithHash) == 0 { 1177 delete(cm.hashIndex, c.PaymentHash) 1178 } 1179 } 1180 1181 // NumPending returns the number of active circuits added to the circuit map. 1182 func (cm *circuitMap) NumPending() int { 1183 cm.mtx.RLock() 1184 defer cm.mtx.RUnlock() 1185 1186 return len(cm.pending) 1187 } 1188 1189 // NumOpen returns the number of circuits that have been opened by way of 1190 // setting their keystones. This is the number of HTLCs that are waiting for a 1191 // settle/fail response from a remote peer. 1192 func (cm *circuitMap) NumOpen() int { 1193 cm.mtx.RLock() 1194 defer cm.mtx.RUnlock() 1195 1196 return len(cm.opened) 1197 }