github.com/simplechain-org/go-simplechain@v1.0.6/p2p/discv5/table_test.go (about) 1 // Copyright 2016 The go-simplechain Authors 2 // This file is part of the go-simplechain library. 3 // 4 // The go-simplechain library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-simplechain library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-simplechain library. If not, see <http://www.gnu.org/licenses/>. 16 17 package discv5 18 19 import ( 20 "crypto/ecdsa" 21 "fmt" 22 "math/rand" 23 24 "net" 25 "reflect" 26 "testing" 27 "testing/quick" 28 "time" 29 30 "github.com/simplechain-org/go-simplechain/common" 31 "github.com/simplechain-org/go-simplechain/crypto" 32 ) 33 34 func TestBucket_bumpNoDuplicates(t *testing.T) { 35 t.Parallel() 36 cfg := &quick.Config{ 37 MaxCount: 1000, 38 Rand: rand.New(rand.NewSource(time.Now().Unix())), 39 Values: func(args []reflect.Value, rand *rand.Rand) { 40 // generate a random list of nodes. this will be the content of the bucket. 41 n := rand.Intn(bucketSize-1) + 1 42 nodes := make([]*Node, n) 43 for i := range nodes { 44 nodes[i] = nodeAtDistance(common.Hash{}, 200) 45 } 46 args[0] = reflect.ValueOf(nodes) 47 // generate random bump positions. 48 bumps := make([]int, rand.Intn(100)) 49 for i := range bumps { 50 bumps[i] = rand.Intn(len(nodes)) 51 } 52 args[1] = reflect.ValueOf(bumps) 53 }, 54 } 55 56 prop := func(nodes []*Node, bumps []int) (ok bool) { 57 b := &bucket{entries: make([]*Node, len(nodes))} 58 copy(b.entries, nodes) 59 for i, pos := range bumps { 60 b.bump(b.entries[pos]) 61 if hasDuplicates(b.entries) { 62 t.Logf("bucket has duplicates after %d/%d bumps:", i+1, len(bumps)) 63 for _, n := range b.entries { 64 t.Logf(" %p", n) 65 } 66 return false 67 } 68 } 69 return true 70 } 71 if err := quick.Check(prop, cfg); err != nil { 72 t.Error(err) 73 } 74 } 75 76 // nodeAtDistance creates a node for which logdist(base, n.sha) == ld. 77 // The node's ID does not correspond to n.sha. 78 func nodeAtDistance(base common.Hash, ld int) (n *Node) { 79 n = new(Node) 80 n.sha = hashAtDistance(base, ld) 81 copy(n.ID[:], n.sha[:]) // ensure the node still has a unique ID 82 return n 83 } 84 85 func TestTable_closest(t *testing.T) { 86 t.Parallel() 87 88 test := func(test *closeTest) bool { 89 // for any node table, Target and N 90 tab := newTable(test.Self, &net.UDPAddr{}) 91 tab.stuff(test.All) 92 93 // check that doClosest(Target, N) returns nodes 94 result := tab.closest(test.Target, test.N).entries 95 if hasDuplicates(result) { 96 t.Errorf("result contains duplicates") 97 return false 98 } 99 if !sortedByDistanceTo(test.Target, result) { 100 t.Errorf("result is not sorted by distance to target") 101 return false 102 } 103 104 // check that the number of results is min(N, tablen) 105 wantN := test.N 106 if tab.count < test.N { 107 wantN = tab.count 108 } 109 if len(result) != wantN { 110 t.Errorf("wrong number of nodes: got %d, want %d", len(result), wantN) 111 return false 112 } else if len(result) == 0 { 113 return true // no need to check distance 114 } 115 116 // check that the result nodes have minimum distance to target. 117 for _, b := range tab.buckets { 118 for _, n := range b.entries { 119 if contains(result, n.ID) { 120 continue // don't run the check below for nodes in result 121 } 122 farthestResult := result[len(result)-1].sha 123 if distcmp(test.Target, n.sha, farthestResult) < 0 { 124 t.Errorf("table contains node that is closer to target but it's not in result") 125 t.Logf(" Target: %v", test.Target) 126 t.Logf(" Farthest Result: %v", farthestResult) 127 t.Logf(" ID: %v", n.ID) 128 return false 129 } 130 } 131 } 132 return true 133 } 134 if err := quick.Check(test, quickcfg()); err != nil { 135 t.Error(err) 136 } 137 } 138 139 func TestTable_ReadRandomNodesGetAll(t *testing.T) { 140 cfg := &quick.Config{ 141 MaxCount: 200, 142 Rand: rand.New(rand.NewSource(time.Now().Unix())), 143 Values: func(args []reflect.Value, rand *rand.Rand) { 144 args[0] = reflect.ValueOf(make([]*Node, rand.Intn(1000))) 145 }, 146 } 147 test := func(buf []*Node) bool { 148 tab := newTable(NodeID{}, &net.UDPAddr{}) 149 for i := 0; i < len(buf); i++ { 150 ld := cfg.Rand.Intn(len(tab.buckets)) 151 tab.stuff([]*Node{nodeAtDistance(tab.self.sha, ld)}) 152 } 153 gotN := tab.readRandomNodes(buf) 154 if gotN != tab.count { 155 t.Errorf("wrong number of nodes, got %d, want %d", gotN, tab.count) 156 return false 157 } 158 if hasDuplicates(buf[:gotN]) { 159 t.Errorf("result contains duplicates") 160 return false 161 } 162 return true 163 } 164 if err := quick.Check(test, cfg); err != nil { 165 t.Error(err) 166 } 167 } 168 169 type closeTest struct { 170 Self NodeID 171 Target common.Hash 172 All []*Node 173 N int 174 } 175 176 func (*closeTest) Generate(rand *rand.Rand, size int) reflect.Value { 177 t := &closeTest{ 178 Self: gen(NodeID{}, rand).(NodeID), 179 Target: gen(common.Hash{}, rand).(common.Hash), 180 N: rand.Intn(bucketSize), 181 } 182 for _, id := range gen([]NodeID{}, rand).([]NodeID) { 183 t.All = append(t.All, &Node{ID: id}) 184 } 185 return reflect.ValueOf(t) 186 } 187 188 func hasDuplicates(slice []*Node) bool { 189 seen := make(map[NodeID]bool) 190 for i, e := range slice { 191 if e == nil { 192 panic(fmt.Sprintf("nil *Node at %d", i)) 193 } 194 if seen[e.ID] { 195 return true 196 } 197 seen[e.ID] = true 198 } 199 return false 200 } 201 202 func sortedByDistanceTo(distbase common.Hash, slice []*Node) bool { 203 var last common.Hash 204 for i, e := range slice { 205 if i > 0 && distcmp(distbase, e.sha, last) < 0 { 206 return false 207 } 208 last = e.sha 209 } 210 return true 211 } 212 213 func contains(ns []*Node, id NodeID) bool { 214 for _, n := range ns { 215 if n.ID == id { 216 return true 217 } 218 } 219 return false 220 } 221 222 // gen wraps quick.Value so it's easier to use. 223 // it generates a random value of the given value's type. 224 func gen(typ interface{}, rand *rand.Rand) interface{} { 225 v, ok := quick.Value(reflect.TypeOf(typ), rand) 226 if !ok { 227 panic(fmt.Sprintf("couldn't generate random value of type %T", typ)) 228 } 229 return v.Interface() 230 } 231 232 func newkey() *ecdsa.PrivateKey { 233 key, err := crypto.GenerateKey() 234 if err != nil { 235 panic("couldn't generate key: " + err.Error()) 236 } 237 return key 238 }