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