merkledag.go 7.15 KB
Newer Older
1
// package merkledag implements the ipfs Merkle DAG datastructures.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
2 3 4
package merkledag

import (
5
	"fmt"
Jeromy's avatar
Jeromy committed
6
	"sync"
Jeromy's avatar
Jeromy committed
7 8
	"time"

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
9
	"github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
Chas Leichner's avatar
Chas Leichner committed
10

11
	mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash"
12
	blocks "github.com/jbenet/go-ipfs/blocks"
13
	bserv "github.com/jbenet/go-ipfs/blockservice"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
14
	u "github.com/jbenet/go-ipfs/util"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
15 16
)

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
17
var log = u.Logger("merkledag")
18
var ErrNotFound = fmt.Errorf("merkledag: not found")
Jeromy's avatar
Jeromy committed
19

Juan Batiz-Benet's avatar
go lint  
Juan Batiz-Benet committed
20 21 22
// NodeMap maps u.Keys to Nodes.
// We cannot use []byte/Multihash for keys :(
// so have to convert Multihash bytes to string (u.Key)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
23 24
type NodeMap map[u.Key]*Node

Jeromy's avatar
Jeromy committed
25 26 27 28 29 30 31 32 33 34 35 36 37
// DAGService is an IPFS Merkle DAG service.
type DAGService interface {
	Add(*Node) (u.Key, error)
	AddRecursive(*Node) error
	Get(u.Key) (*Node, error)
	Remove(*Node) error
	BatchFetch(context.Context, *Node) <-chan *Node
}

func NewDAGService(bs *bserv.BlockService) DAGService {
	return &dagService{bs}
}

Juan Batiz-Benet's avatar
go lint  
Juan Batiz-Benet committed
38
// Node represents a node in the IPFS Merkle DAG.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
39
// nodes have opaque data and a set of navigable links.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
40
type Node struct {
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
41 42
	Links []*Link
	Data  []byte
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
43 44 45

	// cache encoded/marshaled value
	encoded []byte
46 47

	cached mh.Multihash
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
48 49
}

Juan Batiz-Benet's avatar
go lint  
Juan Batiz-Benet committed
50
// Link represents an IPFS Merkle DAG Link between Nodes.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
51
type Link struct {
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
52 53
	// utf string name. should be unique per object
	Name string // utf8
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
54

Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
55 56
	// cumulative size of target object
	Size uint64
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
57

Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
58 59
	// multihash of the target object
	Hash mh.Multihash
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
60 61 62

	// a ptr to the actual node for graph manipulation
	Node *Node
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
63 64
}

Jeromy's avatar
Jeromy committed
65
// MakeLink creates a link to the given node
Jeromy's avatar
Jeromy committed
66 67
func MakeLink(n *Node) (*Link, error) {
	s, err := n.Size()
68
	if err != nil {
Jeromy's avatar
Jeromy committed
69
		return nil, err
70 71
	}

Jeromy's avatar
Jeromy committed
72
	h, err := n.Multihash()
73
	if err != nil {
Jeromy's avatar
Jeromy committed
74
		return nil, err
75
	}
Jeromy's avatar
Jeromy committed
76
	return &Link{
77 78
		Size: s,
		Hash: h,
Jeromy's avatar
Jeromy committed
79
	}, nil
80 81
}

Jeromy's avatar
Jeromy committed
82
// GetNode returns the MDAG Node that this link points to
83
func (l *Link) GetNode(serv DAGService) (*Node, error) {
84 85 86 87 88 89 90
	if l.Node != nil {
		return l.Node, nil
	}

	return serv.Get(u.Key(l.Hash))
}

Jeromy's avatar
Jeromy committed
91 92 93
// AddNodeLink adds a link to another node.
func (n *Node) AddNodeLink(name string, that *Node) error {
	lnk, err := MakeLink(that)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
94 95 96
	if err != nil {
		return err
	}
Jeromy's avatar
Jeromy committed
97 98 99 100 101 102
	lnk.Name = name
	lnk.Node = that

	n.Links = append(n.Links, lnk)
	return nil
}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
103

