github.com/theQRL/go-zond@v0.1.1/p2p/enode/nodedb_test.go (about) 1 // Copyright 2018 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum 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-ethereum 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-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package enode 18 19 import ( 20 "bytes" 21 "fmt" 22 "net" 23 "path/filepath" 24 "reflect" 25 "testing" 26 "time" 27 ) 28 29 var keytestID = HexID("51232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439") 30 31 func TestDBNodeKey(t *testing.T) { 32 enc := nodeKey(keytestID) 33 want := []byte{ 34 'n', ':', 35 0x51, 0x23, 0x2b, 0x8d, 0x78, 0x21, 0x61, 0x7d, // node id 36 0x2b, 0x29, 0xb5, 0x4b, 0x81, 0xcd, 0xef, 0xb9, // 37 0xb3, 0xe9, 0xc3, 0x7d, 0x7f, 0xd5, 0xf6, 0x32, // 38 0x70, 0xbc, 0xc9, 0xe1, 0xa6, 0xf6, 0xa4, 0x39, // 39 ':', 'v', '4', 40 } 41 if !bytes.Equal(enc, want) { 42 t.Errorf("wrong encoded key:\ngot %q\nwant %q", enc, want) 43 } 44 id, _ := splitNodeKey(enc) 45 if id != keytestID { 46 t.Errorf("wrong ID from splitNodeKey") 47 } 48 } 49 50 func TestDBNodeItemKey(t *testing.T) { 51 wantIP := net.IP{127, 0, 0, 3} 52 wantField := "foobar" 53 enc := nodeItemKey(keytestID, wantIP, wantField) 54 want := []byte{ 55 'n', ':', 56 0x51, 0x23, 0x2b, 0x8d, 0x78, 0x21, 0x61, 0x7d, // node id 57 0x2b, 0x29, 0xb5, 0x4b, 0x81, 0xcd, 0xef, 0xb9, // 58 0xb3, 0xe9, 0xc3, 0x7d, 0x7f, 0xd5, 0xf6, 0x32, // 59 0x70, 0xbc, 0xc9, 0xe1, 0xa6, 0xf6, 0xa4, 0x39, // 60 ':', 'v', '4', ':', 61 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // IP 62 0x00, 0x00, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x03, // 63 ':', 'f', 'o', 'o', 'b', 'a', 'r', 64 } 65 if !bytes.Equal(enc, want) { 66 t.Errorf("wrong encoded key:\ngot %q\nwant %q", enc, want) 67 } 68 id, ip, field := splitNodeItemKey(enc) 69 if id != keytestID { 70 t.Errorf("splitNodeItemKey returned wrong ID: %v", id) 71 } 72 if !ip.Equal(wantIP) { 73 t.Errorf("splitNodeItemKey returned wrong IP: %v", ip) 74 } 75 if field != wantField { 76 t.Errorf("splitNodeItemKey returned wrong field: %q", field) 77 } 78 } 79 80 var nodeDBInt64Tests = []struct { 81 key []byte 82 value int64 83 }{ 84 {key: []byte{0x01}, value: 1}, 85 {key: []byte{0x02}, value: 2}, 86 {key: []byte{0x03}, value: 3}, 87 } 88 89 func TestDBInt64(t *testing.T) { 90 db, _ := OpenDB("") 91 defer db.Close() 92 93 tests := nodeDBInt64Tests 94 for i := 0; i < len(tests); i++ { 95 // Insert the next value 96 if err := db.storeInt64(tests[i].key, tests[i].value); err != nil { 97 t.Errorf("test %d: failed to store value: %v", i, err) 98 } 99 // Check all existing and non existing values 100 for j := 0; j < len(tests); j++ { 101 num := db.fetchInt64(tests[j].key) 102 switch { 103 case j <= i && num != tests[j].value: 104 t.Errorf("test %d, item %d: value mismatch: have %v, want %v", i, j, num, tests[j].value) 105 case j > i && num != 0: 106 t.Errorf("test %d, item %d: value mismatch: have %v, want %v", i, j, num, 0) 107 } 108 } 109 } 110 } 111 112 func TestDBFetchStore(t *testing.T) { 113 node := NewV4( 114 hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), 115 net.IP{192, 168, 0, 1}, 116 30303, 117 30303, 118 ) 119 inst := time.Now() 120 num := 314 121 122 db, _ := OpenDB("") 123 defer db.Close() 124 125 // Check fetch/store operations on a node ping object 126 if stored := db.LastPingReceived(node.ID(), node.IP()); stored.Unix() != 0 { 127 t.Errorf("ping: non-existing object: %v", stored) 128 } 129 if err := db.UpdateLastPingReceived(node.ID(), node.IP(), inst); err != nil { 130 t.Errorf("ping: failed to update: %v", err) 131 } 132 if stored := db.LastPingReceived(node.ID(), node.IP()); stored.Unix() != inst.Unix() { 133 t.Errorf("ping: value mismatch: have %v, want %v", stored, inst) 134 } 135 // Check fetch/store operations on a node pong object 136 if stored := db.LastPongReceived(node.ID(), node.IP()); stored.Unix() != 0 { 137 t.Errorf("pong: non-existing object: %v", stored) 138 } 139 if err := db.UpdateLastPongReceived(node.ID(), node.IP(), inst); err != nil { 140 t.Errorf("pong: failed to update: %v", err) 141 } 142 if stored := db.LastPongReceived(node.ID(), node.IP()); stored.Unix() != inst.Unix() { 143 t.Errorf("pong: value mismatch: have %v, want %v", stored, inst) 144 } 145 // Check fetch/store operations on a node findnode-failure object 146 if stored := db.FindFails(node.ID(), node.IP()); stored != 0 { 147 t.Errorf("find-node fails: non-existing object: %v", stored) 148 } 149 if err := db.UpdateFindFails(node.ID(), node.IP(), num); err != nil { 150 t.Errorf("find-node fails: failed to update: %v", err) 151 } 152 if stored := db.FindFails(node.ID(), node.IP()); stored != num { 153 t.Errorf("find-node fails: value mismatch: have %v, want %v", stored, num) 154 } 155 // Check fetch/store operations on an actual node object 156 if stored := db.Node(node.ID()); stored != nil { 157 t.Errorf("node: non-existing object: %v", stored) 158 } 159 if err := db.UpdateNode(node); err != nil { 160 t.Errorf("node: failed to update: %v", err) 161 } 162 if stored := db.Node(node.ID()); stored == nil { 163 t.Errorf("node: not found") 164 } else if !reflect.DeepEqual(stored, node) { 165 t.Errorf("node: data mismatch: have %v, want %v", stored, node) 166 } 167 } 168 169 var nodeDBSeedQueryNodes = []struct { 170 node *Node 171 pong time.Time 172 }{ 173 // This one should not be in the result set because its last 174 // pong time is too far in the past. 175 { 176 node: NewV4( 177 hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), 178 net.IP{127, 0, 0, 3}, 179 30303, 180 30303, 181 ), 182 pong: time.Now().Add(-3 * time.Hour), 183 }, 184 // This one shouldn't be in the result set because its 185 // nodeID is the local node's ID. 186 { 187 node: NewV4( 188 hexPubkey("ff93ff820abacd4351b0f14e47b324bc82ff014c226f3f66a53535734a3c150e7e38ca03ef0964ba55acddc768f5e99cd59dea95ddd4defbab1339c92fa319b2"), 189 net.IP{127, 0, 0, 3}, 190 30303, 191 30303, 192 ), 193 pong: time.Now().Add(-4 * time.Second), 194 }, 195 196 // These should be in the result set. 197 { 198 node: NewV4( 199 hexPubkey("c2b5eb3f5dde05f815b63777809ee3e7e0cbb20035a6b00ce327191e6eaa8f26a8d461c9112b7ab94698e7361fa19fd647e603e73239002946d76085b6f928d6"), 200 net.IP{127, 0, 0, 1}, 201 30303, 202 30303, 203 ), 204 pong: time.Now().Add(-2 * time.Second), 205 }, 206 { 207 node: NewV4( 208 hexPubkey("6ca1d400c8ddf8acc94bcb0dd254911ad71a57bed5e0ae5aa205beed59b28c2339908e97990c493499613cff8ecf6c3dc7112a8ead220cdcd00d8847ca3db755"), 209 net.IP{127, 0, 0, 2}, 210 30303, 211 30303, 212 ), 213 pong: time.Now().Add(-3 * time.Second), 214 }, 215 { 216 node: NewV4( 217 hexPubkey("234dc63fe4d131212b38236c4c3411288d7bec61cbf7b120ff12c43dc60c96182882f4291d209db66f8a38e986c9c010ff59231a67f9515c7d1668b86b221a47"), 218 net.IP{127, 0, 0, 3}, 219 30303, 220 30303, 221 ), 222 pong: time.Now().Add(-1 * time.Second), 223 }, 224 { 225 node: NewV4( 226 hexPubkey("c013a50b4d1ebce5c377d8af8cb7114fd933ffc9627f96ad56d90fef5b7253ec736fd07ef9a81dc2955a997e54b7bf50afd0aa9f110595e2bec5bb7ce1657004"), 227 net.IP{127, 0, 0, 3}, 228 30303, 229 30303, 230 ), 231 pong: time.Now().Add(-2 * time.Second), 232 }, 233 { 234 node: NewV4( 235 hexPubkey("f141087e3e08af1aeec261ff75f48b5b1637f594ea9ad670e50051646b0416daa3b134c28788cbe98af26992a47652889cd8577ccc108ac02c6a664db2dc1283"), 236 net.IP{127, 0, 0, 3}, 237 30303, 238 30303, 239 ), 240 pong: time.Now().Add(-2 * time.Second), 241 }, 242 } 243 244 func TestDBSeedQuery(t *testing.T) { 245 // Querying seeds uses seeks an might not find all nodes 246 // every time when the database is small. Run the test multiple 247 // times to avoid flakes. 248 const attempts = 15 249 var err error 250 for i := 0; i < attempts; i++ { 251 if err = testSeedQuery(); err == nil { 252 return 253 } 254 } 255 if err != nil { 256 t.Errorf("no successful run in %d attempts: %v", attempts, err) 257 } 258 } 259 260 func testSeedQuery() error { 261 db, _ := OpenDB("") 262 defer db.Close() 263 264 // Insert a batch of nodes for querying 265 for i, seed := range nodeDBSeedQueryNodes { 266 if err := db.UpdateNode(seed.node); err != nil { 267 return fmt.Errorf("node %d: failed to insert: %v", i, err) 268 } 269 if err := db.UpdateLastPongReceived(seed.node.ID(), seed.node.IP(), seed.pong); err != nil { 270 return fmt.Errorf("node %d: failed to insert bondTime: %v", i, err) 271 } 272 } 273 274 // Retrieve the entire batch and check for duplicates 275 seeds := db.QuerySeeds(len(nodeDBSeedQueryNodes)*2, time.Hour) 276 have := make(map[ID]struct{}, len(seeds)) 277 for _, seed := range seeds { 278 have[seed.ID()] = struct{}{} 279 } 280 want := make(map[ID]struct{}, len(nodeDBSeedQueryNodes[1:])) 281 for _, seed := range nodeDBSeedQueryNodes[1:] { 282 want[seed.node.ID()] = struct{}{} 283 } 284 if len(seeds) != len(want) { 285 return fmt.Errorf("seed count mismatch: have %v, want %v", len(seeds), len(want)) 286 } 287 for id := range have { 288 if _, ok := want[id]; !ok { 289 return fmt.Errorf("extra seed: %v", id) 290 } 291 } 292 for id := range want { 293 if _, ok := have[id]; !ok { 294 return fmt.Errorf("missing seed: %v", id) 295 } 296 } 297 return nil 298 } 299 300 func TestDBPersistency(t *testing.T) { 301 root := t.TempDir() 302 303 var ( 304 testKey = []byte("somekey") 305 testInt = int64(314) 306 ) 307 308 // Create a persistent database and store some values 309 db, err := OpenDB(filepath.Join(root, "database")) 310 if err != nil { 311 t.Fatalf("failed to create persistent database: %v", err) 312 } 313 if err := db.storeInt64(testKey, testInt); err != nil { 314 t.Fatalf("failed to store value: %v.", err) 315 } 316 db.Close() 317 318 // Reopen the database and check the value 319 db, err = OpenDB(filepath.Join(root, "database")) 320 if err != nil { 321 t.Fatalf("failed to open persistent database: %v", err) 322 } 323 if val := db.fetchInt64(testKey); val != testInt { 324 t.Fatalf("value mismatch: have %v, want %v", val, testInt) 325 } 326 db.Close() 327 } 328 329 var nodeDBExpirationNodes = []struct { 330 node *Node 331 pong time.Time 332 storeNode bool 333 exp bool 334 }{ 335 // Node has new enough pong time and isn't expired: 336 { 337 node: NewV4( 338 hexPubkey("8d110e2ed4b446d9b5fb50f117e5f37fb7597af455e1dab0e6f045a6eeaa786a6781141659020d38bdc5e698ed3d4d2bafa8b5061810dfa63e8ac038db2e9b67"), 339 net.IP{127, 0, 0, 1}, 340 30303, 341 30303, 342 ), 343 storeNode: true, 344 pong: time.Now().Add(-dbNodeExpiration + time.Minute), 345 exp: false, 346 }, 347 // Node with pong time before expiration is removed: 348 { 349 node: NewV4( 350 hexPubkey("913a205579c32425b220dfba999d215066e5bdbf900226b11da1907eae5e93eb40616d47412cf819664e9eacbdfcca6b0c6e07e09847a38472d4be46ab0c3672"), 351 net.IP{127, 0, 0, 2}, 352 30303, 353 30303, 354 ), 355 storeNode: true, 356 pong: time.Now().Add(-dbNodeExpiration - time.Minute), 357 exp: true, 358 }, 359 // Just pong time, no node stored: 360 { 361 node: NewV4( 362 hexPubkey("b56670e0b6bad2c5dab9f9fe6f061a16cf78d68b6ae2cfda3144262d08d97ce5f46fd8799b6d1f709b1abe718f2863e224488bd7518e5e3b43809ac9bd1138ca"), 363 net.IP{127, 0, 0, 3}, 364 30303, 365 30303, 366 ), 367 storeNode: false, 368 pong: time.Now().Add(-dbNodeExpiration - time.Minute), 369 exp: true, 370 }, 371 // Node with multiple pong times, all older than expiration. 372 { 373 node: NewV4( 374 hexPubkey("29f619cebfd32c9eab34aec797ed5e3fe15b9b45be95b4df3f5fe6a9ae892f433eb08d7698b2ef3621568b0fb70d57b515ab30d4e72583b798298e0f0a66b9d1"), 375 net.IP{127, 0, 0, 4}, 376 30303, 377 30303, 378 ), 379 storeNode: true, 380 pong: time.Now().Add(-dbNodeExpiration - time.Minute), 381 exp: true, 382 }, 383 { 384 node: NewV4( 385 hexPubkey("29f619cebfd32c9eab34aec797ed5e3fe15b9b45be95b4df3f5fe6a9ae892f433eb08d7698b2ef3621568b0fb70d57b515ab30d4e72583b798298e0f0a66b9d1"), 386 net.IP{127, 0, 0, 5}, 387 30303, 388 30303, 389 ), 390 storeNode: false, 391 pong: time.Now().Add(-dbNodeExpiration - 2*time.Minute), 392 exp: true, 393 }, 394 // Node with multiple pong times, one newer, one older than expiration. 395 { 396 node: NewV4( 397 hexPubkey("3b73a9e5f4af6c4701c57c73cc8cfa0f4802840b24c11eba92aac3aef65644a3728b4b2aec8199f6d72bd66be2c65861c773129039bd47daa091ca90a6d4c857"), 398 net.IP{127, 0, 0, 6}, 399 30303, 400 30303, 401 ), 402 storeNode: true, 403 pong: time.Now().Add(-dbNodeExpiration + time.Minute), 404 exp: false, 405 }, 406 { 407 node: NewV4( 408 hexPubkey("3b73a9e5f4af6c4701c57c73cc8cfa0f4802840b24c11eba92aac3aef65644a3728b4b2aec8199f6d72bd66be2c65861c773129039bd47daa091ca90a6d4c857"), 409 net.IP{127, 0, 0, 7}, 410 30303, 411 30303, 412 ), 413 storeNode: false, 414 pong: time.Now().Add(-dbNodeExpiration - time.Minute), 415 exp: true, 416 }, 417 } 418 419 func TestDBExpiration(t *testing.T) { 420 db, _ := OpenDB("") 421 defer db.Close() 422 423 // Add all the test nodes and set their last pong time. 424 for i, seed := range nodeDBExpirationNodes { 425 if seed.storeNode { 426 if err := db.UpdateNode(seed.node); err != nil { 427 t.Fatalf("node %d: failed to insert: %v", i, err) 428 } 429 } 430 if err := db.UpdateLastPongReceived(seed.node.ID(), seed.node.IP(), seed.pong); err != nil { 431 t.Fatalf("node %d: failed to update bondTime: %v", i, err) 432 } 433 } 434 435 db.expireNodes() 436 437 // Check that expired entries have been removed. 438 unixZeroTime := time.Unix(0, 0) 439 for i, seed := range nodeDBExpirationNodes { 440 node := db.Node(seed.node.ID()) 441 pong := db.LastPongReceived(seed.node.ID(), seed.node.IP()) 442 if seed.exp { 443 if seed.storeNode && node != nil { 444 t.Errorf("node %d (%s) shouldn't be present after expiration", i, seed.node.ID().TerminalString()) 445 } 446 if !pong.Equal(unixZeroTime) { 447 t.Errorf("pong time %d (%s %v) shouldn't be present after expiration", i, seed.node.ID().TerminalString(), seed.node.IP()) 448 } 449 } else { 450 if seed.storeNode && node == nil { 451 t.Errorf("node %d (%s) should be present after expiration", i, seed.node.ID().TerminalString()) 452 } 453 if !pong.Equal(seed.pong.Truncate(1 * time.Second)) { 454 t.Errorf("pong time %d (%s) should be %v after expiration, but is %v", i, seed.node.ID().TerminalString(), seed.pong, pong) 455 } 456 } 457 } 458 } 459 460 // This test checks that expiration works when discovery v5 data is present 461 // in the database. 462 func TestDBExpireV5(t *testing.T) { 463 db, _ := OpenDB("") 464 defer db.Close() 465 466 ip := net.IP{127, 0, 0, 1} 467 db.UpdateFindFailsV5(ID{}, ip, 4) 468 db.expireNodes() 469 }