/
marshal.go
152 lines (137 loc) · 3.69 KB
/
marshal.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
package smt
import (
"bytes"
"crypto/sha256"
"fmt"
"io"
"github.com/ipld/go-ipld-prime"
dagcosmos "github.com/vulcanize/go-codec-dagcosmos"
"github.com/vulcanize/go-codec-dagcosmos/shared"
)
type NodeKind string
var (
hasher = sha256.New()
hashSize = hasher.Size()
placeholder = bytes.Repeat([]byte{0}, hashSize)
leafPrefix = []byte{0}
innerPrefix = []byte{1}
)
const (
INNER_NODE NodeKind = "inner"
LEAF_NODE NodeKind = "leaf"
)
func (n NodeKind) String() string {
return string(n)
}
// Encode provides an IPLD codec encode interface for Tendermint SMT node IPLDs.
// This function is registered via the go-ipld-prime link loader for multicodec
// code XXXX when this package is invoked via init.
func Encode(node ipld.Node, w io.Writer) error {
// 1KiB can be allocated on the stack, and covers most small nodes
// without having to grow the buffer and cause allocations.
enc := make([]byte, 0, 1024)
enc, err := AppendEncode(enc, node)
if err != nil {
return err
}
_, err = w.Write(enc)
return err
}
// AppendEncode is like Encode, but it uses a destination buffer directly.
// This means less copying of bytes, and if the destination has enough capacity,
// fewer allocations.
func AppendEncode(enc []byte, inNode ipld.Node) ([]byte, error) {
// Wrap in a typed node for some basic schema form checking
builder := dagcosmos.Type.MerkleTreeNode.NewBuilder()
if err := builder.AssignNode(inNode); err != nil {
return nil, err
}
n := builder.Build()
node, kind, err := NodeAndKind(n)
if err != nil {
return nil, err
}
switch kind {
case INNER_NODE:
enc, err = packInnerNode(node)
case LEAF_NODE:
enc, err = packLeafNode(node)
default:
return nil, fmt.Errorf("IPLD node is missing the expected Union keys")
}
return enc, err
}
func packInnerNode(node ipld.Node) ([]byte, error) {
var leftData, rightData []byte
leftNode, err := node.LookupByString("Left")
if err != nil {
return nil, err
}
if leftNode.IsNull() {
leftData = placeholder
} else {
leftData, err = shared.PackLink(leftNode)
if err != nil {
return nil, err
}
}
rightNode, err := node.LookupByString("Right")
if err != nil {
return nil, err
}
if rightNode.IsNull() {
rightData = placeholder
} else {
rightData, err = shared.PackLink(rightNode)
if err != nil {
return nil, err
}
}
nodeVal := make([]byte, 0, len(innerPrefix)+len(leftData)+len(rightData))
nodeVal = append(nodeVal, innerPrefix...)
nodeVal = append(nodeVal, leftData...)
nodeVal = append(nodeVal, rightData...)
return nodeVal, nil
}
func packLeafNode(node ipld.Node) ([]byte, error) {
path, err := packPath(node)
if err != nil {
return nil, err
}
val, err := packValue(node)
if err != nil {
return nil, err
}
nodeVal := make([]byte, 0, len(leafPrefix)+len(path)+len(val))
nodeVal = append(nodeVal, leafPrefix...)
nodeVal = append(nodeVal, path...)
nodeVal = append(nodeVal, val...)
return nodeVal, nil
}
func packPath(node ipld.Node) ([]byte, error) {
pathNode, err := node.LookupByString("Path")
if err != nil {
return nil, err
}
return pathNode.AsBytes()
}
func packValue(node ipld.Node) ([]byte, error) {
valNode, err := node.LookupByString("Value")
if err != nil {
return nil, err
}
// at this time we are not attempting to further handle the arbitrary hash(key, value) stored here
return valNode.AsBytes()
}
// NodeAndKind returns the node and its kind
func NodeAndKind(node ipld.Node) (ipld.Node, NodeKind, error) {
n, err := node.LookupByString(LEAF_NODE.String())
if err == nil {
return n, LEAF_NODE, nil
}
n, err = node.LookupByString(INNER_NODE.String())
if err == nil {
return n, INNER_NODE, nil
}
return nil, "", fmt.Errorf("SMT IPLD node is missing the expected keyed Union keys")
}