merkledag.go 7.81 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
// 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
31 32 33 34

	// GetDAG returns, in order, all the single leve child
	// nodes of the passed in node.
	GetDAG(context.Context, *Node) <-chan *Node
Jeromy's avatar
Jeromy committed
35 36 37 38 39 40
}

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

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

	// cache encoded/marshaled value
	encoded []byte
49 50

	cached mh.Multihash
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
51 52
}

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

Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
58 59
	// cumulative size of target object
	Size uint64
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
60

Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
61 62
	// multihash of the target object
	Hash mh.Multihash
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
63 64 65

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

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

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

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

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

Jeromy's avatar
Jeromy committed
94 95 96
// 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
97 98 99
	if err != nil {
		return err
	}
Jeromy's avatar
Jeromy committed
100 101 102 103 104 105
	lnk.Name = name
	lnk.Node = that

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

Jeromy's avatar
Jeromy committed
107 108 109 110
// 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
111 112 113
	if err != nil {
		return err
	}
Jeromy's avatar
Jeromy committed
114
	lnk.Name = name
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
115

Jeromy's avatar
Jeromy committed
116
	n.Links = append(n.Links, lnk)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
117 118
	return nil
}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
119

Jeromy's avatar
Jeromy committed
120
// Remove a link on this node by the given name
121 122 123 124 125 126 127
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
		}
	}
128
	return ErrNotFound
129 130
}

131 132
// Copy returns a copy of the node.
// NOTE: does not make copies of Node objects in the links.
Jeromy's avatar
Jeromy committed
133 134 135 136 137 138 139 140 141 142
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
143 144
// 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
145
func (n *Node) Size() (uint64, error) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
146
	b, err := n.Encoded(false)
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
147 148 149 150
	if err != nil {
		return 0, err
	}

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

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

166
	return n.cached, nil
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
167
}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
168

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

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

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

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

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

	return n.Blocks.AddBlock(b)
}

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

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

	return nil
}

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

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

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

Jeromy's avatar
Jeromy committed
242
// Remove deletes the given node and all of its children from the BlockService
243
func (n *dagService) Remove(nd *Node) error {
Jeromy's avatar
Jeromy committed
244 245 246 247 248 249 250 251 252 253 254
	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
255

Jeromy's avatar
Jeromy committed
256 257
// 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
258
func FetchGraph(ctx context.Context, root *Node, serv DAGService) chan struct{} {
259
	log.Warning("Untested.")
Jeromy's avatar
Jeromy committed
260 261 262
	var wg sync.WaitGroup
	done := make(chan struct{})

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

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

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

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

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

	return done
Jeromy's avatar
Jeromy committed
291
}
292

Jeromy's avatar
Jeromy committed
293 294 295 296 297 298 299 300 301 302 303
// Searches this nodes links for one to the given key,
// returns the index of said link
func FindLink(n *Node, k u.Key, found []*Node) (int, error) {
	for i, lnk := range n.Links {
		if u.Key(lnk.Hash) == k && found[i] == nil {
			return i, nil
		}
	}
	return -1, u.ErrNotFound
}

304
// GetDAG will fill out all of the links of the given Node.
305 306
// It returns a channel of nodes, which the caller can receive
// all the child nodes of 'root' on, in proper order.
307
func (ds *dagService) GetDAG(ctx context.Context, root *Node) <-chan *Node {
Jeromy's avatar
Jeromy committed
308
	sig := make(chan *Node)
309
	go func() {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
310 311
		defer close(sig)

312
		var keys []u.Key
Jeromy's avatar
Jeromy committed
313 314 315 316 317
		for _, lnk := range root.Links {
			keys = append(keys, u.Key(lnk.Hash))
		}
		blkchan := ds.Blocks.GetBlocks(ctx, keys)

318
		nodes := make([]*Node, len(root.Links))
Jeromy's avatar
Jeromy committed
319
		next := 0
320
		for blk := range blkchan {
Jeromy's avatar
Jeromy committed
321
			i, err := FindLink(root, blk.Key(), nodes)
322
			if err != nil {
Jeromy's avatar
Jeromy committed
323
				// NB: can only occur as a result of programmer error
324 325
				panic("Received block that wasnt in this nodes links!")
			}
326

327 328
			nd, err := Decoded(blk.Data)
			if err != nil {
Jeromy's avatar
Jeromy committed
329
				// NB: can occur in normal situations, with improperly formatted
Brian Tiger Chow's avatar
Brian Tiger Chow committed
330
				// input data
331 332 333 334
				log.Error("Got back bad block!")
				break
			}
			nodes[i] = nd
335 336 337 338 339 340 341
			for { //Check for duplicate links
				ni, err := FindLink(root, blk.Key(), nodes)
				if err != nil {
					break
				}
				nodes[ni] = nd
			}
342 343 344 345 346 347

			if next == i {
				sig <- nd
				next++
				for ; next < len(nodes) && nodes[next] != nil; next++ {
					sig <- nodes[next]
348 349 350
				}
			}
		}
351
		if next < len(nodes) {
Jeromy's avatar
Jeromy committed
352 353
			// TODO: bubble errors back up.
			log.Errorf("Did not receive correct number of nodes!")
354
		}
355
	}()
356

357
	return sig
358
}