-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
8a9c760
commit ba7db5d
Showing
7 changed files
with
212 additions
and
1 deletion.
There are no files selected for viewing
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters