Browse Source

Replace naive AddBatch by optimized AddBatch

- Replace naive AddBatch by optimized AddBatch
- Add blake2b hash support
- Expose needed methods for external usage (ReadLeafValue,
ReadIntermediateChilds)
- Return 'value' in GenProof
master
arnaucube 3 years ago
parent
commit
6dcbbdf4a5
9 changed files with 99 additions and 68 deletions
  1. +2
    -0
      README.md
  2. +5
    -4
      addbatch.go
  3. +7
    -7
      addbatch_test.go
  4. +2
    -1
      go.mod
  5. +11
    -0
      go.sum
  6. +33
    -0
      hash.go
  7. +14
    -0
      hash_test.go
  8. +24
    -55
      tree.go
  9. +1
    -1
      tree_test.go

+ 2
- 0
README.md

@ -1,5 +1,7 @@
# arbo [![GoDoc](https://godoc.org/github.com/arnaucube/arbo?status.svg)](https://godoc.org/github.com/arnaucube/arbo) [![Go Report Card](https://goreportcard.com/badge/github.com/arnaucube/arbo)](https://goreportcard.com/report/github.com/arnaucube/arbo) [![Test](https://github.com/arnaucube/arbo/workflows/Test/badge.svg)](https://github.com/arnaucube/arbo/actions?query=workflow%3ATest) # arbo [![GoDoc](https://godoc.org/github.com/arnaucube/arbo?status.svg)](https://godoc.org/github.com/arnaucube/arbo) [![Go Report Card](https://goreportcard.com/badge/github.com/arnaucube/arbo)](https://goreportcard.com/report/github.com/arnaucube/arbo) [![Test](https://github.com/arnaucube/arbo/workflows/Test/badge.svg)](https://github.com/arnaucube/arbo/actions?query=workflow%3ATest)
> *arbo*: tree in Esperanto.
MerkleTree implementation in Go. Compatible with the circomlib implementation of MerkleTree implementation in Go. Compatible with the circomlib implementation of
the MerkleTree (when using the Poseidon hash function), following the the MerkleTree (when using the Poseidon hash function), following the
specification from https://docs.iden3.io/publications/pdfs/Merkle-Tree.pdf and specification from https://docs.iden3.io/publications/pdfs/Merkle-Tree.pdf and

+ 5
- 4
addbatch.go

@ -145,9 +145,10 @@ const (
minLeafsThreshold = 100 // nolint:gomnd // TMP WIP this will be autocalculated minLeafsThreshold = 100 // nolint:gomnd // TMP WIP this will be autocalculated
) )
// AddBatchOpt is the WIP implementation of the AddBatch method in a more
// optimized approach.
func (t *Tree) AddBatchOpt(keys, values [][]byte) ([]int, error) {
// AddBatch adds a batch of key-values to the Tree. Returns an array containing
// the indexes of the keys failed to add.
func (t *Tree) AddBatch(keys, values [][]byte) ([]int, error) {
// TODO: support vaules=nil
t.updateAccessTime() t.updateAccessTime()
t.Lock() t.Lock()
defer t.Unlock() defer t.Unlock()
@ -678,7 +679,7 @@ func (t *Tree) getLeafs(root []byte) ([][]byte, [][]byte, error) {
if v[0] != PrefixValueLeaf { if v[0] != PrefixValueLeaf {
return return
} }
leafK, leafV := readLeafValue(v)
leafK, leafV := ReadLeafValue(v)
ks = append(ks, leafK) ks = append(ks, leafK)
vs = append(vs, leafV) vs = append(vs, leafV)
}) })

+ 7
- 7
addbatch_test.go

