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