diff --git a/p2p/discover/table.go b/p2p/discover/table.go index 842f55d9f..dbf86c084 100644 --- a/p2p/discover/table.go +++ b/p2p/discover/table.go @@ -328,7 +328,7 @@ func (b *bucket) bump(n *Node) bool { if b.entries[i].ID == n.ID { n.bumpActive() // move it to the front - copy(b.entries[1:], b.entries[:i+1]) + copy(b.entries[1:], b.entries[:i]) b.entries[0] = n return true } diff --git a/p2p/discover/table_test.go b/p2p/discover/table_test.go index 95ec30bea..a98376bca 100644 --- a/p2p/discover/table_test.go +++ b/p2p/discover/table_test.go @@ -66,6 +66,48 @@ func TestTable_pingReplace(t *testing.T) { doit(false, false) } +func TestBucket_bumpNoDuplicates(t *testing.T) { + t.Parallel() + cfg := &quick.Config{ + MaxCount: 1000, + Rand: quickrand, + Values: func(args []reflect.Value, rand *rand.Rand) { + // generate a random list of nodes. this will be the content of the bucket. + n := rand.Intn(bucketSize-1) + 1 + nodes := make([]*Node, n) + for i := range nodes { + nodes[i] = &Node{ID: randomID(NodeID{}, 200)} + } + args[0] = reflect.ValueOf(nodes) + // generate random bump positions. + bumps := make([]int, rand.Intn(100)) + for i := range bumps { + bumps[i] = rand.Intn(len(nodes)) + } + args[1] = reflect.ValueOf(bumps) + }, + } + + prop := func(nodes []*Node, bumps []int) (ok bool) { + b := &bucket{entries: make([]*Node, len(nodes))} + copy(b.entries, nodes) + for i, pos := range bumps { + b.bump(b.entries[pos]) + if hasDuplicates(b.entries) { + t.Logf("bucket has duplicates after %d/%d bumps:", i+1, len(bumps)) + for _, n := range b.entries { + t.Logf(" %p", n) + } + return false + } + } + return true + } + if err := quick.Check(prop, cfg); err != nil { + t.Error(err) + } +} + func fillBucket(tab *Table, ld int) (last *Node) { b := tab.buckets[ld] for len(b.entries) < bucketSize {