@ -75,7 +75,7 @@ func TestAddBatchCaseA(t *testing.T) {
values = append(values, v) values = append(values, v)
} }
start = time.Now() start = time.Now()
indexes, err := tree2.AddBatchOpt(keys, values)
indexes, err := tree2.AddBatch(keys, values)
c.Assert(err, qt.IsNil) c.Assert(err, qt.IsNil)
time2 := time.Since(start) time2 := time.Since(start)
debugTime("CASE A, AddBatch", time1, time2) debugTime("CASE A, AddBatch", time1, time2)
@ -113,7 +113,7 @@ func TestAddBatchCaseANotPowerOf2(t *testing.T) {
keys = append(keys, k) keys = append(keys, k)
values = append(values, v) values = append(values, v)
} }
indexes, err := tree2.AddBatchOpt(keys, values)
indexes, err := tree2.AddBatch(keys, values)
c.Assert(err, qt.IsNil) c.Assert(err, qt.IsNil)
c.Check(len(indexes), qt.Equals, 0) c.Check(len(indexes), qt.Equals, 0)
@ -148,7 +148,7 @@ func TestAddBatchCaseB(t *testing.T) {
values = append(values, v) values = append(values, v)
} }
start = time.Now() start = time.Now()
indexes, err := tree2.AddBatchOpt(keys, values)
indexes, err := tree2.AddBatch(keys, values)
c.Assert(err, qt.IsNil) c.Assert(err, qt.IsNil)
time2 := time.Since(start) time2 := time.Since(start)
debugTime("CASE B, AddBatch", time1, time2) debugTime("CASE B, AddBatch", time1, time2)
@ -182,7 +182,7 @@ func TestAddBatchCaseBRepeatedLeafs(t *testing.T) {
keys = append(keys, k) keys = append(keys, k)
values = append(values, v) values = append(values, v)
} }
indexes, err := tree2.AddBatchOpt(keys, values)
indexes, err := tree2.AddBatch(keys, values)
c.Assert(err, qt.IsNil) c.Assert(err, qt.IsNil)
c.Check(len(indexes), qt.Equals, initialNLeafs) c.Check(len(indexes), qt.Equals, initialNLeafs)
@ -368,7 +368,7 @@ func TestAddBatchCaseC(t *testing.T) {
values = append(values, v) values = append(values, v)
} }
start = time.Now() start = time.Now()
indexes, err := tree2.AddBatchOpt(keys, values)
indexes, err := tree2.AddBatch(keys, values)
c.Assert(err, qt.IsNil) c.Assert(err, qt.IsNil)
time2 := time.Since(start) time2 := time.Since(start)
debugTime("CASE C, AddBatch", time1, time2) debugTime("CASE C, AddBatch", time1, time2)
@ -405,7 +405,7 @@ func TestAddBatchCaseD(t *testing.T) {
values = append(values, v) values = append(values, v)
} }
start = time.Now() start = time.Now()
indexes, err := tree2.AddBatchOpt(keys, values)
indexes, err := tree2.AddBatch(keys, values)
c.Assert(err, qt.IsNil) c.Assert(err, qt.IsNil)
time2 := time.Since(start) time2 := time.Since(start)
debugTime("CASE D, AddBatch", time1, time2) debugTime("CASE D, AddBatch", time1, time2)
@ -462,7 +462,7 @@ func TestAddBatchCaseE(t *testing.T) {
values = append(values, v) values = append(values, v)
} }
start = time.Now() start = time.Now()
indexes, err := tree2.AddBatchOpt(keys, values)
indexes, err := tree2.AddBatch(keys, values)
c.Assert(err, qt.IsNil) c.Assert(err, qt.IsNil)
time2 := time.Since(start) time2 := time.Since(start)
debugTime("CASE E, AddBatch", time1, time2) debugTime("CASE E, AddBatch", time1, time2)

+ 2
- 1
go.mod

@ -1,9 +1,10 @@
module github.com/arnaucube/arbo module github.com/arnaucube/arbo
go 1.14
go 1.16
require ( require (
github.com/frankban/quicktest v1.11.3 github.com/frankban/quicktest v1.11.3
github.com/iden3/go-iden3-crypto v0.0.6-0.20210308142348-8f85683b2cef github.com/iden3/go-iden3-crypto v0.0.6-0.20210308142348-8f85683b2cef
github.com/iden3/go-merkletree v0.0.0-20210308143313-8b63ca866189 github.com/iden3/go-merkletree v0.0.0-20210308143313-8b63ca866189
golang.org/x/crypto v0.0.0-20210317152858-513c2a44f670
) )

+ 11
- 0
go.sum

