github.com/core-coin/go-core/v2@v2.1.9/p2p/discv5/database_test.go (about) 1 // Copyright 2015 by the Authors 2 // This file is part of the go-core library. 3 // 4 // The go-core 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-core 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-core library. If not, see <http://www.gnu.org/licenses/>. 16 17 package discv5 18 19 import ( 20 "bytes" 21 "io/ioutil" 22 "net" 23 "os" 24 "path/filepath" 25 "reflect" 26 "testing" 27 "time" 28 ) 29 30 var nodeDBKeyTests = []struct { 31 id NodeID 32 field string 33 key []byte 34 }{ 35 { 36 id: NodeID{}, 37 field: "version", 38 key: []byte{0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e}, // field 39 }, 40 { 41 id: MustHexID("1033b1bac4c731e800b6399a357e51cf1b20eec942aac608c90b89553003e2ed3f94bd80613ee9006b1e62b6bb45109d0db9a4833e78363992"), 42 field: ":discover", 43 key: []byte{0x6e, 0x3a, // prefix 44 0x10, 0x33, 0xb1, 0xba, 0xc4, 0xc7, 0x31, 0xe8, 0x00, // node id 45 0xb6, 0x39, 0x9a, 0x35, 0x7e, 0x51, 0xcf, 0x1b, 0x20, 46 0xee, 0xc9, 0x42, 0xaa, 0xc6, 0x08, 0xc9, 0x0b, 0x89, 47 0x55, 0x30, 0x03, 0xe2, 0xed, 0x3f, 0x94, 0xbd, 0x80, 48 0x61, 0x3e, 0xe9, 0x00, 0x6b, 0x1e, 0x62, 0xb6, 0xbb, 49 0x45, 0x10, 0x9d, 0x0d, 0xb9, 0xa4, 0x83, 0x3e, 0x78, 50 0x36, 0x39, 0x92, 51 0x3a, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, // field 52 }, 53 }, 54 } 55 56 func TestNodeDBKeys(t *testing.T) { 57 for i, tt := range nodeDBKeyTests { 58 if key := makeKey(tt.id, tt.field); !bytes.Equal(key, tt.key) { 59 t.Errorf("make test %d: key mismatch: have 0x%x, want 0x%x", i, key, tt.key) 60 } 61 id, field := splitKey(tt.key) 62 if !bytes.Equal(id[:], tt.id[:]) { 63 t.Errorf("split test %d: id mismatch: have 0x%x, want 0x%x", i, id, tt.id) 64 } 65 if field != tt.field { 66 t.Errorf("split test %d: field mismatch: have 0x%x, want 0x%x", i, field, tt.field) 67 } 68 } 69 } 70 71 var nodeDBInt64Tests = []struct { 72 key []byte 73 value int64 74 }{ 75 {key: []byte{0x01}, value: 1}, 76 {key: []byte{0x02}, value: 2}, 77 {key: []byte{0x03}, value: 3}, 78 } 79 80 func TestNodeDBInt64(t *testing.T) { 81 db, _ := newNodeDB("", Version, NodeID{}) 82 defer db.close() 83 84 tests := nodeDBInt64Tests 85 for i := 0; i < len(tests); i++ { 86 // Insert the next value 87 if err := db.storeInt64(tests[i].key, tests[i].value); err != nil { 88 t.Errorf("test %d: failed to store value: %v", i, err) 89 } 90 // Check all existing and non existing values 91 for j := 0; j < len(tests); j++ { 92 num := db.fetchInt64(tests[j].key) 93 switch { 94 case j <= i && num != tests[j].value: 95 t.Errorf("test %d, item %d: value mismatch: have %v, want %v", i, j, num, tests[j].value) 96 case j > i && num != 0: 97 t.Errorf("test %d, item %d: value mismatch: have %v, want %v", i, j, num, 0) 98 } 99 } 100 } 101 } 102 103 func TestNodeDBFetchStore(t *testing.T) { 104 node := NewNode( 105 MustHexID("1033b1bac4c731e800b6399a357e51cf1b20eec942aac608c90b89553003e2ed3f94bd80613ee9006b1e62b6bb45109d0db9a4833e78363992"), 106 net.IP{192, 168, 0, 1}, 107 30300, 108 30300, 109 ) 110 inst := time.Now() 111 num := 314 112 113 db, _ := newNodeDB("", Version, NodeID{}) 114 defer db.close() 115 116 // Check fetch/store operations on a node ping object 117 if stored := db.lastPing(node.ID); stored.Unix() != 0 { 118 t.Errorf("ping: non-existing object: %v", stored) 119 } 120 if err := db.updateLastPing(node.ID, inst); err != nil { 121 t.Errorf("ping: failed to update: %v", err) 122 } 123 if stored := db.lastPing(node.ID); stored.Unix() != inst.Unix() { 124 t.Errorf("ping: value mismatch: have %v, want %v", stored, inst) 125 } 126 // Check fetch/store operations on a node pong object 127 if stored := db.lastPong(node.ID); stored.Unix() != 0 { 128 t.Errorf("pong: non-existing object: %v", stored) 129 } 130 if err := db.updateLastPong(node.ID, inst); err != nil { 131 t.Errorf("pong: failed to update: %v", err) 132 } 133 if stored := db.lastPong(node.ID); stored.Unix() != inst.Unix() { 134 t.Errorf("pong: value mismatch: have %v, want %v", stored, inst) 135 } 136 // Check fetch/store operations on a node findnode-failure object 137 if stored := db.findFails(node.ID); stored != 0 { 138 t.Errorf("find-node fails: non-existing object: %v", stored) 139 } 140 if err := db.updateFindFails(node.ID, num); err != nil { 141 t.Errorf("find-node fails: failed to update: %v", err) 142 } 143 if stored := db.findFails(node.ID); stored != num { 144 t.Errorf("find-node fails: value mismatch: have %v, want %v", stored, num) 145 } 146 // Check fetch/store operations on an actual node object 147 if stored := db.node(node.ID); stored != nil { 148 t.Errorf("node: non-existing object: %v", stored) 149 } 150 if err := db.updateNode(node); err != nil { 151 t.Errorf("node: failed to update: %v", err) 152 } 153 if stored := db.node(node.ID); stored == nil { 154 t.Errorf("node: not found") 155 } else if !reflect.DeepEqual(stored, node) { 156 t.Errorf("node: data mismatch: have %v, want %v", stored, node) 157 } 158 } 159 160 var nodeDBSeedQueryNodes = []struct { 161 node *Node 162 pong time.Time 163 }{ 164 // This one should not be in the result set because its last 165 // pong time is too far in the past. 166 { 167 node: NewNode( 168 MustHexID("0x84d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270"), 169 net.IP{127, 0, 0, 3}, 170 30300, 171 30300, 172 ), 173 pong: time.Now().Add(-3 * time.Hour), 174 }, 175 // This one shouldn't be in the result set because its 176 // nodeID is the local node's ID. 177 { 178 node: NewNode( 179 MustHexID("0x57d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270"), 180 net.IP{127, 0, 0, 3}, 181 30300, 182 30300, 183 ), 184 pong: time.Now().Add(-4 * time.Second), 185 }, 186 187 // These should be in the result set. 188 { 189 node: NewNode( 190 MustHexID("0x22d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270"), 191 net.IP{127, 0, 0, 1}, 192 30300, 193 30300, 194 ), 195 pong: time.Now().Add(-2 * time.Second), 196 }, 197 { 198 node: NewNode( 199 MustHexID("0x44d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270"), 200 net.IP{127, 0, 0, 2}, 201 30300, 202 30300, 203 ), 204 pong: time.Now().Add(-3 * time.Second), 205 }, 206 { 207 node: NewNode( 208 MustHexID("0xe2d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270"), 209 net.IP{127, 0, 0, 3}, 210 30300, 211 30300, 212 ), 213 pong: time.Now().Add(-1 * time.Second), 214 }, 215 } 216 217 func TestNodeDBSeedQuery(t *testing.T) { 218 db, _ := newNodeDB("", Version, nodeDBSeedQueryNodes[1].node.ID) 219 defer db.close() 220 221 // Insert a batch of nodes for querying 222 for i, seed := range nodeDBSeedQueryNodes { 223 if err := db.updateNode(seed.node); err != nil { 224 t.Fatalf("node %d: failed to insert: %v", i, err) 225 } 226 if err := db.updateLastPong(seed.node.ID, seed.pong); err != nil { 227 t.Fatalf("node %d: failed to insert lastPong: %v", i, err) 228 } 229 } 230 231 // Retrieve the entire batch and check for duplicates 232 seeds := db.querySeeds(len(nodeDBSeedQueryNodes)*2, time.Hour) 233 have := make(map[NodeID]struct{}) 234 for _, seed := range seeds { 235 have[seed.ID] = struct{}{} 236 } 237 want := make(map[NodeID]struct{}) 238 for _, seed := range nodeDBSeedQueryNodes[2:] { 239 want[seed.node.ID] = struct{}{} 240 } 241 if len(seeds) != len(want) { 242 t.Errorf("seed count mismatch: have %v, want %v", len(seeds), len(want)) 243 } 244 for id := range have { 245 if _, ok := want[id]; !ok { 246 t.Errorf("extra seed: %v", id) 247 } 248 } 249 for id := range want { 250 if _, ok := have[id]; !ok { 251 t.Errorf("missing seed: %v", id) 252 } 253 } 254 } 255 256 func TestNodeDBPersistency(t *testing.T) { 257 root, err := ioutil.TempDir("", "nodedb-") 258 if err != nil { 259 t.Fatalf("failed to create temporary data folder: %v", err) 260 } 261 defer os.RemoveAll(root) 262 263 var ( 264 testKey = []byte("somekey") 265 testInt = int64(314) 266 ) 267 268 // Create a persistent database and store some values 269 db, err := newNodeDB(filepath.Join(root, "database"), Version, NodeID{}) 270 if err != nil { 271 t.Fatalf("failed to create persistent database: %v", err) 272 } 273 if err := db.storeInt64(testKey, testInt); err != nil { 274 t.Fatalf("failed to store value: %v.", err) 275 } 276 db.close() 277 278 // Reopen the database and check the value 279 db, err = newNodeDB(filepath.Join(root, "database"), Version, NodeID{}) 280 if err != nil { 281 t.Fatalf("failed to open persistent database: %v", err) 282 } 283 if val := db.fetchInt64(testKey); val != testInt { 284 t.Fatalf("value mismatch: have %v, want %v", val, testInt) 285 } 286 db.close() 287 288 // Change the database version and check flush 289 db, err = newNodeDB(filepath.Join(root, "database"), Version+1, NodeID{}) 290 if err != nil { 291 t.Fatalf("failed to open persistent database: %v", err) 292 } 293 if val := db.fetchInt64(testKey); val != 0 { 294 t.Fatalf("value mismatch: have %v, want %v", val, 0) 295 } 296 db.close() 297 } 298 299 var nodeDBExpirationNodes = []struct { 300 node *Node 301 pong time.Time 302 exp bool 303 }{ 304 { 305 node: NewNode( 306 MustHexID("1033b1bac4c731e800b6399a357e51cf1b20eec942aac608c90b89553003e2ed3f94bd80613ee9006b1e62b6bb45109d0db9a4833e78363997"), 307 net.IP{127, 0, 0, 1}, 308 30300, 309 30300, 310 ), 311 pong: time.Now().Add(-nodeDBNodeExpiration + time.Minute), 312 exp: false, 313 }, { 314 node: NewNode( 315 MustHexID("1033b1bac4c731e800b6399a357e51cf1b20eec942aac608c90b89553003e2ed3f94bd80613ee9006b1e62b6bb45109d0db9a4833e78363998"), 316 net.IP{127, 0, 0, 2}, 317 30300, 318 30300, 319 ), 320 pong: time.Now().Add(-nodeDBNodeExpiration - time.Minute), 321 exp: true, 322 }, 323 } 324 325 func TestNodeDBExpiration(t *testing.T) { 326 db, _ := newNodeDB("", Version, NodeID{}) 327 defer db.close() 328 329 // Add all the test nodes and set their last pong time 330 for i, seed := range nodeDBExpirationNodes { 331 if err := db.updateNode(seed.node); err != nil { 332 t.Fatalf("node %d: failed to insert: %v", i, err) 333 } 334 if err := db.updateLastPong(seed.node.ID, seed.pong); err != nil { 335 t.Fatalf("node %d: failed to update pong: %v", i, err) 336 } 337 } 338 // Expire some of them, and check the rest 339 if err := db.expireNodes(); err != nil { 340 t.Fatalf("failed to expire nodes: %v", err) 341 } 342 for i, seed := range nodeDBExpirationNodes { 343 node := db.node(seed.node.ID) 344 if (node == nil && !seed.exp) || (node != nil && seed.exp) { 345 t.Errorf("node %d: expiration mismatch: have %v, want %v", i, node, seed.exp) 346 } 347 } 348 } 349 350 func TestNodeDBSelfExpiration(t *testing.T) { 351 // Find a node in the tests that shouldn't expire, and assign it as self 352 var self NodeID 353 for _, node := range nodeDBExpirationNodes { 354 if !node.exp { 355 self = node.node.ID 356 break 357 } 358 } 359 db, _ := newNodeDB("", Version, self) 360 defer db.close() 361 362 // Add all the test nodes and set their last pong time 363 for i, seed := range nodeDBExpirationNodes { 364 if err := db.updateNode(seed.node); err != nil { 365 t.Fatalf("node %d: failed to insert: %v", i, err) 366 } 367 if err := db.updateLastPong(seed.node.ID, seed.pong); err != nil { 368 t.Fatalf("node %d: failed to update pong: %v", i, err) 369 } 370 } 371 // Expire the nodes and make sure self has been evacuated too 372 if err := db.expireNodes(); err != nil { 373 t.Fatalf("failed to expire nodes: %v", err) 374 } 375 node := db.node(self) 376 if node != nil { 377 t.Errorf("self not evacuated") 378 } 379 }