-
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.
feature: arbo verifier with generic hash function (#2)
* remove pprof files * use same arbo verifier and pass the hash function as parameter * fixing tests * new implementation to check addition proof with more detailed comment * final comments and quicktest * update arbo depenedency
- Loading branch information
1 parent
b970030
commit 5bccb51
Showing
8 changed files
with
385 additions
and
146 deletions.
There are no files selected for viewing
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,43 @@ | ||
package arbo | ||
|
||
import ( | ||
"fmt" | ||
"math/big" | ||
|
||
"github.com/consensys/gnark/constraint/solver" | ||
) | ||
|
||
func init() { | ||
solver.RegisterHint(replaceSiblingHint) | ||
} | ||
|
||
// replaceSiblingHint gnark hint function receives the new sibling to set as | ||
// first input, the index of the sibling to be replaced as second input, and the | ||
// rest of the siblings as the rest of the inputs. The function should return | ||
// the new siblings with the replacement done. The caller should ensure that the | ||
// the result of the hint is correct checking that every sibling is the same | ||
// that was passed as input except for the sibling with the index provided that | ||
// should be replaced with the new sibling. | ||
func replaceSiblingHint(_ *big.Int, inputs, outputs []*big.Int) error { | ||
if len(inputs) != len(outputs)+2 { | ||
return fmt.Errorf("invalid number of inputs/outputs") | ||
} | ||
// get the new sibling and the index to replace | ||
newSibling := inputs[0] | ||
index := int(inputs[1].Int64()) | ||
if index >= len(outputs) { | ||
return fmt.Errorf("invalid index") | ||
} | ||
siblings := inputs[2:] | ||
if len(siblings) != len(outputs) { | ||
return fmt.Errorf("invalid number of siblings") | ||
} | ||
for i := 0; i < len(outputs); i++ { | ||
if i == index { | ||
outputs[i] = outputs[i].Set(newSibling) | ||
} else { | ||
outputs[i] = outputs[i].Set(siblings[i]) | ||
} | ||
} | ||
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,92 @@ | ||
package arbo | ||
|
||
import ( | ||
"fmt" | ||
"math/big" | ||
"os" | ||
|
||
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 censusConfig struct { | ||
dir string | ||
validSiblings int | ||
totalSiblings int | ||
keyLen int | ||
hash arbotree.HashFunction | ||
baseFiled *big.Int | ||
} | ||
|
||
// generateCensusProofForTest generates a census proof for testing purposes, it | ||
// receives a configuration and a key-value pair to generate the proof for. | ||
// It returns the root, key, value, and siblings of the proof. The configuration | ||
// includes the temp directory to store the database, the number of valid | ||
// siblings, the total number of siblings, the key length, the hash function to | ||
// use in the merkle tree, and the base field to use in the finite field. | ||
func generateCensusProofForTest(conf censusConfig, k, v []byte) (*big.Int, *big.Int, *big.Int, []*big.Int, error) { | ||
defer func() { | ||
_ = os.RemoveAll(conf.dir) | ||
}() | ||
database, err := pebbledb.New(db.Options{Path: conf.dir}) | ||
if err != nil { | ||
return nil, nil, nil, nil, err | ||
} | ||
tree, err := arbotree.NewTree(arbotree.Config{ | ||
Database: database, | ||
MaxLevels: conf.totalSiblings, | ||
HashFunction: conf.hash, | ||
}) | ||
if err != nil { | ||
return nil, nil, nil, nil, err | ||
} | ||
|
||
k = arbotree.BigToFF(conf.baseFiled, new(big.Int).SetBytes(k)).Bytes() | ||
// add the first key-value pair | ||
if err = tree.Add(k, v); err != nil { | ||
return nil, nil, nil, nil, err | ||
} | ||
// add random addresses | ||
for i := 1; i < conf.validSiblings; i++ { | ||
rk := arbotree.BigToFF(conf.baseFiled, new(big.Int).SetBytes(util.RandomBytes(conf.keyLen))).Bytes() | ||
rv := new(big.Int).SetBytes(util.RandomBytes(8)).Bytes() | ||
if err = tree.Add(rk, rv); err != nil { | ||
return nil, nil, nil, nil, err | ||
} | ||
} | ||
// generate the proof | ||
_, _, siblings, exist, err := tree.GenProof(k) | ||
if err != nil { | ||
return nil, nil, nil, nil, err | ||
} | ||
if !exist { | ||
return nil, nil, nil, nil, fmt.Errorf("error building the merkle tree: key not found") | ||
} | ||
unpackedSiblings, err := arbo.UnpackSiblings(tree.HashFunction(), siblings) | ||
if err != nil { | ||
return nil, nil, nil, nil, err | ||
} | ||
paddedSiblings := make([]*big.Int, conf.totalSiblings) | ||
for i := 0; i < conf.totalSiblings; 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 nil, nil, nil, nil, err | ||
} | ||
verified, err := arbotree.CheckProof(tree.HashFunction(), k, v, root, siblings) | ||
if !verified { | ||
return nil, nil, nil, nil, fmt.Errorf("error verifying the proof") | ||
} | ||
if err != nil { | ||
return nil, nil, nil, nil, err | ||
} | ||
return arbo.BytesLEToBigInt(root), arbo.BytesLEToBigInt(k), new(big.Int).SetBytes(v), paddedSiblings, 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
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,79 @@ | ||
package arbo | ||
|
||
import ( | ||
"fmt" | ||
"math/big" | ||
"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/std/hash/mimc" | ||
"github.com/consensys/gnark/test" | ||
qt "github.com/frankban/quicktest" | ||
arbotree "github.com/vocdoni/arbo" | ||
"go.vocdoni.io/dvote/util" | ||
) | ||
|
||
const ( | ||
v_siblings = 10 | ||
n_siblings = 160 | ||
k_len = n_siblings / 8 | ||
) | ||
|
||
type testVerifierBLS12377 struct { | ||
Root frontend.Variable | ||
Key frontend.Variable | ||
Value frontend.Variable | ||
Siblings [n_siblings]frontend.Variable | ||
} | ||
|
||
func (circuit *testVerifierBLS12377) Define(api frontend.API) error { | ||
// use mimc hash function | ||
hash := func(api frontend.API, data ...frontend.Variable) (frontend.Variable, error) { | ||
h, err := mimc.NewMiMC(api) | ||
if err != nil { | ||
return 0, err | ||
} | ||
h.Write(data...) | ||
return h.Sum(), nil | ||
} | ||
return CheckInclusionProof(api, hash, circuit.Key, circuit.Value, circuit.Root, circuit.Siblings[:]) | ||
} | ||
|
||
func TestVerifierBLS12377(t *testing.T) { | ||
c := qt.New(t) | ||
// profile the circuit compilation | ||
p := profile.Start() | ||
now := time.Now() | ||
_, _ = frontend.Compile(ecc.BLS12_377.ScalarField(), r1cs.NewBuilder, &testVerifierBLS12377{}) | ||
fmt.Println("elapsed", time.Since(now)) | ||
p.Stop() | ||
fmt.Println("constrains", p.NbConstraints()) | ||
// generate census proof | ||
root, key, value, siblings, err := generateCensusProofForTest(censusConfig{ | ||
dir: t.TempDir() + "/bls12377", | ||
validSiblings: v_siblings, | ||
totalSiblings: n_siblings, | ||
keyLen: k_len, | ||
hash: arbotree.HashFunctionMiMC_BLS12_377, | ||
baseFiled: arbotree.BLS12377BaseField, | ||
}, util.RandomBytes(k_len), big.NewInt(10).Bytes()) | ||
c.Assert(err, qt.IsNil) | ||
// init and print inputs | ||
fSiblings := [n_siblings]frontend.Variable{} | ||
for i := 0; i < n_siblings; i++ { | ||
fSiblings[i] = siblings[i] | ||
} | ||
inputs := testVerifierBLS12377{ | ||
Root: root, | ||
Key: key, | ||
Value: value, | ||
Siblings: fSiblings, | ||
} | ||
assert := test.NewAssert(t) | ||
assert.SolvingSucceeded(&testVerifierBLS12377{}, &inputs, test.WithCurves(ecc.BLS12_377), test.WithBackends(backend.GROTH16)) | ||
} |
Oops, something went wrong.