merkledag_test.go 13.3 KB
Newer Older
1
package merkledag_test
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
2 3

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

16
	bserv "github.com/ipfs/go-ipfs/blockservice"
17
	bstest "github.com/ipfs/go-ipfs/blockservice/test"
18 19
	offline "github.com/ipfs/go-ipfs/exchange/offline"
	. "github.com/ipfs/go-ipfs/merkledag"
20
	mdpb "github.com/ipfs/go-ipfs/merkledag/pb"
21
	dstest "github.com/ipfs/go-ipfs/merkledag/test"
Jeromy's avatar
Jeromy committed
22

Steven Allen's avatar
Steven Allen committed
23 24
	u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util"
	cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid"
25
	ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format"
26
	blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
27 28 29 30
)

func TestNode(t *testing.T) {

31 32 33
	n1 := NodeWithData([]byte("beep"))
	n2 := NodeWithData([]byte("boop"))
	n3 := NodeWithData([]byte("beep boop"))
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
34 35 36 37 38 39 40
	if err := n3.AddNodeLink("beep-link", n1); err != nil {
		t.Error(err)
	}
	if err := n3.AddNodeLink("boop-link", n2); err != nil {
		t.Error(err)
	}

41
	printn := func(name string, n *ProtoNode) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
42
		fmt.Println(">", name)
43
		fmt.Println("data:", string(n.Data()))
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
44 45

		fmt.Println("links:")
46 47
		for _, l := range n.Links() {
			fmt.Println("-", l.Name, l.Size, l.Cid)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
48 49
		}

50
		e, err := n.EncodeProtobuf(false)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
51 52 53 54 55 56
		if err != nil {
			t.Error(err)
		} else {
			fmt.Println("encoded:", e)
		}

Jeromy's avatar
Jeromy committed
57
		h := n.Multihash()
Jeromy's avatar
Jeromy committed
58 59
		k := n.Cid().Hash()
		if k.String() != h.String() {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
60 61 62 63
			t.Error("Key is not equivalent to multihash")
		} else {
			fmt.Println("key: ", k)
		}
64 65

		SubtestNodeStat(t, n)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
66 67 68 69 70 71
	}

	printn("beep", n1)
	printn("boop", n2)
	printn("beep boop", n3)
}
72

73
func SubtestNodeStat(t *testing.T, n *ProtoNode) {
74
	enc, err := n.EncodeProtobuf(true)
75
	if err != nil {
76
		t.Error("n.EncodeProtobuf(true) failed")
77 78 79 80 81 82 83 84 85
		return
	}

	cumSize, err := n.Size()
	if err != nil {
		t.Error("n.Size() failed")
		return
	}

Jeromy's avatar
Jeromy committed
86
	k := n.Cid()
87

88
	expected := ipld.NodeStat{
89
		NumLinks:       len(n.Links()),
90
		BlockSize:      len(enc),
91 92
		LinksSize:      len(enc) - len(n.Data()), // includes framing.
		DataSize:       len(n.Data()),
93
		CumulativeSize: int(cumSize),
Jeromy's avatar
Jeromy committed
94
		Hash:           k.String(),
95 96 97 98 99 100 101 102
	}

	actual, err := n.Stat()
	if err != nil {
		t.Error("n.Stat() failed")
		return
	}

103
	if expected != *actual {
104
		t.Errorf("n.Stat incorrect.\nexpect: %s\nactual: %s", expected, actual)
105 106 107 108 109
	} else {
		fmt.Printf("n.Stat correct: %s\n", actual)
	}
}

110 111 112
type devZero struct{}

func (_ devZero) Read(b []byte) (int, error) {
rht's avatar
rht committed
113
	for i := range b {
114 115 116 117 118
		b[i] = 0
	}
	return len(b), nil
}

119
func TestBatchFetch(t *testing.T) {
Jeromy's avatar
Jeromy committed
120 121
	read := io.LimitReader(u.NewTimeSeededRand(), 1024*32)
	runBatchFetchTest(t, read)
122
}
123 124

func TestBatchFetchDupBlock(t *testing.T) {
Jeromy's avatar
Jeromy committed
125 126
	read := io.LimitReader(devZero{}, 1024*32)
	runBatchFetchTest(t, read)
127 128
}

