node.go 7.77 KB
Newer Older
1 2 3
package merkledag

import (
4
	"context"
5
	"encoding/json"
Jeromy's avatar
Jeromy committed
6
	"fmt"
Jeromy's avatar
Jeromy committed
7

Steven Allen's avatar
Steven Allen committed
8 9 10
	mh "gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash"
	cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid"
	node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format"
11 12
)

Jeromy's avatar
Jeromy committed
13
var ErrNotProtobuf = fmt.Errorf("expected protobuf dag node")
14 15
var ErrLinkNotFound = fmt.Errorf("no link by that name")

16 17
// Node represents a node in the IPFS Merkle DAG.
// nodes have opaque data and a set of navigable links.
18
type ProtoNode struct {
Jeromy's avatar
Jeromy committed
19
	links []*node.Link
20
	data  []byte
21 22 23 24

	// cache encoded/marshaled value
	encoded []byte

Jeromy's avatar
Jeromy committed
25
	cached *cid.Cid
26

27 28 29 30
	// Prefix specifies cid version and hashing function
	Prefix cid.Prefix
}

31
var v0CidPrefix = cid.Prefix{
32 33 34 35
	Codec:    cid.DagProtobuf,
	MhLength: -1,
	MhType:   mh.SHA2_256,
	Version:  0,
36 37
}

38 39 40 41 42 43 44
var v1CidPrefix = cid.Prefix{
	Codec:    cid.DagProtobuf,
	MhLength: -1,
	MhType:   mh.SHA2_256,
	Version:  1,
}

Kevin Atkinson's avatar
Kevin Atkinson committed
45
// V0CidPrefix returns a prefix for CIDv0
46
func V0CidPrefix() cid.Prefix { return v0CidPrefix }
Kevin Atkinson's avatar
Kevin Atkinson committed
47 48

// V1CidPrefix returns a prefix for CIDv1 with the default settings
49 50
func V1CidPrefix() cid.Prefix { return v1CidPrefix }

Kevin Atkinson's avatar
Kevin Atkinson committed
51
// PrefixForCidVersion returns the Protobuf prefix for a given CID version
52 53 54 55 56 57 58 59 60 61 62
func PrefixForCidVersion(version int) (cid.Prefix, error) {
	switch version {
	case 0:
		return v0CidPrefix, nil
	case 1:
		return v1CidPrefix, nil
	default:
		return cid.Prefix{}, fmt.Errorf("unknown CID version: %d", version)
	}
}

Kevin Atkinson's avatar
Kevin Atkinson committed
63
// SetPrefix sets the CID prefix if it is non nil, if prefix is nil then
64 65 66 67 68 69 70 71 72 73
// it resets it the default value
func (n *ProtoNode) SetPrefix(prefix *cid.Prefix) {
	if prefix == nil {
		n.Prefix = v0CidPrefix
	} else {
		n.Prefix = *prefix
		n.Prefix.Codec = cid.DagProtobuf
		n.encoded = nil
		n.cached = nil
	}
74 75
}

Jeromy's avatar
Jeromy committed
76
type LinkSlice []*node.Link
77 78 79 80 81

func (ls LinkSlice) Len() int           { return len(ls) }
func (ls LinkSlice) Swap(a, b int)      { ls[a], ls[b] = ls[b], ls[a] }
func (ls LinkSlice) Less(a, b int) bool { return ls[a].Name < ls[b].Name }

82 83
func NodeWithData(d []byte) *ProtoNode {
	return &ProtoNode{data: d}
84 85
}

86
// AddNodeLink adds a link to another node.
87
func (n *ProtoNode) AddNodeLink(name string, that node.Node) error {
88
	n.encoded = nil
89

Jeromy's avatar
Jeromy committed
90
	lnk, err := node.MakeLink(that)
91 92 93 94
	if err != nil {
		return err
	}

Jeromy's avatar
Jeromy committed
95 96
	lnk.Name = name

97 98
	n.AddRawLink(name, lnk)

99 100 101
	return nil
}

102
// AddNodeLinkClean adds a link to another node. without keeping a reference to
103
// the child node
Jeromy's avatar
Jeromy committed
104
func (n *ProtoNode) AddNodeLinkClean(name string, that node.Node) error {
105
	n.encoded = nil
Jeromy's avatar
Jeromy committed
106
	lnk, err := node.MakeLink(that)
107 108 109
	if err != nil {
		return err
	}
110 111 112 113 114 115
	n.AddRawLink(name, lnk)

	return nil
}

