Skip to content

Commit

Permalink
include exclusion proof verifier allowing to append old leafs
Browse files Browse the repository at this point in the history
  • Loading branch information
lucasmenendez committed Nov 20, 2024
1 parent 576e8f9 commit ec22baa
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 12 deletions.
39 changes: 39 additions & 0 deletions arbo/hints.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
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.
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())
fmt.Println(outputs)
if index >= len(outputs) {
return fmt.Errorf("invalid index")
}
siblings := inputs[2:]
for i := 0; i < len(outputs); i++ {
if i == index {
outputs[i] = outputs[i].Set(newSibling)
} else {
outputs[i] = outputs[i].Set(siblings[i])
}
}
fmt.Println(outputs)
return nil
}
68 changes: 62 additions & 6 deletions arbo/verifier.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package arbo

import (
"github.com/consensys/gnark/frontend"
)
import "github.com/consensys/gnark/frontend"

type Hash func(frontend.API, ...frontend.Variable) (frontend.Variable, error)

Expand Down Expand Up @@ -39,9 +37,46 @@ func isValid(api frontend.API, sibling, prevSibling, leaf, prevLeaf frontend.Var
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, hFn Hash, key, value, root frontend.Variable, siblings []frontend.Variable) error {
// replaceFirstPaddedSibling function replaces the first padded sibling with the
// new sibling provided. The function receives the new sibling, the siblings and
// returns the new siblings with the replacement done. It first calculates the
// index of the first padded sibling and then calls the hint function to replace
// it. The hint function should return the new siblings with the replacement
// done. The function ensures that the replacement was done correctly.
func replaceFirstPaddedSibling(api frontend.API, newSibling frontend.Variable, siblings []frontend.Variable) ([]frontend.Variable, error) {
// the valid siblins are always the first n siblings that are not zero, so
// we need to iterate through the siblings in reverse order to find the
// first non-zero sibling and count the number of valid siblings from there,
// so the index of the last padded sibling is the number of valid siblings
index := frontend.Variable(0)
nonZeroFound := frontend.Variable(0)
for i := len(siblings) - 1; i >= 0; i-- {
isNotZero := strictCmp(api, siblings[i], 0)
nonZeroFound = api.Or(nonZeroFound, isNotZero)
index = api.Add(index, nonZeroFound)
}
// call the hint function to replace the sibling with the index to be
// replaced, the new sibling and the rest of the siblings
newSiblings, err := api.Compiler().NewHint(replaceSiblingHint, len(siblings),
append([]frontend.Variable{newSibling, index}, siblings...)...)
if err != nil {
return nil, err
}
// check that the hint successfully replaced the first padded sibling
newSiblingFound := frontend.Variable(0)
for i := 0; i < len(newSiblings); i++ {
correctIndex := api.IsZero(api.Sub(index, frontend.Variable(i)))
correctSibling := api.IsZero(api.Sub(newSiblings[i], newSibling))
newSiblingFound = api.Or(newSiblingFound, api.And(correctIndex, correctSibling))
}
api.AssertIsEqual(newSiblingFound, 1)
return newSiblings, nil
}

// CheckInclusionProof receives the parameters of an inclusion proof of Arbo to
// recalculate the root with them and compare it with the provided one,
// verifiying the proof.
func CheckInclusionProof(api frontend.API, hFn Hash, 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))
Expand Down Expand Up @@ -70,3 +105,24 @@ func CheckProof(api frontend.API, hFn Hash, key, value, root frontend.Variable,
api.AssertIsEqual(leafKey, root)
return nil
}

// CheckExclusionProof receives the parameters of a exclusion proof of Arbo to
// recalculate the root with them and compare it with the provided one. It
// differs from CheckInclusionProof in that it receives the old key and value
// to calculate the old leaf key and replace the first padded sibling with the
// new sibling, then verifies the proof calling CheckInclusionProof with the
// old sibling added to the siblings.
func CheckExclusionProof(api frontend.API, hFn Hash, key, value, oldKey, oldValue, root frontend.Variable, siblings []frontend.Variable) error {
// calculate the old leaf key
newLeafKey, err := hFn(api, oldKey, oldValue, 1)
if err != nil {
return err
}
// replace the first padded sibling with the new sibling
newSiblings, err := replaceFirstPaddedSibling(api, newLeafKey, siblings)
if err != nil {
return err
}
// verify the proof with the old sibling added to the siblings
return CheckInclusionProof(api, hFn, key, value, root, newSiblings)
}
3 changes: 1 addition & 2 deletions arbo/verifier_bls12377_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,7 @@ func (circuit *testVerifierBLS12377) Define(api frontend.API) error {
h.Write(data...)
return h.Sum(), nil
}

return CheckProof(api, hash, circuit.Key, circuit.Value, circuit.Root, circuit.Siblings[:])
return CheckInclusionProof(api, hash, circuit.Key, circuit.Value, circuit.Root, circuit.Siblings[:])
}

func TestVerifierBLS12377(t *testing.T) {
Expand Down
5 changes: 1 addition & 4 deletions arbo/verifier_bn254_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package arbo

import (
"encoding/json"
"fmt"
"math/big"
"testing"
Expand All @@ -27,7 +26,7 @@ type testVerifierBN254 struct {

func (circuit *testVerifierBN254) Define(api frontend.API) error {
// use poseidon hash function
return CheckProof(api, poseidon.Hash, circuit.Key, circuit.Value, circuit.Root, circuit.Siblings[:])
return CheckInclusionProof(api, poseidon.Hash, circuit.Key, circuit.Value, circuit.Root, circuit.Siblings[:])
}

func TestVerifierBN254(t *testing.T) {
Expand Down Expand Up @@ -60,8 +59,6 @@ func TestVerifierBN254(t *testing.T) {
Value: value,
Siblings: fSiblings,
}
binputs, _ := json.MarshalIndent(inputs, " ", " ")
fmt.Println("inputs", string(binputs))
assert := test.NewAssert(t)
assert.SolvingSucceeded(&testVerifierBN254{}, &inputs, test.WithCurves(ecc.BN254), test.WithBackends(backend.GROTH16))
}

0 comments on commit ec22baa

Please sign in to comment.