1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
|
// Contains the discovery cache, storing previously seen nodes to act as seed
// servers during bootstrapping the network.
package discover
import (
"bytes"
"encoding/binary"
"net"
"os"
"github.com/ethereum/go-ethereum/rlp"
"github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/storage"
)
// Cache stores all nodes we know about.
type Cache struct {
db *leveldb.DB
}
// Cache version to allow dumping old data if it changes.
var cacheVersionKey = []byte("pv")
// NewMemoryCache creates a new in-memory peer cache without a persistent backend.
func NewMemoryCache() (*Cache, error) {
db, err := leveldb.Open(storage.NewMemStorage(), nil)
if err != nil {
return nil, err
}
return &Cache{db: db}, nil
}
// NewPersistentCache creates/opens a leveldb backed persistent peer cache, also
// flushing its contents in case of a version mismatch.
func NewPersistentCache(path string) (*Cache, error) {
// Try to open the cache, recovering any corruption
db, err := leveldb.OpenFile(path, nil)
if _, iscorrupted := err.(leveldb.ErrCorrupted); iscorrupted {
db, err = leveldb.RecoverFile(path, nil)
}
if err != nil {
return nil, err
}
// The nodes contained in the cache correspond to a certain protocol version.
// Flush all nodes if the version doesn't match.
currentVer := make([]byte, binary.MaxVarintLen64)
currentVer = currentVer[:binary.PutVarint(currentVer, Version)]
blob, err := db.Get(cacheVersionKey, nil)
switch err {
case leveldb.ErrNotFound:
// Version not found (i.e. empty cache), insert it
err = db.Put(cacheVersionKey, currentVer, nil)
case nil:
// Version present, flush if different
if !bytes.Equal(blob, currentVer) {
db.Close()
if err = os.RemoveAll(path); err != nil {
return nil, err
}
return NewPersistentCache(path)
}
}
// Clean up in case of an error
if err != nil {
db.Close()
return nil, err
}
return &Cache{db: db}, nil
}
// get retrieves a node with a given id from the seed da
func (c *Cache) get(id NodeID) *Node {
blob, err := c.db.Get(id[:], nil)
if err != nil {
return nil
}
node := new(Node)
if err := rlp.DecodeBytes(blob, node); err != nil {
return nil
}
return node
}
// list retrieves a batch of nodes from the database.
func (c *Cache) list(n int) []*Node {
it := c.db.NewIterator(nil, nil)
defer it.Release()
nodes := make([]*Node, 0, n)
for i := 0; i < n && it.Next(); i++ {
var id NodeID
copy(id[:], it.Key())
if node := c.get(id); node != nil {
nodes = append(nodes, node)
}
}
return nodes
}
// update inserts - potentially overwriting - a node in the seed database.
func (c *Cache) update(node *Node) error {
blob, err := rlp.EncodeToBytes(node)
if err != nil {
return err
}
return c.db.Put(node.ID[:], blob, nil)
}
// add inserts a new node into the seed database.
func (c *Cache) add(id NodeID, addr *net.UDPAddr, tcpPort uint16) *Node {
node := &Node{
ID: id,
IP: addr.IP,
DiscPort: addr.Port,
TCPPort: int(tcpPort),
}
c.update(node)
return node
}
// delete removes a node from the database.
func (c *Cache) delete(id NodeID) error {
return c.db.Delete(id[:], nil)
}
// Close flushes and closes the database files.
func (c *Cache) Close() {
c.db.Close()
}
|