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

import (
4
	"fmt"
Chas Leichner's avatar
Chas Leichner committed
5

6
	blocks "github.com/jbenet/go-ipfs/blocks"
7
	bserv "github.com/jbenet/go-ipfs/blockservice"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
8
	u "github.com/jbenet/go-ipfs/util"
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
9
	mh "github.com/jbenet/go-multihash"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
10 11
)

Juan Batiz-Benet's avatar
go lint  
Juan Batiz-Benet committed
12 13 14
// 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
15 16
type NodeMap map[u.Key]*Node

Juan Batiz-Benet's avatar
go lint  
Juan Batiz-Benet committed
17
// Node represents a node in the IPFS Merkle DAG.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
18
// nodes have opaque data and a set of navigable links.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
19
type Node struct {
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
20 21
	Links []*Link
	Data  []byte
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
22 23 24

	// cache encoded/marshaled value
	encoded []byte
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
25 26
}

Juan Batiz-Benet's avatar
go lint  
Juan Batiz-Benet committed
27
// Link represents an IPFS Merkle DAG Link between Nodes.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
28
type Link struct {
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
29 30
	// utf string name. should be unique per object
	Name string // utf8
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
31

Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
32 33
	// cumulative size of target object
	Size uint64
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
34

Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
35 36
	// multihash of the target object
	Hash mh.Multihash
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
37 38 39

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

Juan Batiz-Benet's avatar
go lint  
Juan Batiz-Benet committed
42
// AddNodeLink adds a link to another node.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
func (n *Node) AddNodeLink(name string, that *Node) error {
	s, err := that.Size()
	if err != nil {
		return err
	}

	h, err := that.Multihash()
	if err != nil {
		return err
	}

	n.Links = append(n.Links, &Link{
		Name: name,
		Size: s,
		Hash: h,
58
		Node: that,
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
59 60 61
	})
	return nil
}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
62

Juan Batiz-Benet's avatar
go lint  
Juan Batiz-Benet committed
63 64
// 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
65
func (n *Node) Size() (uint64, error) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
66
	b, err := n.Encoded(false)
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
67 68 69 70
	if err != nil {
		return 0, err
	}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
71
	s := uint64(len(b))
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
72 73 74 75
	for _, l := range n.Links {
		s += l.Size
	}
	return s, nil
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
76
}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
77

Juan Batiz-Benet's avatar
go lint  
Juan Batiz-Benet committed
78
// Multihash hashes the encoded data of this node.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
79 80 81 82 83 84
func (n *Node) Multihash() (mh.Multihash, error) {
	b, err := n.Encoded(false)
	if err != nil {
		return nil, err
	}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
85
	return u.Hash(b)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
86
}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
87

Juan Batiz-Benet's avatar
go lint  
Juan Batiz-Benet committed
88
// Key returns the Multihash as a key, for maps.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
89 90 91 92
func (n *Node) Key() (u.Key, error) {
	h, err := n.Multihash()
	return u.Key(h), err
}
93

Juan Batiz-Benet's avatar
go lint  
Juan Batiz-Benet committed
94 95 96
// DAGService is an IPFS Merkle DAG service.
// - the root is virtual (like a forest)
// - stores nodes' data in a BlockService
97
type DAGService struct {
98
	Blocks *bserv.BlockService
99 100
}

101 102 103 104
// Add adds a node to the DAGService, storing the block in the BlockService
func (n *DAGService) Add(nd *Node) (u.Key, error) {
	k, _ := nd.Key()
	u.DOut("DagService Add [%s]\n", k.Pretty())
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
	if n == nil {
		return "", fmt.Errorf("DAGService is nil")
	}

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

	b, err := blocks.NewBlock(d)
	if err != nil {
		return "", err
	}

	return n.Blocks.AddBlock(b)
}

122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
func (n *DAGService) AddRecursive(nd *Node) error {
	_, err := n.Add(nd)
	if err != nil {
		return err
	}

	for _, link := range nd.Links {
		fmt.Println("Adding link.")
		if link.Node == nil {
			panic("Why does this node have a nil link?\n")
		}
		err := n.AddRecursive(link.Node)
		if err != nil {
			return err
		}
	}

	return nil
}

Juan Batiz-Benet's avatar
go lint  
Juan Batiz-Benet committed
142
// Get retrieves a node from the DAGService, fetching the block in the BlockService
143 144 145 146 147 148 149 150 151 152 153 154
func (n *DAGService) Get(k u.Key) (*Node, error) {
	if n == nil {
		return nil, fmt.Errorf("DAGService is nil")
	}

	b, err := n.Blocks.GetBlock(k)
	if err != nil {
		return nil, err
	}

	return Decoded(b.Data)
}