externalscoreattach.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. package autopilot
  2. import (
  3. "fmt"
  4. "sync"
  5. "github.com/btcsuite/btcd/btcutil"
  6. )
  7. // ExternalScoreAttachment is an implementation of the AttachmentHeuristic
  8. // interface that allows an external source to provide it with node scores.
  9. type ExternalScoreAttachment struct {
  10. // TODO(halseth): persist across restarts.
  11. nodeScores map[NodeID]float64
  12. sync.Mutex
  13. }
  14. // NewExternalScoreAttachment creates a new instance of an
  15. // ExternalScoreAttachment.
  16. func NewExternalScoreAttachment() *ExternalScoreAttachment {
  17. return &ExternalScoreAttachment{}
  18. }
  19. // A compile time assertion to ensure ExternalScoreAttachment meets the
  20. // AttachmentHeuristic and ScoreSettable interfaces.
  21. var _ AttachmentHeuristic = (*ExternalScoreAttachment)(nil)
  22. var _ ScoreSettable = (*ExternalScoreAttachment)(nil)
  23. // Name returns the name of this heuristic.
  24. //
  25. // NOTE: This is a part of the AttachmentHeuristic interface.
  26. func (s *ExternalScoreAttachment) Name() string {
  27. return "externalscore"
  28. }
  29. // SetNodeScores is used to set the internal map from NodeIDs to scores. The
  30. // passed scores must be in the range [0, 1.0]. The fist parameter is the name
  31. // of the targeted heuristic, to allow recursively target specific
  32. // sub-heuristics. The returned boolean indicates whether the targeted
  33. // heuristic was found.
  34. //
  35. // NOTE: This is a part of the ScoreSettable interface.
  36. func (s *ExternalScoreAttachment) SetNodeScores(targetHeuristic string,
  37. newScores map[NodeID]float64) (bool, error) {
  38. // Return if this heuristic wasn't targeted.
  39. if targetHeuristic != s.Name() {
  40. return false, nil
  41. }
  42. // Since there's a requirement that all score are in the range [0,
  43. // 1.0], we validate them before setting the internal list.
  44. for nID, s := range newScores {
  45. if s < 0 || s > 1.0 {
  46. return false, fmt.Errorf("invalid score %v for "+
  47. "nodeID %v", s, nID)
  48. }
  49. }
  50. s.Lock()
  51. defer s.Unlock()
  52. s.nodeScores = newScores
  53. log.Tracef("Setting %v external scores", len(s.nodeScores))
  54. return true, nil
  55. }
  56. // NodeScores is a method that given the current channel graph and current set
  57. // of local channels, scores the given nodes according to the preference of
  58. // opening a channel of the given size with them. The returned channel
  59. // candidates maps the NodeID to a NodeScore for the node.
  60. //
  61. // The returned scores will be in the range [0, 1.0], where 0 indicates no
  62. // improvement in connectivity if a channel is opened to this node, while 1.0
  63. // is the maximum possible improvement in connectivity.
  64. //
  65. // The scores are determined by checking the internal node scores list. Nodes
  66. // not known will get a score of 0.
  67. //
  68. // NOTE: This is a part of the AttachmentHeuristic interface.
  69. func (s *ExternalScoreAttachment) NodeScores(g ChannelGraph, chans []LocalChannel,
  70. chanSize btcutil.Amount, nodes map[NodeID]struct{}) (
  71. map[NodeID]*NodeScore, error) {
  72. existingPeers := make(map[NodeID]struct{})
  73. for _, c := range chans {
  74. existingPeers[c.Node] = struct{}{}
  75. }
  76. s.Lock()
  77. defer s.Unlock()
  78. log.Tracef("External scoring %v nodes, from %v set scores",
  79. len(nodes), len(s.nodeScores))
  80. // Fill the map of candidates to return.
  81. candidates := make(map[NodeID]*NodeScore)
  82. for nID := range nodes {
  83. var score float64
  84. if nodeScore, ok := s.nodeScores[nID]; ok {
  85. score = nodeScore
  86. }
  87. // If the node is among or existing channel peers, we don't
  88. // need another channel.
  89. if _, ok := existingPeers[nID]; ok {
  90. log.Tracef("Skipping existing peer %x from external "+
  91. "score results", nID[:])
  92. continue
  93. }
  94. log.Tracef("External score %v given to node %x", score, nID[:])
  95. // Instead of adding a node with score 0 to the returned set,
  96. // we just skip it.
  97. if score == 0 {
  98. continue
  99. }
  100. candidates[nID] = &NodeScore{
  101. NodeID: nID,
  102. Score: score,
  103. }
  104. }
  105. return candidates, nil
  106. }