Hi Guest

More contents, please log on!

Bitmere.com 区块链技术 Content

bytom源码分析-P2P网络-地址簿

excel436
36 0 0
简介
https://github.com/Bytom/bytom
本章介绍bytom代码P2P网络中addrbook地址簿

作者使用MacOS操作系统,其他平台也大同小异


Golang Version: 1.8

addrbook介绍
addrbook用于存储P2P网络中保留最近的对端节点地址
在MacOS下,默认的地址簿路径存储在~/Library/Bytom/addrbook.json
地址簿格式
  1. ** ~/Library/Bytom/addrbook.json **
  2. {
  3.     "Key": "359be6d08bc0c6e21c84bbb2",
  4.     "Addrs": [
  5.         {
  6.             "Addr": {
  7.                 "IP": "122.224.11.144",
  8.                 "Port": 46657
  9.             },
  10.             "Src": {
  11.                 "IP": "198.74.61.131",
  12.                 "Port": 46657
  13.             },
  14.             "Attempts": 0,
  15.             "LastAttempt": "2018-05-04T12:58:23.894057702+08:00",
  16.             "LastSuccess": "0001-01-01T00:00:00Z",
  17.             "BucketType": 1,
  18.             "Buckets": [
  19.                 181,
  20.                 10
  21.             ]
  22.         }
  23.     ]
  24. }
Copy the Code

地址类型
在addrbook中存储的地址有两种:
  1. ** p2p/addrbook.go **
  2. const (
  3.         bucketTypeNew = 0x01  // 标识新地址,不可靠地址(未成功连接过)。只存储在一个bucket中
  4.         bucketTypeOld = 0x02  // 标识旧地址,可靠地址(已成功连接过)。可以存储在多个bucket中,最多为maxNewBucketsPerAddress个
  5. )
Copy the Code


注意: 一个地址的类型变更不在此文章中做介绍,后期的文章会讨论该问题

地址簿相关结构体
地址簿
  1. type AddrBook struct {
  2.         cmn.BaseService
  3.         mtx               sync.Mutex
  4.         filePath          string  // 地址簿路径
  5.         routabilityStrict bool  // 是否可路由,默认为true
  6.         rand              *rand.Rand
  7.         key               string  // 地址簿标识,用于计算addrNew和addrOld的索引
  8.         ourAddrs          map[string]*NetAddress  // 存储本地网络地址,用于添加p2p地址时做排除使用
  9.         addrLookup        map[string]*knownAddress // 存储新、旧地址集,用于查询
  10.         addrNew           []map[string]*knownAddress // 存储新地址
  11.         addrOld           []map[string]*knownAddress // 存储旧地址
  12.         wg                sync.WaitGroup
  13.         nOld              int // 旧地址数量
  14.         nNew              int // 新地址数量
  15. }
Copy the Code

已知地址
  1. type knownAddress struct {
  2.         Addr        *NetAddress // 已知peer的addr
  3.         Src         *NetAddress // 已知peer的addr的来源addr
  4.         Attempts    int32 // 连接peer的重试次数
  5.         LastAttempt time.Time // 最近一次尝试连接的时间
  6.         LastSuccess time.Time // 最近一次尝试成功连接的时间
  7.         BucketType  byte // 地址的类型(表示可靠地址或不可靠地址)
  8.         Buckets     []int // 当前addr所属的buckets
  9. }
Copy the Code

