mirror of
https://github.com/arnaucube/go-merkletree-old.git
synced 2026-02-28 05:36:39 +01:00
merkletree
This commit is contained in:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
leveldb
|
||||||
|
fmt
|
||||||
|
tmp
|
||||||
6
README.md
Normal file
6
README.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# go-merkletree [](https://goreportcard.com/report/github.com/arnaucube/go-merkletree) [](https://godoc.org/github.com/iden3/arnaucube/go-merkletree)
|
||||||
|
Optimized MerkleTree implementation in Go.
|
||||||
|
|
||||||
|
The MerkleTree is optimized in the design and concepts, to have a faster and lighter MerkleTree, maintaining compatibility with a non optimized MerkleTree. In this way, the MerkleRoot of the optimized MerkleTree will be the same that the MerkleRoot of the non optimized MerkleTree.
|
||||||
|
|
||||||
|
This repo is holds the nostalgic (old) version of the MerkleTree implementation that we used in the past in iden3, as now has been substituted by a new specification.
|
||||||
41
db.go
Normal file
41
db.go
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
package merkletree
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
|
||||||
|
"github.com/fatih/color"
|
||||||
|
common3 "github.com/iden3/go-iden3/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (mt *MerkleTree) Insert(key Hash, nodeType byte, indexLength uint32, nodeBytes []byte) error {
|
||||||
|
// add nodetype at the first byte of the value
|
||||||
|
var value []byte
|
||||||
|
value = append(value, nodeType)
|
||||||
|
indexLengthBytes := common3.Uint32ToBytes(indexLength)
|
||||||
|
value = append(value, indexLengthBytes[:]...)
|
||||||
|
value = append(value, nodeBytes[:]...)
|
||||||
|
|
||||||
|
err := mt.storage.Put(key[:], value, nil)
|
||||||
|
if err != nil {
|
||||||
|
color.Red(err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mt *MerkleTree) Get(key Hash) (byte, uint32, []byte, error) {
|
||||||
|
if bytes.Equal(key[:], EmptyNodeValue[:]) {
|
||||||
|
return 0, 0, EmptyNodeValue[:], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
value, err := mt.storage.Get(key[:], nil)
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, EmptyNodeValue[:], err
|
||||||
|
}
|
||||||
|
|
||||||
|
// get nodetype of the first byte of the value
|
||||||
|
nodeType := value[0]
|
||||||
|
indexLength := common3.BytesToUint32(value[1:5])
|
||||||
|
nodeBytes := value[5:]
|
||||||
|
return nodeType, indexLength, nodeBytes, err
|
||||||
|
}
|
||||||
10
go.mod
Normal file
10
go.mod
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
module github.com/arnaucube/merkletree
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/dchest/uniuri v0.0.0-20160212164326-8902c56451e9
|
||||||
|
github.com/ethereum/go-ethereum v1.8.20
|
||||||
|
github.com/fatih/color v1.7.0
|
||||||
|
github.com/iden3/go-iden3 v0.0.0-20181213124156-20b0f78e14ef
|
||||||
|
github.com/stretchr/testify v1.2.2
|
||||||
|
github.com/syndtr/goleveldb v0.0.0-20180815032940-ae2bd5eed72d
|
||||||
|
)
|
||||||
99
go.sum
Normal file
99
go.sum
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412/go.mod h1:WPjqKcmVOxf0XSf3YxCJs6N6AOSrOx3obionmG7T0y0=
|
||||||
|
github.com/aristanetworks/goarista v0.0.0-20180907105523-ff33da284e76/go.mod h1:D/tb0zPVXnP7fmsLZjtdUhSsumbK/ij54UXjjVgMGxQ=
|
||||||
|
github.com/arnaucube/cryptofun v0.0.0-20181124004321-9b11ae8280bd/go.mod h1:PZE8kKpHPD1UMrS3mTfAMmEEinGtijSwjxLRqRcD64A=
|
||||||
|
github.com/arnaucube/go-snark v0.0.0-20181212130559-7aafcfd5f3f4/go.mod h1:gLycS/B43DufBaH0jH8kqiE4A7w5FdOM8I9S416xh2Y=
|
||||||
|
github.com/btcsuite/btcd v0.0.0-20181013004428-67e573d211ac/go.mod h1:Dmm/EzmjnCiweXmzRIAiUWCInVmPgjkzgv5k4tVyXiQ=
|
||||||
|
github.com/cespare/cp v1.0.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s=
|
||||||
|
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/dchest/uniuri v0.0.0-20160212164326-8902c56451e9 h1:74lLNRzvsdIlkTgfDSMuaPjBr4cf6k7pwQQANm/yLKU=
|
||||||
|
github.com/dchest/uniuri v0.0.0-20160212164326-8902c56451e9/go.mod h1:GgB8SF9nRG+GqaDtLcwJZsQFhcogVCJ79j4EdT0c2V4=
|
||||||
|
github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ=
|
||||||
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||||
|
github.com/ethereum/go-ethereum v1.8.16/go.mod h1:PwpWDrCLZrV+tfrhqqF6kPknbISMHaJv9Ln3kPCZLwY=
|
||||||
|
github.com/ethereum/go-ethereum v1.8.20 h1:Sr6DLbdc7Fl2IMDC0sjF2wO1jTO5nALFC1SoQnyAQEk=
|
||||||
|
github.com/ethereum/go-ethereum v1.8.20/go.mod h1:PwpWDrCLZrV+tfrhqqF6kPknbISMHaJv9Ln3kPCZLwY=
|
||||||
|
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
||||||
|
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
|
github.com/gin-contrib/cors v0.0.0-20180926132136-4f98e8b8e930/go.mod h1:cw+u9IsAkC16e42NtYYVCLsHYXE98nB3M7Dr9mLSeH4=
|
||||||
|
github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
|
||||||
|
github.com/gin-gonic/gin v1.3.0/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6mUr7Y=
|
||||||
|
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
|
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w=
|
||||||
|
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
|
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||||
|
github.com/gxed/hashland v0.0.0-20180221191214-d9f6b97f8db2/go.mod h1:YUhWml1NaWLTNBl4NPptkB8MadfaIhgq+a2TRc+Mw4Q=
|
||||||
|
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||||
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
|
github.com/iden3/go-iden3 v0.0.0-20181213124156-20b0f78e14ef h1:HHgCgIGUb6TUOV6YnA/z9gxlPlzxdlgROWPrpEkkSMs=
|
||||||
|
github.com/iden3/go-iden3 v0.0.0-20181213124156-20b0f78e14ef/go.mod h1:kBO77X3PdJ9EglJy0h/0BKYTa4Hu8Wa+QRSbeXdUi8A=
|
||||||
|
github.com/ipfs/go-ipfs-api v1.3.5/go.mod h1:YWGjU+7Bdls1CpvsKsV6EsQ/KMyQqSpBru2hme/5WQg=
|
||||||
|
github.com/ipfs/go-ipfs-cmdkit v1.1.3/go.mod h1:9FtbMdUabcSqv/G4/8WCxSLxkZxn/aZEFrxxqnVcRbg=
|
||||||
|
github.com/ipfsconsortium/go-ipfsc v0.0.0-20180821102820-2014c66a1b4a/go.mod h1:4MbfcV8YX3CWjkWgUIH4vEVk002kMJlOEmhoSJP8SeE=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v0.0.0-20180402223658-b729f2633dfe/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
github.com/libp2p/go-flow-metrics v0.2.0/go.mod h1:Iv1GH0sG8DtYN3SVJ2eG221wMiNpZxBdp967ls1g+k8=
|
||||||
|
github.com/libp2p/go-libp2p-crypto v2.0.1+incompatible/go.mod h1:WHpT3tvhh7GM2INNJhQBuI6J+5z/o3QI0lTF5UVjppk=
|
||||||
|
github.com/libp2p/go-libp2p-metrics v2.1.7+incompatible/go.mod h1:ko4lRyuvbgwwxD2TJvt2RHONahjJlkn6l7L/iEbJBf0=
|
||||||
|
github.com/libp2p/go-libp2p-peer v2.4.0+incompatible/go.mod h1:fS2eFKRO1IomwBAf+SuE8P1XOT/AAiqSgVPNIFA7Jc0=
|
||||||
|
github.com/libp2p/go-libp2p-protocol v1.0.0/go.mod h1:Af9n4PiruirSDjHycM1QuiMi/1VZNHYcK8cLgFJLZ4s=
|
||||||
|
github.com/libp2p/go-libp2p-pubsub v0.9.36/go.mod h1:E2KoEMwM5nWtdGV+wWueN7g/j++VL9tmCfjzk1fLpWc=
|
||||||
|
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||||
|
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
|
||||||
|
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||||
|
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
|
||||||
|
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||||
|
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||||
|
github.com/mgutz/logxi v0.0.0-20161027140823-aebf8a7d67ab/go.mod h1:y1pL58r5z2VvAjeG1VLGc8zOQgSOzbKN7kMHPvFXJ+8=
|
||||||
|
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ=
|
||||||
|
github.com/minio/sha256-simd v0.0.0-20181005183134-51976451ce19/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U=
|
||||||
|
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
|
github.com/mitchellh/mapstructure v1.0.0/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
|
github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8=
|
||||||
|
github.com/multiformats/go-multiaddr v1.3.0/go.mod h1:1JAWc2R8uiQTLrCHI/lmOkXYu5B8025fQbZjq8//YgY=
|
||||||
|
github.com/multiformats/go-multiaddr-net v1.6.3/go.mod h1:AO4WqKzxLt+paJ0N0kufj6teQ2R6fZbnItDvGTwilmk=
|
||||||
|
github.com/multiformats/go-multihash v1.0.8/go.mod h1:sT17phG+xVgnrZc8ht/ZoCIV0sKRwvmZkXk46UfSxM4=
|
||||||
|
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
|
github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||||
|
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
||||||
|
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||||
|
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/rjeczalik/notify v0.9.2/go.mod h1:aErll2f0sUX9PXZnVNyeiObbmTlk5jnMoCa4QEjJeqM=
|
||||||
|
github.com/rs/cors v1.5.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
|
||||||
|
github.com/sirupsen/logrus v1.1.0 h1:65VZabgUiV9ktjGM5nTq0+YurgTyX+YI2lSSfDjI+qU=
|
||||||
|
github.com/sirupsen/logrus v1.1.0/go.mod h1:zrgwTnHtNr00buQ1vSptGe8m1f/BbgsPukg8qsT7A+A=
|
||||||
|
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
|
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||||
|
github.com/spf13/cast v1.2.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg=
|
||||||
|
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||||
|
github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||||
|
github.com/spf13/viper v1.2.1/go.mod h1:P4AexN0a+C9tGAnUFNwDMYYZv3pjFuvmeiMyKRaNVlI=
|
||||||
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||||
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
github.com/syndtr/goleveldb v0.0.0-20180815032940-ae2bd5eed72d h1:4J9HCZVpvDmj2tiKGSTUnb3Ok/9CEQb9oqu9LHKQQpc=
|
||||||
|
github.com/syndtr/goleveldb v0.0.0-20180815032940-ae2bd5eed72d/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
|
||||||
|
github.com/ugorji/go/codec v0.0.0-20180927125128-99ea80c8b19a/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||||
|
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||||
|
github.com/whyrusleeping/tar-utils v0.0.0-20180509141711-8c6c8ba81d5c/go.mod h1:xxcJeBb7SIUl/Wzkz1eVKJE/CB34YNrqX2TQI6jY9zs=
|
||||||
|
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793 h1:u+LnwYTOOW7Ukr/fppxEb1Nwz0AtPflrblfvUudpo+I=
|
||||||
|
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20180926154720-4dfa2610cdf3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20180906133057-8cf3aee42992/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20180926160741-c2ed4eda69e7 h1:bit1t3mgdR35yN0cX0G8orgLtOuyL9Wqxa1mccLB0ig=
|
||||||
|
golang.org/x/sys v0.0.0-20180926160741-c2ed4eda69e7/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
|
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
|
||||||
|
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
|
||||||
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
352
merkletree.go
Normal file
352
merkletree.go
Normal file
@@ -0,0 +1,352 @@
|
|||||||
|
package merkletree
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/syndtr/goleveldb/leveldb"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// EmptyNodeType indicates the type of an EmptyNodeValue Node
|
||||||
|
EmptyNodeType = 00
|
||||||
|
// NormalNodeType indicates the type of a middle Node
|
||||||
|
normalNodeType = 01
|
||||||
|
// FinalNodeType indicates the type of middle Node that is in an optimized branch, then in the value contains the value of the final leaf node of that branch
|
||||||
|
finalNodeType = 02
|
||||||
|
// ValueNodeType indicates the type of a value Node
|
||||||
|
valueNodeType = 03
|
||||||
|
// RootNodeType indicates the type of a root Node
|
||||||
|
rootNodeType = 04
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrNodeAlreadyExists is an error that indicates that a node already exists in the merkletree database
|
||||||
|
ErrNodeAlreadyExists = errors.New("node already exists")
|
||||||
|
rootNodeValue = HashBytes([]byte("root"))
|
||||||
|
// EmptyNodeValue is a [32]byte EmptyNodeValue array, all to zero
|
||||||
|
EmptyNodeValue = Hash{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Hash used in this tree, is the [32]byte keccak()
|
||||||
|
type Hash [32]byte
|
||||||
|
|
||||||
|
// Value is the interface of a generic leaf, a key value object stored in the leveldb
|
||||||
|
type Value interface {
|
||||||
|
IndexLength() uint32 // returns the index length value
|
||||||
|
Bytes() []byte // returns the value in byte array representation
|
||||||
|
}
|
||||||
|
|
||||||
|
//MerkleTree struct with the main elements of the Merkle Tree
|
||||||
|
type MerkleTree struct {
|
||||||
|
// sync.RWMutex
|
||||||
|
storage *leveldb.DB
|
||||||
|
root Hash
|
||||||
|
numLevels int // Height of the Merkle Tree, number of levels
|
||||||
|
}
|
||||||
|
|
||||||
|
// New generates a new Merkle Tree
|
||||||
|
func New(storage *leveldb.DB, numLevels int) (*MerkleTree, error) {
|
||||||
|
var mt MerkleTree
|
||||||
|
mt.storage = storage
|
||||||
|
mt.numLevels = numLevels
|
||||||
|
var err error
|
||||||
|
_, _, rootHash, err := mt.Get(rootNodeValue)
|
||||||
|
if err != nil {
|
||||||
|
mt.root = EmptyNodeValue
|
||||||
|
err = mt.Insert(rootNodeValue, rootNodeType, 0, mt.root[:])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
copy(mt.root[:], rootHash)
|
||||||
|
return &mt, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Root returns the merkletree.Root
|
||||||
|
func (mt *MerkleTree) Root() Hash {
|
||||||
|
return mt.root
|
||||||
|
}
|
||||||
|
|
||||||
|
// NumLevels returns the merkletree.NumLevels
|
||||||
|
func (mt *MerkleTree) NumLevels() int {
|
||||||
|
return mt.numLevels
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add adds the leaf to the MT
|
||||||
|
func (mt *MerkleTree) Add(v Value) error {
|
||||||
|
// add the leaf that we are adding
|
||||||
|
mt.Insert(HashBytes(v.Bytes()), valueNodeType, v.IndexLength(), v.Bytes())
|
||||||
|
|
||||||
|
hi := HashBytes(v.Bytes()[:v.IndexLength()])
|
||||||
|
path := getPath(mt.numLevels, hi)
|
||||||
|
|
||||||
|
nodeHash := mt.root
|
||||||
|
var siblings []Hash
|
||||||
|
for i := mt.numLevels - 2; i >= 0; i-- {
|
||||||
|
nodeType, indexLength, nodeBytes, err := mt.Get(nodeHash)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if nodeType == byte(finalNodeType) {
|
||||||
|
hiChild := HashBytes(nodeBytes[:indexLength])
|
||||||
|
pathChild := getPath(mt.numLevels, hiChild)
|
||||||
|
posDiff := comparePaths(pathChild, path)
|
||||||
|
if posDiff == -1 {
|
||||||
|
return ErrNodeAlreadyExists
|
||||||
|
}
|
||||||
|
finalNode1Hash := calcHashFromLeafAndLevel(posDiff, pathChild, HashBytes(nodeBytes))
|
||||||
|
mt.Insert(finalNode1Hash, finalNodeType, indexLength, nodeBytes)
|
||||||
|
finalNode2Hash := calcHashFromLeafAndLevel(posDiff, path, HashBytes(v.Bytes()))
|
||||||
|
mt.Insert(finalNode2Hash, finalNodeType, v.IndexLength(), v.Bytes())
|
||||||
|
// now the parent
|
||||||
|
var parentNode treeNode
|
||||||
|
if path[posDiff] {
|
||||||
|
parentNode = treeNode{
|
||||||
|
ChildL: finalNode1Hash,
|
||||||
|
ChildR: finalNode2Hash,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
parentNode = treeNode{
|
||||||
|
ChildL: finalNode2Hash,
|
||||||
|
ChildR: finalNode1Hash,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
siblings = append(siblings, getEmptiesBetweenIAndPosHash(mt, i, posDiff+1)...)
|
||||||
|
if mt.root, err = mt.replaceLeaf(siblings, path[posDiff+1:], parentNode.Ht(), normalNodeType, 0, parentNode.Bytes()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
mt.Insert(rootNodeValue, rootNodeType, 0, mt.root[:])
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
node := parseNodeBytes(nodeBytes)
|
||||||
|
var sibling Hash
|
||||||
|
if !path[i] {
|
||||||
|
nodeHash = node.ChildL
|
||||||
|
sibling = node.ChildR
|
||||||
|
} else {
|
||||||
|
nodeHash = node.ChildR
|
||||||
|
sibling = node.ChildL
|
||||||
|
}
|
||||||
|
siblings = append(siblings, sibling)
|
||||||
|
|
||||||
|
if bytes.Equal(nodeHash[:], EmptyNodeValue[:]) {
|
||||||
|
// if the node is EmptyNodeValue, the leaf data will go directly at that height, as a Final Node
|
||||||
|
if i == mt.numLevels-2 && bytes.Equal(siblings[len(siblings)-1][:], EmptyNodeValue[:]) {
|
||||||
|
// if the pt node is the unique in the tree, just put it into the root node
|
||||||
|
// this means to be in i==mt.NumLevels-2 && nodeHash==EmptyNodeValue
|
||||||
|
finalNodeHash := calcHashFromLeafAndLevel(i+1, path, HashBytes(v.Bytes()))
|
||||||
|
mt.Insert(finalNodeHash, finalNodeType, v.IndexLength(), v.Bytes())
|
||||||
|
mt.root = finalNodeHash
|
||||||
|
mt.Insert(rootNodeValue, rootNodeType, 0, mt.root[:])
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
finalNodeHash := calcHashFromLeafAndLevel(i, path, HashBytes(v.Bytes()))
|
||||||
|
if mt.root, err = mt.replaceLeaf(siblings, path[i:], finalNodeHash, finalNodeType, v.IndexLength(), v.Bytes()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
mt.Insert(rootNodeValue, rootNodeType, 0, mt.root[:])
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
mt.root, err = mt.replaceLeaf(siblings, path, HashBytes(v.Bytes()), valueNodeType, v.IndexLength(), v.Bytes())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
mt.Insert(rootNodeValue, rootNodeType, 0, mt.root[:])
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateProof generates the Merkle Proof from a given leafHash for the current root
|
||||||
|
func (mt *MerkleTree) GenerateProof(hi Hash) ([]byte, error) {
|
||||||
|
var empties [32]byte
|
||||||
|
|
||||||
|
path := getPath(mt.numLevels, hi)
|
||||||
|
var siblings []Hash
|
||||||
|
nodeHash := mt.root
|
||||||
|
|
||||||
|
for level := 0; level < mt.numLevels-1; level++ {
|
||||||
|
nodeType, indexLength, nodeBytes, err := mt.Get(nodeHash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if nodeType == byte(finalNodeType) {
|
||||||
|
realValueInPos, err := mt.GetValueInPos(hi)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if bytes.Equal(realValueInPos[:], EmptyNodeValue[:]) {
|
||||||
|
// go until the path is different, then get the nodes between this FinalNode and the node in the diffPath, they will be the siblings of the merkle proof
|
||||||
|
leafHi := HashBytes(nodeBytes[:indexLength]) // hi of element that was in the end of the branch (the finalNode)
|
||||||
|
pathChild := getPath(mt.numLevels, leafHi)
|
||||||
|
|
||||||
|
// get the position where the path is different
|
||||||
|
posDiff := comparePaths(pathChild, path)
|
||||||
|
if posDiff == -1 {
|
||||||
|
return nil, ErrNodeAlreadyExists
|
||||||
|
}
|
||||||
|
|
||||||
|
if posDiff != mt.NumLevels()-1-level {
|
||||||
|
sibling := calcHashFromLeafAndLevel(posDiff, pathChild, HashBytes(nodeBytes))
|
||||||
|
setbitmap(empties[:], uint(mt.NumLevels()-2-posDiff))
|
||||||
|
siblings = append([]Hash{sibling}, siblings...)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
node := parseNodeBytes(nodeBytes)
|
||||||
|
|
||||||
|
var sibling Hash
|
||||||
|
if !path[mt.numLevels-level-2] {
|
||||||
|
nodeHash = node.ChildL
|
||||||
|
sibling = node.ChildR
|
||||||
|
} else {
|
||||||
|
nodeHash = node.ChildR
|
||||||
|
sibling = node.ChildL
|
||||||
|
}
|
||||||
|
if !bytes.Equal(sibling[:], EmptyNodeValue[:]) {
|
||||||
|
setbitmap(empties[:], uint(level))
|
||||||
|
siblings = append([]Hash{sibling}, siblings...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// merge empties and siblings
|
||||||
|
var mp []byte
|
||||||
|
mp = append(mp, empties[:]...)
|
||||||
|
for k := range siblings {
|
||||||
|
mp = append(mp, siblings[k][:]...)
|
||||||
|
}
|
||||||
|
return mp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetValueInPos returns the merkletree value in the position of the Hash of the Index (Hi)
|
||||||
|
func (mt *MerkleTree) GetValueInPos(hi Hash) ([]byte, error) {
|
||||||
|
path := getPath(mt.numLevels, hi)
|
||||||
|
nodeHash := mt.root
|
||||||
|
for i := mt.numLevels - 2; i >= 0; i-- {
|
||||||
|
nodeType, indexLength, nodeBytes, err := mt.Get(nodeHash)
|
||||||
|
if err != nil {
|
||||||
|
return nodeBytes, err
|
||||||
|
}
|
||||||
|
if nodeType == byte(finalNodeType) {
|
||||||
|
// check if nodeBytes path is different of hi
|
||||||
|
index := nodeBytes[:indexLength]
|
||||||
|
hi := HashBytes(index)
|
||||||
|
nodePath := getPath(mt.numLevels, hi)
|
||||||
|
posDiff := comparePaths(path, nodePath)
|
||||||
|
// if is different, return an EmptyNodeValue, else return the nodeBytes
|
||||||
|
if posDiff != -1 {
|
||||||
|
return EmptyNodeValue[:], nil
|
||||||
|
}
|
||||||
|
return nodeBytes, nil
|
||||||
|
}
|
||||||
|
node := parseNodeBytes(nodeBytes)
|
||||||
|
if !path[i] {
|
||||||
|
nodeHash = node.ChildL
|
||||||
|
} else {
|
||||||
|
nodeHash = node.ChildR
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_, _, valueBytes, err := mt.Get(nodeHash)
|
||||||
|
if err != nil {
|
||||||
|
return valueBytes, err
|
||||||
|
}
|
||||||
|
return valueBytes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func calcHashFromLeafAndLevel(untilLevel int, path []bool, leafHash Hash) Hash {
|
||||||
|
nodeCurrLevel := leafHash
|
||||||
|
for i := 0; i < untilLevel; i++ {
|
||||||
|
if path[i] {
|
||||||
|
node := treeNode{
|
||||||
|
ChildL: EmptyNodeValue,
|
||||||
|
ChildR: nodeCurrLevel,
|
||||||
|
}
|
||||||
|
nodeCurrLevel = node.Ht()
|
||||||
|
} else {
|
||||||
|
node := treeNode{
|
||||||
|
ChildL: nodeCurrLevel,
|
||||||
|
ChildR: EmptyNodeValue,
|
||||||
|
}
|
||||||
|
nodeCurrLevel = node.Ht()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nodeCurrLevel
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mt *MerkleTree) replaceLeaf(siblings []Hash, path []bool, newLeafHash Hash, nodetype byte, indexLength uint32, newLeafValue []byte) (Hash, error) {
|
||||||
|
// add the new leaf
|
||||||
|
mt.Insert(newLeafHash, nodetype, indexLength, newLeafValue)
|
||||||
|
currNode := newLeafHash
|
||||||
|
// here the path is only the path[posDiff+1]
|
||||||
|
for i := 0; i < len(siblings); i++ {
|
||||||
|
if !path[i] {
|
||||||
|
node := treeNode{
|
||||||
|
ChildL: currNode,
|
||||||
|
ChildR: siblings[len(siblings)-1-i],
|
||||||
|
}
|
||||||
|
mt.Insert(node.Ht(), normalNodeType, 0, node.Bytes())
|
||||||
|
currNode = node.Ht()
|
||||||
|
} else {
|
||||||
|
|
||||||
|
node := treeNode{
|
||||||
|
ChildL: siblings[len(siblings)-1-i],
|
||||||
|
ChildR: currNode,
|
||||||
|
}
|
||||||
|
mt.Insert(node.Ht(), normalNodeType, 0, node.Bytes())
|
||||||
|
currNode = node.Ht()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return currNode, nil // currNode = root
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckProof validates the Merkle Proof for the leafHash and root
|
||||||
|
func CheckProof(root Hash, proof []byte, hi Hash, ht Hash, numLevels int) bool {
|
||||||
|
var empties [32]byte
|
||||||
|
copy(empties[:], proof[:len(empties)])
|
||||||
|
hashLen := len(EmptyNodeValue)
|
||||||
|
|
||||||
|
var siblings []Hash
|
||||||
|
for i := len(empties); i < len(proof); i += hashLen {
|
||||||
|
var siblingHash Hash
|
||||||
|
copy(siblingHash[:], proof[i:i+hashLen])
|
||||||
|
siblings = append(siblings, siblingHash)
|
||||||
|
}
|
||||||
|
|
||||||
|
path := getPath(numLevels, hi)
|
||||||
|
nodeHash := ht
|
||||||
|
siblingUsedPos := 0
|
||||||
|
|
||||||
|
for level := numLevels - 2; level >= 0; level-- {
|
||||||
|
var sibling Hash
|
||||||
|
if testbitmap(empties[:], uint(level)) {
|
||||||
|
sibling = siblings[siblingUsedPos]
|
||||||
|
siblingUsedPos++
|
||||||
|
} else {
|
||||||
|
sibling = EmptyNodeValue
|
||||||
|
}
|
||||||
|
// calculate the nodeHash with the current nodeHash and the sibling
|
||||||
|
var node treeNode
|
||||||
|
if path[numLevels-level-2] {
|
||||||
|
node = treeNode{
|
||||||
|
ChildL: sibling,
|
||||||
|
ChildR: nodeHash,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
node = treeNode{
|
||||||
|
ChildL: nodeHash,
|
||||||
|
ChildR: sibling,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if both childs are EmptyNodeValue, the parent will be EmptyNodeValue
|
||||||
|
if bytes.Equal(nodeHash[:], EmptyNodeValue[:]) && bytes.Equal(sibling[:], EmptyNodeValue[:]) {
|
||||||
|
nodeHash = EmptyNodeValue
|
||||||
|
} else {
|
||||||
|
nodeHash = node.Ht()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bytes.Equal(nodeHash[:], root[:])
|
||||||
|
}
|
||||||
162
merkletreeSpecification_test.go
Normal file
162
merkletreeSpecification_test.go
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
package merkletree
|
||||||
|
|
||||||
|
/*
|
||||||
|
This is just an example of basic tests for the iden3-merkletree-specification.
|
||||||
|
The methods and variables names can be different.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
common3 "github.com/iden3/go-iden3/common"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
type testBytesLeaf struct {
|
||||||
|
data []byte
|
||||||
|
indexLength uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c testBytesLeaf) Bytes() (b []byte) {
|
||||||
|
return c.data
|
||||||
|
}
|
||||||
|
func (c testBytesLeaf) IndexLength() uint32 {
|
||||||
|
return c.indexLength
|
||||||
|
}
|
||||||
|
func (c testBytesLeaf) Hi() Hash {
|
||||||
|
h := HashBytes(c.Bytes()[:c.IndexLength()])
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
func newTestBytesLeaf(data string, indexLength uint32) testBytesLeaf {
|
||||||
|
return testBytesLeaf{
|
||||||
|
data: []byte(data),
|
||||||
|
indexLength: indexLength,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test to check the iden3-merkletree-specification
|
||||||
|
func TestIden3MerkletreeSpecification(t *testing.T) {
|
||||||
|
h := HashBytes([]byte("test")).Hex()
|
||||||
|
assert.Equal(t, "0x9c22ff5f21f0b81b113e63f7db6da94fedef11b2119b4088b89664fb9a3cb658", h)
|
||||||
|
|
||||||
|
h = HashBytes([]byte("authorizeksign")).Hex()
|
||||||
|
assert.Equal(t, "0x353f867ef725411de05e3d4b0a01c37cf7ad24bcc213141a05ed7726d7932a1f", h)
|
||||||
|
|
||||||
|
mt := newTestingMerkle(t, 140)
|
||||||
|
defer mt.storage.Close()
|
||||||
|
|
||||||
|
// empty tree
|
||||||
|
assert.Equal(t, "0x0000000000000000000000000000000000000000000000000000000000000000", mt.Root().Hex())
|
||||||
|
|
||||||
|
// add leaf
|
||||||
|
leaf := testBytesLeaf{
|
||||||
|
data: []byte("this is a test leaf"),
|
||||||
|
indexLength: 15,
|
||||||
|
}
|
||||||
|
assert.Nil(t, mt.Add(leaf))
|
||||||
|
assert.Equal(t, "0xb4fdf8a653198f0e179ccb3af7e4fc09d76247f479d6cfc95cd92d6fda589f27", mt.Root().Hex())
|
||||||
|
|
||||||
|
// proof with only one leaf in the MerkleTree
|
||||||
|
proof, err := mt.GenerateProof(leaf.Hi())
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, "0x0000000000000000000000000000000000000000000000000000000000000000", common3.BytesToHex(proof))
|
||||||
|
|
||||||
|
// add a second leaf
|
||||||
|
leaf2 := testBytesLeaf{
|
||||||
|
data: []byte("this is a second test leaf"),
|
||||||
|
indexLength: 15,
|
||||||
|
}
|
||||||
|
err = mt.Add(leaf2)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, "0x8ac95e9c8a6fbd40bb21de7895ee35f9c8f30ca029dbb0972c02344f49462e82", mt.Root().Hex())
|
||||||
|
|
||||||
|
// proof of the second leaf, with two leafs in the MerkleTree
|
||||||
|
proof2, err := mt.GenerateProof(leaf2.Hi())
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, "0x0000000000000000000000000000000000000000000000000000000000000001fd8e1a60cdb23c0c7b2cf8462c99fafd905054dccb0ed75e7c8a7d6806749b6b", common3.BytesToHex(proof2))
|
||||||
|
|
||||||
|
// proof of emptyLeaf
|
||||||
|
leaf3 := testBytesLeaf{
|
||||||
|
data: []byte("this is a third test leaf"),
|
||||||
|
indexLength: 15,
|
||||||
|
}
|
||||||
|
proof3, err := mt.GenerateProof(leaf3.Hi())
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, "0x000000000000000000000000000000000000000000000000000000000000000389741fa23da77c259781ad8f4331a5a7d793eef1db7e5200ddfc8e5f5ca7ce2bfd8e1a60cdb23c0c7b2cf8462c99fafd905054dccb0ed75e7c8a7d6806749b6b", common3.BytesToHex(proof3))
|
||||||
|
|
||||||
|
// getLeafByHi/GetValueInPos
|
||||||
|
bytesInHi, err := mt.GetValueInPos(leaf2.Hi())
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, leaf2.Bytes(), bytesInHi)
|
||||||
|
|
||||||
|
// check proof
|
||||||
|
rootBytes, err := common3.HexToBytes("0x7d7c5e8f4b3bf434f3d9d223359c4415e2764dd38de2e025fbf986e976a7ed3d")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
mp, err := common3.HexToBytes("0x0000000000000000000000000000000000000000000000000000000000000002d45aada6eec346222eaa6b5d3a9260e08c9b62fcf63c72bc05df284de07e6a52")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
hiBytes, err := common3.HexToBytes("0x786677808ba77bdd9090a969f1ef2cbd1ac5aecd9e654f340500159219106878")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
htBytes, err := common3.HexToBytes("0x786677808ba77bdd9090a969f1ef2cbd1ac5aecd9e654f340500159219106878")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
var root, hi, ht Hash
|
||||||
|
copy(root[:], rootBytes)
|
||||||
|
copy(hi[:], hiBytes)
|
||||||
|
copy(ht[:], htBytes)
|
||||||
|
verified := CheckProof(root, mp, hi, ht, 140)
|
||||||
|
assert.True(t, verified)
|
||||||
|
|
||||||
|
// check proof of empty
|
||||||
|
rootBytes, err = common3.HexToBytes("0x8f021d00c39dcd768974ddfe0d21f5d13f7215bea28db1f1cb29842b111332e7")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
mp, err = common3.HexToBytes("0x0000000000000000000000000000000000000000000000000000000000000004bf8e980d2ed328ae97f65c30c25520aeb53ff837579e392ea1464934c7c1feb9")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
hiBytes, err = common3.HexToBytes("0xa69792a4cff51f40b7a1f7ae596c6ded4aba241646a47538898f17f2a8dff647")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
htBytes, err = common3.HexToBytes("0x0000000000000000000000000000000000000000000000000000000000000000")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
copy(root[:], rootBytes)
|
||||||
|
copy(hi[:], hiBytes)
|
||||||
|
copy(ht[:], htBytes)
|
||||||
|
verified = CheckProof(root, mp, hi, ht, 140)
|
||||||
|
assert.True(t, verified)
|
||||||
|
|
||||||
|
// check the proof generated in the previous steps
|
||||||
|
verified = CheckProof(mt.Root(), proof2, leaf2.Hi(), HashBytes(leaf2.Bytes()), 140)
|
||||||
|
assert.True(t, verified)
|
||||||
|
// check proof of no existence (emptyLeaf), as we are prooving an empty leaf, the Ht is an empty value (0x000...0)
|
||||||
|
verified = CheckProof(mt.Root(), proof3, leaf3.Hi(), EmptyNodeValue, 140)
|
||||||
|
assert.True(t, verified)
|
||||||
|
|
||||||
|
// add leafs in different orders
|
||||||
|
mt1 := newTestingMerkle(t, 140)
|
||||||
|
defer mt.storage.Close()
|
||||||
|
|
||||||
|
mt1.Add(newTestBytesLeaf("0 this is a test leaf", 15))
|
||||||
|
mt1.Add(newTestBytesLeaf("1 this is a test leaf", 15))
|
||||||
|
mt1.Add(newTestBytesLeaf("2 this is a test leaf", 15))
|
||||||
|
mt1.Add(newTestBytesLeaf("3 this is a test leaf", 15))
|
||||||
|
mt1.Add(newTestBytesLeaf("4 this is a test leaf", 15))
|
||||||
|
|
||||||
|
mt2 := newTestingMerkle(t, 140)
|
||||||
|
defer mt.storage.Close()
|
||||||
|
|
||||||
|
mt2.Add(newTestBytesLeaf("2 this is a test leaf", 15))
|
||||||
|
mt2.Add(newTestBytesLeaf("1 this is a test leaf", 15))
|
||||||
|
mt2.Add(newTestBytesLeaf("0 this is a test leaf", 15))
|
||||||
|
mt2.Add(newTestBytesLeaf("3 this is a test leaf", 15))
|
||||||
|
mt2.Add(newTestBytesLeaf("4 this is a test leaf", 15))
|
||||||
|
|
||||||
|
assert.Equal(t, mt1.Root().Hex(), mt2.Root().Hex())
|
||||||
|
|
||||||
|
// adding 1000 leafs
|
||||||
|
mt1000 := newTestingMerkle(t, 140)
|
||||||
|
defer mt.storage.Close()
|
||||||
|
|
||||||
|
numToAdd := 1000
|
||||||
|
for i := 0; i < numToAdd; i++ {
|
||||||
|
leaf := newTestBytesLeaf(strconv.Itoa(i)+" this is a test leaf", 15)
|
||||||
|
mt1000.Add(leaf)
|
||||||
|
}
|
||||||
|
assert.Equal(t, "0x6e2da580b2920cd78ed8d4e4bf41e209dfc99ef28bc19560042f0ac803e0d6f7", mt1000.Root().Hex())
|
||||||
|
}
|
||||||
351
merkletree_test.go
Normal file
351
merkletree_test.go
Normal file
@@ -0,0 +1,351 @@
|
|||||||
|
package merkletree
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/dchest/uniuri"
|
||||||
|
common3 "github.com/iden3/go-iden3/common"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/syndtr/goleveldb/leveldb"
|
||||||
|
)
|
||||||
|
|
||||||
|
type testBase struct {
|
||||||
|
Length [4]byte
|
||||||
|
Namespace Hash
|
||||||
|
Type Hash
|
||||||
|
Version uint32
|
||||||
|
}
|
||||||
|
type testLeaf struct {
|
||||||
|
testBase
|
||||||
|
extraIndex struct {
|
||||||
|
Data []byte
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseTestLeafBytes(b []byte) testLeaf {
|
||||||
|
var c testLeaf
|
||||||
|
copy(c.testBase.Length[:], b[0:4])
|
||||||
|
copy(c.testBase.Namespace[:], b[4:36])
|
||||||
|
copy(c.testBase.Type[:], b[36:68])
|
||||||
|
versionBytes := b[68:72]
|
||||||
|
c.testBase.Version = common3.BytesToUint32(versionBytes)
|
||||||
|
c.extraIndex.Data = b[72:]
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
func (c testLeaf) Bytes() (b []byte) {
|
||||||
|
b = append(b, c.testBase.Length[:]...)
|
||||||
|
b = append(b, c.testBase.Namespace[:]...)
|
||||||
|
b = append(b, c.testBase.Type[:]...)
|
||||||
|
versionBytes := common3.Uint32ToBytes(c.testBase.Version)
|
||||||
|
b = append(b, versionBytes[:]...)
|
||||||
|
b = append(b, c.extraIndex.Data[:]...)
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
func (c testLeaf) IndexLength() uint32 {
|
||||||
|
return uint32(len(c.Bytes()))
|
||||||
|
}
|
||||||
|
func (c testLeaf) hi() Hash {
|
||||||
|
h := HashBytes(c.Bytes())
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
func newTestLeaf(namespaceStr, typeStr string, data []byte) testLeaf {
|
||||||
|
var c testLeaf
|
||||||
|
c.testBase.Length = [4]byte{0x00, 0x00, 0x00, 0x48}
|
||||||
|
c.testBase.Namespace = HashBytes([]byte(namespaceStr))
|
||||||
|
c.testBase.Type = HashBytes([]byte(typeStr))
|
||||||
|
c.testBase.Version = 0
|
||||||
|
c.extraIndex.Data = data
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
type Fatalable interface {
|
||||||
|
Fatal(args ...interface{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTestingMerkle(f Fatalable, numLevels int) *MerkleTree {
|
||||||
|
tim := time.Now().Unix()
|
||||||
|
tstr := fmt.Sprint(tim)
|
||||||
|
s := uniuri.New()
|
||||||
|
db, err := leveldb.OpenFile("tmp/db"+s+"-"+tstr, nil)
|
||||||
|
if err != nil {
|
||||||
|
f.Fatal(err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// defer db.Close()
|
||||||
|
mt, err := New(db, numLevels)
|
||||||
|
if err != nil {
|
||||||
|
f.Fatal(err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return mt
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewMT(t *testing.T) {
|
||||||
|
|
||||||
|
//create a new MT
|
||||||
|
mt := newTestingMerkle(t, 140)
|
||||||
|
defer mt.storage.Close()
|
||||||
|
assert.Equal(t, "0x0000000000000000000000000000000000000000000000000000000000000000", mt.Root().Hex())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddLeaf(t *testing.T) {
|
||||||
|
|
||||||
|
mt := newTestingMerkle(t, 140)
|
||||||
|
defer mt.storage.Close()
|
||||||
|
|
||||||
|
leaf := newTestLeaf("iden3.io", "typespec", []byte("c1"))
|
||||||
|
assert.Equal(t, "0x939862c94ca9772fc9e2621df47128b1d4041b514e19edc969a92d8f0dae558f", leaf.hi().Hex())
|
||||||
|
|
||||||
|
assert.Nil(t, mt.Add(leaf))
|
||||||
|
assert.Equal(t, "0x9d3c407ff02c813cd474c0a6366b4f7c58bf417a38268f7a0d73a8bca2490b9b", mt.Root().Hex())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddLeafs(t *testing.T) {
|
||||||
|
|
||||||
|
mt := newTestingMerkle(t, 140)
|
||||||
|
defer mt.storage.Close()
|
||||||
|
|
||||||
|
leaf := newTestLeaf("iden3.io", "typespec", []byte("c1"))
|
||||||
|
assert.Equal(t, "0x939862c94ca9772fc9e2621df47128b1d4041b514e19edc969a92d8f0dae558f", leaf.hi().Hex())
|
||||||
|
assert.Nil(t, mt.Add(leaf))
|
||||||
|
|
||||||
|
assert.Nil(t, mt.Add(newTestLeaf("iden3.io2", "typespec2", []byte("c2"))))
|
||||||
|
assert.Equal(t, "0xebae8fb483b48ba6c337136535198eb8bcf891daba40ac81e28958c09b9b229b", mt.Root().Hex())
|
||||||
|
|
||||||
|
mt.Add(newTestLeaf("iden3.io3", "typespec3", []byte("c3")))
|
||||||
|
mt.Add(newTestLeaf("iden3.io4", "typespec4", []byte("c4")))
|
||||||
|
assert.Equal(t, "0xb4b51aa0c77a8e5ed0a099d7c11c7d2a9219ef241da84f0689da1f40a5f6ac31", mt.Root().Hex())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddLeafsCollision(t *testing.T) {
|
||||||
|
|
||||||
|
mt := newTestingMerkle(t, 140)
|
||||||
|
defer mt.storage.Close()
|
||||||
|
|
||||||
|
leaf := newTestLeaf("iden3.io", "typespec", []byte("c1"))
|
||||||
|
assert.Nil(t, mt.Add(leaf))
|
||||||
|
|
||||||
|
root1 := mt.Root()
|
||||||
|
assert.EqualError(t, mt.Add(leaf), ErrNodeAlreadyExists.Error())
|
||||||
|
|
||||||
|
assert.Equal(t, root1.Hex(), mt.Root().Hex())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddLeafsDifferentOrders(t *testing.T) {
|
||||||
|
|
||||||
|
mt1 := newTestingMerkle(t, 140)
|
||||||
|
defer mt1.storage.Close()
|
||||||
|
|
||||||
|
mt1.Add(newTestLeaf("iden3.io", "typespec", []byte("c1")))
|
||||||
|
mt1.Add(newTestLeaf("iden3.io2", "typespec2", []byte("c2")))
|
||||||
|
mt1.Add(newTestLeaf("iden3.io3", "typespec3", []byte("c3")))
|
||||||
|
mt1.Add(newTestLeaf("iden3.io4", "typespec4", []byte("c4")))
|
||||||
|
mt1.Add(newTestLeaf("iden3.io5", "typespec5", []byte("c5")))
|
||||||
|
|
||||||
|
mt2 := newTestingMerkle(t, 140)
|
||||||
|
defer mt2.storage.Close()
|
||||||
|
|
||||||
|
mt2.Add(newTestLeaf("iden3.io3", "typespec3", []byte("c3")))
|
||||||
|
mt2.Add(newTestLeaf("iden3.io2", "typespec2", []byte("c2")))
|
||||||
|
mt2.Add(newTestLeaf("iden3.io", "typespec", []byte("c1")))
|
||||||
|
mt2.Add(newTestLeaf("iden3.io4", "typespec4", []byte("c4")))
|
||||||
|
mt2.Add(newTestLeaf("iden3.io5", "typespec5", []byte("c5")))
|
||||||
|
|
||||||
|
assert.Equal(t, mt1.Root().Hex(), mt2.Root().Hex())
|
||||||
|
}
|
||||||
|
func TestBenchmarkAddingLeafs(t *testing.T) {
|
||||||
|
|
||||||
|
mt := newTestingMerkle(t, 140)
|
||||||
|
defer mt.storage.Close()
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
|
numToAdd := 1000
|
||||||
|
for i := 0; i < numToAdd; i++ {
|
||||||
|
leaf := newTestLeaf("iden3.io"+strconv.Itoa(i), "typespec"+strconv.Itoa(i), []byte("c"+strconv.Itoa(i)))
|
||||||
|
mt.Add(leaf)
|
||||||
|
}
|
||||||
|
fmt.Print("time elapsed adding " + strconv.Itoa(numToAdd) + " leafs: ")
|
||||||
|
fmt.Println(time.Since(start))
|
||||||
|
}
|
||||||
|
func BenchmarkAddingLeafs(b *testing.B) {
|
||||||
|
|
||||||
|
mt := newTestingMerkle(b, 140)
|
||||||
|
defer mt.storage.Close()
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
leaf := newTestLeaf("iden3.io"+strconv.Itoa(i), "typespec"+strconv.Itoa(i), []byte("c"+strconv.Itoa(i)))
|
||||||
|
if err := mt.Add(leaf); err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGenerateProof(t *testing.T) {
|
||||||
|
mt := newTestingMerkle(t, 140)
|
||||||
|
defer mt.storage.Close()
|
||||||
|
|
||||||
|
mt.Add(newTestLeaf("iden3.io_3", "typespec_3", []byte("c3")))
|
||||||
|
mt.Add(newTestLeaf("iden3.io_2", "typespec_2", []byte("c2")))
|
||||||
|
|
||||||
|
leaf1 := newTestLeaf("iden3.io_1", "typespec_1", []byte("c1"))
|
||||||
|
assert.Nil(t, mt.Add(leaf1))
|
||||||
|
|
||||||
|
mp, err := mt.GenerateProof(parseTestLeafBytes(leaf1.Bytes()).hi())
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
mpHexExpected := "0000000000000000000000000000000000000000000000000000000000000002beb0fd6dcf18d37fe51cf34beacd4c524d9c039ef9da2a27ccd3e7edf662c39c"
|
||||||
|
assert.Equal(t, mpHexExpected, hex.EncodeToString(mp))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCheckProof(t *testing.T) {
|
||||||
|
mt := newTestingMerkle(t, 140)
|
||||||
|
defer mt.storage.Close()
|
||||||
|
|
||||||
|
leaf1 := newTestLeaf("iden3.io_1", "typespec_1", []byte("c1"))
|
||||||
|
assert.Nil(t, mt.Add(leaf1))
|
||||||
|
|
||||||
|
leaf3 := newTestLeaf("iden3.io_3", "typespec_3", []byte("c3"))
|
||||||
|
assert.Nil(t, mt.Add(leaf3))
|
||||||
|
|
||||||
|
mp, err := mt.GenerateProof(parseTestLeafBytes(leaf1.Bytes()).hi())
|
||||||
|
assert.Nil(t, err)
|
||||||
|
verified := CheckProof(mt.Root(), mp, leaf1.hi(), HashBytes(leaf1.Bytes()), mt.NumLevels())
|
||||||
|
assert.True(t, verified)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProofOfEmpty(t *testing.T) { // proof of a non revocated leaf, prove that is empty the hi position of the leaf.version+1
|
||||||
|
mt := newTestingMerkle(t, 140)
|
||||||
|
defer mt.storage.Close()
|
||||||
|
|
||||||
|
leaf1 := newTestLeaf("iden3.io_1", "typespec_1", []byte("c1"))
|
||||||
|
// proof when there is nothing in the tree
|
||||||
|
mp, err := mt.GenerateProof(leaf1.hi())
|
||||||
|
assert.Nil(t, err)
|
||||||
|
verified := CheckProof(mt.Root(), mp, leaf1.hi(), EmptyNodeValue, mt.NumLevels())
|
||||||
|
assert.True(t, verified)
|
||||||
|
|
||||||
|
// add the first leaf
|
||||||
|
assert.Nil(t, mt.Add(leaf1))
|
||||||
|
|
||||||
|
// proof when there is only one leaf in the tree
|
||||||
|
leaf2 := newTestLeaf("iden3.io_2", "typespec_2", []byte("c2"))
|
||||||
|
mp, err = mt.GenerateProof(leaf2.hi())
|
||||||
|
assert.Nil(t, err)
|
||||||
|
verified = CheckProof(mt.Root(), mp, leaf2.hi(), EmptyNodeValue, mt.NumLevels())
|
||||||
|
assert.True(t, verified)
|
||||||
|
|
||||||
|
// check that the value in Hi is Empty
|
||||||
|
valueInPos, err := mt.GetValueInPos(leaf2.hi())
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, EmptyNodeValue.Bytes(), valueInPos)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DifferentNonExistenceProofs(t *testing.T) {
|
||||||
|
mt1 := newTestingMerkle(t, 140)
|
||||||
|
defer mt1.storage.Close()
|
||||||
|
|
||||||
|
mt2 := newTestingMerkle(t, 140)
|
||||||
|
defer mt2.storage.Close()
|
||||||
|
|
||||||
|
leaf1 := newTestLeaf("iden3.io_1", "typespec_1", []byte("c1"))
|
||||||
|
leaf2 := newTestLeaf("iden3.io_1", "typespec_1", []byte("c2"))
|
||||||
|
|
||||||
|
assert.Nil(t, mt1.Add(leaf1))
|
||||||
|
assert.Nil(t, mt2.Add(leaf2))
|
||||||
|
|
||||||
|
leaf1.Version++
|
||||||
|
leaf2.Version++
|
||||||
|
|
||||||
|
np1, err := mt1.GenerateProof(leaf1.hi())
|
||||||
|
assert.Nil(t, err)
|
||||||
|
np2, err := mt2.GenerateProof(leaf2.hi())
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
assert.True(t, CheckProof(mt1.Root(), np1, leaf1.hi(), EmptyNodeValue, mt1.NumLevels()))
|
||||||
|
assert.True(t, CheckProof(mt2.Root(), np2, leaf2.hi(), EmptyNodeValue, mt2.NumLevels()))
|
||||||
|
|
||||||
|
assert.Equal(t, "0000000000000000000000000000000000000000000000000000000000000010a40617c8c3390736831d00b2003e2133353190f5d3b3a586cf829f0f2009aacc", hex.EncodeToString(np1))
|
||||||
|
assert.Equal(t, "0000000000000000000000000000000000000000000000000000000000000001b274a34a3bd95915fe982a0163e3e0a2f79a371b8307661341f8914e22b313e1", hex.EncodeToString(np2))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetLeafInPos(t *testing.T) {
|
||||||
|
mt := newTestingMerkle(t, 140)
|
||||||
|
defer mt.storage.Close()
|
||||||
|
|
||||||
|
for i := 0; i < 50; i++ {
|
||||||
|
leaf := newTestLeaf("iden3.io"+strconv.Itoa(i), "typespec"+strconv.Itoa(i), []byte("c"+strconv.Itoa(i)))
|
||||||
|
mt.Add(leaf)
|
||||||
|
|
||||||
|
}
|
||||||
|
leaf1 := newTestLeaf("iden3.io_x", "typespec_x", []byte("cx"))
|
||||||
|
assert.Nil(t, mt.Add(leaf1))
|
||||||
|
|
||||||
|
leaf := parseTestLeafBytes(leaf1.Bytes())
|
||||||
|
leafInPosBytes, err := mt.GetValueInPos(leaf.hi())
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, leaf1.Bytes(), leafInPosBytes)
|
||||||
|
|
||||||
|
// empty value in position
|
||||||
|
leaf2 := newTestLeaf("iden3.io_y", "typespec_y", []byte("cy"))
|
||||||
|
leafInPosBytes, err = mt.GetValueInPos(leaf2.hi())
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, EmptyNodeValue[:], leafInPosBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
type vt struct {
|
||||||
|
v []byte
|
||||||
|
idxlen uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v vt) IndexLength() uint32 {
|
||||||
|
return v.idxlen
|
||||||
|
}
|
||||||
|
func (v vt) Bytes() []byte {
|
||||||
|
return v.v
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVector4(t *testing.T) {
|
||||||
|
mt := newTestingMerkle(t, 4)
|
||||||
|
defer mt.storage.Close()
|
||||||
|
|
||||||
|
zeros := make([]byte, 32, 32)
|
||||||
|
zeros[31] = 1 // to avoid adding Empty element
|
||||||
|
assert.Nil(t, mt.Add(vt{zeros, uint32(1)}))
|
||||||
|
v := vt{zeros, uint32(2)}
|
||||||
|
assert.Nil(t, mt.Add(v))
|
||||||
|
proof, _ := mt.GenerateProof(HashBytes(v.Bytes()[:v.IndexLength()]))
|
||||||
|
assert.True(t, CheckProof(mt.Root(), proof, HashBytes(v.Bytes()[:v.IndexLength()]), HashBytes(v.Bytes()), mt.NumLevels()))
|
||||||
|
assert.Equal(t, 4, mt.NumLevels())
|
||||||
|
assert.Equal(t, "0000000000000000000000000000000000000000000000000000000000000001", hex.EncodeToString(v.Bytes()))
|
||||||
|
assert.Equal(t, "0xc1b95ffbb999a6dd7a472a610a98891ffae95cc973d1d1e21acfdd68db830b51", mt.Root().Hex())
|
||||||
|
assert.Equal(t, "00000000000000000000000000000000000000000000000000000000000000023cf025e4b4fc3ebe57374bf0e0c78ceb0009bdc4466a45174d80e8f508d1a4e3", hex.EncodeToString(proof))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVector140(t *testing.T) {
|
||||||
|
mt := newTestingMerkle(t, 140)
|
||||||
|
defer mt.storage.Close()
|
||||||
|
|
||||||
|
zeros := make([]byte, 32, 32)
|
||||||
|
zeros[31] = 1 // to avoid adding Empty element
|
||||||
|
for i := 1; i < len(zeros)-1; i++ {
|
||||||
|
v := vt{zeros, uint32(i)}
|
||||||
|
assert.Nil(t, mt.Add(v))
|
||||||
|
proof, err := mt.GenerateProof(HashBytes(v.Bytes()[:v.IndexLength()]))
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.True(t, CheckProof(mt.Root(), proof, HashBytes(v.Bytes()[:v.IndexLength()]), HashBytes(v.Bytes()), mt.NumLevels()))
|
||||||
|
if i == len(zeros)-2 {
|
||||||
|
assert.Equal(t, 140, mt.NumLevels())
|
||||||
|
assert.Equal(t, uint32(30), v.IndexLength())
|
||||||
|
assert.Equal(t, "0000000000000000000000000000000000000000000000000000000000000001", hex.EncodeToString(v.Bytes()))
|
||||||
|
assert.Equal(t, "0x35f83288adf03bfb61d8d57fab9ed092da79833b58bbdbe9579b636753494ebd", mt.Root().Hex())
|
||||||
|
assert.Equal(t, "000000000000000000000000000000000000000000000000000000000000001f0d1f363115f3333197a009b6674f46bba791308af220ad71515567702b3b44a2b540c1abad0ff81386a78b77e8907a56b7268d24513928ae83497adf4ad93a55e380267ead8305202da0640c1518e144dee87717c732b738fa182c6ef458defd6baf50022b01e3222715d4fca4c198e94536101f6ac314b3d261d3aaa0684395c1db60626e01c39fe4f69418055c2ebd70e0c07b6d9db5c4aed0a11ed2b6a773", hex.EncodeToString(proof))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
36
node.go
Normal file
36
node.go
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package merkletree
|
||||||
|
|
||||||
|
import "bytes"
|
||||||
|
|
||||||
|
// treeNode is the data structure of an intermediate node of the Merkle Tree
|
||||||
|
type treeNode struct {
|
||||||
|
ChildL Hash // hash of the left child
|
||||||
|
ChildR Hash // hash of the right child
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bytes returns an array of bytes with the Node data
|
||||||
|
func (n *treeNode) Bytes() (b []byte) {
|
||||||
|
b = append(b, n.ChildL[:]...)
|
||||||
|
b = append(b, n.ChildR[:]...)
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ht returns the hash of the full node
|
||||||
|
func (n *treeNode) Ht() Hash {
|
||||||
|
h := HashBytes(n.Bytes())
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseNodeBytes returns a Node struct from an array of bytes
|
||||||
|
func parseNodeBytes(b []byte) treeNode {
|
||||||
|
if bytes.Equal(b, EmptyNodeValue[:]) {
|
||||||
|
var node treeNode
|
||||||
|
node.ChildL = EmptyNodeValue
|
||||||
|
node.ChildR = EmptyNodeValue
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
var node treeNode
|
||||||
|
copy(node.ChildL[:], b[:32])
|
||||||
|
copy(node.ChildR[:], b[32:])
|
||||||
|
return node
|
||||||
|
}
|
||||||
63
print.go
Normal file
63
print.go
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
package merkletree
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/fatih/color"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (mt *MerkleTree) printLevel(parent Hash, iLevel int, maxLevel int) {
|
||||||
|
for i := 0; i < iLevel; i++ {
|
||||||
|
fmt.Print(" ")
|
||||||
|
}
|
||||||
|
fmt.Print("level ")
|
||||||
|
fmt.Print(iLevel)
|
||||||
|
fmt.Print(" - ")
|
||||||
|
fmt.Print("'" + parent.Hex() + "' = ")
|
||||||
|
nodeType, _, nodeBytes, err := mt.Get(parent)
|
||||||
|
if err != nil {
|
||||||
|
color.Red(err.Error())
|
||||||
|
}
|
||||||
|
var node treeNode
|
||||||
|
if nodeType == byte(normalNodeType) {
|
||||||
|
node = parseNodeBytes(nodeBytes)
|
||||||
|
color.Blue("'" + node.ChildL.Hex() + "' - '" + node.ChildR.Hex() + "'")
|
||||||
|
} else if nodeType == byte(valueNodeType) {
|
||||||
|
color.Green("value")
|
||||||
|
} else if nodeType == byte(finalNodeType) { //typ==FINAL_NODE
|
||||||
|
fmt.Print("[FinalTree]:")
|
||||||
|
color.Cyan("final tree node: " + HashBytes(nodeBytes).Hex())
|
||||||
|
_, _, leafNodeBytes, err := mt.Get(HashBytes(nodeBytes))
|
||||||
|
if err != nil {
|
||||||
|
color.Red(err.Error())
|
||||||
|
}
|
||||||
|
for i := 0; i < iLevel; i++ {
|
||||||
|
fmt.Print(" ")
|
||||||
|
}
|
||||||
|
color.Cyan(" leaf value: 0x" + hex.EncodeToString(leafNodeBytes))
|
||||||
|
} else {
|
||||||
|
//EMPTY_NODE
|
||||||
|
fmt.Print("[EmptyBranch]:")
|
||||||
|
fmt.Println(EmptyNodeValue.Bytes())
|
||||||
|
}
|
||||||
|
iLevel++
|
||||||
|
if len(node.ChildR) > 0 && iLevel < maxLevel && nodeType != byte(EmptyNodeType) && nodeType != byte(finalNodeType) {
|
||||||
|
mt.printLevel(node.ChildL, iLevel, maxLevel)
|
||||||
|
mt.printLevel(node.ChildR, iLevel, maxLevel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrintFullMT prints the tree in the terminal, all the levels with all the nodes
|
||||||
|
func (mt *MerkleTree) PrintFullMT() {
|
||||||
|
mt.printLevel(mt.root, 0, mt.numLevels-1)
|
||||||
|
fmt.Print("root: ")
|
||||||
|
color.Yellow(mt.Root().Hex())
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrintLevelsMT prints the tree in the terminal until a specified depth
|
||||||
|
func (mt *MerkleTree) PrintLevelsMT(maxLevel int) {
|
||||||
|
mt.printLevel(mt.root, 0, mt.numLevels-1-maxLevel)
|
||||||
|
fmt.Print("root: ")
|
||||||
|
color.Yellow(mt.Root().Hex())
|
||||||
|
}
|
||||||
61
utils.go
Normal file
61
utils.go
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
package merkletree
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Hex returns a hex string from the Hash type
|
||||||
|
func (hash Hash) Hex() string {
|
||||||
|
r := "0x"
|
||||||
|
h := hex.EncodeToString(hash[:])
|
||||||
|
r = r + h
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bytes returns a byte array from a Hash
|
||||||
|
func (hash Hash) Bytes() []byte {
|
||||||
|
return hash[:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// HashBytes performs a Keccak256 hash over the bytes
|
||||||
|
func HashBytes(b []byte) (hash Hash) {
|
||||||
|
h := crypto.Keccak256(b)
|
||||||
|
copy(hash[:], h)
|
||||||
|
return hash
|
||||||
|
}
|
||||||
|
|
||||||
|
// getPath returns the binary path, from the leaf to the root
|
||||||
|
func getPath(numLevels int, hi Hash) []bool {
|
||||||
|
|
||||||
|
path := []bool{}
|
||||||
|
for bitno := numLevels - 2; bitno >= 0; bitno-- {
|
||||||
|
path = append(path, testbitmap(hi[:], uint(bitno)))
|
||||||
|
}
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
|
func comparePaths(b1 []bool, b2 []bool) int {
|
||||||
|
for i := len(b1) - 1; i >= 0; i-- {
|
||||||
|
if b1[i] != b2[i] {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func getEmptiesBetweenIAndPosHash(mt *MerkleTree, iPos int, posHash int) []Hash {
|
||||||
|
var sibl []Hash
|
||||||
|
for i := iPos; i >= posHash; i-- {
|
||||||
|
sibl = append(sibl, EmptyNodeValue)
|
||||||
|
}
|
||||||
|
return sibl
|
||||||
|
}
|
||||||
|
|
||||||
|
func setbitmap(bitmap []byte, bitno uint) {
|
||||||
|
bitmap[uint(len(bitmap))-bitno/8-1] |= 1 << (bitno % 8)
|
||||||
|
}
|
||||||
|
func testbitmap(bitmap []byte, bitno uint) bool {
|
||||||
|
return bitmap[uint(len(bitmap))-bitno/8-1]&(1<<(bitno%8)) > 0
|
||||||
|
}
|
||||||
32
utils_test.go
Normal file
32
utils_test.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package merkletree
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetSetBitmap(t *testing.T) {
|
||||||
|
|
||||||
|
var v Hash
|
||||||
|
setbitmap(v[:], 7)
|
||||||
|
setbitmap(v[:], 8)
|
||||||
|
setbitmap(v[:], 255)
|
||||||
|
expected := "0x8000000000000000000000000000000000000000000000000000000000000180"
|
||||||
|
assert.Equal(t, expected, v.Hex())
|
||||||
|
|
||||||
|
assert.Equal(t, false, testbitmap(v[:], 6))
|
||||||
|
assert.Equal(t, true, testbitmap(v[:], 7))
|
||||||
|
assert.Equal(t, true, testbitmap(v[:], 8))
|
||||||
|
assert.Equal(t, false, testbitmap(v[:], 9))
|
||||||
|
assert.Equal(t, true, testbitmap(v[:], 255))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHashBytes(t *testing.T) {
|
||||||
|
h := HashBytes([]byte("test")).Hex()
|
||||||
|
assert.Equal(t, "0x9c22ff5f21f0b81b113e63f7db6da94fedef11b2119b4088b89664fb9a3cb658", h)
|
||||||
|
|
||||||
|
h = HashBytes([]byte("authorizeksign")).Hex()
|
||||||
|
assert.Equal(t, "0x353f867ef725411de05e3d4b0a01c37cf7ad24bcc213141a05ed7726d7932a1f", h)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user