// AddRawLink adds a copy of a link to this node
Jeromy's avatar
Jeromy committed
116
func (n *ProtoNode) AddRawLink(name string, l *node.Link) error {
117
	n.encoded = nil
Jeromy's avatar
Jeromy committed
118
	n.links = append(n.links, &node.Link{
119 120
		Name: name,
		Size: l.Size,
121
		Cid:  l.Cid,
122
	})
123 124 125 126 127

	return nil
}

// Remove a link on this node by the given name
128
func (n *ProtoNode) RemoveNodeLink(name string) error {
129
	n.encoded = nil
Jeromy's avatar
Jeromy committed
130
	good := make([]*node.Link, 0, len(n.links))
Jeromy's avatar
Jeromy committed
131 132
	var found bool

133
	for _, l := range n.links {
Jeromy's avatar
Jeromy committed
134 135 136 137
		if l.Name != name {
			good = append(good, l)
		} else {
			found = true
138 139
		}
	}
140
	n.links = good
Jeromy's avatar
Jeromy committed
141 142

	if !found {
143
		return node.ErrNotFound
Jeromy's avatar
Jeromy committed
144 145 146
	}

	return nil
147 148
}

149
// Return a copy of the link with given name
Jeromy's avatar
Jeromy committed
150
func (n *ProtoNode) GetNodeLink(name string) (*node.Link, error) {
151
	for _, l := range n.links {
152
		if l.Name == name {
Jeromy's avatar
Jeromy committed
153
			return &node.Link{
154 155
				Name: l.Name,
				Size: l.Size,
156
				Cid:  l.Cid,
157 158 159
			}, nil
		}
	}
160
	return nil, ErrLinkNotFound
161 162
}

163
func (n *ProtoNode) GetLinkedProtoNode(ctx context.Context, ds node.DAGService, name string) (*ProtoNode, error) {
164 165 166 167 168 169 170 171 172 173 174 175 176
	nd, err := n.GetLinkedNode(ctx, ds, name)
	if err != nil {
		return nil, err
	}

	pbnd, ok := nd.(*ProtoNode)
	if !ok {
		return nil, ErrNotProtobuf
	}

	return pbnd, nil
}

177
func (n *ProtoNode) GetLinkedNode(ctx context.Context, ds node.DAGService, name string) (node.Node, error) {
178 179 180 181 182 183 184 185
	lnk, err := n.GetNodeLink(name)
	if err != nil {
		return nil, err
	}

	return lnk.GetNode(ctx, ds)
}

186
// Copy returns a copy of the node.
187
// NOTE: Does not make copies of Node objects in the links.
188
func (n *ProtoNode) Copy() node.Node {
189
	nnode := new(ProtoNode)
190 191 192
	if len(n.data) > 0 {
		nnode.data = make([]byte, len(n.data))
		copy(nnode.data, n.data)
Jeromy's avatar
Jeromy committed
193
	}
194

195
	if len(n.links) > 0 {
Jeromy's avatar
Jeromy committed
196
		nnode.links = make([]*node.Link, len(n.links))
197
		copy(nnode.links, n.links)
Jeromy's avatar
Jeromy committed
198
	}
199 200 201

	nnode.Prefix = n.Prefix

202 203 204
	return nnode
}

205
func (n *ProtoNode) RawData() []byte {
Jeromy's avatar
Jeromy committed
206 207 208 209
	out, _ := n.EncodeProtobuf(false)
	return out
}

210
func (n *ProtoNode) Data() []byte {
211 212 213
	return n.data
}

214
func (n *ProtoNode) SetData(d []byte) {
215 216 217 218 219
	n.encoded = nil
	n.cached = nil
	n.data = d
}

220 221
// UpdateNodeLink return a copy of the node with the link name set to point to
// that. If a link of the same name existed, it is removed.
222
func (n *ProtoNode) UpdateNodeLink(name string, that *ProtoNode) (*ProtoNode, error) {
223
	newnode := n.Copy().(*ProtoNode)
224 225
	_ = newnode.RemoveNodeLink(name) // ignore error
	err := newnode.AddNodeLink(name, that)
226 227 228
	return newnode, err
}

229 230
// Size returns the total size of the data addressed by node,
// including the total sizes of references.
231
func (n *ProtoNode) Size() (uint64, error) {
232
	b, err := n.EncodeProtobuf(false)
233 234 235 236 237
	if err != nil {
		return 0, err
	}

	s := uint64(len(b))
238
	for _, l := range n.links {
239 240 241 242 243 244
		s += l.Size
	}
	return s, nil
}