routabilityStrict参数表示地址簿是否存储的ip是否可路由。可路由是根据RFC划分,具体参考资料:RFC标准
初始化地址簿
  1. // NewAddrBook creates a new address book.
  2. // Use Start to begin processing asynchronous address updates.
  3. func NewAddrBook(filePath string, routabilityStrict bool) *AddrBook {
  4.         am := &AddrBook{
  5.                 rand:              rand.New(rand.NewSource(time.Now().UnixNano())),
  6.                 ourAddrs:          make(map[string]*NetAddress),
  7.                 addrLookup:        make(map[string]*knownAddress),
  8.                 filePath:          filePath,
  9.                 routabilityStrict: routabilityStrict,
  10.         }
  11.         am.init()
  12.         am.BaseService = *cmn.NewBaseService(nil, "AddrBook", am)
  13.         return am
  14. }
  15. // When modifying this, don't forget to update loadFromFile()
  16. func (a *AddrBook) init() {
  17.   // 地址簿唯一标识
  18.         a.key = crypto.CRandHex(24) // 24/2 * 8 = 96 bits
  19.         // New addr buckets, 默认为256个大小
  20.         a.addrNew = make([]map[string]*knownAddress, newBucketCount)
  21.         for i := range a.addrNew {
  22.                 a.addrNew<i> = make(map[string]*knownAddress)
  23.         }
  24.         // Old addr buckets,默认为64个大小
  25.         a.addrOld = make([]map[string]*knownAddress, oldBucketCount)
  26.         for i := range a.addrOld {
  27.                 a.addrOld<i> = make(map[string]*knownAddress)
  28.         }
  29. }</i></i>
Copy the Code

bytomd启动时加载本地地址簿
loadFromFile在bytomd启动时,首先会加载本地的地址簿
  1. // OnStart implements Service.
  2. func (a *AddrBook) OnStart() error {
  3.         a.BaseService.OnStart()
  4.         a.loadFromFile(a.filePath)
  5.         a.wg.Add(1)
  6.         go a.saveRoutine()
  7.         return nil
  8. }
  9. // Returns false if file does not exist.
  10. // cmn.Panics if file is corrupt.
  11. func (a *AddrBook) loadFromFile(filePath string) bool {
  12.         // If doesn't exist, do nothing.
  13.         // 如果本地地址簿不存在则直接返回
  14.         _, err := os.Stat(filePath)
  15.         if os.IsNotExist(err) {
  16.                 return false
  17.         }
  18.   // 加载地址簿json内容
  19.         // Load addrBookJSON{}
  20.         r, err := os.Open(filePath)
  21.         if err != nil {
  22.                 cmn.PanicCrisis(cmn.Fmt("Error opening file %s: %v", filePath, err))
  23.         }
  24.         defer r.Close()
  25.         aJSON := &addrBookJSON{}
  26.         dec := json.NewDecoder(r)
  27.         err = dec.Decode(aJSON)
  28.         if err != nil {
  29.                 cmn.PanicCrisis(cmn.Fmt("Error reading file %s: %v", filePath, err))
  30.         }
  31.   // 填充addrNew、addrOld等
  32.         // Restore all the fields...
  33.         // Restore the key
  34.         a.key = aJSON.Key
  35.         // Restore .addrNew & .addrOld
  36.         for _, ka := range aJSON.Addrs {
  37.                 for _, bucketIndex := range ka.Buckets {
  38.                         bucket := a.getBucket(ka.BucketType, bucketIndex)
  39.                         bucket[ka.Addr.String()] = ka
  40.                 }
  41.                 a.addrLookup[ka.Addr.String()] = ka
  42.                 if ka.BucketType == bucketTypeNew {
  43.                         a.nNew++
  44.                 } else {
  45.                         a.nOld++
  46.                 }
  47.         }
  48.         return true
  49. }
Copy the Code