@ -58,6 +58,7 @@ github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dchest/blake512 v1.0.0/go.mod h1:FV1x7xPPLWukZlpDpWQ88rF/SFwZ5qbskrzhLMB92JI= github.com/dchest/blake512 v1.0.0/go.mod h1:FV1x7xPPLWukZlpDpWQ88rF/SFwZ5qbskrzhLMB92JI=
github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ=
@ -226,6 +227,7 @@ github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTw
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
@ -259,6 +261,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d/go.mod h1:9OrXJhf154huy1nPWmuSrkgjPUtUNhA+Zmy+6AESzuA= github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d/go.mod h1:9OrXJhf154huy1nPWmuSrkgjPUtUNhA+Zmy+6AESzuA=
github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs= github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs=
@ -284,6 +287,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210317152858-513c2a44f670 h1:gzMM0EjIYiRmJI3+jBdFuoynZlpxa2JQZsolKu09BXo=
golang.org/x/crypto v0.0.0-20210317152858-513c2a44f670/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20200513190911-00229845015e/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= golang.org/x/exp v0.0.0-20200513190911-00229845015e/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
@ -308,6 +313,7 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -331,8 +337,12 @@ golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@ -379,6 +389,7 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

+ 33
- 0
hash.go

@ -5,6 +5,7 @@ import (
"math/big" "math/big"
"github.com/iden3/go-iden3-crypto/poseidon" "github.com/iden3/go-iden3-crypto/poseidon"
"golang.org/x/crypto/blake2b"
) )
var ( var (
@ -13,6 +14,8 @@ var (
// TypeHashPoseidon represents the label for the HashFunction of // TypeHashPoseidon represents the label for the HashFunction of
// Poseidon // Poseidon
TypeHashPoseidon = []byte("poseidon") TypeHashPoseidon = []byte("poseidon")
// TypeHashBlake2b represents the label for the HashFunction of Blake2b
TypeHashBlake2b = []byte("blake2b")
// HashFunctionSha256 contains the HashSha256 struct which implements // HashFunctionSha256 contains the HashSha256 struct which implements
// the HashFunction interface // the HashFunction interface
@ -20,6 +23,9 @@ var (
// HashFunctionPoseidon contains the HashPoseidon struct which implements // HashFunctionPoseidon contains the HashPoseidon struct which implements
// the HashFunction interface // the HashFunction interface
HashFunctionPoseidon HashPoseidon HashFunctionPoseidon HashPoseidon
// HashFunctionBlake2b contains the HashBlake2b struct which implements
// the HashFunction interface
HashFunctionBlake2b HashBlake2b
) )
// Once Generics are at Go, this will be updated (August 2021 // Once Generics are at Go, this will be updated (August 2021
@ -85,3 +91,30 @@ func (f HashPoseidon) Hash(b ...[]byte) ([]byte, error) {
hB := BigIntToBytes(h) hB := BigIntToBytes(h)
return hB, nil return hB, nil
} }
// HashBlake2b implements the HashFunction interface for the Blake2b hash
type HashBlake2b struct{}
// Type returns the type of HashFunction for the HashBlake2b
func (f HashBlake2b) Type() []byte {
return TypeHashBlake2b
}
// Len returns the length of the Hash output
func (f HashBlake2b) Len() int {
return 32 //nolint:gomnd
}
// Hash implements the hash method for the HashFunction HashBlake2b
func (f HashBlake2b) Hash(b ...[]byte) ([]byte, error) {
hasher, err := blake2b.New256(nil)
if err != nil {
return nil, err
}
for i := 0; i < len(b); i++ {
if _, err = hasher.Write(b[i]); err != nil {
return nil, err
}
}
return hasher.Sum(nil), nil
}

+ 14
- 0
hash_test.go

@ -38,3 +38,17 @@ func TestHashPoseidon(t *testing.T) {
qt.Equals, qt.Equals,
"7853200120776062878684798364095072458815029376092732009249414926327459813530") "7853200120776062878684798364095072458815029376092732009249414926327459813530")
} }
func TestHashBlake2b(t *testing.T) {
// Blake2b hash
hashFunc := &HashBlake2b{}
b := []byte("test")
h, err := hashFunc.Hash(b)
if err != nil {
t.Fatal(err)
}
c := qt.New(t)
c.Assert(hex.EncodeToString(h),
qt.Equals,
"928b20366943e2afd11ebc0eae2e53a93bf177a4fcf35bcc64d503704e65e202")
}

