Skip to content

Commit

Permalink
initial version (does not work)
Browse files Browse the repository at this point in the history
  • Loading branch information
lucasmenendez committed Nov 14, 2024
1 parent 8a9c760 commit ba7db5d
Show file tree
Hide file tree
Showing 7 changed files with 212 additions and 1 deletion.
Binary file added arbo/mimc_bls12_377/gnark.pprof
Binary file not shown.
74 changes: 74 additions & 0 deletions arbo/mimc_bls12_377/verifier.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package arbo

import (
"github.com/consensys/gnark/frontend"
"github.com/consensys/gnark/std/hash/mimc"
)

// prevLevel function calculates the previous level of the merkle tree given the
// current leaf, the current path bit of the leaf, the validity of the sibling
// and the sibling itself.
func prevLevel(api frontend.API, leaf, ipath, valid, sibling frontend.Variable) (frontend.Variable, error) {
// l, r = path == 1 ? sibling, current : current, sibling
l, r := api.Select(ipath, sibling, leaf), api.Select(ipath, leaf, sibling)
// intermediateLeafKey = H(l | r)
hash, err := mimc.NewMiMC(api)
if err != nil {
return 0, err
}
hash.Write(l, r)
intermediateLeafKey := hash.Sum()
// newCurrent = valid == 1 ? current : intermediateLeafKey
return api.Select(valid, intermediateLeafKey, leaf), nil
}

// strictCmp function compares a and b and returns:
//
// 1 a != b
// 0 a == b
func strictCmp(api frontend.API, a, b frontend.Variable) frontend.Variable {
return api.Select(api.IsZero(api.Sub(a, b)), 0, 1)
}

// isValid function returns 1 if the the sibling provided is a valid sibling or
// 0 otherwise. To check if the sibling is valid, its leaf value and it must be
// different from the previous leaf value and the previous sibling.
func isValid(api frontend.API, sibling, prevSibling, leaf, prevLeaf frontend.Variable) frontend.Variable {
cmp1, cmp2 := strictCmp(api, leaf, prevLeaf), strictCmp(api, sibling, prevSibling)
return api.Select(api.Or(cmp1, cmp2), 1, 0)
}

// CheckProof receives the parameters of a proof of Arbo to recalculate the
// root with them and compare it with the provided one, verifiying the proof.
func CheckProof(api frontend.API, key, value, root frontend.Variable, siblings []frontend.Variable) error {
// calculate the path from the provided key to decide which leaf is the
// correct one in every level of the tree
path := api.ToBinary(key, len(siblings))
// calculate the value leaf to start with it to rebuild the tree
// leafValue = H(key | value | 1)
hash, err := mimc.NewMiMC(api)
if err != nil {
return err
}
hash.Write(key, value, 1)
leafValue := hash.Sum()
api.Println("[gnark] leafKey", key)
api.Println("[gnark] leafValue", leafValue)
// calculate the root and compare it with the provided one
prevLeaf := leafValue
currentLeaf := leafValue
prevSibling := frontend.Variable(0)
for i := len(siblings) - 1; i >= 0; i-- {
// check if the sibling is valid
valid := isValid(api, siblings[i], prevSibling, currentLeaf, prevLeaf)
prevLeaf = currentLeaf
prevSibling = siblings[i]
// compute the next leaf value
currentLeaf, err = prevLevel(api, currentLeaf, path[i], valid, siblings[i])
if err != nil {
return err
}
}
api.AssertIsEqual(currentLeaf, root)
return nil
}
134 changes: 134 additions & 0 deletions arbo/mimc_bls12_377/verifier_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package arbo

import (
"fmt"
"log"
"math/big"
"os"
"testing"
"time"

"github.com/consensys/gnark-crypto/ecc"
"github.com/consensys/gnark/backend"
"github.com/consensys/gnark/frontend"
"github.com/consensys/gnark/frontend/cs/r1cs"
"github.com/consensys/gnark/profile"
"github.com/consensys/gnark/test"
arbotree "github.com/vocdoni/arbo"
"go.vocdoni.io/dvote/db"
"go.vocdoni.io/dvote/db/pebbledb"
"go.vocdoni.io/dvote/tree/arbo"
"go.vocdoni.io/dvote/util"
)

type testVerifierCircuit struct {
Root frontend.Variable
Key frontend.Variable
Value frontend.Variable
Siblings [160]frontend.Variable
}

func (circuit *testVerifierCircuit) Define(api frontend.API) error {
return CheckProof(api, circuit.Key, circuit.Value, circuit.Root, circuit.Siblings[:])
}

