testbridge.go 3.47 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12
package testbridge

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io"

	cid "github.com/ipfs/go-cid"
	ipldbridge "github.com/ipfs/go-graphsync/ipldbridge"
	ipld "github.com/ipld/go-ipld-prime"
13 14 15
	"github.com/ipld/go-ipld-prime/encoding/dagjson"
	"github.com/ipld/go-ipld-prime/fluent"
	free "github.com/ipld/go-ipld-prime/impl/free"
16
	"github.com/ipld/go-ipld-prime/linking/cid"
17 18 19 20 21 22 23 24 25 26
	multihash "github.com/multiformats/go-multihash"
)

type mockIPLDBridge struct {
}

// NewMockIPLDBridge returns an IPLD bridge that works with MockSelectors
func NewMockIPLDBridge() ipldbridge.IPLDBridge {
	return &mockIPLDBridge{}
}
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41

func (mb *mockIPLDBridge) ExtractData(
	node ipld.Node,
	buildFn func(ipldbridge.SimpleNode) interface{}) (interface{}, error) {
	var value interface{}
	err := fluent.Recover(func() {
		simpleNode := fluent.WrapNode(node)
		value = buildFn(simpleNode)
	})
	if err != nil {
		return nil, err
	}
	return value, nil
}

42 43 44 45 46 47 48 49 50 51 52
func (mb *mockIPLDBridge) BuildNode(buildFn func(ipldbridge.NodeBuilder) ipld.Node) (ipld.Node, error) {
	var node ipld.Node
	err := fluent.Recover(func() {
		nb := fluent.WrapNodeBuilder(free.NodeBuilder())
		node = buildFn(nb)
	})
	if err != nil {
		return nil, err
	}
	return node, nil
}
53 54

func (mb *mockIPLDBridge) ValidateSelectorSpec(cidRootedSelector ipld.Node) []error {
55 56
	spec, ok := cidRootedSelector.(*mockSelectorSpec)
	if !ok || spec.failValidation {
57 58 59 60 61 62 63
		return []error{fmt.Errorf("not a selector")}
	}
	return nil
}

func (mb *mockIPLDBridge) EncodeNode(node ipld.Node) ([]byte, error) {
	spec, ok := node.(*mockSelectorSpec)
64 65 66 67 68 69 70
	if ok {
		if !spec.failEncode {
			data, err := json.Marshal(spec.cidsVisited)
			if err != nil {
				return nil, err
			}
			return data, nil
71
		}
72 73 74 75 76 77
		return nil, fmt.Errorf("format not supported")
	}
	var buffer bytes.Buffer
	err := dagjson.Encoder(node, &buffer)
	if err != nil {
		return nil, err
78
	}
79
	return buffer.Bytes(), nil
80 81 82 83 84 85
}

func (mb *mockIPLDBridge) DecodeNode(data []byte) (ipld.Node, error) {
	var cidsVisited []cid.Cid
	err := json.Unmarshal(data, &cidsVisited)
	if err == nil {
86
		return &mockSelectorSpec{cidsVisited, false, false}, nil
87
	}
88 89
	reader := bytes.NewReader(data)
	return dagjson.Decoder(free.NodeBuilder(), reader)
90 91 92 93
}

func (mb *mockIPLDBridge) DecodeSelectorSpec(cidRootedSelector ipld.Node) (ipld.Node, ipldbridge.Selector, error) {
	spec, ok := cidRootedSelector.(*mockSelectorSpec)
94
	if !ok || spec.failValidation {
95 96 97 98 99
		return nil, nil, fmt.Errorf("not a selector")
	}
	return nil, newMockSelector(spec), nil
}

100
func (mb *mockIPLDBridge) Traverse(ctx context.Context, loader ipldbridge.Loader, root ipld.Node, s ipldbridge.Selector, fn ipldbridge.AdvVisitFn) error {
101 102 103 104 105
	ms, ok := s.(*mockSelector)
	if !ok {
		return fmt.Errorf("not supported")
	}
	for _, lnk := range ms.cidsVisited {
106 107

		node, err := loadNode(lnk, loader)
108
		if err == nil {
109 110 111 112
			fn(ipldbridge.TraversalProgress{LastBlock: struct {
				ipld.Path
				ipld.Link
			}{ipld.Path{}, cidlink.Link{Cid: lnk}}}, node, 0)
113
		}
114 115
		select {
		case <-ctx.Done():
116
			return nil
117 118
		default:
		}
119
	}
120
	return nil
121
}
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139

func loadNode(lnk cid.Cid, loader ipldbridge.Loader) (ipld.Node, error) {
	r, err := loader(cidlink.Link{Cid: lnk}, ipldbridge.LinkContext{})
	if err != nil {
		return nil, err
	}
	var buffer bytes.Buffer
	io.Copy(&buffer, r)
	data := buffer.Bytes()
	hash, err := multihash.Sum(data, lnk.Prefix().MhType, lnk.Prefix().MhLength)
	if err != nil {
		return nil, err
	}
	if hash.B58String() != lnk.Hash().B58String() {
		return nil, fmt.Errorf("hash mismatch")
	}
	return NewMockBlockNode(data), nil
}