129 130 131 132 133 134 135
// makeTestDAG creates a simple DAG from the data in a reader.
// First, a node is created from each 512 bytes of data from the reader
// (like a the Size chunker would do). Then all nodes are added as children
// to a root node, which is returned.
func makeTestDAG(t *testing.T, read io.Reader, ds ipld.DAGService) ipld.Node {
	p := make([]byte, 512)
	nodes := []*ProtoNode{}
Hector Sanjuan's avatar
Hector Sanjuan committed
136
	var err error
137 138 139 140
	n, err = io.ReadFull(read, p)
	if n != len(p) {
		t.Fatal("should have read 512 bytes from the reader")
	}
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
	for err == nil {
		protoNode := NodeWithData(p)
		nodes = append(nodes, protoNode)
		_, err = read.Read(p)
	}

	if err != io.EOF {
		t.Fatal(err)
	}

	ctx := context.Background()
	// Add a root referencing all created nodes
	root := NodeWithData(nil)
	for _, n := range nodes {
		root.AddNodeLink(n.Cid().String(), n)
		err := ds.Add(ctx, n)
		if err != nil {
			t.Fatal(err)
		}
	}
	err = ds.Add(ctx, root)
	if err != nil {
		t.Fatal(err)
	}
	return root
}

// makeTestDAGReader takes the root node as returned by makeTestDAG and
// provides a reader that reads all the RawData from that node and its children.
func makeTestDAGReader(t *testing.T, root ipld.Node, ds ipld.DAGService) io.Reader {
	ctx := context.Background()
	buf := new(bytes.Buffer)
	buf.Write(root.RawData())
	for _, l := range root.Links() {
		n, err := ds.Get(ctx, l.Cid)
		if err != nil {
			t.Fatal(err)
		}
		_, err = buf.Write(n.RawData())
		if err != nil {
			t.Fatal(err)
		}
	}
	return buf
}

Jeromy's avatar
Jeromy committed
187
func runBatchFetchTest(t *testing.T, read io.Reader) {
188
	ctx := context.Background()
189
	var dagservs []ipld.DAGService
190
	for _, bsi := range bstest.Mocks(5) {
191 192
		dagservs = append(dagservs, NewDAGService(bsi))
	}
Jeromy's avatar
Jeromy committed
193

194
	root := makeTestDAG(t, read, dagservs[0])
Jeromy's avatar
Jeromy committed
195

196 197
	t.Log("finished setup.")

198
	dagr := makeTestDAGReader(t, root, dagservs[0])
Jeromy's avatar
Jeromy committed
199 200

	expected, err := ioutil.ReadAll(dagr)
201 202 203 204
	if err != nil {
		t.Fatal(err)
	}

205
	err = dagservs[0].Add(ctx, root)
206 207 208 209 210 211
	if err != nil {
		t.Fatal(err)
	}

	t.Log("Added file to first node.")

Jeromy's avatar
Jeromy committed
212
	c := root.Cid()
213

214
	wg := sync.WaitGroup{}
215 216
	errs := make(chan error)

217
	for i := 1; i < len(dagservs); i++ {
218
		wg.Add(1)
219
		go func(i int) {
220
			defer wg.Done()
Jeromy's avatar
Jeromy committed
221
			first, err := dagservs[i].Get(ctx, c)
222
			if err != nil {
223
				errs <- err
224 225 226
			}
			fmt.Println("Got first node back.")

227 228 229 230
			firstpb, ok := first.(*ProtoNode)
			if !ok {
				errs <- ErrNotProtobuf
			}
231
			read := makeTestDAGReader(t, firstpb, dagservs[i])
232 233
			datagot, err := ioutil.ReadAll(read)
			if err != nil {
234
				errs <- err
235 236 237
			}

			if !bytes.Equal(datagot, expected) {
238
				errs <- errors.New("Got bad data back!")
239 240 241 242
			}
		}(i)
	}

243 244 245 246 247 248 249 250 251 252
	go func() {
		wg.Wait()
		close(errs)
	}()

	for err := range errs {
		if err != nil {
			t.Fatal(err)
		}
	}
253
}
254 255

func TestCantGet(t *testing.T) {
256
	ds := dstest.Mock()
257
	a := NodeWithData([]byte("A"))
258

Jeromy's avatar
Jeromy committed
259 260
	c := a.Cid()
	_, err := ds.Get(context.Background(), c)
261 262 263 264
	if !strings.Contains(err.Error(), "not found") {
		t.Fatal("expected err not found, got: ", err)
	}
}
265 266