Jeromy's avatar
Jeromy committed
104 105 106 107
// AddNodeLink adds a link to another node. without keeping a reference to
// the child node
func (n *Node) AddNodeLinkClean(name string, that *Node) error {
	lnk, err := MakeLink(that)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
108 109 110
	if err != nil {
		return err
	}
Jeromy's avatar
Jeromy committed
111
	lnk.Name = name
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
112

Jeromy's avatar
Jeromy committed
113
	n.Links = append(n.Links, lnk)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
114 115
	return nil
}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
116

Jeromy's avatar
Jeromy committed
117
// Remove a link on this node by the given name
118 119 120 121 122 123 124
func (n *Node) RemoveNodeLink(name string) error {
	for i, l := range n.Links {
		if l.Name == name {
			n.Links = append(n.Links[:i], n.Links[i+1:]...)
			return nil
		}
	}
125
	return ErrNotFound
126 127
}

128 129
// Copy returns a copy of the node.
// NOTE: does not make copies of Node objects in the links.
Jeromy's avatar
Jeromy committed
130 131 132 133 134 135 136 137 138 139
func (n *Node) Copy() *Node {
	nnode := new(Node)
	nnode.Data = make([]byte, len(n.Data))
	copy(nnode.Data, n.Data)

	nnode.Links = make([]*Link, len(n.Links))
	copy(nnode.Links, n.Links)
	return nnode
}

Juan Batiz-Benet's avatar
go lint  
Juan Batiz-Benet committed
140 141
// Size returns the total size of the data addressed by node,
// including the total sizes of references.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
142
func (n *Node) Size() (uint64, error) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
143
	b, err := n.Encoded(false)
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
144 145 146 147
	if err != nil {
		return 0, err
	}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
148
	s := uint64(len(b))
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
149 150 151 152
	for _, l := range n.Links {
		s += l.Size
	}
	return s, nil
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
153
}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
154

Juan Batiz-Benet's avatar
go lint  
Juan Batiz-Benet committed
155
// Multihash hashes the encoded data of this node.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
156
func (n *Node) Multihash() (mh.Multihash, error) {
157
	// Note: Encoded generates the hash and puts it in n.cached.
158
	_, err := n.Encoded(false)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
159 160 161 162
	if err != nil {
		return nil, err
	}

163
	return n.cached, nil
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
164
}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
165

Juan Batiz-Benet's avatar
go lint  
Juan Batiz-Benet committed
166
// Key returns the Multihash as a key, for maps.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
167 168 169 170
func (n *Node) Key() (u.Key, error) {
	h, err := n.Multihash()
	return u.Key(h), err
}
171

172
// dagService is an IPFS Merkle DAG service.
Juan Batiz-Benet's avatar
go lint  
Juan Batiz-Benet committed
173 174
// - the root is virtual (like a forest)
// - stores nodes' data in a BlockService
175 176
// TODO: should cache Nodes that are in memory, and be
//       able to free some of them when vm pressure is high
177
type dagService struct {
178
	Blocks *bserv.BlockService
179 180
}

181 182
// Add adds a node to the dagService, storing the block in the BlockService
func (n *dagService) Add(nd *Node) (u.Key, error) {
183
	k, _ := nd.Key()
184
	log.Debugf("DagService Add [%s]", k)
185
	if n == nil {
186
		return "", fmt.Errorf("dagService is nil")
187 188 189 190 191 192 193
	}

	d, err := nd.Encoded(false)
	if err != nil {
		return "", err
	}

194 195 196
	b := new(blocks.Block)
	b.Data = d
	b.Multihash, err = nd.Multihash()
197 198 199 200 201 202 203
	if err != nil {
		return "", err
	}

	return n.Blocks.AddBlock(b)
}

Jeromy's avatar
Jeromy committed
204
// AddRecursive adds the given node and all child nodes to the BlockService
205
func (n *dagService) AddRecursive(nd *Node) error {
206 207
	_, err := n.Add(nd)
	if err != nil {
Jeromy's avatar
Jeromy committed
208
		log.Info("AddRecursive Error: %s\n", err)
209 210 211 212
		return err
	}

	for _, link := range nd.Links {
213 214 215 216 217
		if link.Node != nil {
			err := n.AddRecursive(link.Node)
			if err != nil {
				return err
			}
218 219 220 221 222 223
		}
	}

	return nil
}

