Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

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

excel436
135 0 0
简介
/ d4 b( D8 L. q0 A8 chttps://github.com/Bytom/bytom. r' l) w6 q3 X0 ~& [
本章介绍bytom代码P2P网络中addrbook地址簿( K( R" p+ m  b$ [: y$ S, {

2 |" M$ y- K. S: l* r1 i4 X作者使用MacOS操作系统,其他平台也大同小异
- H6 i: g% Q3 R# K2 x

) X* Y- T1 j, d: W& B8 l  B
& K( R5 t8 B: A7 Q8 `Golang Version: 1.8+ Q% W, n: x8 W# s$ B" X! E

$ N! C- Q: |( p' J0 saddrbook介绍" f' J5 j1 R1 P: h0 x, x9 X) }
addrbook用于存储P2P网络中保留最近的对端节点地址
* x9 T; v2 J7 O) c1 v4 v4 d9 H在MacOS下,默认的地址簿路径存储在~/Library/Bytom/addrbook.json$ @9 [1 N' ^! g; D6 w" ?" ^# T$ A
地址簿格式
8 Y+ W. p! U" ?$ I5 r* E6 }0 }
  1. ** ~/Library/Bytom/addrbook.json **) b$ e2 _$ k8 p9 J  b, k: @
  2. {; {; T4 i3 S6 ]8 M6 [
  3.     "Key": "359be6d08bc0c6e21c84bbb2",
    1 |$ _/ ?  y! Z4 `" ~" F1 }
  4.     "Addrs": [/ w, B- l" J$ E! j# }2 d  Q
  5.         {. o- S( c6 |7 t7 [8 F: S
  6.             "Addr": {
    ; a# N, k8 A6 p" [0 M2 C
  7.                 "IP": "122.224.11.144",
    0 O% t2 \% x7 E$ X9 u. O
  8.                 "Port": 466574 C* I8 y$ V3 Z$ ~" b
  9.             },$ Q% y! U6 A0 t3 @. n% i
  10.             "Src": {
    & ~; S  n* A- a* g5 g( j" }
  11.                 "IP": "198.74.61.131",/ C9 U0 T- h3 L0 C
  12.                 "Port": 46657" m6 M- P- [- }1 S" \
  13.             },: |$ p/ m- g- q2 N6 H& P+ Z; f
  14.             "Attempts": 0,
    * K. {: z: a7 r
  15.             "LastAttempt": "2018-05-04T12:58:23.894057702+08:00"," ]% T/ n& A7 @3 `
  16.             "LastSuccess": "0001-01-01T00:00:00Z",2 ]& ?& J# J4 o2 T
  17.             "BucketType": 1,' b6 `& s' D7 {9 i
  18.             "Buckets": [
    + S/ P% A- N. u
  19.                 181,' u8 M) G% E( z( v# a
  20.                 10; x) H1 F0 q! M# l- H+ F  q" T
  21.             ]+ b1 y% @; Q0 O! B6 l
  22.         }
    " `5 Z5 i$ Z7 s- S4 R
  23.     ]
    5 `$ F5 b" y5 v3 y
  24. }
复制代码

* A/ P* P- R. I% T4 R  R地址类型
; S7 Q' G6 L( |1 b2 }在addrbook中存储的地址有两种:
, P8 C8 b2 X7 ^; c2 Q
  1. ** p2p/addrbook.go **1 {4 ]* t4 U$ X% P2 q2 ]0 w/ z& [
  2. const (
    ) X- Q, y' A/ ]2 ^1 E
  3.         bucketTypeNew = 0x01  // 标识新地址,不可靠地址(未成功连接过)。只存储在一个bucket中/ ?3 X# n; M  {+ }' ^; U
  4.         bucketTypeOld = 0x02  // 标识旧地址,可靠地址(已成功连接过)。可以存储在多个bucket中,最多为maxNewBucketsPerAddress个
    ) k  e# O: V8 ?+ u) }8 @
  5. )
复制代码

# a; W9 i  t% \1 L  E7 z: ^! j6 x! N5 F
注意: 一个地址的类型变更不在此文章中做介绍,后期的文章会讨论该问题
% l7 P8 l5 m; w( f2 X
$ r  I8 @0 K+ s- U# D3 H9 I地址簿相关结构体
% x; i+ F* u9 P地址簿! E  K$ b2 L+ h; l( A8 ~) s
  1. type AddrBook struct {
    8 |: i& ^9 W/ F. `# a* N* E1 A: l* g
  2.         cmn.BaseService- h/ G0 m6 B) J8 H2 z8 M7 B
  3.         mtx               sync.Mutex# C  N! j  y% r/ I7 j0 A. W' k4 e
  4.         filePath          string  // 地址簿路径# l4 z  T5 M  t% b
  5.         routabilityStrict bool  // 是否可路由,默认为true
    / r1 x0 K& X- G& k6 K8 e
  6.         rand              *rand.Rand , |6 y2 K& I0 k
  7.         key               string  // 地址簿标识,用于计算addrNew和addrOld的索引
    . X6 {, q3 \: q% q$ d) y
  8.         ourAddrs          map[string]*NetAddress  // 存储本地网络地址,用于添加p2p地址时做排除使用" n4 }/ e& j# N! j+ o. g( f
  9.         addrLookup        map[string]*knownAddress // 存储新、旧地址集,用于查询5 A% g$ U2 @- i2 o- x
  10.         addrNew           []map[string]*knownAddress // 存储新地址7 m4 [0 a4 V; U2 V7 t8 t5 o# u2 F* p
  11.         addrOld           []map[string]*knownAddress // 存储旧地址  C5 W% u6 R0 r
  12.         wg                sync.WaitGroup( ]; ]/ a4 e& M: G7 H
  13.         nOld              int // 旧地址数量; v: \$ @# Z6 W8 ~( ~7 Q
  14.         nNew              int // 新地址数量( y1 B' j8 I4 y, D
  15. }
复制代码

: A0 U2 i, Y' F9 [- w1 t已知地址3 F- @) |* o7 Q9 }9 }8 g
  1. type knownAddress struct {
    ; b5 @# _- @1 R2 A2 m" u
  2.         Addr        *NetAddress // 已知peer的addr
    ! p" E7 x1 Z* z$ K- c6 {) R/ }4 b
  3.         Src         *NetAddress // 已知peer的addr的来源addr
    / v! R8 y3 L/ i1 I/ g2 n8 @- m
  4.         Attempts    int32 // 连接peer的重试次数7 V# m) m* j8 m; r% }2 j/ B
  5.         LastAttempt time.Time // 最近一次尝试连接的时间! r& ]; d  u' t7 C
  6.         LastSuccess time.Time // 最近一次尝试成功连接的时间0 _5 i! ?# p9 X
  7.         BucketType  byte // 地址的类型(表示可靠地址或不可靠地址)
    % F: V4 j' f  c1 z
  8.         Buckets     []int // 当前addr所属的buckets7 T7 F  L5 B9 e6 q4 U3 U7 O4 {% L
  9. }
复制代码

( d8 t3 s5 j( p) `routabilityStrict参数表示地址簿是否存储的ip是否可路由。可路由是根据RFC划分,具体参考资料:RFC标准/ ~) E% @) h; X0 ^+ ]1 L
初始化地址簿, r. L, g7 F- ^/ I" ~5 B# j
  1. // NewAddrBook creates a new address book.2 e$ @3 S( g8 ~7 {
  2. // Use Start to begin processing asynchronous address updates.
    ( j3 y8 l3 e6 [" i9 v
  3. func NewAddrBook(filePath string, routabilityStrict bool) *AddrBook {
      @+ Q5 Z8 l! x8 X1 t& r) J
  4.         am := &AddrBook{
    ' e5 s( n# Z$ `5 V" o" T" Q1 ]
  5.                 rand:              rand.New(rand.NewSource(time.Now().UnixNano())),
    3 ]' p$ d0 t" [+ I* d
  6.                 ourAddrs:          make(map[string]*NetAddress)," e+ h0 U! `3 D: M9 ?/ I
  7.                 addrLookup:        make(map[string]*knownAddress),( r; y% _7 M+ H* B! y7 F4 O
  8.                 filePath:          filePath,
    5 Y4 D* P( F% }0 P, O
  9.                 routabilityStrict: routabilityStrict,, G- j; U. v; {' N( W/ z
  10.         }
    & {! Z( j  d2 q5 v1 g% {5 R
  11.         am.init()& O& \2 _7 v5 W; ?0 H2 Z7 K
  12.         am.BaseService = *cmn.NewBaseService(nil, "AddrBook", am)
    5 i# V, x0 y4 u% y% y
  13.         return am
    / ?1 j: t+ {6 D4 Z" V2 B2 Y
  14. }7 q4 R5 O9 x6 j6 W$ k
  15. // When modifying this, don't forget to update loadFromFile()8 j* [  \; B4 B- W; o8 Z7 W- V
  16. func (a *AddrBook) init() {
    . X9 P: j5 u% f9 b! r' P$ t/ V
  17.   // 地址簿唯一标识
    2 g4 d9 u) I. N& a4 m& U# y
  18.         a.key = crypto.CRandHex(24) // 24/2 * 8 = 96 bits
    : U$ L8 }7 ]0 B
  19.         // New addr buckets, 默认为256个大小. I; Q7 z! I; @& B. G& Y: a, ?0 B  V
  20.         a.addrNew = make([]map[string]*knownAddress, newBucketCount)
    - u1 [' D6 e  s
  21.         for i := range a.addrNew {; V9 u; u$ t( U6 \4 Z
  22.                 a.addrNew<i> = make(map[string]*knownAddress)
    3 c: j% D) L% v. [( t" D
  23.         }  n8 D% K, u: m+ _1 B
  24.         // Old addr buckets,默认为64个大小6 G. Y8 V; B% C: b6 i
  25.         a.addrOld = make([]map[string]*knownAddress, oldBucketCount)
    " }  Z) W& P7 y+ _
  26.         for i := range a.addrOld {1 W; n. a& C: T4 g
  27.                 a.addrOld<i> = make(map[string]*knownAddress)9 o, L  L" f' w, g5 V' b
  28.         }
    3 ^* t( l4 l% _# A
  29. }</i></i>
复制代码
* \3 O7 J' [; k( u: S& r" t
bytomd启动时加载本地地址簿5 }+ f+ [  P& r& H/ Z" J* M' ~
loadFromFile在bytomd启动时,首先会加载本地的地址簿$ l  k5 q5 F0 F! z; D; ^
  1. // OnStart implements Service.
    - z1 f( j: h( e) E2 J0 A1 c! @
  2. func (a *AddrBook) OnStart() error {( l9 u- A1 F" z! X0 h' F3 d
  3.         a.BaseService.OnStart()  A  {* \+ ?- x. j, G( v- f
  4.         a.loadFromFile(a.filePath)" n8 o* W( d4 Q. [' P9 G
  5.         a.wg.Add(1); J- F) Y/ B; U
  6.         go a.saveRoutine()4 e# q; w) [2 N! [
  7.         return nil' @$ t/ R$ L$ e, W5 [, g5 P
  8. }
    $ |0 a% f/ L) h. [1 Q% j
  9. // Returns false if file does not exist./ T0 d' t( \! Z, X* o  L
  10. // cmn.Panics if file is corrupt.) Y- Q3 s1 K1 X* U& L# R
  11. func (a *AddrBook) loadFromFile(filePath string) bool {* p8 I3 A, ^5 q
  12.         // If doesn't exist, do nothing.6 ?" `, B) ?5 B' m3 E" W
  13.         // 如果本地地址簿不存在则直接返回
    " \- v; q: U2 A* L* s. O, N
  14.         _, err := os.Stat(filePath)
    0 p/ r3 E% O* F0 Q) h' s/ t
  15.         if os.IsNotExist(err) {0 _: j! C% s0 J3 i+ j1 P9 U
  16.                 return false
    - j6 f- ^' J( B3 n# o9 H# l& L; ^
  17.         }
      J3 f9 F2 P, [9 j6 t" L
  18.   // 加载地址簿json内容
    $ b0 f# ^, j) ^, n. x  }3 p
  19.         // Load addrBookJSON{}* m! u$ \7 n0 N
  20.         r, err := os.Open(filePath)3 f& ~4 e6 k1 F# Y
  21.         if err != nil {
    ' _! y- z* I, v! U1 F$ v% E4 G
  22.                 cmn.PanicCrisis(cmn.Fmt("Error opening file %s: %v", filePath, err)), v9 A+ y3 i: |0 i2 b! e+ {: H
  23.         }
    1 `0 U! t' `% ~# r
  24.         defer r.Close()2 v' r0 ]  u& i% N
  25.         aJSON := &addrBookJSON{}3 l! M& L7 k( r7 f9 T5 y
  26.         dec := json.NewDecoder(r)0 c1 G; s4 r6 [2 l9 a0 F
  27.         err = dec.Decode(aJSON)
    # I  w* _& y8 |
  28.         if err != nil {1 l; Y+ b( Z& o2 b4 G' H
  29.                 cmn.PanicCrisis(cmn.Fmt("Error reading file %s: %v", filePath, err))- e4 @1 M/ _: K% ]" t9 c" U
  30.         }
    # t# X4 q' |4 Y" q
  31.   // 填充addrNew、addrOld等
    8 h7 E8 @. \6 B
  32.         // Restore all the fields...
    " O4 x8 f% d- v7 Y& _
  33.         // Restore the key) P. S5 V7 [8 X
  34.         a.key = aJSON.Key
    % i( g: K8 Y% q$ ^' m7 S
  35.         // Restore .addrNew & .addrOld; l6 I4 m: V' k7 e
  36.         for _, ka := range aJSON.Addrs {( Y7 @, o* R; b- ?+ l( H7 m
  37.                 for _, bucketIndex := range ka.Buckets {* q/ \4 f6 m/ A- O1 d  h9 C
  38.                         bucket := a.getBucket(ka.BucketType, bucketIndex)
    * W6 T3 q5 q7 }2 _" c/ m- D* l7 A
  39.                         bucket[ka.Addr.String()] = ka7 F; `  W' ?7 B( t, X
  40.                 }4 G0 t' E; V$ T+ W' f' Z+ K2 E7 G
  41.                 a.addrLookup[ka.Addr.String()] = ka) u  b" s0 B5 w, B- B4 A
  42.                 if ka.BucketType == bucketTypeNew {) ]1 v, J5 i7 s8 c+ x
  43.                         a.nNew++
    3 E+ u0 ^- p0 C' z( r# @1 J) E, i
  44.                 } else {
    # i2 {/ _- o8 z. H# w
  45.                         a.nOld++
    " K2 M$ R! y3 R- N6 e" b
  46.                 }5 h$ C$ ~+ m2 s! a
  47.         }
    7 e% ?% Z; b) X
  48.         return true
    * R/ o( ]+ @9 D0 g8 F' @
  49. }
复制代码

& K6 C2 C$ z8 x3 d; ~; k定时更新地址簿; {) j3 `& p$ K! _' A
bytomd会定时更新本地地址簿,默认2分钟一次
. {/ E! A$ |9 t  v; S! _/ U- k
  1. func (a *AddrBook) saveRoutine() {
    4 A: P) T1 u, I' |/ L  N6 n
  2.         dumpAddressTicker := time.NewTicker(dumpAddressInterval)
    9 c* m$ p  z) E0 ^
  3. out:
    6 j( }5 x( p5 X5 [; Y* w- w
  4.         for {/ h) H, N5 x" _" ?
  5.                 select {
    4 `1 @) N! ?% m. c
  6.                 case
复制代码

6 L  r" [4 }7 T. i( a1 H添加新地址
5 i: A- K& \  x, z当peer之间交换addr时,节点会收到对端节点已知的地址信息,这些信息会被当前节点添加到地址簿中
4 N# k; i$ ?( n0 v$ P
  1. func (a *AddrBook) AddAddress(addr *NetAddress, src *NetAddress) {
    5 f3 q8 H, ]& D. X; g, t' L5 ?
  2.         a.mtx.Lock()
    % V1 p/ a4 l  ^& U4 ]
  3.         defer a.mtx.Unlock()- C$ H6 T% K, b% U* D
  4.         log.WithFields(log.Fields{
    . h* x' ~7 g: \9 j
  5.                 "addr": addr," h& _, U% H( {) N9 X
  6.                 "src":  src,* b) P! |0 S2 n& `& q" I# _
  7.         }).Debug("Add address to book"), u+ c( C5 }+ z2 J7 ~
  8.         a.addAddress(addr, src)# r3 U+ y! c4 p# A* Z$ }' `: x- V
  9. }) ^% l  s" B1 {
  10. func (a *AddrBook) addAddress(addr, src *NetAddress) {% w9 f) W+ M" Q- p# W
  11.         // 验证地址是否为可路由地址
    7 H5 ~) x& L2 D* ]7 a
  12.         if a.routabilityStrict && !addr.Routable() {
    . c" t* `7 K& |& Q' J
  13.                 log.Error(cmn.Fmt("Cannot add non-routable address %v", addr))3 a. q% u3 {1 U
  14.                 return
    % D  Y& P( W! k( e# D1 |
  15.         }- c! E8 x! k4 `; s8 E" B. z
  16.         // 验证地址是否为本地节点地址, v7 ^$ F2 I; {" ^( e( n, X
  17.         if _, ok := a.ourAddrs[addr.String()]; ok {
    6 D. y. D& O: B2 i. M' B& R, k
  18.                 // Ignore our own listener address.
    7 M8 ]7 {  i5 u" d7 w
  19.                 return: j0 Z) H" s, y( W: @
  20.         }
    , g5 }/ e  G* o: r% x/ y
  21.         // 验证地址是否存在地址集中
    4 |  @4 M) o/ }. l1 Z$ ^( o1 x' O5 i
  22.         // 如果存在:则判断该地址是否为old可靠地址、是否超过了最大buckets中。否则根据该地址已经被ka.Buckets引用的个数来随机决定是否添加到地址集中
    6 J( n6 p& b& b2 n
  23.         // 如果不存在:则添加到地址集中。并标识为bucketTypeNew地址类型/ B( Q0 @! M, A4 X
  24.         ka := a.addrLookup[addr.String()]
    . V8 h. |/ {4 \
  25.         if ka != nil {) V2 ]7 }( l! u: D# O0 ]1 P; @4 `
  26.                 // Already old.) N7 ]# R  W* V6 p4 F. X- A
  27.                 if ka.isOld() {3 Y# a# N: _4 h
  28.                         return, s1 s) l  v! f( Z& V* x
  29.                 }+ M2 w3 S6 @; i0 H/ c; V6 v
  30.                 // Already in max new buckets.' b2 \2 d+ t) f! m
  31.                 if len(ka.Buckets) == maxNewBucketsPerAddress {) C" O; |- Q1 Q3 Q: {. ~  A  {
  32.                         return
    ( ?) V3 J/ D1 L( Q8 D, |  M
  33.                 }
    - z% |# z+ }; Q1 k1 h5 P3 N5 ~
  34.                 // The more entries we have, the less likely we are to add more.' A. A8 E, j" H5 ^2 U  L3 t5 ~/ g- U
  35.                 factor := int32(2 * len(ka.Buckets)); W4 ^/ i# W8 y) e+ i8 u
  36.                 if a.rand.Int31n(factor) != 0 {& D; ]* J4 T0 t4 c( N
  37.                         return0 u6 C: g0 P$ r4 i0 r. C
  38.                 }, R8 [& @; m# Q! {+ f' P9 k
  39.         } else {# t' H* N( z0 F3 H
  40.                 ka = newKnownAddress(addr, src)
    0 ?4 _* V$ W4 l5 ~0 g  E( p
  41.         }2 M4 e: s. ]" f' {5 u6 k
  42.         // 找到该地址在地址集的索引位置并添加
    5 X  r3 D. i4 Y( e1 w5 W$ N' k
  43.         bucket := a.calcNewBucket(addr, src)3 {/ f' t8 c7 D9 `1 |
  44.         a.addToNewBucket(ka, bucket)
    - b3 J/ G; [5 ^8 A: k' ]
  45.         log.Info("Added new address ", "address:", addr, " total:", a.size())
    " t+ j, Q) n9 H3 l  A
  46. }
复制代码

) u, X, m9 K" d+ r( G* Z选择最优节点
" x! Y! [0 s- {$ O0 z9 N地址簿中存储众多地址,在p2p网络中需选择最优的地址去连接- _( ?5 ?' L% ^% E+ v) V
PickAddress(newBias int)函数中newBias是由pex_reactor产生的地址评分。如何计算地址分数在其他章节中再讲1 T" M# Y' u5 j. v" A
根据地址评分随机选择地址可增加区块链安全性
7 e- D& s8 b6 @! I
  1. // Pick an address to connect to with new/old bias.: [' M* n2 z5 Z3 O# ~+ c: [( Q9 N8 |
  2. func (a *AddrBook) PickAddress(newBias int) *NetAddress {: ^( I( f! B% G( U% K5 }9 _
  3.         a.mtx.Lock()" u6 L( O4 L% y! ~- [. H" p
  4.         defer a.mtx.Unlock()8 S4 {4 q; u1 b" g( h0 R
  5.         if a.size() == 0 {: E0 n# g& u- Y6 k
  6.                 return nil
    0 p# q: _. s5 u0 \3 U' z, V
  7.         }# N$ D% N4 M8 s8 H: ]2 a
  8.         // newBias地址分数限制在0-100分数之间
    . m1 R% J5 e( z- ?
  9.         if newBias > 100 {- _  H- P. ]" D+ ]
  10.                 newBias = 1006 Q5 B: Z/ G/ ~
  11.         }. d9 `: @9 h  D# K
  12.         if newBias
复制代码

- q5 X) A5 Z9 \2 C: `% Y4 k- Z7 A移除一个地址
- b! y$ a2 N6 h当一个地址被标记为Bad时则从地址集中移除。目前bytomd的代码版本并未调用过& x4 f; N. ^7 Q* u/ r  z9 D' F
  1. func (a *AddrBook) MarkBad(addr *NetAddress) {
    & G* A' F5 E( E( \- R
  2.         a.RemoveAddress(addr)8 n! i( s- f, a( ?& M
  3. }
    7 [3 ?, T; V+ M. g# L. f
  4. // RemoveAddress removes the address from the book.
    ) ]% J% j. E; k$ a8 Q
  5. func (a *AddrBook) RemoveAddress(addr *NetAddress) {
    % S+ n6 l* b" p
  6.         a.mtx.Lock()
    & _. w* F, |6 R) t0 w
  7.         defer a.mtx.Unlock(); B1 I" U/ u/ ]9 @8 s' L$ W1 u4 \
  8.         ka := a.addrLookup[addr.String()]- k) @9 m$ i& E: t
  9.         if ka == nil {
    ' L7 I- J! {  d* T: z# ]
  10.                 return
    , [' E  N( K& B! C/ S. l
  11.         }; B5 I/ E( B0 ^6 [& X" S
  12.         log.WithField("addr", addr).Info("Remove address from book")" d9 ]( k7 r% o4 ~& A, D
  13.         a.removeFromAllBuckets(ka)
    ; P/ ^2 c+ L% e  Q$ p
  14. }5 i/ x* B) {8 r3 y+ A' k
  15. func (a *AddrBook) removeFromAllBuckets(ka *knownAddress) {1 q( p% y7 _& U- |$ x6 ^6 p
  16.         for _, bucketIdx := range ka.Buckets {( U: g( @0 D* W0 ]( e5 w
  17.                 bucket := a.getBucket(ka.BucketType, bucketIdx)" w! J3 U! A* ~5 t1 ]+ d) v
  18.                 delete(bucket, ka.Addr.String())9 w3 P2 M0 I6 g6 W& ^
  19.         }( a) l  A. z# K! E2 |6 V
  20.         ka.Buckets = nil; i& [% h0 {  a; H* @
  21.         if ka.BucketType == bucketTypeNew {
    % K* k. @* |. r  o
  22.                 a.nNew--
    9 b5 s% t8 \+ M& V1 V/ U5 a' W& }
  23.         } else {; D; b& q+ e+ J1 f" ?1 S7 c! ~
  24.                 a.nOld--3 e8 D1 \3 ^) g0 V* G+ K" P
  25.         }; _8 Y# Z! y3 p+ e7 [: z$ A, b
  26.         delete(a.addrLookup, ka.Addr.String())2 F" n% O+ }% F  r
  27. }
复制代码

& K! u( ^* E4 e9 b' O4 U
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

excel436 小学生
  • 粉丝

    0

  • 关注

    0

  • 主题

    7