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