github.com/daeglee/go-ethereum@v0.0.0-20190504220456-cad3e8d18e9b/swarm/network/simulation/kademlia.go (about)

     1  // Copyright 2018 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package simulation
    18  
    19  import (
    20  	"context"
    21  	"encoding/binary"
    22  	"encoding/hex"
    23  	"time"
    24  
    25  	"github.com/ethereum/go-ethereum/common"
    26  	"github.com/ethereum/go-ethereum/log"
    27  	"github.com/ethereum/go-ethereum/p2p/enode"
    28  	"github.com/ethereum/go-ethereum/p2p/simulations"
    29  	"github.com/ethereum/go-ethereum/swarm/network"
    30  )
    31  
    32  // BucketKeyKademlia is the key to be used for storing the kademlia
    33  // instance for particular node, usually inside the ServiceFunc function.
    34  var BucketKeyKademlia BucketKey = "kademlia"
    35  
    36  // WaitTillHealthy is blocking until the health of all kademlias is true.
    37  // If error is not nil, a map of kademlia that was found not healthy is returned.
    38  // TODO: Check correctness since change in kademlia depth calculation logic
    39  func (s *Simulation) WaitTillHealthy(ctx context.Context) (ill map[enode.ID]*network.Kademlia, err error) {
    40  	// Prepare PeerPot map for checking Kademlia health
    41  	var ppmap map[string]*network.PeerPot
    42  	kademlias := s.kademlias()
    43  	addrs := make([][]byte, 0, len(kademlias))
    44  	// TODO verify that all kademlias have same params
    45  	for _, k := range kademlias {
    46  		addrs = append(addrs, k.BaseAddr())
    47  	}
    48  	ppmap = network.NewPeerPotMap(s.neighbourhoodSize, addrs)
    49  
    50  	// Wait for healthy Kademlia on every node before checking files
    51  	ticker := time.NewTicker(200 * time.Millisecond)
    52  	defer ticker.Stop()
    53  
    54  	ill = make(map[enode.ID]*network.Kademlia)
    55  	for {
    56  		select {
    57  		case <-ctx.Done():
    58  			return ill, ctx.Err()
    59  		case <-ticker.C:
    60  			for k := range ill {
    61  				delete(ill, k)
    62  			}
    63  			log.Debug("kademlia health check", "addr count", len(addrs), "kad len", len(kademlias))
    64  			for id, k := range kademlias {
    65  				//PeerPot for this node
    66  				addr := common.Bytes2Hex(k.BaseAddr())
    67  				pp := ppmap[addr]
    68  				//call Healthy RPC
    69  				h := k.GetHealthInfo(pp)
    70  				//print info
    71  				log.Debug(k.String())
    72  				log.Debug("kademlia", "connectNN", h.ConnectNN, "knowNN", h.KnowNN)
    73  				log.Debug("kademlia", "health", h.ConnectNN && h.KnowNN, "addr", hex.EncodeToString(k.BaseAddr()), "node", id)
    74  				log.Debug("kademlia", "ill condition", !h.ConnectNN, "addr", hex.EncodeToString(k.BaseAddr()), "node", id)
    75  				if !h.Healthy() {
    76  					ill[id] = k
    77  				}
    78  			}
    79  			if len(ill) == 0 {
    80  				return nil, nil
    81  			}
    82  		}
    83  	}
    84  }
    85  
    86  // kademlias returns all Kademlia instances that are set
    87  // in simulation bucket.
    88  func (s *Simulation) kademlias() (ks map[enode.ID]*network.Kademlia) {
    89  	items := s.UpNodesItems(BucketKeyKademlia)
    90  	log.Debug("kademlia len items", "len", len(items))
    91  	ks = make(map[enode.ID]*network.Kademlia, len(items))
    92  	for id, v := range items {
    93  		k, ok := v.(*network.Kademlia)
    94  		if !ok {
    95  			continue
    96  		}
    97  		ks[id] = k
    98  	}
    99  	return ks
   100  }
   101  
   102  // WaitTillSnapshotRecreated is blocking until all the connections specified
   103  // in the snapshot are registered in the kademlia.
   104  // It differs from WaitTillHealthy, which waits only until all the kademlias are
   105  // healthy (it might happen even before all the connections are established).
   106  func (s *Simulation) WaitTillSnapshotRecreated(ctx context.Context, snap *simulations.Snapshot) error {
   107  	expected := getSnapshotConnections(snap.Conns)
   108  	ticker := time.NewTicker(150 * time.Millisecond)
   109  	defer ticker.Stop()
   110  
   111  	for {
   112  		select {
   113  		case <-ctx.Done():
   114  			return ctx.Err()
   115  		case <-ticker.C:
   116  			actual := s.getActualConnections()
   117  			if isAllDeployed(expected, actual) {
   118  				return nil
   119  			}
   120  		}
   121  	}
   122  }
   123  
   124  func (s *Simulation) getActualConnections() (res []uint64) {
   125  	kademlias := s.kademlias()
   126  	for base, k := range kademlias {
   127  		k.EachConn(base[:], 256, func(p *network.Peer, _ int) bool {
   128  			res = append(res, getConnectionHash(base, p.ID()))
   129  			return true
   130  		})
   131  	}
   132  
   133  	// only list those connections that appear twice (both peers should recognize connection as active)
   134  	res = removeDuplicatesAndSingletons(res)
   135  	return res
   136  }
   137  
   138  func getSnapshotConnections(conns []simulations.Conn) (res []uint64) {
   139  	for _, c := range conns {
   140  		res = append(res, getConnectionHash(c.One, c.Other))
   141  	}
   142  	return res
   143  }
   144  
   145  // returns an integer connection identifier (similar to 8-byte hash)
   146  func getConnectionHash(a, b enode.ID) uint64 {
   147  	var h [8]byte
   148  	for i := 0; i < 8; i++ {
   149  		h[i] = a[i] ^ b[i]
   150  	}
   151  	res := binary.LittleEndian.Uint64(h[:])
   152  	return res
   153  }
   154  
   155  // returns true if all connections in expected are listed in actual
   156  func isAllDeployed(expected []uint64, actual []uint64) bool {
   157  	if len(expected) == 0 {
   158  		return true
   159  	}
   160  
   161  	exp := make([]uint64, len(expected))
   162  	copy(exp, expected)
   163  	for _, c := range actual {
   164  		// remove value c from exp
   165  		for i := 0; i < len(exp); i++ {
   166  			if exp[i] == c {
   167  				exp = removeListElement(exp, i)
   168  				if len(exp) == 0 {
   169  					return true
   170  				}
   171  			}
   172  		}
   173  	}
   174  	return len(exp) == 0
   175  }
   176  
   177  func removeListElement(arr []uint64, i int) []uint64 {
   178  	last := len(arr) - 1
   179  	arr[i] = arr[last]
   180  	arr = arr[:last]
   181  	return arr
   182  }
   183  
   184  func removeDuplicatesAndSingletons(arr []uint64) []uint64 {
   185  	for i := 0; i < len(arr); {
   186  		found := false
   187  		for j := i + 1; j < len(arr); j++ {
   188  			if arr[i] == arr[j] {
   189  				arr = removeListElement(arr, j) // remove duplicate
   190  				found = true
   191  				break
   192  			}
   193  		}
   194  
   195  		if found {
   196  			i++
   197  		} else {
   198  			arr = removeListElement(arr, i) // remove singleton
   199  		}
   200  	}
   201  
   202  	return arr
   203  }