From 9bcb139bcce8ae34fc535321fb51e2b5dbda213b Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Mon, 30 Oct 2017 18:55:48 -0700 Subject: [PATCH] =?UTF-8?q?autopilot:=20shuffle=20set=20of=20candidates=20?= =?UTF-8?q?using=20Fisher=E2=80=93Yates=20before=20selecting?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In this commit we modify the ConstrainedPrefAttachment.Select method to first shuffle the set of potential candidates before selecting them. This serves to remove the existing grouping between candidates which may have influenced the selection. --- autopilot/prefattach.go | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/autopilot/prefattach.go b/autopilot/prefattach.go index f93f75ef..89046a22 100644 --- a/autopilot/prefattach.go +++ b/autopilot/prefattach.go @@ -107,6 +107,21 @@ func NewNodeID(pub *btcec.PublicKey) NodeID { return n } +// shuffleCandidates shuffles the set of candidate nodes for preferential +// attachment in order to break any ordering already enforced by the sorted +// order of the public key for each node. To shuffle the set of candidates, we +// use a version of the Fisher–Yates shuffle algorithm. +func shuffleCandidates(candidates []Node) []Node { + shuffledNodes := make([]Node, len(candidates)) + perm := prand.Perm(len(candidates)) + + for i, v := range perm { + shuffledNodes[v] = candidates[i] + } + + return shuffledNodes +} + // Select returns a candidate set of attachment directives that should be // executed based on the current internal state, the state of the channel // graph, the set of nodes we should exclude, and the amount of funds @@ -208,8 +223,9 @@ func (p *ConstrainedPrefAttachment) Select(self *btcec.PublicKey, g ChannelGraph // Given our selection slice, we'll now generate a random index // into this slice. The node we select will be recommended by // us to create a channel to. - selectedIndex := prand.Int31n(int32(len(selectionSlice))) - selectedNode := selectionSlice[selectedIndex] + candidates := shuffleCandidates(selectionSlice) + selectedIndex := prand.Int31n(int32(len(candidates))) + selectedNode := candidates[selectedIndex] // TODO(roasbeef): cap on num channels to same participant?