224 225
// Get retrieves a node from the dagService, fetching the block in the BlockService
func (n *dagService) Get(k u.Key) (*Node, error) {
226
	if n == nil {
227
		return nil, fmt.Errorf("dagService is nil")
228 229
	}

Jeromy's avatar
Jeromy committed
230 231
	ctx, _ := context.WithTimeout(context.TODO(), time.Second*5)
	b, err := n.Blocks.GetBlock(ctx, k)
232 233 234 235 236 237
	if err != nil {
		return nil, err
	}

	return Decoded(b.Data)
}
Jeromy's avatar
Jeromy committed
238

Jeromy's avatar
Jeromy committed
239
// Remove deletes the given node and all of its children from the BlockService
240
func (n *dagService) Remove(nd *Node) error {
Jeromy's avatar
Jeromy committed
241 242 243 244 245 246 247 248 249 250 251
	for _, l := range nd.Links {
		if l.Node != nil {
			n.Remove(l.Node)
		}
	}
	k, err := nd.Key()
	if err != nil {
		return err
	}
	return n.Blocks.DeleteBlock(k)
}
Jeromy's avatar
Jeromy committed
252

Jeromy's avatar
Jeromy committed
253 254
// FetchGraph asynchronously fetches all nodes that are children of the given
// node, and returns a channel that may be waited upon for the fetch to complete
Jeromy's avatar
Jeromy committed
255
func FetchGraph(ctx context.Context, root *Node, serv DAGService) chan struct{} {
256
	log.Warning("Untested.")
Jeromy's avatar
Jeromy committed
257 258 259
	var wg sync.WaitGroup
	done := make(chan struct{})

Jeromy's avatar
Jeromy committed
260
	for _, l := range root.Links {
Jeromy's avatar
Jeromy committed
261
		wg.Add(1)
Jeromy's avatar
Jeromy committed
262
		go func(lnk *Link) {
Jeromy's avatar
Jeromy committed
263 264 265

			// Signal child is done on way out
			defer wg.Done()
Jeromy's avatar
Jeromy committed
266 267 268 269 270 271 272 273 274 275
			select {
			case <-ctx.Done():
				return
			}

			nd, err := lnk.GetNode(serv)
			if err != nil {
				log.Error(err)
				return
			}
Jeromy's avatar
Jeromy committed
276 277 278

			// Wait for children to finish
			<-FetchGraph(ctx, nd, serv)
Jeromy's avatar
Jeromy committed
279 280
		}(l)
	}
Jeromy's avatar
Jeromy committed
281 282 283 284 285 286 287

	go func() {
		wg.Wait()
		done <- struct{}{}
	}()

	return done
Jeromy's avatar
Jeromy committed
288
}
289

Jeromy's avatar
Jeromy committed
290 291 292 293 294 295
// BatchFetch will fill out all of the links of the given Node.
// It returns a channel of indicies, which will be returned in order
// from 0 to len(root.Links) - 1, signalling that the link specified by
// the index has been filled out.
func (ds *dagService) BatchFetch(ctx context.Context, root *Node) <-chan *Node {
	sig := make(chan *Node)
296 297
	go func() {
		var keys []u.Key
Jeromy's avatar
Jeromy committed
298
		nodes := make([]*Node, len(root.Links))
299 300 301 302 303 304

		//
		next := 0
		seen := make(map[int]struct{})
		//

Jeromy's avatar
Jeromy committed
305 306 307 308 309 310
		for _, lnk := range root.Links {
			keys = append(keys, u.Key(lnk.Hash))
		}

		blkchan := ds.Blocks.GetBlocks(ctx, keys)

311 312
		for blk := range blkchan {
			for i, lnk := range root.Links {
Jeromy's avatar
Jeromy committed
313 314 315
				if u.Key(lnk.Hash) != blk.Key() {
					continue
				}
316 317 318 319 320 321 322 323 324 325

				//
				seen[i] = struct{}{}
				//

				nd, err := Decoded(blk.Data)
				if err != nil {
					log.Error("Got back bad block!")
					break
				}
Jeromy's avatar
Jeromy committed
326
				nodes[i] = nd
327 328

				if next == i {
Jeromy's avatar
Jeromy committed
329
					sig <- nd
330
					next++
Jeromy's avatar
Jeromy committed
331 332
					for ; nodes[next] != nil; next++ {
						sig <- nodes[next]
333 334 335 336
					}
				}
			}
		}
Jeromy's avatar
Jeromy committed
337
		close(sig)
338
	}()
339

340
	return sig
341
}