func TestFetchGraph(t *testing.T) {
267
	var dservs []ipld.DAGService
268
	bsis := bstest.Mocks(2)
Jeromy's avatar
Jeromy committed
269 270 271
	for _, bsi := range bsis {
		dservs = append(dservs, NewDAGService(bsi))
	}
272 273

	read := io.LimitReader(u.NewTimeSeededRand(), 1024*32)
274
	root := makeTestDAG(t, read, dservs[0])
275

276
	err := FetchGraph(context.TODO(), root.Cid(), dservs[1])
277 278 279 280
	if err != nil {
		t.Fatal(err)
	}

Jeromy's avatar
Jeromy committed
281
	// create an offline dagstore and ensure all blocks were fetched
282
	bs := bserv.New(bsis[1].Blockstore(), offline.Exchange(bsis[1].Blockstore()))
283

284
	offlineDS := NewDAGService(bs)
Jeromy's avatar
Jeromy committed
285

286
	err = EnumerateChildren(context.Background(), offlineDS.GetLinks, root.Cid(), func(_ *cid.Cid) bool { return true })
287 288 289 290 291 292
	if err != nil {
		t.Fatal(err)
	}
}

func TestEnumerateChildren(t *testing.T) {
293
	bsi := bstest.Mocks(1)
294 295 296
	ds := NewDAGService(bsi[0])

	read := io.LimitReader(u.NewTimeSeededRand(), 1024*1024)
297
	root := makeTestDAG(t, read, ds)
298

Jeromy's avatar
Jeromy committed
299
	set := cid.NewSet()
300

301
	err := EnumerateChildren(context.Background(), ds.GetLinks, root.Cid(), set.Visit)
302 303 304 305
	if err != nil {
		t.Fatal(err)
	}

306 307
	var traverse func(n ipld.Node)
	traverse = func(n ipld.Node) {
308
		// traverse dag and check
309 310
		for _, lnk := range n.Links() {
			c := lnk.Cid
Jeromy's avatar
Jeromy committed
311
			if !set.Has(c) {
312
				t.Fatal("missing key in set! ", lnk.Cid.String())
313
			}
Jeromy's avatar
Jeromy committed
314
			child, err := ds.Get(context.Background(), c)
315 316 317 318 319 320 321 322 323
			if err != nil {
				t.Fatal(err)
			}
			traverse(child)
		}
	}

	traverse(root)
}
324 325

func TestFetchFailure(t *testing.T) {
326 327
	ctx := context.Background()

328 329 330
	ds := dstest.Mock()
	ds_bad := dstest.Mock()

331
	top := new(ProtoNode)
332
	for i := 0; i < 10; i++ {
333
		nd := NodeWithData([]byte{byte('a' + i)})
334
		err := ds.Add(ctx, nd)
335 336 337 338 339 340 341 342 343 344 345
		if err != nil {
			t.Fatal(err)
		}

		err = top.AddNodeLinkClean(fmt.Sprintf("AA%d", i), nd)
		if err != nil {
			t.Fatal(err)
		}
	}

	for i := 0; i < 10; i++ {
346
		nd := NodeWithData([]byte{'f', 'a' + byte(i)})
347
		err := ds_bad.Add(ctx, nd)
348 349 350 351 352 353 354 355 356 357
		if err != nil {
			t.Fatal(err)
		}

		err = top.AddNodeLinkClean(fmt.Sprintf("BB%d", i), nd)
		if err != nil {
			t.Fatal(err)
		}
	}

358
	getters := ipld.GetDAG(ctx, ds, top)
359
	for i, getter := range getters {
360
		_, err := getter.Get(ctx)
361 362 363 364 365 366 367 368
		if err != nil && i < 10 {
			t.Fatal(err)
		}
		if err == nil && i >= 10 {
			t.Fatal("should have failed request")
		}
	}
}
369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389

