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