定时更新地址簿
bytomd会定时更新本地地址簿,默认2分钟一次
  1. func (a *AddrBook) saveRoutine() {
  2.         dumpAddressTicker := time.NewTicker(dumpAddressInterval)
  3. out:
  4.         for {
  5.                 select {
  6.                 case
Copy the Code

添加新地址
当peer之间交换addr时,节点会收到对端节点已知的地址信息,这些信息会被当前节点添加到地址簿中
  1. func (a *AddrBook) AddAddress(addr *NetAddress, src *NetAddress) {
  2.         a.mtx.Lock()
  3.         defer a.mtx.Unlock()
  4.         log.WithFields(log.Fields{
  5.                 "addr": addr,
  6.                 "src":  src,
  7.         }).Debug("Add address to book")
  8.         a.addAddress(addr, src)
  9. }
  10. func (a *AddrBook) addAddress(addr, src *NetAddress) {
  11.         // 验证地址是否为可路由地址
  12.         if a.routabilityStrict && !addr.Routable() {
  13.                 log.Error(cmn.Fmt("Cannot add non-routable address %v", addr))
  14.                 return
  15.         }
  16.         // 验证地址是否为本地节点地址
  17.         if _, ok := a.ourAddrs[addr.String()]; ok {
  18.                 // Ignore our own listener address.
  19.                 return
  20.         }
  21.         // 验证地址是否存在地址集中
  22.         // 如果存在:则判断该地址是否为old可靠地址、是否超过了最大buckets中。否则根据该地址已经被ka.Buckets引用的个数来随机决定是否添加到地址集中
  23.         // 如果不存在:则添加到地址集中。并标识为bucketTypeNew地址类型
  24.         ka := a.addrLookup[addr.String()]
  25.         if ka != nil {
  26.                 // Already old.
  27.                 if ka.isOld() {
  28.                         return
  29.                 }
  30.                 // Already in max new buckets.
  31.                 if len(ka.Buckets) == maxNewBucketsPerAddress {
  32.                         return
  33.                 }
  34.                 // The more entries we have, the less likely we are to add more.
  35.                 factor := int32(2 * len(ka.Buckets))
  36.                 if a.rand.Int31n(factor) != 0 {
  37.                         return
  38.                 }
  39.         } else {
  40.                 ka = newKnownAddress(addr, src)
  41.         }
  42.         // 找到该地址在地址集的索引位置并添加
  43.         bucket := a.calcNewBucket(addr, src)
  44.         a.addToNewBucket(ka, bucket)
  45.         log.Info("Added new address ", "address:", addr, " total:", a.size())
  46. }
Copy the Code

选择最优节点
地址簿中存储众多地址,在p2p网络中需选择最优的地址去连接
PickAddress(newBias int)函数中newBias是由pex_reactor产生的地址评分。如何计算地址分数在其他章节中再讲
根据地址评分随机选择地址可增加区块链安全性
  1. // Pick an address to connect to with new/old bias.
  2. func (a *AddrBook) PickAddress(newBias int) *NetAddress {
  3.         a.mtx.Lock()
  4.         defer a.mtx.Unlock()
  5.         if a.size() == 0 {
  6.                 return nil
  7.         }
  8.         // newBias地址分数限制在0-100分数之间
  9.         if newBias > 100 {
  10.                 newBias = 100
  11.         }
  12.         if newBias
Copy the Code

移除一个地址
当一个地址被标记为Bad时则从地址集中移除。目前bytomd的代码版本并未调用过
  1. func (a *AddrBook) MarkBad(addr *NetAddress) {
  2.         a.RemoveAddress(addr)
  3. }
  4. // RemoveAddress removes the address from the book.
  5. func (a *AddrBook) RemoveAddress(addr *NetAddress) {
  6.         a.mtx.Lock()
  7.         defer a.mtx.Unlock()
  8.         ka := a.addrLookup[addr.String()]
  9.         if ka == nil {
  10.                 return
  11.         }
  12.         log.WithField("addr", addr).Info("Remove address from book")
  13.         a.removeFromAllBuckets(ka)
  14. }
  15. func (a *AddrBook) removeFromAllBuckets(ka *knownAddress) {
  16.         for _, bucketIdx := range ka.Buckets {
  17.                 bucket := a.getBucket(ka.BucketType, bucketIdx)
  18.                 delete(bucket, ka.Addr.String())
  19.         }
  20.         ka.Buckets = nil
  21.         if ka.BucketType == bucketTypeNew {
  22.                 a.nNew--
  23.         } else {
  24.                 a.nOld--
  25.         }
  26.         delete(a.addrLookup, ka.Addr.String())
  27. }
Copy the Code

BitMere.com is Information release platform,just provides information storage space services.
The opinions expressed are solely those of the author,Does not constitute advice, please treat with caution.
You have to log in before you can reply Login | 立即注册

Points Rules

Write the first review

excel436 小学生
  • Follow

    0

  • Following

    0

  • Articles

    5

59600
Promoted