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

import (
4
	"fmt"
Jeromy's avatar
Jeromy committed
5 6
	"time"

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

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

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

Juan Batiz-Benet's avatar
go lint  
Juan Batiz-Benet committed
18 19 20
// 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
21 22
type NodeMap map[u.Key]*Node

Juan Batiz-Benet's avatar
go lint  
Juan Batiz-Benet committed
23
// Node represents a node in the IPFS Merkle DAG.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
24
// nodes have opaque data and a set of navigable links.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
25
type Node struct {
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
26 27
	Links []*Link
	Data  []byte
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
28 29 30

	// cache encoded/marshaled value
	encoded []byte
31 32

	cached mh.Multihash
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
33 34
}

Juan Batiz-Benet's avatar
go lint  
Juan Batiz-Benet committed
35
// Link represents an IPFS Merkle DAG Link between Nodes.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
36
type Link struct {
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
37 38
	// utf string name. should be unique per object
	Name string // utf8
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
39

Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
40 41
	// cumulative size of target object
	Size uint64
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
42

Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
43 44
	// multihash of the target object
	Hash mh.Multihash
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
45 46 47

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

Jeromy's avatar
Jeromy committed
50 51
func MakeLink(n *Node) (*Link, error) {
	s, err := n.Size()
52
	if err != nil {
Jeromy's avatar
Jeromy committed
53
		return nil, err
54 55
	}

Jeromy's avatar
Jeromy committed
56
	h, err := n.Multihash()
57
	if err != nil {
Jeromy's avatar
Jeromy committed
58
		return nil, err
59
	}
Jeromy's avatar
Jeromy committed
60
	return &Link{
61 62
		Size: s,
		Hash: h,
Jeromy's avatar
Jeromy committed
63
	}, nil
64 65
}

66
func (l *Link) GetNode(serv DAGService) (*Node, error) {
67 68 69 70 71 72 73
	if l.Node != nil {
		return l.Node, nil
	}

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

Jeromy's avatar
Jeromy committed
74 75 76
// 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
77 78 79
	if err != nil {
		return err
	}
Jeromy's avatar
Jeromy committed
80 81 82 83 84 85
	lnk.Name = name
	lnk.Node = that

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

Jeromy's avatar
Jeromy committed
87 88 89 90
// 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
91 92 93
	if err != nil {
		return err
	}
Jeromy's avatar
Jeromy committed
94
	lnk.Name = name
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
95

Jeromy's avatar
Jeromy committed
96
	n.Links = append(n.Links, lnk)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
97 98
	return nil
}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
99

100 101 102 103 104 105 106
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
		}
	}
107
	return ErrNotFound
108 109
}

110 111
// Copy returns a copy of the node.
// NOTE: does not make copies of Node objects in the links.
Jeromy's avatar
Jeromy committed
112 113 114 115 116 117 118 119 120 121
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
122 123
// 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
124
func (n *Node) Size() (uint64, error) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
125
	b, err := n.Encoded(false)
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
126 127 128 129
	if err != nil {
		return 0, err
	}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
130
	s := uint64(len(b))
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
131 132 133 134
	for _, l := range n.Links {
		s += l.Size
	}
	return s, nil
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
135
}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
136

Juan Batiz-Benet's avatar
go lint  
Juan Batiz-Benet committed
137
// Multihash hashes the encoded data of this node.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
138
func (n *Node) Multihash() (mh.Multihash, error) {
139
	// Note: Encoded generates the hash and puts it in n.cached.
140
	_, err := n.Encoded(false)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
141 142 143 144
	if err != nil {
		return nil, err
	}

145
	return n.cached, nil
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
146
}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
147

Juan Batiz-Benet's avatar
go lint  
Juan Batiz-Benet committed
148
// Key returns the Multihash as a key, for maps.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
149 150 151 152
func (n *Node) Key() (u.Key, error) {
	h, err := n.Multihash()
	return u.Key(h), err
}
153

Juan Batiz-Benet's avatar
go lint  
Juan Batiz-Benet committed
154
// DAGService is an IPFS Merkle DAG service.
155 156 157 158 159 160 161 162 163 164 165 166
type DAGService interface {
	Add(*Node) (u.Key, error)
	AddRecursive(*Node) error
	Get(u.Key) (*Node, error)
	Remove(*Node) error
}

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

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

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

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

188 189 190
	b := new(blocks.Block)
	b.Data = d
	b.Multihash, err = nd.Multihash()
191 192 193 194 195 196 197
	if err != nil {
		return "", err
	}

	return n.Blocks.AddBlock(b)
}

198
func (n *dagService) AddRecursive(nd *Node) error {
199 200
	_, err := n.Add(nd)
	if err != nil {
Jeromy's avatar
Jeromy committed
201
		log.Info("AddRecursive Error: %s\n", err)
202 203 204 205
		return err
	}

	for _, link := range nd.Links {
206 207 208 209 210
		if link.Node != nil {
			err := n.AddRecursive(link.Node)
			if err != nil {
				return err
			}
211 212 213 214 215 216
		}
	}

	return nil
}

217 218
// Get retrieves a node from the dagService, fetching the block in the BlockService
func (n *dagService) Get(k u.Key) (*Node, error) {
219
	if n == nil {
220
		return nil, fmt.Errorf("dagService is nil")
221 222
	}

Jeromy's avatar
Jeromy committed
223 224
	ctx, _ := context.WithTimeout(context.TODO(), time.Second*5)
	b, err := n.Blocks.GetBlock(ctx, k)
225 226 227 228 229 230
	if err != nil {
		return nil, err
	}

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

232
func (n *dagService) Remove(nd *Node) error {
Jeromy's avatar
Jeromy committed
233 234 235 236 237 238 239 240 241 242 243
	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
244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261

func FetchGraph(ctx context.Context, root *Node, serv *DAGService) {
	for _, l := range root.Links {
		go func(lnk *Link) {
			select {
			case <-ctx.Done():
				return
			}

			nd, err := lnk.GetNode(serv)
			if err != nil {
				log.Error(err)
				return
			}
			FetchGraph(ctx, nd, serv)
		}(l)
	}
}