asmt/censustree.go
censustree.goRaw
1package asmtree
2
3import (
4 "bytes"
5 "fmt"
6 "path"
7 "sync/atomic"
8 "time"
9
10 "go.vocdoni.io/dvote/censustree"
11 "go.vocdoni.io/dvote/log"
12
13 "git.sr.ht/~sircmpwn/go-bare"
14 "github.com/p4u/asmt/db"
15 asmt "github.com/p4u/asmt/smt"
16)
17
18// We use go-bare for export/import the trie. In order to support
19// big census (up to 8 Million entries) we need to increase the maximums.
20const bareMaxArrayLength uint64 = 1024 * 1014 * 8 // 8 Million
21
22const bareMaxUnmarshalBytes uint64 = bareMaxArrayLength * 32 * 2 // 512 MiB
23
24type Tree struct {
25 Tree *asmt.Trie
26 db db.DB
27 public uint32
28 lastAccessUnix int64 // a unix timestamp, used via sync/atomic
29 size uint64
30 snapshotRoot []byte // if not nil, this trie is considered an inmutable snapshot
31 snapshotSize uint64
32}
33
34type Proof struct {
35 Bitmap []byte
36 Length int
37 Siblings [][]byte
38 Value []byte
39}
40
41type exportElement struct {
42 Key []byte `bare:"key"`
43 Value []byte `bare:"value"`
44}
45
46type exportData struct {
47 Elements []exportElement `bare:"elements"`
48}
49
50const (
51 MaxKeySize = 32
52 MaxValueSize = 64
53 dbRootPrefix = "this is the last root for the SMT tree"
54)
55
56// NewTree initializes a new AergoSMT tree following the censustree.Tree interface specification.
57func NewTree(name, storageDir string) (censustree.Tree, error) {
58 tr := &Tree{}
59 err := tr.Init(name, storageDir)
60 return tr, err
61}
62
63// newTree opens or creates a merkle tree under the given storage.
64func newTree(name, storageDir string) (*asmt.Trie, db.DB, error) {
65 dir := path.Join(storageDir, name)
66 log.Debugf("creating new tree on %s", dir)
67 d := db.NewDB(db.LevelImpl, dir)
68 root := d.Get([]byte(dbRootPrefix))
69 tr := asmt.NewTrie(root, asmt.Hasher, d)
70 if root != nil {
71 if err := tr.LoadCache(root); err != nil {
72 return nil, nil, err
73 }
74 }
75 return tr, d, nil
76}
77
78// Init initializes a new asmt tree
79func (t *Tree) Init(name, storageDir string) error {
80 var err error
81 t.Tree, t.db, err = newTree(name, storageDir)
82 t.updateAccessTime()
83 t.size = 0
84 return err
85}
86
87func (t *Tree) MaxKeySize() int {
88 return MaxKeySize
89}
90
91// LastAccess returns the last time the Tree was accessed, in the form of a unix
92// timestamp.
93func (t *Tree) LastAccess() int64 {
94 return atomic.LoadInt64(&t.lastAccessUnix)
95}
96
97func (t *Tree) updateAccessTime() {
98 atomic.StoreInt64(&t.lastAccessUnix, time.Now().Unix())
99}
100
101// Publish makes a merkle tree available for queries.
102// Application layer should check IsPublish() before considering the Tree available.
103func (t *Tree) Publish() {
104 atomic.StoreUint32(&t.public, 1)
105}
106
107// UnPublish makes a merkle tree not available for queries
108func (t *Tree) UnPublish() {
109 atomic.StoreUint32(&t.public, 0)
110}
111
112// IsPublic returns true if the tree is available
113func (t *Tree) IsPublic() bool {
114 return atomic.LoadUint32(&t.public) == 1
115}
116
117// Commit saves permanently the tree on disk
118func (t *Tree) Commit() error {
119 if t.snapshotRoot != nil {
120 return fmt.Errorf("cannot commit to a snapshot trie")
121 }
122 err := t.Tree.Commit()
123 if err != nil {
124 return err
125 }
126 t.db.Set([]byte(dbRootPrefix), t.Root())
127 return nil
128}
129
130// Add adds a new claim to the merkle tree
131// A claim is composed of two parts: index and value
132// 1.index is mandatory, the data will be used for indexing the claim into to merkle tree
133// 2.value is optional, the data will not affect the indexing
134func (t *Tree) Add(index, value []byte) error {
135 t.updateAccessTime()
136 if t.snapshotRoot != nil {
137 return fmt.Errorf("cannot add to a snapshot trie")
138 }
139 if len(index) < 4 {
140 return fmt.Errorf("index too small (%d), minimum size is 4 bytes", len(index))
141 }
142 if len(value) > MaxValueSize {
143 return fmt.Errorf("index or value claim data too big")
144 }
145 _, err := t.Tree.Update([][]byte{asmt.Hasher(index)}, [][]byte{asmt.Hasher(value)})
146 if err != nil {
147 return err
148 }
149 atomic.StoreUint64(&t.size, 0) // TBD: improve this
150 return t.Commit()
151}
152
153// AddBatch adds a list of indexes and values.
154// The commit to disk is executed only once.
155// The values slince could be empty or as long as indexes.
156func (t *Tree) AddBatch(indexes, values [][]byte) ([]int, error) {
157 var wrongIndexes []int
158 t.updateAccessTime()
159 if t.snapshotRoot != nil {
160 return wrongIndexes, fmt.Errorf("cannot add to a snapshot trie")
161 }
162 if len(values) > 0 && len(indexes) != len(values) {
163 return wrongIndexes, fmt.Errorf("indexes and values have different size")
164 }
165 var hashedIndexes [][]byte
166 var hashedValues [][]byte
167 var value []byte
168 for i, key := range indexes {
169 if len(key) < 4 {
170 wrongIndexes = append(wrongIndexes, i)
171 continue
172 }
173 value = nil
174 if len(values) > 0 {
175 if len(values[i]) > MaxValueSize {
176 wrongIndexes = append(wrongIndexes, i)
177 continue
178 }
179 value = values[i]
180 }
181 hashedIndexes = append(hashedIndexes, asmt.Hasher(key))
182 hashedValues = append(hashedValues, asmt.Hasher(value))
183 }
184 _, err := t.Tree.Update(hashedIndexes, hashedValues)
185 if err != nil {
186 return wrongIndexes, err
187 }
188 atomic.StoreUint64(&t.size, 0) // TBD: improve this
189 return wrongIndexes, t.Commit()
190}
191
192// Get returns the value of a key
193func (t *Tree) Get(key []byte) []byte { // Do something with error
194 var value []byte
195 if t.snapshotRoot != nil {
196 value, _ = t.Tree.GetWithRoot(key, t.snapshotRoot)
197 } else {
198 value, _ = t.Tree.Get(key)
199 }
200 return value
201}
202
203// GenProof generates a merkle tree proof that can be later used on CheckProof() to validate it.
204func (t *Tree) GenProof(index, value []byte) ([]byte, error) {
205 t.updateAccessTime()
206 var err error
207 var ap [][]byte
208 var pvalue []byte
209 var bitmap []byte
210 var length int
211 if t.snapshotRoot != nil {
212 bitmap, ap, length, _, _, pvalue, err = t.Tree.MerkleProofCompressedR(
213 asmt.Hasher(index),
214 t.snapshotRoot)
215 if err != nil {
216 return nil, err
217 }
218 } else {
219 bitmap, ap, length, _, _, pvalue, err = t.Tree.MerkleProofCompressed(
220 asmt.Hasher(index))
221 if err != nil {
222 return nil, err
223 }
224 }
225 //if !included {
226 // return nil, fmt.Errorf("not included")
227 //}
228 if !bytes.Equal(pvalue, asmt.Hasher(value)) {
229 return nil, fmt.Errorf("incorrect value on genProof")
230 }
231 return bare.Marshal(&Proof{Bitmap: bitmap, Length: length, Siblings: ap, Value: pvalue})
232}
233
234// CheckProof validates a merkle proof and its data.
235func (t *Tree) CheckProof(index, value, root, mproof []byte) (bool, error) {
236 t.updateAccessTime()
237 p := Proof{}
238 if err := bare.Unmarshal(mproof, &p); err != nil {
239 return false, err
240 }
241 if !bytes.Equal(p.Value, asmt.Hasher(value)) {
242 return false, fmt.Errorf("values mismatch %x != %x", p.Value, asmt.Hasher(value))
243 }
244 if root != nil {
245 return t.Tree.VerifyInclusionWithRootC(
246 root,
247 p.Bitmap,
248 asmt.Hasher(index),
249 p.Value,
250 p.Siblings,
251 p.Length), nil
252 }
253 if t.snapshotRoot != nil {
254 return t.Tree.VerifyInclusionWithRootC(
255 t.snapshotRoot,
256 p.Bitmap,
257 asmt.Hasher(index),
258 p.Value,
259 p.Siblings,
260 p.Length), nil
261 }
262 return t.Tree.VerifyInclusionC(
263 p.Bitmap,
264 asmt.Hasher(index),
265 p.Value,
266 p.Siblings,
267 p.Length), nil
268}
269
270// Root returns the current root hash of the merkle tree
271func (t *Tree) Root() []byte {
272 t.updateAccessTime()
273 if t.snapshotRoot != nil {
274 return t.snapshotRoot
275 }
276 return t.Tree.Root
277}
278
279// Dump returns the whole merkle tree serialized in a format that can be used on Import.
280// Byte seralization is performed using bare message protocol, it is a 40% size win over JSON.
281func (t *Tree) Dump(root []byte) ([]byte, error) {
282 t.updateAccessTime()
283 if root == nil && t.snapshotRoot != nil {
284 root = t.snapshotRoot
285 }
286 dump := exportData{}
287 t.iterateWithRoot(root, nil, func(k, v []byte) bool {
288 ee := exportElement{Key: make([]byte, len(k)), Value: make([]byte, len(v))}
289 // Copy elements since it's not safe to hold on to the []byte values from Iterate
290 copy(ee.Key, k[:])
291 copy(ee.Value, v[:])
292 dump.Elements = append(dump.Elements, ee)
293 return false
294 })
295 bare.MaxArrayLength(bareMaxArrayLength)
296 bare.MaxUnmarshalBytes(bareMaxUnmarshalBytes)
297
298 return bare.Marshal(&dump)
299}
300
301// String returns a human readable representation of the tree.
302func (t *Tree) String() string {
303 s := bytes.Buffer{}
304 t.iterate(t.snapshotRoot, func(k, v []byte) bool {
305 s.WriteString(fmt.Sprintf("%x => %x\n", k, v))
306 return false
307 })
308 return s.String()
309}
310
311// Size returns the number of leaf nodes on the merkle tree.
312// TO-DO: root is currently ignored
313func (t *Tree) Size(root []byte) (int64, error) {
314 if t.snapshotRoot != nil {
315 return int64(t.snapshotSize), nil
316 }
317 return int64(t.count()), nil
318}
319
320// DumpPlain returns the entire list of added claims for a specific root hash.
321// First return parametre are the indexes and second the values.
322// If root is not specified, the last one is used.
323func (t *Tree) DumpPlain(root []byte) ([][]byte, [][]byte, error) {
324 var indexes, values [][]byte
325 var err error
326 t.updateAccessTime()
327
328 t.iterateWithRoot(root, nil, func(k, v []byte) bool {
329 indexes = append(indexes, k)
330 values = append(values, v)
331 return false
332 })
333
334 return indexes, values, err
335}
336
337// ImportDump imports a partial or whole tree previously exported with Dump()
338func (t *Tree) ImportDump(data []byte) error {
339 t.updateAccessTime()
340 if t.snapshotRoot != nil {
341 return fmt.Errorf("cannot import to a snapshot")
342 }
343 census := new(exportData)
344 bare.MaxArrayLength(bareMaxArrayLength)
345 bare.MaxUnmarshalBytes(bareMaxUnmarshalBytes)
346 if err := bare.Unmarshal(data, census); err != nil {
347 return fmt.Errorf("importdump cannot unmarshal data: %w", err)
348 }
349 keys := [][]byte{}
350 values := [][]byte{}
351 for _, ee := range census.Elements {
352 keys = append(keys, ee.Key)
353 values = append(values, ee.Value)
354 }
355 _, err := t.Tree.Update(keys, values)
356 if err != nil {
357 return err
358 }
359 atomic.StoreUint64(&t.size, 0) // TBD: improve this
360 return t.Commit()
361}
362
363// Snapshot returns a Tree instance of a exiting merkle root.
364// A Snapshot cannot be modified.
365func (t *Tree) Snapshot(root []byte) (censustree.Tree, error) {
366 exist, err := t.HashExists(root)
367 if err != nil {
368 return nil, err
369 }
370 if !exist {
371 return nil, fmt.Errorf("root %x does not exist, cannot build snapshot", root)
372 }
373 return &Tree{Tree: t.Tree, public: t.public, snapshotRoot: root, snapshotSize: t.count()}, nil
374}
375
376func (t *Tree) Close() error {
377 t.db.Close()
378 return nil
379}
380
381// HashExists checks if a hash exists as a node in the merkle tree
382func (t *Tree) HashExists(hash []byte) (bool, error) {
383 t.updateAccessTime()
384 return t.Tree.TrieRootExists(hash), nil
385}
386
387func (t *Tree) count() uint64 {
388 if v := atomic.LoadUint64(&t.size); v != 0 {
389 return v
390 }
391 counter := uint64(0)
392 if err := t.Tree.Walk(t.snapshotRoot, func(*asmt.WalkResult) int32 {
393 counter++
394 return 0
395 }); err != nil {
396 return 0
397 }
398 atomic.StoreUint64(&t.size, counter)
399 return counter
400}
401
402func (t *Tree) iterate(prefix []byte, callback func(key, value []byte) bool) {
403 t.Tree.Walk(t.snapshotRoot, func(v *asmt.WalkResult) int32 {
404 if callback(v.Key, v.Value) {
405 return 1
406 } else {
407 return 0
408 }
409 })
410}
411
412func (t *Tree) iterateWithRoot(root, prefix []byte, callback func(key, value []byte) bool) {
413 t.Tree.Walk(root, func(v *asmt.WalkResult) int32 {
414 if callback(v.Key, v.Value) {
415 return 1
416 } else {
417 return 0
418 }
419 })
420}