github.com/SmartMeshFoundation/Spectrum@v0.0.0-20220621030607-452a266fee1e/swarm/network/kademlia/kademlia_test.go (about)

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