github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/p2p/enode/nodedb.go (about)

     1  
     2  //<developer>
     3  //    <name>linapex 曹一峰</name>
     4  //    <email>linapex@163.com</email>
     5  //    <wx>superexc</wx>
     6  //    <qqgroup>128148617</qqgroup>
     7  //    <url>https://jsq.ink</url>
     8  //    <role>pku engineer</role>
     9  //    <date>2019-03-16 19:16:41</date>
    10  //</624450104455073792>
    11  
    12  
    13  package enode
    14  
    15  import (
    16  	"bytes"
    17  	"crypto/rand"
    18  	"encoding/binary"
    19  	"fmt"
    20  	"os"
    21  	"sync"
    22  	"time"
    23  
    24  	"github.com/ethereum/go-ethereum/log"
    25  	"github.com/ethereum/go-ethereum/rlp"
    26  	"github.com/syndtr/goleveldb/leveldb"
    27  	"github.com/syndtr/goleveldb/leveldb/errors"
    28  	"github.com/syndtr/goleveldb/leveldb/iterator"
    29  	"github.com/syndtr/goleveldb/leveldb/opt"
    30  	"github.com/syndtr/goleveldb/leveldb/storage"
    31  	"github.com/syndtr/goleveldb/leveldb/util"
    32  )
    33  
    34  //节点数据库中的键。
    35  const (
    36  dbVersionKey = "version" //更改时要刷新的数据库版本
    37  dbItemPrefix = "n:"      //为节点条目加前缀的标识符
    38  
    39  	dbDiscoverRoot      = ":discover"
    40  	dbDiscoverSeq       = dbDiscoverRoot + ":seq"
    41  	dbDiscoverPing      = dbDiscoverRoot + ":lastping"
    42  	dbDiscoverPong      = dbDiscoverRoot + ":lastpong"
    43  	dbDiscoverFindFails = dbDiscoverRoot + ":findfail"
    44  	dbLocalRoot         = ":local"
    45  	dbLocalSeq          = dbLocalRoot + ":seq"
    46  )
    47  
    48  var (
    49  dbNodeExpiration = 24 * time.Hour //删除未查看节点的时间。
    50  dbCleanupCycle   = time.Hour      //运行过期任务的时间段。
    51  	dbVersion        = 7
    52  )
    53  
    54  //db是节点数据库,存储以前看到的节点和有关
    55  //它们用于QoS目的。
    56  type DB struct {
    57  lvl    *leveldb.DB   //与数据库本身的接口
    58  runner sync.Once     //确保我们最多可以启动一个到期日
    59  quit   chan struct{} //向过期线程发出停止信号的通道
    60  }
    61  
    62  //opendb打开一个节点数据库,用于存储和检索
    63  //网络。如果没有给内存中的路径,则构建临时数据库。
    64  func OpenDB(path string) (*DB, error) {
    65  	if path == "" {
    66  		return newMemoryDB()
    67  	}
    68  	return newPersistentDB(path)
    69  }
    70  
    71  //newmemorynodedb创建一个没有持久后端的新内存节点数据库。
    72  func newMemoryDB() (*DB, error) {
    73  	db, err := leveldb.Open(storage.NewMemStorage(), nil)
    74  	if err != nil {
    75  		return nil, err
    76  	}
    77  	return &DB{lvl: db, quit: make(chan struct{})}, nil
    78  }
    79  
    80  //newpersistentnodedb创建/打开一个支持leveldb的持久节点数据库,
    81  //同时在版本不匹配的情况下刷新其内容。
    82  func newPersistentDB(path string) (*DB, error) {
    83  	opts := &opt.Options{OpenFilesCacheCapacity: 5}
    84  	db, err := leveldb.OpenFile(path, opts)
    85  	if _, iscorrupted := err.(*errors.ErrCorrupted); iscorrupted {
    86  		db, err = leveldb.RecoverFile(path, nil)
    87  	}
    88  	if err != nil {
    89  		return nil, err
    90  	}
    91  //缓存中包含的节点对应于某个协议版本。
    92  //如果版本不匹配,则刷新所有节点。
    93  	currentVer := make([]byte, binary.MaxVarintLen64)
    94  	currentVer = currentVer[:binary.PutVarint(currentVer, int64(dbVersion))]
    95  
    96  	blob, err := db.Get([]byte(dbVersionKey), nil)
    97  	switch err {
    98  	case leveldb.ErrNotFound:
    99  //找不到版本(即空缓存),请将其插入
   100  		if err := db.Put([]byte(dbVersionKey), currentVer, nil); err != nil {
   101  			db.Close()
   102  			return nil, err
   103  		}
   104  
   105  	case nil:
   106  //存在版本,如果不同则刷新
   107  		if !bytes.Equal(blob, currentVer) {
   108  			db.Close()
   109  			if err = os.RemoveAll(path); err != nil {
   110  				return nil, err
   111  			}
   112  			return newPersistentDB(path)
   113  		}
   114  	}
   115  	return &DB{lvl: db, quit: make(chan struct{})}, nil
   116  }
   117  
   118  //makekey从节点ID及其特定的
   119  //感兴趣的领域。
   120  func makeKey(id ID, field string) []byte {
   121  	if (id == ID{}) {
   122  		return []byte(field)
   123  	}
   124  	return append([]byte(dbItemPrefix), append(id[:], field...)...)
   125  }
   126  
   127  //SplitKey尝试将数据库键拆分为节点ID和字段部分。
   128  func splitKey(key []byte) (id ID, field string) {
   129  //如果键不是节点的,则直接返回
   130  	if !bytes.HasPrefix(key, []byte(dbItemPrefix)) {
   131  		return ID{}, string(key)
   132  	}
   133  //否则拆分ID和字段
   134  	item := key[len(dbItemPrefix):]
   135  	copy(id[:], item[:len(id)])
   136  	field = string(item[len(id):])
   137  
   138  	return id, field
   139  }
   140  
   141  //fetchint64检索与特定键关联的整数。
   142  func (db *DB) fetchInt64(key []byte) int64 {
   143  	blob, err := db.lvl.Get(key, nil)
   144  	if err != nil {
   145  		return 0
   146  	}
   147  	val, read := binary.Varint(blob)
   148  	if read <= 0 {
   149  		return 0
   150  	}
   151  	return val
   152  }
   153  
   154  //storeInt64在给定的键中存储一个整数。
   155  func (db *DB) storeInt64(key []byte, n int64) error {
   156  	blob := make([]byte, binary.MaxVarintLen64)
   157  	blob = blob[:binary.PutVarint(blob, n)]
   158  	return db.lvl.Put(key, blob, nil)
   159  }
   160  
   161  //fetchuint64检索与特定键关联的整数。
   162  func (db *DB) fetchUint64(key []byte) uint64 {
   163  	blob, err := db.lvl.Get(key, nil)
   164  	if err != nil {
   165  		return 0
   166  	}
   167  	val, _ := binary.Uvarint(blob)
   168  	return val
   169  }
   170  
   171  //storeUInt64在给定的键中存储一个整数。
   172  func (db *DB) storeUint64(key []byte, n uint64) error {
   173  	blob := make([]byte, binary.MaxVarintLen64)
   174  	blob = blob[:binary.PutUvarint(blob, n)]
   175  	return db.lvl.Put(key, blob, nil)
   176  }
   177  
   178  //节点从数据库中检索具有给定ID的节点。
   179  func (db *DB) Node(id ID) *Node {
   180  	blob, err := db.lvl.Get(makeKey(id, dbDiscoverRoot), nil)
   181  	if err != nil {
   182  		return nil
   183  	}
   184  	return mustDecodeNode(id[:], blob)
   185  }
   186  
   187  func mustDecodeNode(id, data []byte) *Node {
   188  	node := new(Node)
   189  	if err := rlp.DecodeBytes(data, &node.r); err != nil {
   190  		panic(fmt.Errorf("p2p/enode: can't decode node %x in DB: %v", id, err))
   191  	}
   192  //还原节点ID缓存。
   193  	copy(node.id[:], id)
   194  	return node
   195  }
   196  
   197  //updateNode将节点插入到对等数据库中(可能会覆盖)。
   198  func (db *DB) UpdateNode(node *Node) error {
   199  	if node.Seq() < db.NodeSeq(node.ID()) {
   200  		return nil
   201  	}
   202  	blob, err := rlp.EncodeToBytes(&node.r)
   203  	if err != nil {
   204  		return err
   205  	}
   206  	if err := db.lvl.Put(makeKey(node.ID(), dbDiscoverRoot), blob, nil); err != nil {
   207  		return err
   208  	}
   209  	return db.storeUint64(makeKey(node.ID(), dbDiscoverSeq), node.Seq())
   210  }
   211  
   212  //nodeseq返回给定节点的存储记录序列号。
   213  func (db *DB) NodeSeq(id ID) uint64 {
   214  	return db.fetchUint64(makeKey(id, dbDiscoverSeq))
   215  }
   216  
   217  //如果节点具有较大的序列,resolve将返回该节点的存储记录
   218  //比N多
   219  func (db *DB) Resolve(n *Node) *Node {
   220  	if n.Seq() > db.NodeSeq(n.ID()) {
   221  		return n
   222  	}
   223  	return db.Node(n.ID())
   224  }
   225  
   226  //删除节点删除与节点关联的所有信息/键。
   227  func (db *DB) DeleteNode(id ID) error {
   228  	deleter := db.lvl.NewIterator(util.BytesPrefix(makeKey(id, "")), nil)
   229  	for deleter.Next() {
   230  		if err := db.lvl.Delete(deleter.Key(), nil); err != nil {
   231  			return err
   232  		}
   233  	}
   234  	return nil
   235  }
   236  
   237  //EnsureExpirer是一个小助手方法,可确保数据过期
   238  //机制正在运行。如果到期goroutine已经在运行,则
   239  //方法只返回。
   240  //
   241  //目标是在网络成功后才开始数据疏散
   242  //引导自身(以防止转储可能有用的种子节点)。自从
   243  //准确跟踪第一个成功的
   244  //收敛性,在适当的时候“确保”正确的状态比较简单
   245  //条件发生(即成功结合),并丢弃进一步的事件。
   246  func (db *DB) ensureExpirer() {
   247  	db.runner.Do(func() { go db.expirer() })
   248  }
   249  
   250  //Expirer应该在Go例程中启动,并负责循环广告
   251  //无穷大并从数据库中删除过时数据。
   252  func (db *DB) expirer() {
   253  	tick := time.NewTicker(dbCleanupCycle)
   254  	defer tick.Stop()
   255  	for {
   256  		select {
   257  		case <-tick.C:
   258  			if err := db.expireNodes(); err != nil {
   259  				log.Error("Failed to expire nodedb items", "err", err)
   260  			}
   261  		case <-db.quit:
   262  			return
   263  		}
   264  	}
   265  }
   266  
   267  //expirenodes迭代数据库并删除所有没有
   268  //在一段指定的时间内被看见(即收到乒乓球)。
   269  func (db *DB) expireNodes() error {
   270  	threshold := time.Now().Add(-dbNodeExpiration)
   271  
   272  //查找发现的早于允许的节点
   273  	it := db.lvl.NewIterator(nil, nil)
   274  	defer it.Release()
   275  
   276  	for it.Next() {
   277  //如果不是发现节点,则跳过该项
   278  		id, field := splitKey(it.Key())
   279  		if field != dbDiscoverRoot {
   280  			continue
   281  		}
   282  //如果尚未过期(而不是自己),则跳过节点
   283  		if seen := db.LastPongReceived(id); seen.After(threshold) {
   284  			continue
   285  		}
   286  //否则删除所有相关信息
   287  		db.DeleteNode(id)
   288  	}
   289  	return nil
   290  }
   291  
   292  //LastpingReceived检索从中接收的最后一个ping数据包的时间
   293  //远程节点。
   294  func (db *DB) LastPingReceived(id ID) time.Time {
   295  	return time.Unix(db.fetchInt64(makeKey(id, dbDiscoverPing)), 0)
   296  }
   297  
   298  //上一次尝试联系远程节点时,UpdateLastpingReceived更新。
   299  func (db *DB) UpdateLastPingReceived(id ID, instance time.Time) error {
   300  	return db.storeInt64(makeKey(id, dbDiscoverPing), instance.Unix())
   301  }
   302  
   303  //lastpongreceived从远程节点检索上次成功的pong的时间。
   304  func (db *DB) LastPongReceived(id ID) time.Time {
   305  //发射呼气
   306  	db.ensureExpirer()
   307  	return time.Unix(db.fetchInt64(makeKey(id, dbDiscoverPong)), 0)
   308  }
   309  
   310  //updateLastPongreeived更新节点的最后一次挂起时间。
   311  func (db *DB) UpdateLastPongReceived(id ID, instance time.Time) error {
   312  	return db.storeInt64(makeKey(id, dbDiscoverPong), instance.Unix())
   313  }
   314  
   315  //findfails检索自绑定以来findnode失败的次数。
   316  func (db *DB) FindFails(id ID) int {
   317  	return int(db.fetchInt64(makeKey(id, dbDiscoverFindFails)))
   318  }
   319  
   320  //updatefindfails更新自绑定以来findnode失败的次数。
   321  func (db *DB) UpdateFindFails(id ID, fails int) error {
   322  	return db.storeInt64(makeKey(id, dbDiscoverFindFails), int64(fails))
   323  }
   324  
   325  //localseq检索本地记录序列计数器。
   326  func (db *DB) localSeq(id ID) uint64 {
   327  	return db.fetchUint64(makeKey(id, dbLocalSeq))
   328  }
   329  
   330  //storelocalseq存储本地记录序列计数器。
   331  func (db *DB) storeLocalSeq(id ID, n uint64) {
   332  	db.storeUint64(makeKey(id, dbLocalSeq), n)
   333  }
   334  
   335  //queryseeds检索用作潜在种子节点的随机节点
   336  //用于引导。
   337  func (db *DB) QuerySeeds(n int, maxAge time.Duration) []*Node {
   338  	var (
   339  		now   = time.Now()
   340  		nodes = make([]*Node, 0, n)
   341  		it    = db.lvl.NewIterator(nil, nil)
   342  		id    ID
   343  	)
   344  	defer it.Release()
   345  
   346  seek:
   347  	for seeks := 0; len(nodes) < n && seeks < n*5; seeks++ {
   348  //寻找一个随机条目。第一个字节的增量为
   349  //每次随机数以增加可能性
   350  //攻击非常小的数据库中的所有现有节点。
   351  		ctr := id[0]
   352  		rand.Read(id[:])
   353  		id[0] = ctr + id[0]%16
   354  		it.Seek(makeKey(id, dbDiscoverRoot))
   355  
   356  		n := nextNode(it)
   357  		if n == nil {
   358  			id[0] = 0
   359  continue seek //迭代器已用完
   360  		}
   361  		if now.Sub(db.LastPongReceived(n.ID())) > maxAge {
   362  			continue seek
   363  		}
   364  		for i := range nodes {
   365  			if nodes[i].ID() == n.ID() {
   366  continue seek //复制品
   367  			}
   368  		}
   369  		nodes = append(nodes, n)
   370  	}
   371  	return nodes
   372  }
   373  
   374  //从迭代器读取下一个节点记录,跳过其他节点记录
   375  //数据库条目。
   376  func nextNode(it iterator.Iterator) *Node {
   377  	for end := false; !end; end = !it.Next() {
   378  		id, field := splitKey(it.Key())
   379  		if field != dbDiscoverRoot {
   380  			continue
   381  		}
   382  		return mustDecodeNode(id[:], it.Value())
   383  	}
   384  	return nil
   385  }
   386  
   387  //关闭刷新并关闭数据库文件。
   388  func (db *DB) Close() {
   389  	close(db.quit)
   390  	db.lvl.Close()
   391  }
   392