dht_test.go 26.4 KB
Newer Older
1 2
package dht

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
3
import (
4
	"bytes"
Jeromy's avatar
Jeromy committed
5
	"context"
6
	"errors"
7
	"fmt"
8
	"math/rand"
9
	"sort"
10
	"strings"
11
	"sync"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
12
	"testing"
13
	"time"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
14

15
	opts "github.com/libp2p/go-libp2p-kad-dht/opts"
16 17
	pb "github.com/libp2p/go-libp2p-kad-dht/pb"

18
	cid "github.com/ipfs/go-cid"
19
	u "github.com/ipfs/go-ipfs-util"
20
	kb "github.com/libp2p/go-libp2p-kbucket"
Jeromy's avatar
Jeromy committed
21
	netutil "github.com/libp2p/go-libp2p-netutil"
22 23
	peer "github.com/libp2p/go-libp2p-peer"
	pstore "github.com/libp2p/go-libp2p-peerstore"
George Antoniadis's avatar
George Antoniadis committed
24
	record "github.com/libp2p/go-libp2p-record"
25
	routing "github.com/libp2p/go-libp2p-routing"
Jeromy's avatar
Jeromy committed
26
	bhost "github.com/libp2p/go-libp2p/p2p/host/basic"
George Antoniadis's avatar
George Antoniadis committed
27 28
	ci "github.com/libp2p/go-testutil/ci"
	travisci "github.com/libp2p/go-testutil/ci/travis"
29
	ma "github.com/multiformats/go-multiaddr"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
30 31
)

32 33
var testCaseValues = map[string][]byte{}
var testCaseCids []*cid.Cid
34 35 36 37

func init() {
	for i := 0; i < 100; i++ {
		v := fmt.Sprintf("%d -- value", i)
38 39 40

		mhv := u.Hash([]byte(v))
		testCaseCids = append(testCaseCids, cid.NewCidV0(mhv))
41 42 43
	}
}

44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
type blankValidator struct{}

func (blankValidator) Validate(_ string, _ []byte) error        { return nil }
func (blankValidator) Select(_ string, _ [][]byte) (int, error) { return 0, nil }

type testValidator struct{}

func (testValidator) Select(_ string, bs [][]byte) (int, error) {
	index := -1
	for i, b := range bs {
		if bytes.Compare(b, []byte("newer")) == 0 {
			index = i
		} else if bytes.Compare(b, []byte("valid")) == 0 {
			if index == -1 {
				index = i
			}
		}
	}
	if index == -1 {
		return -1, errors.New("no rec found")
	}
	return index, nil
}
func (testValidator) Validate(_ string, b []byte) error {
	if bytes.Compare(b, []byte("expired")) == 0 {
		return errors.New("expired")
	}
	return nil
}

