github.com/yinchengtsinghua/golang-Eos-dpos-Ethereum@v0.0.0-20190121132951-92cc4225ed8e/p2p/discover/database.go (about) 1 2 //此源码被清华学神尹成大魔王专业翻译分析并修改 3 //尹成QQ77025077 4 //尹成微信18510341407 5 //尹成所在QQ群721929980 6 //尹成邮箱 yinc13@mails.tsinghua.edu.cn 7 //尹成毕业于清华大学,微软区块链领域全球最有价值专家 8 //https://mvp.microsoft.com/zh-cn/PublicProfile/4033620 9 //版权所有2015 Go Ethereum作者 10 //此文件是Go以太坊库的一部分。 11 // 12 //Go-Ethereum库是免费软件:您可以重新分发它和/或修改 13 //根据GNU发布的较低通用公共许可证的条款 14 //自由软件基金会,或者许可证的第3版,或者 15 //(由您选择)任何更高版本。 16 // 17 //Go以太坊图书馆的发行目的是希望它会有用, 18 //但没有任何保证;甚至没有 19 //适销性或特定用途的适用性。见 20 //GNU较低的通用公共许可证,了解更多详细信息。 21 // 22 //你应该收到一份GNU较低级别的公共许可证副本 23 //以及Go以太坊图书馆。如果没有,请参见<http://www.gnu.org/licenses/>。 24 25 //包含节点数据库,存储以前看到的节点和收集的所有 26 //用于QoS的元数据。 27 28 package discover 29 30 import ( 31 "bytes" 32 "crypto/rand" 33 "encoding/binary" 34 "os" 35 "sync" 36 "time" 37 38 "github.com/ethereum/go-ethereum/crypto" 39 "github.com/ethereum/go-ethereum/log" 40 "github.com/ethereum/go-ethereum/rlp" 41 "github.com/syndtr/goleveldb/leveldb" 42 "github.com/syndtr/goleveldb/leveldb/errors" 43 "github.com/syndtr/goleveldb/leveldb/iterator" 44 "github.com/syndtr/goleveldb/leveldb/opt" 45 "github.com/syndtr/goleveldb/leveldb/storage" 46 "github.com/syndtr/goleveldb/leveldb/util" 47 ) 48 49 var ( 50 nodeDBNilNodeID = NodeID{} //要用作nil元素的特殊节点ID。 51 nodeDBNodeExpiration = 24 * time.Hour //删除未查看节点的时间。 52 nodeDBCleanupCycle = time.Hour //运行过期任务的时间段。 53 nodeDBVersion = 5 54 ) 55 56 //nodedb存储我们知道的所有节点。 57 type nodeDB struct { 58 lvl *leveldb.DB //与数据库本身的接口 59 self NodeID //拥有节点ID以防止将其添加到数据库中 60 runner sync.Once //确保我们最多可以启动一个到期日 61 quit chan struct{} //向过期线程发出停止信号的通道 62 } 63 64 //节点数据库的架构布局 65 var ( 66 nodeDBVersionKey = []byte("version") //更改时要刷新的数据库版本 67 nodeDBItemPrefix = []byte("n:") //为节点条目加前缀的标识符 68 69 nodeDBDiscoverRoot = ":discover" 70 nodeDBDiscoverPing = nodeDBDiscoverRoot + ":lastping" 71 nodeDBDiscoverPong = nodeDBDiscoverRoot + ":lastpong" 72 nodeDBDiscoverFindFails = nodeDBDiscoverRoot + ":findfail" 73 ) 74 75 //newnodedb创建一个新的节点数据库,用于存储和检索有关的信息 76 //网络中的已知对等点。如果没有给定路径,则内存中的 77 //数据库已构建。 78 func newNodeDB(path string, version int, self NodeID) (*nodeDB, error) { 79 if path == "" { 80 return newMemoryNodeDB(self) 81 } 82 return newPersistentNodeDB(path, version, self) 83 } 84 85 //newmemorynodedb创建一个新的内存节点数据库,而不使用持久的 86 //后端。 87 func newMemoryNodeDB(self NodeID) (*nodeDB, error) { 88 db, err := leveldb.Open(storage.NewMemStorage(), nil) 89 if err != nil { 90 return nil, err 91 } 92 return &nodeDB{ 93 lvl: db, 94 self: self, 95 quit: make(chan struct{}), 96 }, nil 97 } 98 99 //newpersistentnodedb创建/打开一个支持leveldb的持久节点数据库, 100 //同时在版本不匹配的情况下刷新其内容。 101 func newPersistentNodeDB(path string, version int, self NodeID) (*nodeDB, error) { 102 opts := &opt.Options{OpenFilesCacheCapacity: 5} 103 db, err := leveldb.OpenFile(path, opts) 104 if _, iscorrupted := err.(*errors.ErrCorrupted); iscorrupted { 105 db, err = leveldb.RecoverFile(path, nil) 106 } 107 if err != nil { 108 return nil, err 109 } 110 //缓存中包含的节点对应于某个协议版本。 111 //如果版本不匹配,则刷新所有节点。 112 currentVer := make([]byte, binary.MaxVarintLen64) 113 currentVer = currentVer[:binary.PutVarint(currentVer, int64(version))] 114 115 blob, err := db.Get(nodeDBVersionKey, nil) 116 switch err { 117 case leveldb.ErrNotFound: 118 //找不到版本(即空缓存),请将其插入 119 if err := db.Put(nodeDBVersionKey, currentVer, nil); err != nil { 120 db.Close() 121 return nil, err 122 } 123 124 case nil: 125 //存在版本,如果不同则刷新 126 if !bytes.Equal(blob, currentVer) { 127 db.Close() 128 if err = os.RemoveAll(path); err != nil { 129 return nil, err 130 } 131 return newPersistentNodeDB(path, version, self) 132 } 133 } 134 return &nodeDB{ 135 lvl: db, 136 self: self, 137 quit: make(chan struct{}), 138 }, nil 139 } 140 141 //makekey从节点ID及其特定的 142 //感兴趣的领域。 143 func makeKey(id NodeID, field string) []byte { 144 if bytes.Equal(id[:], nodeDBNilNodeID[:]) { 145 return []byte(field) 146 } 147 return append(nodeDBItemPrefix, append(id[:], field...)...) 148 } 149 150 //SplitKey尝试将数据库键拆分为节点ID和字段部分。 151 func splitKey(key []byte) (id NodeID, field string) { 152 //如果键不是节点的,则直接返回 153 if !bytes.HasPrefix(key, nodeDBItemPrefix) { 154 return NodeID{}, string(key) 155 } 156 //否则拆分ID和字段 157 item := key[len(nodeDBItemPrefix):] 158 copy(id[:], item[:len(id)]) 159 field = string(item[len(id):]) 160 161 return id, field 162 } 163 164 //fetchin64检索与特定 165 //数据库密钥。 166 func (db *nodeDB) fetchInt64(key []byte) int64 { 167 blob, err := db.lvl.Get(key, nil) 168 if err != nil { 169 return 0 170 } 171 val, read := binary.Varint(blob) 172 if read <= 0 { 173 return 0 174 } 175 return val 176 } 177 178 //storeInt64将特定数据库条目作为 179 //UNIX时间戳。 180 func (db *nodeDB) storeInt64(key []byte, n int64) error { 181 blob := make([]byte, binary.MaxVarintLen64) 182 blob = blob[:binary.PutVarint(blob, n)] 183 184 return db.lvl.Put(key, blob, nil) 185 } 186 187 //节点从数据库中检索具有给定ID的节点。 188 func (db *nodeDB) node(id NodeID) *Node { 189 blob, err := db.lvl.Get(makeKey(id, nodeDBDiscoverRoot), nil) 190 if err != nil { 191 return nil 192 } 193 node := new(Node) 194 if err := rlp.DecodeBytes(blob, node); err != nil { 195 log.Error("Failed to decode node RLP", "err", err) 196 return nil 197 } 198 node.sha = crypto.Keccak256Hash(node.ID[:]) 199 return node 200 } 201 202 //updateNode将节点插入到对等数据库中(可能会覆盖)。 203 func (db *nodeDB) updateNode(node *Node) error { 204 blob, err := rlp.EncodeToBytes(node) 205 if err != nil { 206 return err 207 } 208 return db.lvl.Put(makeKey(node.ID, nodeDBDiscoverRoot), blob, nil) 209 } 210 211 //删除节点删除与节点关联的所有信息/键。 212 func (db *nodeDB) deleteNode(id NodeID) error { 213 deleter := db.lvl.NewIterator(util.BytesPrefix(makeKey(id, "")), nil) 214 for deleter.Next() { 215 if err := db.lvl.Delete(deleter.Key(), nil); err != nil { 216 return err 217 } 218 } 219 return nil 220 } 221 222 //EnsureExpirer是一个小助手方法,可确保数据过期 223 //机制正在运行。如果到期goroutine已经在运行,则 224 //方法只返回。 225 // 226 //目标是在网络成功后才开始数据疏散 227 //引导自身(以防止转储可能有用的种子节点)。自从 228 //准确跟踪第一个成功的 229 //收敛性,在适当的时候“确保”正确的状态比较简单 230 //条件发生(即成功结合),并丢弃进一步的事件。 231 func (db *nodeDB) ensureExpirer() { 232 db.runner.Do(func() { go db.expirer() }) 233 } 234 235 //Expirer应该在Go例程中启动,并负责循环广告 236 //无穷大并从数据库中删除过时数据。 237 func (db *nodeDB) expirer() { 238 tick := time.NewTicker(nodeDBCleanupCycle) 239 defer tick.Stop() 240 for { 241 select { 242 case <-tick.C: 243 if err := db.expireNodes(); err != nil { 244 log.Error("Failed to expire nodedb items", "err", err) 245 } 246 case <-db.quit: 247 return 248 } 249 } 250 } 251 252 //expirenodes迭代数据库并删除所有没有 253 //在一段指定的时间内被看见(即收到乒乓球)。 254 func (db *nodeDB) expireNodes() error { 255 threshold := time.Now().Add(-nodeDBNodeExpiration) 256 257 //查找发现的早于允许的节点 258 it := db.lvl.NewIterator(nil, nil) 259 defer it.Release() 260 261 for it.Next() { 262 //如果不是发现节点,则跳过该项 263 id, field := splitKey(it.Key()) 264 if field != nodeDBDiscoverRoot { 265 continue 266 } 267 //如果尚未过期(而不是自己),则跳过节点 268 if !bytes.Equal(id[:], db.self[:]) { 269 if seen := db.lastPongReceived(id); seen.After(threshold) { 270 continue 271 } 272 } 273 //否则删除所有相关信息 274 db.deleteNode(id) 275 } 276 return nil 277 } 278 279 //LastpingReceived检索远程节点发送的最后一个ping数据包的时间。 280 func (db *nodeDB) lastPingReceived(id NodeID) time.Time { 281 return time.Unix(db.fetchInt64(makeKey(id, nodeDBDiscoverPing)), 0) 282 } 283 284 //updateLastping更新上次远程节点对我们执行ping操作的时间。 285 func (db *nodeDB) updateLastPingReceived(id NodeID, instance time.Time) error { 286 return db.storeInt64(makeKey(id, nodeDBDiscoverPing), instance.Unix()) 287 } 288 289 //lastpongreceived从远程节点检索上次成功的pong的时间。 290 func (db *nodeDB) lastPongReceived(id NodeID) time.Time { 291 return time.Unix(db.fetchInt64(makeKey(id, nodeDBDiscoverPong)), 0) 292 } 293 294 //HasBond报告给定节点是否被视为已绑定。 295 func (db *nodeDB) hasBond(id NodeID) bool { 296 return time.Since(db.lastPongReceived(id)) < nodeDBNodeExpiration 297 } 298 299 //updateLastPongreeived更新节点的最后一次挂起时间。 300 func (db *nodeDB) updateLastPongReceived(id NodeID, instance time.Time) error { 301 return db.storeInt64(makeKey(id, nodeDBDiscoverPong), instance.Unix()) 302 } 303 304 //findfails检索自绑定以来findnode失败的次数。 305 func (db *nodeDB) findFails(id NodeID) int { 306 return int(db.fetchInt64(makeKey(id, nodeDBDiscoverFindFails))) 307 } 308 309 //updatefindfails更新自绑定以来findnode失败的次数。 310 func (db *nodeDB) updateFindFails(id NodeID, fails int) error { 311 return db.storeInt64(makeKey(id, nodeDBDiscoverFindFails), int64(fails)) 312 } 313 314 //queryseeds检索用作潜在种子节点的随机节点 315 //用于引导。 316 func (db *nodeDB) querySeeds(n int, maxAge time.Duration) []*Node { 317 var ( 318 now = time.Now() 319 nodes = make([]*Node, 0, n) 320 it = db.lvl.NewIterator(nil, nil) 321 id NodeID 322 ) 323 defer it.Release() 324 325 seek: 326 for seeks := 0; len(nodes) < n && seeks < n*5; seeks++ { 327 //寻找一个随机条目。第一个字节的增量为 328 //每次随机数以增加可能性 329 //攻击非常小的数据库中的所有现有节点。 330 ctr := id[0] 331 rand.Read(id[:]) 332 id[0] = ctr + id[0]%16 333 it.Seek(makeKey(id, nodeDBDiscoverRoot)) 334 335 n := nextNode(it) 336 if n == nil { 337 id[0] = 0 338 continue seek //迭代器已用完 339 } 340 if n.ID == db.self { 341 continue seek 342 } 343 if now.Sub(db.lastPongReceived(n.ID)) > maxAge { 344 continue seek 345 } 346 for i := range nodes { 347 if nodes[i].ID == n.ID { 348 continue seek //复制品 349 } 350 } 351 nodes = append(nodes, n) 352 } 353 return nodes 354 } 355 356 //从迭代器读取下一个节点记录,跳过其他节点记录 357 //数据库条目。 358 func nextNode(it iterator.Iterator) *Node { 359 for end := false; !end; end = !it.Next() { 360 id, field := splitKey(it.Key()) 361 if field != nodeDBDiscoverRoot { 362 continue 363 } 364 var n Node 365 if err := rlp.DecodeBytes(it.Value(), &n); err != nil { 366 log.Warn("Failed to decode node RLP", "id", id, "err", err) 367 continue 368 } 369 return &n 370 } 371 return nil 372 } 373 374 //关闭刷新并关闭数据库文件。 375 func (db *nodeDB) close() { 376 close(db.quit) 377 db.lvl.Close() 378 }