OSDN Git Service

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