+ 24
- 55
tree.go

@ -52,7 +52,7 @@ type Tree struct {
sync.RWMutex sync.RWMutex
tx db.Tx tx db.Tx
db db.Storage db db.Storage
lastAccess int64 // in unix time
lastAccess int64 // in unix time // TODO delete, is a feature of a upper abstraction level
maxLevels int maxLevels int
root []byte root []byte
@ -107,45 +107,9 @@ func (t *Tree) Root() []byte {
return t.root return t.root
} }
// AddBatch adds a batch of key-values to the Tree. This method will be
// optimized to do some internal parallelization. Returns an array containing
// the indexes of the keys failed to add.
func (t *Tree) AddBatch(keys, values [][]byte) ([]int, error) {
t.updateAccessTime()
if len(keys) != len(values) {
return nil, fmt.Errorf("len(keys)!=len(values) (%d!=%d)",
len(keys), len(values))
}
t.Lock()
defer t.Unlock()
var err error
t.tx, err = t.db.NewTx()
if err != nil {
return nil, err
}
var indexes []int
for i := 0; i < len(keys); i++ {
err = t.add(0, keys[i], values[i])
if err != nil {
indexes = append(indexes, i)
}
}
// store root to db
if err := t.tx.Put(dbKeyRoot, t.root); err != nil {
return indexes, err
}
// update nLeafs
if err = t.incNLeafs(len(keys) - len(indexes)); err != nil {
return indexes, err
}
if err := t.tx.Commit(); err != nil {
return nil, err
}
return indexes, nil
// HashFunction returns Tree.hashFunction
func (t *Tree) HashFunction() HashFunction {
return t.hashFunction
} }
// Add inserts the key-value into the Tree. If the inputs come from a *big.Int, // Add inserts the key-value into the Tree. If the inputs come from a *big.Int,
@ -248,7 +212,7 @@ func (t *Tree) down(newKey, currKey []byte, siblings [][]byte,
if getLeaf { if getLeaf {
return currKey, currValue, siblings, nil return currKey, currValue, siblings, nil
} }
oldLeafKey, _ := readLeafValue(currValue)
oldLeafKey, _ := ReadLeafValue(currValue)
oldLeafKeyFull := make([]byte, t.hashFunction.Len()) oldLeafKeyFull := make([]byte, t.hashFunction.Len())
copy(oldLeafKeyFull[:], oldLeafKey) copy(oldLeafKeyFull[:], oldLeafKey)
@ -269,12 +233,12 @@ func (t *Tree) down(newKey, currKey []byte, siblings [][]byte,
// collect siblings while going down // collect siblings while going down
if path[currLvl] { if path[currLvl] {
// right // right
lChild, rChild := readIntermediateChilds(currValue)
lChild, rChild := ReadIntermediateChilds(currValue)
siblings = append(siblings, lChild) siblings = append(siblings, lChild)
return t.down(newKey, rChild, siblings, path, currLvl+1, getLeaf) return t.down(newKey, rChild, siblings, path, currLvl+1, getLeaf)
} }
// left // left
lChild, rChild := readIntermediateChilds(currValue)
lChild, rChild := ReadIntermediateChilds(currValue)
siblings = append(siblings, rChild) siblings = append(siblings, rChild)
return t.down(newKey, lChild, siblings, path, currLvl+1, getLeaf) return t.down(newKey, lChild, siblings, path, currLvl+1, getLeaf)
default: default:
@ -347,7 +311,8 @@ func newLeafValue(hashFunc HashFunction, k, v []byte) ([]byte, []byte, error) {
return leafKey, leafValue, nil return leafKey, leafValue, nil
} }
func readLeafValue(b []byte) ([]byte, []byte) {
// ReadLeafValue reads from a byte array the leaf key & value
func ReadLeafValue(b []byte) ([]byte, []byte) {
if len(b) < PrefixValueLen { if len(b) < PrefixValueLen {
return []byte{}, []byte{} return []byte{}, []byte{}
} }
@ -376,7 +341,8 @@ func newIntermediate(hashFunc HashFunction, l, r []byte) ([]byte, []byte, error)
return key, b, nil return key, b, nil
} }
func readIntermediateChilds(b []byte) ([]byte, []byte) {
// ReadIntermediateChilds reads from a byte array the two childs keys
func ReadIntermediateChilds(b []byte) ([]byte, []byte) {
if len(b) < PrefixValueLen { if len(b) < PrefixValueLen {
return []byte{}, []byte{} return []byte{}, []byte{}
} }
@ -421,7 +387,7 @@ func (t *Tree) Update(k, v []byte) error {
if err != nil { if err != nil {
return err return err
} }
oldKey, _ := readLeafValue(valueAtBottom)
oldKey, _ := ReadLeafValue(valueAtBottom)
if !bytes.Equal(oldKey, k) { if !bytes.Equal(oldKey, k) {
return fmt.Errorf("key %s does not exist", hex.EncodeToString(k)) return fmt.Errorf("key %s does not exist", hex.EncodeToString(k))
} }
@ -456,7 +422,7 @@ func (t *Tree) Update(k, v []byte) error {
// GenProof generates a MerkleTree proof for the given key. If the key exists in // GenProof generates a MerkleTree proof for the given key. If the key exists in
// the Tree, the proof will be of existence, if the key does not exist in the // the Tree, the proof will be of existence, if the key does not exist in the
// tree, the proof will be of non-existence. // tree, the proof will be of non-existence.
func (t *Tree) GenProof(k []byte) ([]byte, error) {
func (t *Tree) GenProof(k []byte) ([]byte, []byte, error) {
t.updateAccessTime() t.updateAccessTime()
keyPath := make([]byte, t.hashFunction.Len()) keyPath := make([]byte, t.hashFunction.Len())
copy(keyPath[:], k) copy(keyPath[:], k)
@ -466,10 +432,10 @@ func (t *Tree) GenProof(k []byte) ([]byte, error) {
var siblings [][]byte var siblings [][]byte
_, value, siblings, err := t.down(k, t.root, siblings, path, 0, true) _, value, siblings, err := t.down(k, t.root, siblings, path, 0, true)
if err != nil { if err != nil {
return nil, err
return nil, nil, err
} }
leafK, leafV := readLeafValue(value)
leafK, leafV := ReadLeafValue(value)
if !bytes.Equal(k, leafK) { if !bytes.Equal(k, leafK) {
fmt.Println("key not in Tree") fmt.Println("key not in Tree")
fmt.Println(leafK) fmt.Println(leafK)
@ -479,7 +445,7 @@ func (t *Tree) GenProof(k []byte) ([]byte, error) {
} }
s := PackSiblings(t.hashFunction, siblings) s := PackSiblings(t.hashFunction, siblings)
return s, nil
return value, s, nil
} }
// PackSiblings packs the siblings into a byte array. // PackSiblings packs the siblings into a byte array.
@ -567,7 +533,7 @@ func (t *Tree) Get(k []byte) ([]byte, []byte, error) {
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
leafK, leafV := readLeafValue(value)
leafK, leafV := ReadLeafValue(value)
if !bytes.Equal(k, leafK) { if !bytes.Equal(k, leafK) {
panic(fmt.Errorf("%s != %s", BytesToBigInt(k), BytesToBigInt(leafK))) panic(fmt.Errorf("%s != %s", BytesToBigInt(k), BytesToBigInt(leafK)))
} }
@ -662,6 +628,7 @@ func (t *Tree) GetNLeafs() (int, error) {
// Iterate iterates through the full Tree, executing the given function on each // Iterate iterates through the full Tree, executing the given function on each
// node of the Tree. // node of the Tree.
func (t *Tree) Iterate(f func([]byte, []byte)) error { func (t *Tree) Iterate(f func([]byte, []byte)) error {
// TODO allow to define which root to use
t.updateAccessTime() t.updateAccessTime()
return t.iter(t.root, f) return t.iter(t.root, f)
} }
@ -691,7 +658,7 @@ func (t *Tree) iterWithStop(k []byte, currLevel int, f func(int, []byte, []byte)
if stop { if stop {
return nil return nil
} }
l, r := readIntermediateChilds(v)
l, r := ReadIntermediateChilds(v)
if err = t.iterWithStop(l, currLevel, f); err != nil { if err = t.iterWithStop(l, currLevel, f); err != nil {
return err return err
} }
@ -719,6 +686,8 @@ func (t *Tree) iter(k []byte, f func([]byte, []byte)) error {
// Where S is the size of the output of the hash function used for the Tree. // Where S is the size of the output of the hash function used for the Tree.
func (t *Tree) Dump() ([]byte, error) { func (t *Tree) Dump() ([]byte, error) {
t.updateAccessTime() t.updateAccessTime()
// TODO allow to define which root to use
// WARNING current encoding only supports key & values of 255 bytes each // WARNING current encoding only supports key & values of 255 bytes each
// (due using only 1 byte for the length headers). // (due using only 1 byte for the length headers).
var b []byte var b []byte
@ -726,7 +695,7 @@ func (t *Tree) Dump() ([]byte, error) {
if v[0] != PrefixValueLeaf { if v[0] != PrefixValueLeaf {
return return
} }
leafK, leafV := readLeafValue(v)
leafK, leafV := ReadLeafValue(v)
kv := make([]byte, 2+len(leafK)+len(leafV)) kv := make([]byte, 2+len(leafK)+len(leafV))
kv[0] = byte(len(leafK)) kv[0] = byte(len(leafK))
kv[1] = byte(len(leafV)) kv[1] = byte(len(leafV))
@ -809,14 +778,14 @@ node [fontname=Monospace,fontsize=10,shape=box]
case PrefixValueLeaf: case PrefixValueLeaf:
fmt.Fprintf(w, "\"%v\" [style=filled];\n", hex.EncodeToString(k[:nChars])) fmt.Fprintf(w, "\"%v\" [style=filled];\n", hex.EncodeToString(k[:nChars]))
// key & value from the leaf // key & value from the leaf
kB, vB := readLeafValue(v)
kB, vB := ReadLeafValue(v)
fmt.Fprintf(w, "\"%v\" -> {\"k:%v\\nv:%v\"}\n", fmt.Fprintf(w, "\"%v\" -> {\"k:%v\\nv:%v\"}\n",
hex.EncodeToString(k[:nChars]), hex.EncodeToString(kB[:nChars]), hex.EncodeToString(k[:nChars]), hex.EncodeToString(kB[:nChars]),
hex.EncodeToString(vB[:nChars])) hex.EncodeToString(vB[:nChars]))
fmt.Fprintf(w, "\"k:%v\\nv:%v\" [style=dashed]\n", fmt.Fprintf(w, "\"k:%v\\nv:%v\" [style=dashed]\n",
hex.EncodeToString(kB[:nChars]), hex.EncodeToString(vB[:nChars])) hex.EncodeToString(kB[:nChars]), hex.EncodeToString(vB[:nChars]))
case PrefixValueIntermediate: case PrefixValueIntermediate:
l, r := readIntermediateChilds(v)
l, r := ReadIntermediateChilds(v)
lStr := hex.EncodeToString(l[:nChars]) lStr := hex.EncodeToString(l[:nChars])
rStr := hex.EncodeToString(r[:nChars]) rStr := hex.EncodeToString(r[:nChars])
eStr := "" eStr := ""

+ 1
- 1
tree_test.go

@ -267,7 +267,7 @@ func TestGenProofAndVerify(t *testing.T) {
} }
k := BigIntToBytes(big.NewInt(int64(7))) k := BigIntToBytes(big.NewInt(int64(7)))
siblings, err := tree.GenProof(k)
_, siblings, err := tree.GenProof(k)
c.Assert(err, qt.IsNil) c.Assert(err, qt.IsNil)
k = BigIntToBytes(big.NewInt(int64(7))) k = BigIntToBytes(big.NewInt(int64(7)))

Loading…
Cancel
Save