74
func setupDHT(ctx context.Context, t *testing.T, client bool) *IpfsDHT {
75 76 77 78 79 80
	d, err := New(
		ctx,
		bhost.New(netutil.GenSwarmNetwork(t, ctx)),
		opts.Client(client),
		opts.NamespacedValidator("v", blankValidator{}),
	)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
81

82 83
	if err != nil {
		t.Fatal(err)
84
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
85 86 87
	return d
}

88 89
func setupDHTS(ctx context.Context, n int, t *testing.T) ([]ma.Multiaddr, []peer.ID, []*IpfsDHT) {
	addrs := make([]ma.Multiaddr, n)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
90
	dhts := make([]*IpfsDHT, n)
91 92
	peers := make([]peer.ID, n)

93 94 95
	sanityAddrsMap := make(map[string]struct{})
	sanityPeersMap := make(map[string]struct{})

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
96
	for i := 0; i < n; i++ {
97
		dhts[i] = setupDHT(ctx, t, false)
98
		peers[i] = dhts[i].self
99
		addrs[i] = dhts[i].peerstore.Addrs(dhts[i].self)[0]
100 101

		if _, lol := sanityAddrsMap[addrs[i].String()]; lol {
Jakub Sztandera's avatar
Jakub Sztandera committed
102
			t.Fatal("While setting up DHTs address got duplicated.")
103 104 105 106
		} else {
			sanityAddrsMap[addrs[i].String()] = struct{}{}
		}
		if _, lol := sanityPeersMap[peers[i].String()]; lol {
Jakub Sztandera's avatar
Jakub Sztandera committed
107
			t.Fatal("While setting up DHTs peerid got duplicated.")
108 109 110
		} else {
			sanityPeersMap[peers[i].String()] = struct{}{}
		}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
111 112 113 114 115
	}

	return addrs, peers, dhts
}

116
func connectNoSync(t *testing.T, ctx context.Context, a, b *IpfsDHT) {
117
	idB := b.self
118
	addrB := b.peerstore.Addrs(idB)
119 120
	if len(addrB) == 0 {
		t.Fatal("peers setup incorrectly: no local address")
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
121
	}
122

Jeromy's avatar
Jeromy committed
123 124
	a.peerstore.AddAddrs(idB, addrB, pstore.TempAddrTTL)
	pi := pstore.PeerInfo{ID: idB}
Jeromy's avatar
Jeromy committed
125
	if err := a.host.Connect(ctx, pi); err != nil {
126
		t.Fatal(err)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
127
	}
128 129 130 131
}

func connect(t *testing.T, ctx context.Context, a, b *IpfsDHT) {
	connectNoSync(t, ctx, a, b)
132

133 134
	// loop until connection notification has been received.
	// under high load, this may not happen as immediately as we would like.
135 136 137 138 139 140 141
	for a.routingTable.Find(b.self) == "" {
		time.Sleep(time.Millisecond * 5)
	}

	for b.routingTable.Find(a.self) == "" {
		time.Sleep(time.Millisecond * 5)
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
142 143
}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
144
func bootstrap(t *testing.T, ctx context.Context, dhts []*IpfsDHT) {
145

146
	ctx, cancel := context.WithCancel(ctx)
Steven Allen's avatar
Steven Allen committed
147 148
	defer cancel()

Richard Littauer's avatar
Richard Littauer committed
149
	log.Debugf("Bootstrapping DHTs...")
150 151 152 153 154 155

	// tried async. sequential fares much better. compare:
	// 100 async https://gist.github.com/jbenet/56d12f0578d5f34810b2
	// 100 sync https://gist.github.com/jbenet/6c59e7c15426e48aaedd
	// probably because results compound

156 157 158 159
	var cfg BootstrapConfig
	cfg = DefaultBootstrapConfig
	cfg.Queries = 3

160 161 162
	start := rand.Intn(len(dhts)) // randomize to decrease bias.
	for i := range dhts {
		dht := dhts[(start+i)%len(dhts)]
163
		dht.runBootstrap(ctx, cfg)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
164 165 166
	}
}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
167
func TestValueGetSet(t *testing.T) {
168 169
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
170

171 172
	dhtA := setupDHT(ctx, t, false)
	dhtB := setupDHT(ctx, t, false)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
173

174 175
	defer dhtA.Close()
	defer dhtB.Close()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
176 177
	defer dhtA.host.Close()
	defer dhtB.host.Close()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
178

179
	connect(t, ctx, dhtA, dhtB)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
180

Steven Allen's avatar
Steven Allen committed
181
	log.Debug("adding value on: ", dhtA.self)
Jeromy's avatar
Jeromy committed
182 183
	ctxT, cancel := context.WithTimeout(ctx, time.Second)
	defer cancel()
184
	err := dhtA.PutValue(ctxT, "/v/hello", []byte("world"))
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
185 186 187 188
	if err != nil {
		t.Fatal(err)
	}

189 190 191 192 193 194 195 196 197 198 199
	/*
		ctxT, _ = context.WithTimeout(ctx, time.Second*2)
		val, err := dhtA.GetValue(ctxT, "/v/hello")
		if err != nil {
			t.Fatal(err)
		}

		if string(val) != "world" {
			t.Fatalf("Expected 'world' got '%s'", string(val))
		}
	*/
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
200

Steven Allen's avatar
Steven Allen committed
201
	log.Debug("requesting value on dht: ", dhtB.self)
Jeromy's avatar
Jeromy committed
202 203
	ctxT, cancel = context.WithTimeout(ctx, time.Second*2)
	defer cancel()
204
	valb, err := dhtB.GetValue(ctxT, "/v/hello")
205 206 207 208
	if err != nil {
		t.Fatal(err)
	}

209 210
	if string(valb) != "world" {
		t.Fatalf("Expected 'world' got '%s'", string(valb))
211
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
212 213
}

214 215 216 217 218 219 220 221 222 223 224 225
func TestValueSetInvalid(t *testing.T) {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	dhtA := setupDHT(ctx, t, false)
	dhtB := setupDHT(ctx, t, false)

	defer dhtA.Close()
	defer dhtB.Close()
	defer dhtA.host.Close()
	defer dhtB.host.Close()

226 227
	dhtA.Validator.(record.NamespacedValidator)["v"] = testValidator{}
	dhtB.Validator.(record.NamespacedValidator)["v"] = testValidator{}
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261

	connect(t, ctx, dhtA, dhtB)

	testSetGet := func(val string, exp string, experr error) {
		ctxT, cancel := context.WithTimeout(ctx, time.Second)
		defer cancel()
		err := dhtA.PutValue(ctxT, "/v/hello", []byte(val))
		if err != nil {
			t.Fatal(err)
		}

		ctxT, cancel = context.WithTimeout(ctx, time.Second*2)
		defer cancel()
		valb, err := dhtB.GetValue(ctxT, "/v/hello")
		if err != experr {
			t.Fatalf("Set/Get %v: Expected %v error but got %v", val, experr, err)
		}
		if err == nil {
			if string(valb) != exp {
				t.Fatalf("Expected '%v' got '%s'", exp, string(valb))
			}
		}
	}

	// Expired records should not be returned
	testSetGet("expired", "", routing.ErrNotFound)
	// Valid record should be returned
	testSetGet("valid", "valid", nil)
	// Newer record should supersede previous record
	testSetGet("newer", "newer", nil)
	// Attempt to set older record again should be ignored
	testSetGet("valid", "newer", nil)
}

262
func TestInvalidMessageSenderTracking(t *testing.T) {
Steven Allen's avatar
Steven Allen committed
263 264 265
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

266
	dht := setupDHT(ctx, t, false)
Steven Allen's avatar
Steven Allen committed
267 268
	defer dht.Close()

269 270 271 272 273 274 275
	foo := peer.ID("asdasd")
	_, err := dht.messageSenderForPeer(foo)
	if err == nil {
		t.Fatal("that shouldnt have succeeded")
	}

	dht.smlk.Lock()
Steven Allen's avatar
Steven Allen committed
276 277 278 279
	mscnt := len(dht.strmap)
	dht.smlk.Unlock()

	if mscnt > 0 {
280 281 282 283
		t.Fatal("should have no message senders in map")
	}
}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
284 285
func TestProvides(t *testing.T) {
	// t.Skip("skipping test to debug another")
Steven Allen's avatar
Steven Allen committed
286 287
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
288

289
	_, _, dhts := setupDHTS(ctx, 4, t)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
290 291
	defer func() {
		for i := 0; i < 4; i++ {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
292
			dhts[i].Close()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
293
			defer dhts[i].host.Close()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
294 295 296
		}
	}()

297 298 299
	connect(t, ctx, dhts[0], dhts[1])
	connect(t, ctx, dhts[1], dhts[2])
	connect(t, ctx, dhts[1], dhts[3])
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
300

301
	for _, k := range testCaseCids {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
302
		log.Debugf("announcing provider for %s", k)
Jeromy's avatar
Jeromy committed
303
		if err := dhts[3].Provide(ctx, k, true); err != nil {
304 305
			t.Fatal(err)
		}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
306 307
	}

308 309 310 311
	// what is this timeout for? was 60ms before.
	time.Sleep(time.Millisecond * 6)

	n := 0
312
	for _, c := range testCaseCids {
313 314
		n = (n + 1) % 3

315
		log.Debugf("getting providers for %s from %d", c, n)
Jeromy's avatar
Jeromy committed
316 317
		ctxT, cancel := context.WithTimeout(ctx, time.Second)
		defer cancel()
318
		provchan := dhts[n].FindProvidersAsync(ctxT, c, 1)
319 320 321 322 323 324 325 326 327 328 329 330 331 332 333

		select {
		case prov := <-provchan:
			if prov.ID == "" {
				t.Fatal("Got back nil provider")
			}
			if prov.ID != dhts[3].self {
				t.Fatal("Got back wrong provider")
			}
		case <-ctxT.Done():
			t.Fatal("Did not get a provider back.")
		}
	}
}

Jeromy's avatar
Jeromy committed
334 335
func TestLocalProvides(t *testing.T) {
	// t.Skip("skipping test to debug another")
Steven Allen's avatar
Steven Allen committed
336 337
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
Jeromy's avatar
Jeromy committed
338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369

	_, _, dhts := setupDHTS(ctx, 4, t)
	defer func() {
		for i := 0; i < 4; i++ {
			dhts[i].Close()
			defer dhts[i].host.Close()
		}
	}()

	connect(t, ctx, dhts[0], dhts[1])
	connect(t, ctx, dhts[1], dhts[2])
	connect(t, ctx, dhts[1], dhts[3])

	for _, k := range testCaseCids {
		log.Debugf("announcing provider for %s", k)
		if err := dhts[3].Provide(ctx, k, false); err != nil {
			t.Fatal(err)
		}
	}

	time.Sleep(time.Millisecond * 10)

	for _, c := range testCaseCids {
		for i := 0; i < 3; i++ {
			provs := dhts[i].providers.GetProviders(ctx, c)
			if len(provs) > 0 {
				t.Fatal("shouldnt know this")
			}
		}
	}
}

370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396
// if minPeers or avgPeers is 0, dont test for it.
func waitForWellFormedTables(t *testing.T, dhts []*IpfsDHT, minPeers, avgPeers int, timeout time.Duration) bool {
	// test "well-formed-ness" (>= minPeers peers in every routing table)

	checkTables := func() bool {
		totalPeers := 0
		for _, dht := range dhts {
			rtlen := dht.routingTable.Size()
			totalPeers += rtlen
			if minPeers > 0 && rtlen < minPeers {
				t.Logf("routing table for %s only has %d peers (should have >%d)", dht.self, rtlen, minPeers)
				return false
			}
		}
		actualAvgPeers := totalPeers / len(dhts)
		t.Logf("avg rt size: %d", actualAvgPeers)
		if avgPeers > 0 && actualAvgPeers < avgPeers {
			t.Logf("avg rt size: %d < %d", actualAvgPeers, avgPeers)
			return false
		}
		return true
	}

	timeoutA := time.After(timeout)
	for {
		select {
		case <-timeoutA:
397
			log.Debugf("did not reach well-formed routing tables by %s", timeout)
398 399 400 401 402 403 404 405 406 407 408
			return false // failed
		case <-time.After(5 * time.Millisecond):
			if checkTables() {
				return true // succeeded
			}
		}
	}
}

func printRoutingTables(dhts []*IpfsDHT) {
	// the routing tables should be full now. let's inspect them.
409
	fmt.Printf("checking routing table of %d\n", len(dhts))
410 411 412 413 414 415 416
	for _, dht := range dhts {
		fmt.Printf("checking routing table of %s\n", dht.self)
		dht.routingTable.Print()
		fmt.Println("")
	}
}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
417
func TestBootstrap(t *testing.T) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
418
	// t.Skip("skipping test to debug another")
419 420 421 422
	if testing.Short() {
		t.SkipNow()
	}

Steven Allen's avatar
Steven Allen committed
423 424
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
425

426
	nDHTs := 30
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
427 428 429 430
	_, _, dhts := setupDHTS(ctx, nDHTs, t)
	defer func() {
		for i := 0; i < nDHTs; i++ {
			dhts[i].Close()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
431
			defer dhts[i].host.Close()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
432 433 434 435 436 437 438 439
		}
	}()

	t.Logf("connecting %d dhts in a ring", nDHTs)
	for i := 0; i < nDHTs; i++ {
		connect(t, ctx, dhts[i], dhts[(i+1)%len(dhts)])
	}

440
	<-time.After(100 * time.Millisecond)
441 442 443 444
	// bootstrap a few times until we get good tables.
	stop := make(chan struct{})
	go func() {
		for {
445
			t.Logf("bootstrapping them so they find each other %d", nDHTs)
Jeromy's avatar
Jeromy committed
446 447
			ctxT, cancel := context.WithTimeout(ctx, 5*time.Second)
			defer cancel()
448 449 450 451 452 453 454 455 456 457 458
			bootstrap(t, ctxT, dhts)

			select {
			case <-time.After(50 * time.Millisecond):
				continue // being explicit
			case <-stop:
				return
			}
		}
	}()

459
	waitForWellFormedTables(t, dhts, 7, 10, 20*time.Second)
460
	close(stop)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
461

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
462 463
	if u.Debug {
		// the routing tables should be full now. let's inspect them.
464 465 466 467 468 469
		printRoutingTables(dhts)
	}
}

func TestPeriodicBootstrap(t *testing.T) {
	// t.Skip("skipping test to debug another")
470 471 472
	if ci.IsRunning() {
		t.Skip("skipping on CI. highly timing dependent")
	}
473 474 475 476
	if testing.Short() {
		t.SkipNow()
	}

Steven Allen's avatar
Steven Allen committed
477 478
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
479 480 481 482 483 484 485 486 487 488

	nDHTs := 30
	_, _, dhts := setupDHTS(ctx, nDHTs, t)
	defer func() {
		for i := 0; i < nDHTs; i++ {
			dhts[i].Close()
			defer dhts[i].host.Close()
		}
	}()

489
	signals := []chan time.Time{}
490

491 492 493 494
	var cfg BootstrapConfig
	cfg = DefaultBootstrapConfig
	cfg.Queries = 5

495 496 497
	// kick off periodic bootstrappers with instrumented signals.
	for _, dht := range dhts {
		s := make(chan time.Time)
498 499 500 501 502 503
		signals = append(signals, s)
		proc, err := dht.BootstrapOnSignal(cfg, s)
		if err != nil {
			t.Fatal(err)
		}
		defer proc.Close()
504 505
	}

506
	t.Logf("dhts are not connected. %d", nDHTs)
507 508 509 510 511 512 513 514 515 516 517
	for _, dht := range dhts {
		rtlen := dht.routingTable.Size()
		if rtlen > 0 {
			t.Errorf("routing table for %s should have 0 peers. has %d", dht.self, rtlen)
		}
	}

	for i := 0; i < nDHTs; i++ {
		connect(t, ctx, dhts[i], dhts[(i+1)%len(dhts)])
	}

518
	t.Logf("DHTs are now connected to 1-2 others. %d", nDHTs)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
519
	for _, dht := range dhts {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
520
		rtlen := dht.routingTable.Size()
521 522
		if rtlen > 2 {
			t.Errorf("routing table for %s should have at most 2 peers. has %d", dht.self, rtlen)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
523
		}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
524
	}
525

526 527 528 529
	if u.Debug {
		printRoutingTables(dhts)
	}

530
	t.Logf("bootstrapping them so they find each other. %d", nDHTs)
531 532 533 534
	now := time.Now()
	for _, signal := range signals {
		go func(s chan time.Time) { s <- now }(signal)
	}
535 536 537

	// this is async, and we dont know when it's finished with one cycle, so keep checking
	// until the routing tables look better, or some long timeout for the failure case.
538
	waitForWellFormedTables(t, dhts, 7, 10, 20*time.Second)
539 540 541

	if u.Debug {
		printRoutingTables(dhts)
542
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
543 544
}

545 546
func TestProvidesMany(t *testing.T) {
	t.Skip("this test doesn't work")
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
547
	// t.Skip("skipping test to debug another")
Steven Allen's avatar
Steven Allen committed
548 549
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
550 551 552 553 554 555

	nDHTs := 40
	_, _, dhts := setupDHTS(ctx, nDHTs, t)
	defer func() {
		for i := 0; i < nDHTs; i++ {
			dhts[i].Close()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
556
			defer dhts[i].host.Close()
557 558 559 560 561 562 563 564
		}
	}()

	t.Logf("connecting %d dhts in a ring", nDHTs)
	for i := 0; i < nDHTs; i++ {
		connect(t, ctx, dhts[i], dhts[(i+1)%len(dhts)])
	}

565
	<-time.After(100 * time.Millisecond)
566
	t.Logf("bootstrapping them so they find each other. %d", nDHTs)
Jeromy's avatar
Jeromy committed
567 568
	ctxT, cancel := context.WithTimeout(ctx, 20*time.Second)
	defer cancel()
569 570
	bootstrap(t, ctxT, dhts)

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
571 572 573 574 575 576 577 578
	if u.Debug {
		// the routing tables should be full now. let's inspect them.
		t.Logf("checking routing table of %d", nDHTs)
		for _, dht := range dhts {
			fmt.Printf("checking routing table of %s\n", dht.self)
			dht.routingTable.Print()
			fmt.Println("")
		}
579
	}
580

581
	providers := make(map[string]peer.ID)
582

583
	d := 0
584
	for _, c := range testCaseCids {
585 586
		d = (d + 1) % len(dhts)
		dht := dhts[d]
587
		providers[c.KeyString()] = dht.self
588

589
		t.Logf("announcing provider for %s", c)
Jeromy's avatar
Jeromy committed
590
		if err := dht.Provide(ctx, c, true); err != nil {
591 592
			t.Fatal(err)
		}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
593 594
	}

595 596
	// what is this timeout for? was 60ms before.
	time.Sleep(time.Millisecond * 6)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
597

598 599
	errchan := make(chan error)

Jeromy's avatar
Jeromy committed
600 601
	ctxT, cancel = context.WithTimeout(ctx, 5*time.Second)
	defer cancel()
602 603

	var wg sync.WaitGroup
604
	getProvider := func(dht *IpfsDHT, k *cid.Cid) {
605
		defer wg.Done()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
606

607
		expected := providers[k.KeyString()]
608

609 610 611
		provchan := dht.FindProvidersAsync(ctxT, k, 1)
		select {
		case prov := <-provchan:
612 613
			actual := prov.ID
			if actual == "" {
614
				errchan <- fmt.Errorf("Got back nil provider (%s at %s)", k, dht.self)
615 616 617
			} else if actual != expected {
				errchan <- fmt.Errorf("Got back wrong provider (%s != %s) (%s at %s)",
					expected, actual, k, dht.self)
618 619 620
			}
		case <-ctxT.Done():
			errchan <- fmt.Errorf("Did not get a provider back (%s at %s)", k, dht.self)
Jeromy's avatar
Jeromy committed
621
		}
622 623
	}

624
	for _, c := range testCaseCids {
625 626
		// everyone should be able to find it...
		for _, dht := range dhts {
627
			log.Debugf("getting providers for %s at %s", c, dht.self)
628
			wg.Add(1)
629
			go getProvider(dht, c)
630
		}
631 632 633 634 635 636 637 638 639 640
	}

	// we need this because of printing errors
	go func() {
		wg.Wait()
		close(errchan)
	}()

	for err := range errchan {
		t.Error(err)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
641 642 643
	}
}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
644
func TestProvidesAsync(t *testing.T) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
645
	// t.Skip("skipping test to debug another")
646 647 648
	if testing.Short() {
		t.SkipNow()
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
649

Steven Allen's avatar
Steven Allen committed
650 651
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
652

653
	_, _, dhts := setupDHTS(ctx, 4, t)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
654 655
	defer func() {
		for i := 0; i < 4; i++ {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
656
			dhts[i].Close()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
657
			defer dhts[i].host.Close()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
658 659 660
		}
	}()

661 662 663
	connect(t, ctx, dhts[0], dhts[1])
	connect(t, ctx, dhts[1], dhts[2])
	connect(t, ctx, dhts[1], dhts[3])
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
664

Jeromy's avatar
Jeromy committed
665
	err := dhts[3].Provide(ctx, testCaseCids[0], true)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
666 667 668 669 670 671
	if err != nil {
		t.Fatal(err)
	}

	time.Sleep(time.Millisecond * 60)

Jeromy's avatar
Jeromy committed
672 673
	ctxT, cancel := context.WithTimeout(ctx, time.Millisecond*300)
	defer cancel()
674
	provs := dhts[0].FindProvidersAsync(ctxT, testCaseCids[0], 5)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
675
	select {
Jeromy's avatar
Jeromy committed
676 677 678 679
	case p, ok := <-provs:
		if !ok {
			t.Fatal("Provider channel was closed...")
		}
680
		if p.ID == "" {
Jeromy's avatar
Jeromy committed
681 682
			t.Fatal("Got back nil provider!")
		}
683
		if p.ID != dhts[3].self {
684
			t.Fatalf("got a provider, but not the right one. %s", p)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
685
		}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
686
	case <-ctxT.Done():
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
687 688 689 690
		t.Fatal("Didnt get back providers")
	}
}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
691
func TestLayeredGet(t *testing.T) {
692 693
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
694

695
	_, _, dhts := setupDHTS(ctx, 4, t)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
696 697
	defer func() {
		for i := 0; i < 4; i++ {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
698
			dhts[i].Close()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
699
			defer dhts[i].host.Close()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
700 701 702
		}
	}()

703 704
	connect(t, ctx, dhts[0], dhts[1])
	connect(t, ctx, dhts[1], dhts[2])
705
	connect(t, ctx, dhts[2], dhts[3])
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
706

707
	err := dhts[3].PutValue(ctx, "/v/hello", []byte("world"))
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
708 709 710 711
	if err != nil {
		t.Fatal(err)
	}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
712
	time.Sleep(time.Millisecond * 6)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
713

Jeromy's avatar
Jeromy committed
714 715
	ctxT, cancel := context.WithTimeout(ctx, time.Second)
	defer cancel()
716 717 718
	val, err := dhts[0].GetValue(ctxT, "/v/hello")
	if err != nil {
		t.Fatal(err)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
719
	}
720 721 722

	if string(val) != "world" {
		t.Error("got wrong value")
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
723 724 725 726
	}
}

func TestFindPeer(t *testing.T) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
727
	// t.Skip("skipping test to debug another")
728 729 730
	if testing.Short() {
		t.SkipNow()
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
731

Steven Allen's avatar
Steven Allen committed
732 733
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
734

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
735
	_, peers, dhts := setupDHTS(ctx, 4, t)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
736 737
	defer func() {
		for i := 0; i < 4; i++ {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
738
			dhts[i].Close()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
739
			dhts[i].host.Close()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
740 741 742
		}
	}()

743 744 745
	connect(t, ctx, dhts[0], dhts[1])
	connect(t, ctx, dhts[1], dhts[2])
	connect(t, ctx, dhts[1], dhts[3])
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
746

Jeromy's avatar
Jeromy committed
747 748
	ctxT, cancel := context.WithTimeout(ctx, time.Second)
	defer cancel()
749
	p, err := dhts[0].FindPeer(ctxT, peers[2])
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
750 751 752 753
	if err != nil {
		t.Fatal(err)
	}

754
	if p.ID == "" {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
755 756 757
		t.Fatal("Failed to find peer.")
	}

758
	if p.ID != peers[2] {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
759 760 761
		t.Fatal("Didnt find expected peer.")
	}
}
762

763
func TestFindPeersConnectedToPeer(t *testing.T) {
764 765
	t.Skip("not quite correct (see note)")

766 767 768 769
	if testing.Short() {
		t.SkipNow()
	}

Steven Allen's avatar
Steven Allen committed
770 771
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
772 773 774 775 776

	_, peers, dhts := setupDHTS(ctx, 4, t)
	defer func() {
		for i := 0; i < 4; i++ {
			dhts[i].Close()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
777
			dhts[i].host.Close()
778 779 780 781 782
		}
	}()

	// topology:
	// 0-1, 1-2, 1-3, 2-3
783 784 785 786
	connect(t, ctx, dhts[0], dhts[1])
	connect(t, ctx, dhts[1], dhts[2])
	connect(t, ctx, dhts[1], dhts[3])
	connect(t, ctx, dhts[2], dhts[3])
787 788 789 790 791 792

	// fmt.Println("0 is", peers[0])
	// fmt.Println("1 is", peers[1])
	// fmt.Println("2 is", peers[2])
	// fmt.Println("3 is", peers[3])

Jeromy's avatar
Jeromy committed
793 794
	ctxT, cancel := context.WithTimeout(ctx, time.Second)
	defer cancel()
795
	pchan, err := dhts[0].FindPeersConnectedToPeer(ctxT, peers[2])
796 797 798 799
	if err != nil {
		t.Fatal(err)
	}

800
	// shouldFind := []peer.ID{peers[1], peers[3]}
Jeromy's avatar
Jeromy committed
801
	var found []*pstore.PeerInfo
802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817
	for nextp := range pchan {
		found = append(found, nextp)
	}

	// fmt.Printf("querying 0 (%s) FindPeersConnectedToPeer 2 (%s)\n", peers[0], peers[2])
	// fmt.Println("should find 1, 3", shouldFind)
	// fmt.Println("found", found)

	// testPeerListsMatch(t, shouldFind, found)

	log.Warning("TestFindPeersConnectedToPeer is not quite correct")
	if len(found) == 0 {
		t.Fatal("didn't find any peers.")
	}
}

818
func testPeerListsMatch(t *testing.T, p1, p2 []peer.ID) {
819 820 821 822 823 824 825 826 827

	if len(p1) != len(p2) {
		t.Fatal("did not find as many peers as should have", p1, p2)
	}

	ids1 := make([]string, len(p1))
	ids2 := make([]string, len(p2))

	for i, p := range p1 {
828
		ids1[i] = string(p)
829 830 831
	}

	for i, p := range p2 {
832
		ids2[i] = string(p)
833 834 835 836 837 838 839 840 841 842 843 844
	}

	sort.Sort(sort.StringSlice(ids1))
	sort.Sort(sort.StringSlice(ids2))

	for i := range ids1 {
		if ids1[i] != ids2[i] {
			t.Fatal("Didnt find expected peer", ids1[i], ids2)
		}
	}
}

845
func TestConnectCollision(t *testing.T) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
846
	// t.Skip("skipping test to debug another")
847 848 849
	if testing.Short() {
		t.SkipNow()
	}
850 851 852
	if travisci.IsRunning() {
		t.Skip("Skipping on Travis-CI.")
	}
853

854
	runTimes := 10
855

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
856
	for rtime := 0; rtime < runTimes; rtime++ {
rht's avatar
rht committed
857
		log.Info("Running Time: ", rtime)
858

Steven Allen's avatar
Steven Allen committed
859
		ctx, cancel := context.WithCancel(context.Background())
860

861 862
		dhtA := setupDHT(ctx, t, false)
		dhtB := setupDHT(ctx, t, false)
863

864 865
		addrA := dhtA.peerstore.Addrs(dhtA.self)[0]
		addrB := dhtB.peerstore.Addrs(dhtB.self)[0]
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
866

867 868
		peerA := dhtA.self
		peerB := dhtB.self
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
869

870
		errs := make(chan error)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
871
		go func() {
Jeromy's avatar
Jeromy committed
872 873
			dhtA.peerstore.AddAddr(peerB, addrB, pstore.TempAddrTTL)
			pi := pstore.PeerInfo{ID: peerB}
Jeromy's avatar
Jeromy committed
874
			err := dhtA.host.Connect(ctx, pi)
875
			errs <- err
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
876 877
		}()
		go func() {
Jeromy's avatar
Jeromy committed
878 879
			dhtB.peerstore.AddAddr(peerA, addrA, pstore.TempAddrTTL)
			pi := pstore.PeerInfo{ID: peerA}
Jeromy's avatar
Jeromy committed
880
			err := dhtB.host.Connect(ctx, pi)
881
			errs <- err
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
882 883
		}()

884
		timeout := time.After(5 * time.Second)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
885
		select {
886 887 888 889
		case e := <-errs:
			if e != nil {
				t.Fatal(e)
			}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
890 891 892 893
		case <-timeout:
			t.Fatal("Timeout received!")
		}
		select {
894 895 896 897
		case e := <-errs:
			if e != nil {
				t.Fatal(e)
			}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
898 899 900 901
		case <-timeout:
			t.Fatal("Timeout received!")
		}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
902 903
		dhtA.Close()
		dhtB.Close()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
904 905
		dhtA.host.Close()
		dhtB.host.Close()
Steven Allen's avatar
Steven Allen committed
906
		cancel()
Jeromy's avatar
Jeromy committed
907
	}
908
}
909 910 911 912 913

func TestBadProtoMessages(t *testing.T) {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

914
	d := setupDHT(ctx, t, false)
915 916 917 918 919 920

	nilrec := new(pb.Message)
	if _, err := d.handlePutValue(ctx, "testpeer", nilrec); err == nil {
		t.Fatal("should have errored on nil record")
	}
}
921 922 923 924 925 926 927 928 929 930

func TestClientModeConnect(t *testing.T) {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	a := setupDHT(ctx, t, false)
	b := setupDHT(ctx, t, true)

	connectNoSync(t, ctx, a, b)

931
	c := testCaseCids[0]
932
	p := peer.ID("TestPeer")
933 934
	a.providers.AddProvider(ctx, c, p)
	time.Sleep(time.Millisecond * 5) // just in case...
935

936
	provs, err := b.FindProviders(ctx, c)
937 938 939 940 941 942 943 944 945 946 947 948
	if err != nil {
		t.Fatal(err)
	}

	if len(provs) == 0 {
		t.Fatal("Expected to get a provider back")
	}

	if provs[0].ID != p {
		t.Fatal("expected it to be our test peer")
	}
}
949

950 951 952 953
func TestFindPeerQuery(t *testing.T) {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

Jeromy's avatar
Jeromy committed
954
	nDHTs := 101
955 956 957 958 959 960 961 962
	_, allpeers, dhts := setupDHTS(ctx, nDHTs, t)
	defer func() {
		for i := 0; i < nDHTs; i++ {
			dhts[i].Close()
			defer dhts[i].host.Close()
		}
	}()

Jeromy's avatar
Jeromy committed
963
	mrand := rand.New(rand.NewSource(42))
964 965 966
	guy := dhts[0]
	others := dhts[1:]
	for i := 0; i < 20; i++ {
Jeromy's avatar
Jeromy committed
967 968 969
		for j := 0; j < 16; j++ { // 16, high enough to probably not have any partitions
			v := mrand.Intn(80)
			connect(t, ctx, others[i], others[20+v])
970 971 972 973 974 975 976 977 978 979
		}
	}

	for i := 0; i < 20; i++ {
		connect(t, ctx, guy, others[i])
	}

	val := "foobar"
	rtval := kb.ConvertKey(val)

Jeromy's avatar
Jeromy committed
980 981 982
	rtablePeers := guy.routingTable.NearestPeers(rtval, AlphaValue)
	if len(rtablePeers) != 3 {
		t.Fatalf("expected 3 peers back from routing table, got %d", len(rtablePeers))
983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014
	}

	netpeers := guy.host.Network().Peers()
	if len(netpeers) != 20 {
		t.Fatalf("expected 20 peers to be connected, got %d", len(netpeers))
	}

	rtableset := make(map[peer.ID]bool)
	for _, p := range rtablePeers {
		rtableset[p] = true
	}

	out, err := guy.GetClosestPeers(ctx, val)
	if err != nil {
		t.Fatal(err)
	}

	var notfromrtable int
	var count int
	var outpeers []peer.ID
	for p := range out {
		count++
		if !rtableset[p] {
			notfromrtable++
		}
		outpeers = append(outpeers, p)
	}

	if notfromrtable == 0 {
		t.Fatal("got entirely peers from our routing table")
	}

Jeromy's avatar
Jeromy committed
1015 1016 1017 1018 1019 1020
	if count != 20 {
		t.Fatal("should have only gotten 20 peers from getclosestpeers call")
	}

	sort.Sort(peer.IDSlice(allpeers[1:]))
	sort.Sort(peer.IDSlice(outpeers))
1021 1022 1023 1024
	fmt.Println("counts: ", count, notfromrtable)
	actualclosest := kb.SortClosestPeers(allpeers[1:], rtval)
	exp := actualclosest[:20]
	got := kb.SortClosestPeers(outpeers, rtval)
Jeromy's avatar
Jeromy committed
1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041

	diffp := countDiffPeers(exp, got)
	if diffp > 0 {
		// could be a partition created during setup
		t.Fatal("didnt get expected closest peers")
	}
}

func countDiffPeers(a, b []peer.ID) int {
	s := make(map[peer.ID]bool)
	for _, p := range a {
		s[p] = true
	}
	var out int
	for _, p := range b {
		if !s[p] {
			out++
1042 1043
		}
	}
Jeromy's avatar
Jeromy committed
1044
	return out
1045 1046
}

1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078
func TestFindClosestPeers(t *testing.T) {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	nDHTs := 30
	_, _, dhts := setupDHTS(ctx, nDHTs, t)
	defer func() {
		for i := 0; i < nDHTs; i++ {
			dhts[i].Close()
			defer dhts[i].host.Close()
		}
	}()

	t.Logf("connecting %d dhts in a ring", nDHTs)
	for i := 0; i < nDHTs; i++ {
		connect(t, ctx, dhts[i], dhts[(i+1)%len(dhts)])
	}

	peers, err := dhts[1].GetClosestPeers(ctx, "foo")
	if err != nil {
		t.Fatal(err)
	}

	var out []peer.ID
	for p := range peers {
		out = append(out, p)
	}

	if len(out) != KValue {
		t.Fatalf("got wrong number of peers (got %d, expected %d)", len(out), KValue)
	}
}
1079 1080

func TestGetSetPluggedProtocol(t *testing.T) {
1081 1082 1083
	t.Run("PutValue/GetValue - same protocol", func(t *testing.T) {
		ctx, cancel := context.WithCancel(context.Background())
		defer cancel()
1084

1085 1086 1087 1088 1089
		os := []opts.Option{
			opts.Protocols("/esh/dht"),
			opts.Client(false),
			opts.NamespacedValidator("v", blankValidator{}),
		}
1090

1091 1092 1093 1094
		dhtA, err := New(ctx, bhost.New(netutil.GenSwarmNetwork(t, ctx)), os...)
		if err != nil {
			t.Fatal(err)
		}
1095

1096 1097 1098 1099
		dhtB, err := New(ctx, bhost.New(netutil.GenSwarmNetwork(t, ctx)), os...)
		if err != nil {
			t.Fatal(err)
		}
1100

1101
		connect(t, ctx, dhtA, dhtB)
1102

1103
		ctxT, cancel := context.WithTimeout(ctx, time.Second)
1104
		defer cancel()
1105 1106 1107
		if err := dhtA.PutValue(ctxT, "/v/cat", []byte("meow")); err != nil {
			t.Fatal(err)
		}
1108

1109 1110 1111 1112
		value, err := dhtB.GetValue(ctxT, "/v/cat")
		if err != nil {
			t.Fatal(err)
		}
1113

1114 1115 1116 1117 1118
		if string(value) != "meow" {
			t.Fatalf("Expected 'meow' got '%s'", string(value))
		}
	})

1119 1120
	t.Run("DHT routing table for peer A won't contain B if A and B don't use same protocol", func(t *testing.T) {
		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
1121 1122
		defer cancel()

1123
		dhtA, err := New(ctx, bhost.New(netutil.GenSwarmNetwork(t, ctx)), []opts.Option{
1124 1125 1126
			opts.Protocols("/esh/dht"),
			opts.Client(false),
			opts.NamespacedValidator("v", blankValidator{}),
1127
		}...)
1128 1129 1130 1131
		if err != nil {
			t.Fatal(err)
		}

1132
		dhtB, err := New(ctx, bhost.New(netutil.GenSwarmNetwork(t, ctx)), []opts.Option{
1133 1134 1135
			opts.Protocols("/lsr/dht"),
			opts.Client(false),
			opts.NamespacedValidator("v", blankValidator{}),
1136
		}...)
1137 1138 1139 1140
		if err != nil {
			t.Fatal(err)
		}

1141
		connectNoSync(t, ctx, dhtA, dhtB)
1142

1143 1144 1145 1146 1147 1148 1149 1150
		// We don't expect connection notifications for A to reach B (or vice-versa), given
		// that they've been configured with different protocols - but we'll give them a
		// chance, anyhow.
		time.Sleep(time.Second * 2)

		err = dhtA.PutValue(ctx, "/v/cat", []byte("meow"))
		if err == nil || !strings.Contains(err.Error(), "failed to find any peer in table") {
			t.Fatal("should not have been able to find any peers in routing table")
1151 1152
		}

1153 1154 1155 1156
		_, err = dhtB.GetValue(ctx, "/v/cat")
		if err == nil || !strings.Contains(err.Error(), "failed to find any peer in table") {
			t.Fatal("should not have been able to find any peers in routing table")
		}
1157
	})
1158
}