// Stat returns statistics on the node.
Jeromy's avatar
Jeromy committed
245
func (n *ProtoNode) Stat() (*node.NodeStat, error) {
246
	enc, err := n.EncodeProtobuf(false)
247
	if err != nil {
248
		return nil, err
249 250 251 252
	}

	cumSize, err := n.Size()
	if err != nil {
253
		return nil, err
254 255
	}

Jeromy's avatar
Jeromy committed
256
	return &node.NodeStat{
Jeromy's avatar
Jeromy committed
257
		Hash:           n.Cid().String(),
258
		NumLinks:       len(n.links),
259
		BlockSize:      len(enc),
260 261
		LinksSize:      len(enc) - len(n.data), // includes framing.
		DataSize:       len(n.data),
262 263 264 265
		CumulativeSize: int(cumSize),
	}, nil
}

266
func (n *ProtoNode) Loggable() map[string]interface{} {
Jeromy's avatar
Jeromy committed
267 268 269 270 271
	return map[string]interface{}{
		"node": n.String(),
	}
}

Jeromy's avatar
Jeromy committed
272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
func (n *ProtoNode) UnmarshalJSON(b []byte) error {
	s := struct {
		Data  []byte       `json:"data"`
		Links []*node.Link `json:"links"`
	}{}

	err := json.Unmarshal(b, &s)
	if err != nil {
		return err
	}

	n.data = s.Data
	n.links = s.Links
	return nil
}

288 289 290 291 292 293 294 295 296
func (n *ProtoNode) MarshalJSON() ([]byte, error) {
	out := map[string]interface{}{
		"data":  n.data,
		"links": n.links,
	}

	return json.Marshal(out)
}

297
func (n *ProtoNode) Cid() *cid.Cid {
298 299 300 301 302
	if n.encoded != nil && n.cached != nil {
		return n.cached
	}

	if n.Prefix.Codec == 0 {
303
		n.SetPrefix(nil)
304 305 306 307 308
	}

	c, err := n.Prefix.Sum(n.RawData())
	if err != nil {
		// programmer error
309
		err = fmt.Errorf("invalid CID of length %d: %x: %v", len(n.RawData()), n.RawData(), err)
310 311
		panic(err)
	}
Jeromy's avatar
Jeromy committed
312

313 314
	n.cached = c
	return c
Jeromy's avatar
Jeromy committed
315 316
}

317
func (n *ProtoNode) String() string {
Jeromy's avatar
Jeromy committed
318 319 320
	return n.Cid().String()
}

321
// Multihash hashes the encoded data of this node.
322
func (n *ProtoNode) Multihash() mh.Multihash {
323
	// NOTE: EncodeProtobuf generates the hash and puts it in n.cached.
324
	_, err := n.EncodeProtobuf(false)
325
	if err != nil {
Jeromy's avatar
Jeromy committed
326 327
		// Note: no possibility exists for an error to be returned through here
		panic(err)
328 329
	}

Jeromy's avatar
Jeromy committed
330
	return n.cached.Hash()
331
}
332

Jeromy's avatar
Jeromy committed
333
func (n *ProtoNode) Links() []*node.Link {
334 335 336
	return n.links
}

Jeromy's avatar
Jeromy committed
337
func (n *ProtoNode) SetLinks(links []*node.Link) {
338 339 340
	n.links = links
}

341 342 343 344 345
func (n *ProtoNode) Resolve(path []string) (interface{}, []string, error) {
	return n.ResolveLink(path)
}

func (n *ProtoNode) ResolveLink(path []string) (*node.Link, []string, error) {
346 347 348 349 350 351 352 353 354 355 356 357
	if len(path) == 0 {
		return nil, nil, fmt.Errorf("end of path, no more links to resolve")
	}

	lnk, err := n.GetNodeLink(path[0])
	if err != nil {
		return nil, nil, err
	}

	return lnk, path[1:], nil
}

358 359 360 361 362 363 364
func (n *ProtoNode) Tree(p string, depth int) []string {
	// ProtoNodes are only ever one path deep, anything below that results in
	// nothing
	if p != "" {
		return nil
	}

365 366 367 368 369 370
	out := make([]string, 0, len(n.links))
	for _, lnk := range n.links {
		out = append(out, lnk.Name)
	}
	return out
}