func TestUnmarshalFailure(t *testing.T) {
	badData := []byte("hello world")

	_, err := DecodeProtobuf(badData)
	if err == nil {
		t.Fatal("shouldnt succeed to parse this")
	}

	// now with a bad link
	pbn := &mdpb.PBNode{Links: []*mdpb.PBLink{{Hash: []byte("not a multihash")}}}
	badlink, err := pbn.Marshal()
	if err != nil {
		t.Fatal(err)
	}

	_, err = DecodeProtobuf(badlink)
	if err == nil {
		t.Fatal("should have failed to parse node with bad link")
	}

390
	n := &ProtoNode{}
391 392
	n.Marshal()
}
Jeromy's avatar
Jeromy committed
393 394

func TestBasicAddGet(t *testing.T) {
395 396
	ctx := context.Background()

Jeromy's avatar
Jeromy committed
397
	ds := dstest.Mock()
398
	nd := new(ProtoNode)
Jeromy's avatar
Jeromy committed
399

400
	err := ds.Add(ctx, nd)
Jeromy's avatar
Jeromy committed
401 402 403 404
	if err != nil {
		t.Fatal(err)
	}

405
	out, err := ds.Get(ctx, nd.Cid())
Jeromy's avatar
Jeromy committed
406 407 408 409 410 411 412 413
	if err != nil {
		t.Fatal(err)
	}

	if !nd.Cid().Equals(out.Cid()) {
		t.Fatal("output didnt match input")
	}
}
414 415

func TestGetRawNodes(t *testing.T) {
416 417
	ctx := context.Background()

418 419 420 421
	rn := NewRawNode([]byte("test"))

	ds := dstest.Mock()

422
	err := ds.Add(ctx, rn)
423 424 425 426
	if err != nil {
		t.Fatal(err)
	}

427
	if !rn.Cid().Equals(rn.Cid()) {
428 429 430
		t.Fatal("output cids didnt match")
	}

431
	out, err := ds.Get(ctx, rn.Cid())
432 433 434 435 436 437 438 439 440 441 442 443
	if err != nil {
		t.Fatal(err)
	}

	if !bytes.Equal(out.RawData(), []byte("test")) {
		t.Fatal("raw block should match input data")
	}

	if out.Links() != nil {
		t.Fatal("raw blocks shouldnt have links")
	}

444
	if out.Tree("", -1) != nil {
445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473
		t.Fatal("tree should return no paths in a raw block")
	}

	size, err := out.Size()
	if err != nil {
		t.Fatal(err)
	}
	if size != 4 {
		t.Fatal("expected size to be 4")
	}

	ns, err := out.Stat()
	if err != nil {
		t.Fatal(err)
	}

	if ns.DataSize != 4 {
		t.Fatal("expected size to be 4, got: ", ns.DataSize)
	}

	_, _, err = out.Resolve([]string{"foo"})
	if err != ErrLinkNotFound {
		t.Fatal("shouldnt find links under raw blocks")
	}
}

func TestProtoNodeResolve(t *testing.T) {

	nd := new(ProtoNode)
474
	nd.SetLinks([]*ipld.Link{{Name: "foo"}})
475

476
	lnk, left, err := nd.ResolveLink([]string{"foo", "bar"})
477 478 479 480 481 482 483 484 485 486 487 488
	if err != nil {
		t.Fatal(err)
	}

	if len(left) != 1 || left[0] != "bar" {
		t.Fatal("expected the single path element 'bar' to remain")
	}

	if lnk.Name != "foo" {
		t.Fatal("how did we get anything else?")
	}

489
	tvals := nd.Tree("", -1)
490 491 492 493
	if len(tvals) != 1 || tvals[0] != "foo" {
		t.Fatal("expected tree to return []{\"foo\"}")
	}
}
494 495

func TestCidRetention(t *testing.T) {
496 497
	ctx := context.Background()

498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514
	nd := new(ProtoNode)
	nd.SetData([]byte("fooooo"))

	pref := nd.Cid().Prefix()
	pref.Version = 1

	c2, err := pref.Sum(nd.RawData())
	if err != nil {
		t.Fatal(err)
	}

	blk, err := blocks.NewBlockWithCid(nd.RawData(), c2)
	if err != nil {
		t.Fatal(err)
	}

	bs := dstest.Bserv()
515
	err = bs.AddBlock(blk)
516 517 518 519 520
	if err != nil {
		t.Fatal(err)
	}

	ds := NewDAGService(bs)
521
	out, err := ds.Get(ctx, c2)
522 523 524 525 526 527 528 529
	if err != nil {
		t.Fatal(err)
	}

	if !out.Cid().Equals(c2) {
		t.Fatal("output cid didnt match")
	}
}
530 531

