github.com/sberex/go-sberex@v1.8.2-0.20181113200658-ed96ac38f7d7/swarm/network/kademlia/kademlia_test.go (about)

     1  // This file is part of the go-sberex library. The go-sberex library is 
     2  // free software: you can redistribute it and/or modify it under the terms 
     3  // of the GNU Lesser General Public License as published by the Free 
     4  // Software Foundation, either version 3 of the License, or (at your option)
     5  // any later version.
     6  //
     7  // The go-sberex library is distributed in the hope that it will be useful, 
     8  // but WITHOUT ANY WARRANTY; without even the implied warranty of
     9  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 
    10  // General Public License <http://www.gnu.org/licenses/> for more details.
    11  
    12  package kademlia
    13  
    14  import (
    15  	"fmt"
    16  	"math"
    17  	"math/rand"
    18  	"os"
    19  	"path/filepath"
    20  	"reflect"
    21  	"testing"
    22  	"testing/quick"
    23  	"time"
    24  )
    25  
    26  var (
    27  	quickrand           = rand.New(rand.NewSource(time.Now().Unix()))
    28  	quickcfgFindClosest = &quick.Config{MaxCount: 50, Rand: quickrand}
    29  	quickcfgBootStrap   = &quick.Config{MaxCount: 100, Rand: quickrand}
    30  )
    31  
    32  type testNode struct {
    33  	addr Address
    34  }
    35  
    36  func (n *testNode) String() string {
    37  	return fmt.Sprintf("%x", n.addr[:])
    38  }
    39  
    40  func (n *testNode) Addr() Address {
    41  	return n.addr
    42  }
    43  
    44  func (n *testNode) Drop() {
    45  }
    46  
    47  func (n *testNode) Url() string {
    48  	return ""
    49  }
    50  
    51  func (n *testNode) LastActive() time.Time {
    52  	return time.Now()
    53  }
    54  
    55  func TestOn(t *testing.T) {
    56  	addr, ok1 := gen(Address{}, quickrand).(Address)
    57  	other, ok2 := gen(Address{}, quickrand).(Address)
    58  	if !ok1 || !ok2 {
    59  		t.Errorf("oops")
    60  	}
    61  	kad := New(addr, NewDefaultKadParams())
    62  	err := kad.On(&testNode{addr: other}, nil)
    63  	_ = err
    64  }
    65  
    66  func TestBootstrap(t *testing.T) {
    67  
    68  	test := func(test *bootstrapTest) bool {
    69  		// for any node kad.le, Target and N
    70  		params := NewDefaultKadParams()
    71  		params.MaxProx = test.MaxProx
    72  		params.BucketSize = test.BucketSize
    73  		params.ProxBinSize = test.BucketSize
    74  		kad := New(test.Self, params)
    75  		var err error
    76  
    77  		for p := 0; p < 9; p++ {
    78  			var nrs []*NodeRecord
    79  			n := math.Pow(float64(2), float64(7-p))
    80  			for i := 0; i < int(n); i++ {
    81  				addr := RandomAddressAt(test.Self, p)
    82  				nrs = append(nrs, &NodeRecord{
    83  					Addr: addr,
    84  				})
    85  			}
    86  			kad.Add(nrs)
    87  		}
    88  
    89  		node := &testNode{test.Self}
    90  
    91  		n := 0
    92  		for n < 100 {
    93  			err = kad.On(node, nil)
    94  			if err != nil {
    95  				t.Fatalf("backend not accepting node: %v", err)
    96  			}
    97  
    98  			record, need, _ := kad.Suggest()
    99  			if !need {
   100  				break
   101  			}
   102  			n++
   103  			if record == nil {
   104  				continue
   105  			}
   106  			node = &testNode{record.Addr}
   107  		}
   108  		exp := test.BucketSize * (test.MaxProx + 1)
   109  		if kad.Count() != exp {
   110  			t.Errorf("incorrect number of peers, expected %d, got %d\n%v", exp, kad.Count(), kad)
   111  			return false
   112  		}
   113  		return true
   114  	}
   115  	if err := quick.Check(test, quickcfgBootStrap); err != nil {
   116  		t.Error(err)
   117  	}
   118  
   119  }
   120  
   121  func TestFindClosest(t *testing.T) {
   122  
   123  	test := func(test *FindClosestTest) bool {
   124  		// for any node kad.le, Target and N
   125  		params := NewDefaultKadParams()
   126  		params.MaxProx = 7
   127  		kad := New(test.Self, params)
   128  		var err error
   129  		for _, node := range test.All {
   130  			err = kad.On(node, nil)
   131  			if err != nil && err.Error() != "bucket full" {
   132  				t.Fatalf("backend not accepting node: %v", err)
   133  			}
   134  		}
   135  
   136  		if len(test.All) == 0 || test.N == 0 {
   137  			return true
   138  		}
   139  		nodes := kad.FindClosest(test.Target, test.N)
   140  
   141  		// check that the number of results is min(N, kad.len)
   142  		wantN := test.N
   143  		if tlen := kad.Count(); tlen < test.N {
   144  			wantN = tlen
   145  		}
   146  
   147  		if len(nodes) != wantN {
   148  			t.Errorf("wrong number of nodes: got %d, want %d", len(nodes), wantN)
   149  			return false
   150  		}
   151  
   152  		if hasDuplicates(nodes) {
   153  			t.Errorf("result contains duplicates")
   154  			return false
   155  		}
   156  
   157  		if !sortedByDistanceTo(test.Target, nodes) {
   158  			t.Errorf("result is not sorted by distance to target")
   159  			return false
   160  		}
   161  
   162  		// check that the result nodes have minimum distance to target.
   163  		farthestResult := nodes[len(nodes)-1].Addr()
   164  		for i, b := range kad.buckets {
   165  			for j, n := range b {
   166  				if contains(nodes, n.Addr()) {
   167  					continue // don't run the check below for nodes in result
   168  				}
   169  				if test.Target.ProxCmp(n.Addr(), farthestResult) < 0 {
   170  					_ = i * j
   171  					t.Errorf("kad.le contains node that is closer to target but it's not in result")
   172  					return false
   173  				}
   174  			}
   175  		}
   176  		return true
   177  	}
   178  	if err := quick.Check(test, quickcfgFindClosest); err != nil {
   179  		t.Error(err)
   180  	}
   181  }
   182  
   183  type proxTest struct {
   184  	add   bool
   185  	index int
   186  	addr  Address
   187  }
   188  
   189  var (
   190  	addresses []Address
   191  )
   192  
   193  func TestProxAdjust(t *testing.T) {
   194  	r := rand.New(rand.NewSource(time.Now().UnixNano()))
   195  	self := gen(Address{}, r).(Address)
   196  	params := NewDefaultKadParams()
   197  	params.MaxProx = 7
   198  	kad := New(self, params)
   199  
   200  	var err error
   201  	for i := 0; i < 100; i++ {
   202  		a := gen(Address{}, r).(Address)
   203  		addresses = append(addresses, a)
   204  		err = kad.On(&testNode{addr: a}, nil)
   205  		if err != nil && err.Error() != "bucket full" {
   206  			t.Fatalf("backend not accepting node: %v", err)
   207  		}
   208  		if !kad.proxCheck(t) {
   209  			return
   210  		}
   211  	}
   212  	test := func(test *proxTest) bool {
   213  		node := &testNode{test.addr}
   214  		if test.add {
   215  			kad.On(node, nil)
   216  		} else {
   217  			kad.Off(node, nil)
   218  		}
   219  		return kad.proxCheck(t)
   220  	}
   221  	if err := quick.Check(test, quickcfgFindClosest); err != nil {
   222  		t.Error(err)
   223  	}
   224  }
   225  
   226  func TestSaveLoad(t *testing.T) {
   227  	r := rand.New(rand.NewSource(time.Now().UnixNano()))
   228  	addresses := gen([]Address{}, r).([]Address)
   229  	self := RandomAddress()
   230  	params := NewDefaultKadParams()
   231  	params.MaxProx = 7
   232  	kad := New(self, params)
   233  
   234  	var err error
   235  
   236  	for _, a := range addresses {
   237  		err = kad.On(&testNode{addr: a}, nil)
   238  		if err != nil && err.Error() != "bucket full" {
   239  			t.Fatalf("backend not accepting node: %v", err)
   240  		}
   241  	}
   242  	nodes := kad.FindClosest(self, 100)
   243  
   244  	path := filepath.Join(os.TempDir(), "bzz-kad-test-save-load.peers")
   245  	err = kad.Save(path, nil)
   246  	if err != nil && err.Error() != "bucket full" {
   247  		t.Fatalf("unepected error saving kaddb: %v", err)
   248  	}
   249  	kad = New(self, params)
   250  	err = kad.Load(path, nil)
   251  	if err != nil && err.Error() != "bucket full" {
   252  		t.Fatalf("unepected error loading kaddb: %v", err)
   253  	}
   254  	for _, b := range kad.db.Nodes {
   255  		for _, node := range b {
   256  			err = kad.On(&testNode{node.Addr}, nil)
   257  			if err != nil && err.Error() != "bucket full" {
   258  				t.Fatalf("backend not accepting node: %v", err)
   259  			}
   260  		}
   261  	}
   262  	loadednodes := kad.FindClosest(self, 100)
   263  	for i, node := range loadednodes {
   264  		if nodes[i].Addr() != node.Addr() {
   265  			t.Errorf("node mismatch at %d/%d: %v != %v", i, len(nodes), nodes[i].Addr(), node.Addr())
   266  		}
   267  	}
   268  }
   269  
   270  func (self *Kademlia) proxCheck(t *testing.T) bool {
   271  	var sum int
   272  	for i, b := range self.buckets {
   273  		l := len(b)
   274  		// if we are in the high prox multibucket
   275  		if i >= self.proxLimit {
   276  			sum += l
   277  		} else if l == 0 {
   278  			t.Errorf("bucket %d empty, yet proxLimit is %d\n%v", len(b), self.proxLimit, self)
   279  			return false
   280  		}
   281  	}
   282  	// check if merged high prox bucket does not exceed size
   283  	if sum > 0 {
   284  		if sum != self.proxSize {
   285  			t.Errorf("proxSize incorrect, expected %v, got %v", sum, self.proxSize)
   286  			return false
   287  		}
   288  		last := len(self.buckets[self.proxLimit])
   289  		if last > 0 && sum >= self.ProxBinSize+last {
   290  			t.Errorf("proxLimit %v incorrect, redundant non-empty bucket %d added to proxBin with %v (target %v)\n%v", self.proxLimit, last, sum-last, self.ProxBinSize, self)
   291  			return false
   292  		}
   293  		if self.proxLimit > 0 && sum < self.ProxBinSize {
   294  			t.Errorf("proxLimit %v incorrect. proxSize %v is less than target %v, yet there is more peers", self.proxLimit, sum, self.ProxBinSize)
   295  			return false
   296  		}
   297  	}
   298  	return true
   299  }
   300  
   301  type bootstrapTest struct {
   302  	MaxProx    int
   303  	BucketSize int
   304  	Self       Address
   305  }
   306  
   307  func (*bootstrapTest) Generate(rand *rand.Rand, size int) reflect.Value {
   308  	t := &bootstrapTest{
   309  		Self:       gen(Address{}, rand).(Address),
   310  		MaxProx:    5 + rand.Intn(2),
   311  		BucketSize: rand.Intn(3) + 1,
   312  	}
   313  	return reflect.ValueOf(t)
   314  }
   315  
   316  type FindClosestTest struct {
   317  	Self   Address
   318  	Target Address
   319  	All    []Node
   320  	N      int
   321  }
   322  
   323  func (c FindClosestTest) String() string {
   324  	return fmt.Sprintf("A: %064x\nT: %064x\n(%d)\n", c.Self[:], c.Target[:], c.N)
   325  }
   326  
   327  func (*FindClosestTest) Generate(rand *rand.Rand, size int) reflect.Value {
   328  	t := &FindClosestTest{
   329  		Self:   gen(Address{}, rand).(Address),
   330  		Target: gen(Address{}, rand).(Address),
   331  		N:      rand.Intn(bucketSize),
   332  	}
   333  	for _, a := range gen([]Address{}, rand).([]Address) {
   334  		t.All = append(t.All, &testNode{addr: a})
   335  	}
   336  	return reflect.ValueOf(t)
   337  }
   338  
   339  func (*proxTest) Generate(rand *rand.Rand, size int) reflect.Value {
   340  	var add bool
   341  	if rand.Intn(1) == 0 {
   342  		add = true
   343  	}
   344  	var t *proxTest
   345  	if add {
   346  		t = &proxTest{
   347  			addr: gen(Address{}, rand).(Address),
   348  			add:  add,
   349  		}
   350  	} else {
   351  		t = &proxTest{
   352  			index: rand.Intn(len(addresses)),
   353  			add:   add,
   354  		}
   355  	}
   356  	return reflect.ValueOf(t)
   357  }
   358  
   359  func hasDuplicates(slice []Node) bool {
   360  	seen := make(map[Address]bool)
   361  	for _, node := range slice {
   362  		if seen[node.Addr()] {
   363  			return true
   364  		}
   365  		seen[node.Addr()] = true
   366  	}
   367  	return false
   368  }
   369  
   370  func contains(nodes []Node, addr Address) bool {
   371  	for _, n := range nodes {
   372  		if n.Addr() == addr {
   373  			return true
   374  		}
   375  	}
   376  	return false
   377  }
   378  
   379  // gen wraps quick.Value so it's easier to use.
   380  // it generates a random value of the given value's type.
   381  func gen(typ interface{}, rand *rand.Rand) interface{} {
   382  	v, ok := quick.Value(reflect.TypeOf(typ), rand)
   383  	if !ok {
   384  		panic(fmt.Sprintf("couldn't generate random value of type %T", typ))
   385  	}
   386  	return v.Interface()
   387  }