func TestVerifier(t *testing.T) {
p := profile.Start()
now := time.Now()
_, _ = frontend.Compile(ecc.BLS12_377.ScalarField(), r1cs.NewBuilder, &testVerifierCircuit{})
fmt.Println("elapsed", time.Since(now))
p.Stop()
fmt.Println("constrains", p.NbConstraints())

assert := test.NewAssert(t)

// inputs := successInputs(t, 10)
inputs, err := generateCensusProof(10, util.RandomBytes(20), big.NewInt(10).Bytes())
if err != nil {
t.Fatal(err)
}
// binputs, _ := json.MarshalIndent(inputs, " ", " ")
// fmt.Println("inputs", string(binputs))
assert.SolvingSucceeded(&testVerifierCircuit{}, &inputs, test.WithCurves(ecc.BLS12_377), test.WithBackends(backend.GROTH16))
}

var baseField, _ = new(big.Int).SetString("25825498262808887005865186224201665565126143020923472090132963926938185026661", 10)

// BigToFF function returns the finite field representation of the big.Int
// provided. It uses Euclidean Modulus and the BN254 curve scalar field to
// represent the provided number.
func BigToFF(iv *big.Int) *big.Int {
z := big.NewInt(0)
if c := iv.Cmp(baseField); c == 0 {
return z
} else if c != 1 && iv.Cmp(z) != -1 {
return iv
}
return z.Mod(iv, baseField)
}

func generateCensusProof(n int, k, v []byte) (testVerifierCircuit, error) {
dir := os.TempDir()
defer func() {
_ = os.RemoveAll(dir)
}()
database, err := pebbledb.New(db.Options{Path: dir})
if err != nil {
return testVerifierCircuit{}, err
}
tree, err := arbotree.NewTree(arbotree.Config{
Database: database,
MaxLevels: 160,
HashFunction: arbotree.HashFunctionMiMC_BLS12_377,
})
if err != nil {
return testVerifierCircuit{}, err
}
k = BigToFF(new(big.Int).SetBytes(k)).Bytes()
// add the first key-value pair
if err = tree.Add(k, v); err != nil {
return testVerifierCircuit{}, err
}
h := tree.HashFunction()
r, _ := h.Hash(k, v, []byte{1})
log.Println("[go] leafKey", new(big.Int).SetBytes(k))
log.Println("[go] leafValue", new(big.Int).SetBytes(r))
// add random addresses
for i := 1; i < n; i++ {
rk := BigToFF(new(big.Int).SetBytes(util.RandomBytes(20))).Bytes()
rv := new(big.Int).SetBytes(util.RandomBytes(8)).Bytes()
if err = tree.Add(rk, rv); err != nil {
return testVerifierCircuit{}, err
}
}
// generate the proof
_, _, siblings, exist, err := tree.GenProof(k)
if err != nil {
return testVerifierCircuit{}, err
}
if !exist {
return testVerifierCircuit{}, fmt.Errorf("error building the merkle tree: key not found")
}
unpackedSiblings, err := arbo.UnpackSiblings(arbo.HashFunctionPoseidon, siblings)
if err != nil {
return testVerifierCircuit{}, err
}
paddedSiblings := [160]frontend.Variable{}
for i := 0; i < 160; i++ {
if i < len(unpackedSiblings) {
paddedSiblings[i] = arbo.BytesLEToBigInt(unpackedSiblings[i])
} else {
paddedSiblings[i] = big.NewInt(0)
}
}
root, err := tree.Root()
if err != nil {
return testVerifierCircuit{}, err
}
return testVerifierCircuit{
Root: root,
Key: k,
Value: new(big.Int).SetBytes(v),
Siblings: paddedSiblings,
}, nil
}
Binary file added arbo/poseidon_bn254/gnark.pprof
Binary file not shown.
2 changes: 1 addition & 1 deletion arbo/verifier.go → arbo/poseidon_bn254/verifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func isValid(api frontend.API, sibling, prevSibling, leaf, prevLeaf frontend.Var
func CheckProof(api frontend.API, key, value, root frontend.Variable, siblings []frontend.Variable) error {
// calculate the path from the provided key to decide which leaf is the
// correct one in every level of the tree
path := api.ToBinary(key, api.Compiler().FieldBitLen())
path := api.ToBinary(key, len(siblings))
// calculate the value leaf to start with it to rebuild the tree
// leafValue = H(key | value | 1)
leafValue, err := poseidon.Hash(api, key, value, 1)
Expand Down
File renamed without changes.
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@ module github.com/vocdoni/gnark-crypto-primitives

go 1.23.2

replace github.com/vocdoni/arbo => ../arbo

require (
github.com/consensys/gnark v0.11.0
github.com/consensys/gnark-crypto v0.14.0
github.com/iden3/go-iden3-crypto v0.0.17
github.com/vocdoni/arbo v0.0.0-20241114123238-8b237b4e83fa
github.com/vocdoni/vocdoni-z-sandbox v0.0.0-20241113074257-1a711ad38a6b
go.vocdoni.io/dvote v1.10.2-0.20241024102542-c1ce6d744bc5
)
Expand Down

0 comments on commit ba7db5d

Please sign in to comment.