Skip to content

Commit

Permalink
Proof json marshaller/unmarshaller and constructor from raw data (#5)
Browse files Browse the repository at this point in the history
Proof json marshaller/unmarshaller and constructor from raw data
  • Loading branch information
OBrezhniev authored Apr 14, 2022
1 parent ee724b4 commit 50712d4
Show file tree
Hide file tree
Showing 6 changed files with 262 additions and 27 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ module github.com/iden3/go-merkletree-sql
go 1.16

require (
github.com/iden3/go-iden3-crypto v0.0.11
github.com/iden3/go-iden3-crypto v0.0.13
github.com/stretchr/testify v1.7.0
)
10 changes: 8 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dchest/blake512 v1.0.0/go.mod h1:FV1x7xPPLWukZlpDpWQ88rF/SFwZ5qbskrzhLMB92JI=
github.com/iden3/go-iden3-crypto v0.0.11 h1:5cFQ/BIqH9gjwe9Z68Hmc5jOTGqmMYnewIaTGCeO+6A=
github.com/iden3/go-iden3-crypto v0.0.11/go.mod h1:yUBWcXgAUDZxa1PvRl0zIT4Q4/rQO5PacE52Z06i8kw=
github.com/iden3/go-iden3-crypto v0.0.12 h1:dXZF+R9iI07DK49LHX/EKC3jTa0O2z+TUyvxjGK7V38=
github.com/iden3/go-iden3-crypto v0.0.12/go.mod h1:swXIv0HFbJKobbQBtsB50G7IHr6PbTowutSew/iBEoo=
github.com/iden3/go-iden3-crypto v0.0.13 h1:ixWRiaqDULNyIDdOWz2QQJG5t4PpNHkQk2P6GV94cok=
github.com/iden3/go-iden3-crypto v0.0.13/go.mod h1:swXIv0HFbJKobbQBtsB50G7IHr6PbTowutSew/iBEoo=
github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c=
github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8=
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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
Expand All @@ -13,6 +17,8 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
Expand Down
File renamed without changes.
8 changes: 4 additions & 4 deletions merkletree.go
Original file line number Diff line number Diff line change
Expand Up @@ -633,8 +633,8 @@ func getPath(numLevels int, k []byte) []bool {

// NodeAux contains the auxiliary node used in a non-existence proof.
type NodeAux struct {
Key *Hash
Value *Hash
Key *Hash `json:"key"`
Value *Hash `json:"value"`
}

// CircomSiblingsFromSiblings returns the full siblings compatible with circom
Expand Down Expand Up @@ -784,8 +784,8 @@ func (mt *MerkleTree) GenerateProof(ctx context.Context, k *big.Int,
return nil, nil, ErrInvalidNodeFound
}
if !bytes.Equal(siblingKey[:], HashZero[:]) {
SetBitBigEndian(p.notempties[:], uint(p.depth))
p.Siblings = append(p.Siblings, siblingKey)
SetBitBigEndian(p.notempties[:], p.depth)
p.siblings = append(p.siblings, siblingKey)
}
}
return nil, nil, ErrKeyNotFound
Expand Down
102 changes: 82 additions & 20 deletions proof.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,39 @@ package merkletree

import (
"bytes"
"encoding/json"
"fmt"
"math/big"
)

// Proof defines the required elements for a MT proof of existence or
// non-existence.
type Proof struct {
// existence indicates wether this is a proof of existence or
// non-existence.
// existence indicates whether this is a proof of existence or
// non-existence
Existence bool
// depth indicates how deep in the tree the proof goes.
// depth indicates how deep in the tree the proof goes
depth uint
// notempties is a bitmap of non-empty Siblings found in Siblings.
// notempties is a bitmap of non-empty siblings found in siblings
notempties [ElemBytesLen - proofFlagsLen]byte
// Siblings is a list of non-empty sibling keys.
Siblings []*Hash
NodeAux *NodeAux
// siblings is a list of non-empty sibling keys
siblings []*Hash
// Auxiliary node if needed
NodeAux *NodeAux
}

// NewProofFromBytes parses a byte array into a Proof.
// proofJSON defines the required elements for a MT proof in json serializable structure
type proofJSON struct {
// existence indicates whether this is a proof of existence or
// non-existence
Existence bool `json:"existence"`
// Siblings is a list of all sibling keys
Siblings []*Hash `json:"siblings"`
// Auxiliary node if needed
NodeAux *NodeAux `json:"node_aux,omitempty"`
}

// NewProofFromBytes parses a byte array into a Proof
func NewProofFromBytes(bs []byte) (*Proof, error) {
if len(bs) < ElemBytesLen {
return nil, ErrInvalidProofBytes
Expand All @@ -41,14 +54,14 @@ func NewProofFromBytes(bs []byte) (*Proof, error) {
}
var sib Hash
copy(sib[:], siblingBytes[sibIdx*ElemBytesLen:(sibIdx+1)*ElemBytesLen])
p.Siblings = append(p.Siblings, &sib)
p.siblings = append(p.siblings, &sib)
sibIdx++
}
}

if !p.Existence && ((bs[0] & 0x02) != 0) {
p.NodeAux = &NodeAux{Key: &Hash{}, Value: &Hash{}}
nodeAuxBytes := siblingBytes[len(p.Siblings)*ElemBytesLen:]
nodeAuxBytes := siblingBytes[len(p.siblings)*ElemBytesLen:]
if len(nodeAuxBytes) != 2*ElemBytesLen {
return nil, ErrInvalidProofBytes
}
Expand All @@ -58,9 +71,26 @@ func NewProofFromBytes(bs []byte) (*Proof, error) {
return p, nil
}

// NewProofFromData reconstructs proof from siblings and auxiliary node
func NewProofFromData(existence bool, allSiblings []*Hash, nodeAux *NodeAux) (*Proof, error) {
var p Proof
p.Existence = existence
p.NodeAux = nodeAux
var siblings []*Hash
p.depth = uint(len(allSiblings))
for lvl, sibling := range allSiblings {
if !sibling.Equals(&HashZero) {
SetBitBigEndian(p.notempties[:], uint(lvl))
siblings = append(siblings, sibling)
}
}
p.siblings = siblings
return &p, nil
}

// Bytes serializes a Proof into a byte array.
func (p *Proof) Bytes() []byte {
bsLen := proofFlagsLen + len(p.notempties) + ElemBytesLen*len(p.Siblings)
bsLen := proofFlagsLen + len(p.notempties) + ElemBytesLen*len(p.siblings)
if p.NodeAux != nil {
bsLen += 2 * ElemBytesLen
}
Expand All @@ -72,7 +102,7 @@ func (p *Proof) Bytes() []byte {
bs[1] = byte(p.depth)
copy(bs[proofFlagsLen:len(p.notempties)+proofFlagsLen], p.notempties[:])
siblingsBytes := bs[len(p.notempties)+proofFlagsLen:]
for i, k := range p.Siblings {
for i, k := range p.siblings {
copy(siblingsBytes[i*ElemBytesLen:(i+1)*ElemBytesLen], k[:])
}
if p.NodeAux != nil {
Expand All @@ -83,13 +113,50 @@ func (p *Proof) Bytes() []byte {
return bs
}

// AllSiblings returns all the siblings of the proof.
func (p *Proof) AllSiblings() []*Hash {
return SiblingsFromProof(p)
}

// MarshalJSON implements json.Marshaler interface
func (p Proof) MarshalJSON() ([]byte, error) {
obj := proofJSON{
Existence: p.Existence,
Siblings: p.AllSiblings(),
NodeAux: p.NodeAux,
}
return json.Marshal(obj)
}

// UnmarshalJSON implements json.Unmarshaler interface
func (p *Proof) UnmarshalJSON(data []byte) error {
var obj proofJSON
err := json.Unmarshal(data, &obj)
if err != nil {
return err
}

proof, err := NewProofFromData(obj.Existence, obj.Siblings, obj.NodeAux)
if err != nil {
return err
}

p.siblings = proof.siblings
p.Existence = proof.Existence
p.NodeAux = proof.NodeAux
p.notempties = proof.notempties
p.depth = proof.depth

return nil
}

// SiblingsFromProof returns all the siblings of the proof.
func SiblingsFromProof(proof *Proof) []*Hash {
sibIdx := 0
siblings := []*Hash{}
for lvl := 0; lvl < int(proof.depth); lvl++ {
if TestBitBigEndian(proof.notempties[:], uint(lvl)) {
siblings = append(siblings, proof.Siblings[sibIdx])
siblings = append(siblings, proof.siblings[sibIdx])
sibIdx++
} else {
siblings = append(siblings, &HashZero)
Expand All @@ -98,11 +165,6 @@ func SiblingsFromProof(proof *Proof) []*Hash {
return siblings
}

// AllSiblings returns all the siblings of the proof.
func (p *Proof) AllSiblings() []*Hash {
return SiblingsFromProof(p)
}

// VerifyProof verifies the Merkle Proof for the entry and root.
func VerifyProof(rootKey *Hash, proof *Proof, k, v *big.Int) bool {
rootFromProof, err := RootFromProof(proof, k, v)
Expand All @@ -118,7 +180,7 @@ func VerifyProof(rootKey *Hash, proof *Proof, k, v *big.Int) bool {
func RootFromProof(proof *Proof, k, v *big.Int) (*Hash, error) {
kHash := NewHashFromBigInt(k)
vHash := NewHashFromBigInt(v)
sibIdx := len(proof.Siblings) - 1
sibIdx := len(proof.siblings) - 1
var err error
var midKey *Hash
if proof.Existence {
Expand All @@ -144,7 +206,7 @@ func RootFromProof(proof *Proof, k, v *big.Int) (*Hash, error) {
var siblingKey *Hash
for lvl := int(proof.depth) - 1; lvl >= 0; lvl-- {
if TestBitBigEndian(proof.notempties[:], uint(lvl)) {
siblingKey = proof.Siblings[sibIdx]
siblingKey = proof.siblings[sibIdx]
sibIdx--
} else {
siblingKey = &HashZero
Expand Down
Loading

0 comments on commit 50712d4

Please sign in to comment.