arnaucube
arnaucube/asmtPublic Self-hosted repository
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 = 1024 * 1024 * 200 // 200 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   = 256
 52	MaxValueSize = 256
 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}