func TestCidRawDoesnNeedData(t *testing.T) {
532
	srv := NewDAGService(dstest.Bserv())
533 534 535 536 537 538 539 540 541 542 543 544 545 546 547
	nd := NewRawNode([]byte("somedata"))

	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	// there is no data for this node in the blockservice
	// so dag service can't load it
	links, err := srv.GetLinks(ctx, nd.Cid())
	if err != nil {
		t.Fatal(err)
	}
	if len(links) != 0 {
		t.Fatal("raw node shouldn't have any links")
	}
}
548 549

func TestEnumerateAsyncFailsNotFound(t *testing.T) {
550 551
	ctx := context.Background()

552 553 554 555 556 557
	a := NodeWithData([]byte("foo1"))
	b := NodeWithData([]byte("foo2"))
	c := NodeWithData([]byte("foo3"))
	d := NodeWithData([]byte("foo4"))

	ds := dstest.Mock()
558
	for _, n := range []ipld.Node{a, b, c} {
559
		err := ds.Add(ctx, n)
560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581
		if err != nil {
			t.Fatal(err)
		}
	}

	parent := new(ProtoNode)
	if err := parent.AddNodeLinkClean("a", a); err != nil {
		t.Fatal(err)
	}

	if err := parent.AddNodeLinkClean("b", b); err != nil {
		t.Fatal(err)
	}

	if err := parent.AddNodeLinkClean("c", c); err != nil {
		t.Fatal(err)
	}

	if err := parent.AddNodeLinkClean("d", d); err != nil {
		t.Fatal(err)
	}

582
	err := ds.Add(ctx, parent)
583 584 585 586 587
	if err != nil {
		t.Fatal(err)
	}

	cset := cid.NewSet()
588
	err = EnumerateChildrenAsync(ctx, GetLinksDirect(ds), parent.Cid(), cset.Visit)
589 590 591 592
	if err == nil {
		t.Fatal("this should have failed")
	}
}
593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620

func TestProgressIndicator(t *testing.T) {
	testProgressIndicator(t, 5)
}

func TestProgressIndicatorNoChildren(t *testing.T) {
	testProgressIndicator(t, 0)
}

func testProgressIndicator(t *testing.T, depth int) {
	ds := dstest.Mock()

	top, numChildren := mkDag(ds, depth)

	v := new(ProgressTracker)
	ctx := v.DeriveContext(context.Background())

	err := FetchGraph(ctx, top, ds)
	if err != nil {
		t.Fatal(err)
	}

	if v.Value() != numChildren+1 {
		t.Errorf("wrong number of children reported in progress indicator, expected %d, got %d",
			numChildren+1, v.Value())
	}
}

621
func mkDag(ds ipld.DAGService, depth int) (*cid.Cid, int) {
622 623
	ctx := context.Background()

624 625 626 627 628 629 630
	totalChildren := 0
	f := func() *ProtoNode {
		p := new(ProtoNode)
		buf := make([]byte, 16)
		rand.Read(buf)

		p.SetData(buf)
631
		err := ds.Add(ctx, p)
632 633 634 635 636 637 638 639 640 641
		if err != nil {
			panic(err)
		}
		return p
	}

	for i := 0; i < depth; i++ {
		thisf := f
		f = func() *ProtoNode {
			pn := mkNodeWithChildren(thisf, 10)
642
			err := ds.Add(ctx, pn)
643 644 645 646 647 648 649 650 651
			if err != nil {
				panic(err)
			}
			totalChildren += 10
			return pn
		}
	}

	nd := f()
652
	err := ds.Add(ctx, nd)
653 654 655 656
	if err != nil {
		panic(err)
	}

657
	return nd.Cid(), totalChildren
658 659 660 661 662 663 664 665 666 667 668 669 670 671
}

func mkNodeWithChildren(getChild func() *ProtoNode, width int) *ProtoNode {
	cur := new(ProtoNode)

	for i := 0; i < width; i++ {
		c := getChild()
		if err := cur.AddNodeLinkClean(fmt.Sprint(i), c); err != nil {
			panic(err)
		}
	}

	return cur
}