Documentation ¶
Overview ¶
Package selector provides a lookup table and map, based on linear scan.
The native frontend.API provides 1- and 2-bit lookups through the interface methods Select and Lookup2. This package extends the lookups to arbitrary-sized vectors. The lookups can be performed using the index of the elements (function Mux) or using a key, for which the user needs to provide the slice of keys (function Map).
The implementation uses linear scan over all inputs.
Index ¶
- func BinaryMux(api frontend.API, selBits, inputs []frontend.Variable) frontend.Variable
- func Decoder(api frontend.API, n int, sel frontend.Variable) []frontend.Variable
- func GetHints() []solver.Hint
- func KeyDecoder(api frontend.API, queryKey frontend.Variable, keys []frontend.Variable) []frontend.Variable
- func Map(api frontend.API, queryKey frontend.Variable, keys []frontend.Variable, ...) frontend.Variable
- func Mux(api frontend.API, sel frontend.Variable, inputs ...frontend.Variable) frontend.Variable
- func Partition(api frontend.API, pivotPosition frontend.Variable, rightSide bool, ...) (out []frontend.Variable)
- func Slice(api frontend.API, start, end frontend.Variable, input []frontend.Variable) []frontend.Variable
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func BinaryMux ¶
BinaryMux is a 2^k to 1 multiplexer which uses a binary selector. selBits are the selector bits, and the input at the index equal to the binary number represented by the selector bits will be selected. More precisely the output will be:
inputs[selBits[0]+selBits[1]*(1<<1)+selBits[2]*(1<<2)+...]
len(inputs) must be 2^len(selBits).
func Decoder ¶
Decoder is a decoder with n outputs. It outputs 1 on the wire with index sel, and 0 otherwise. Indices start from zero. In other words:
if i == sel out[i] = 1 else out[i] = 0
sel needs to be between 0 and n - 1 (inclusive) otherwise no proof can be generated.
func GetHints ¶
GetHints returns all hint functions used in this package. This method is useful for registering all hints in the solver.
func KeyDecoder ¶
func KeyDecoder(api frontend.API, queryKey frontend.Variable, keys []frontend.Variable) []frontend.Variable
KeyDecoder is a decoder that associates keys to its output wires. It outputs 1 on the wire that is associated to a key that equals to queryKey. In other words:
if keys[i] == queryKey out[i] = 1 else out[i] = 0
If keys has more than one key that equals to queryKey, the output is undefined. However, the output is guaranteed to be zero for the wires that are associated with a key which is not equal to queryKey.
func Map ¶
func Map(api frontend.API, queryKey frontend.Variable, keys []frontend.Variable, values []frontend.Variable) frontend.Variable
Map is a key value associative array: the output will be values[i] such that keys[i] == queryKey. If keys does not contain queryKey, no proofs can be generated. If keys has more than one key that equals to queryKey, the output will be undefined, and the output could be any linear combination of all the corresponding values with that queryKey.
In case keys and values do not have the same length, this function will panic.
Example ¶
ExampleMap gives an example on how to use map selector.
package main import ( "fmt" "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/backend/groth16" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/frontend/cs/r1cs" "github.com/consensys/gnark/std/selector" ) // MapCircuit is a minimal circuit using a selector map. type MapCircuit struct { QueryKey frontend.Variable Keys [10]frontend.Variable // we use array in witness to allocate sufficiently sized vector Values [10]frontend.Variable // we use array in witness to allocate sufficiently sized vector ExpectedValue frontend.Variable } // Define defines the arithmetic circuit. func (c *MapCircuit) Define(api frontend.API) error { result := selector.Map(api, c.QueryKey, c.Keys[:], c.Values[:]) api.AssertIsEqual(result, c.ExpectedValue) return nil } // ExampleMap gives an example on how to use map selector. func main() { circuit := MapCircuit{} witness := MapCircuit{ QueryKey: 55, Keys: [10]frontend.Variable{0, 11, 22, 33, 44, 55, 66, 77, 88, 99}, Values: [10]frontend.Variable{0, 2, 4, 6, 8, 10, 12, 14, 16, 18}, ExpectedValue: 10, // element in values which corresponds to the position of 55 in keys } ccs, err := frontend.Compile(ecc.BN254.ScalarField(), r1cs.NewBuilder, &circuit) if err != nil { panic(err) } pk, vk, err := groth16.Setup(ccs) if err != nil { panic(err) } secretWitness, err := frontend.NewWitness(&witness, ecc.BN254.ScalarField()) if err != nil { panic(err) } publicWitness, err := secretWitness.Public() if err != nil { panic(err) } proof, err := groth16.Prove(ccs, pk, secretWitness) if err != nil { panic(err) } err = groth16.Verify(proof, vk, publicWitness) if err != nil { panic(err) } fmt.Println("done") }
Output: done
func Mux ¶
Mux is an n to 1 multiplexer: out = inputs[sel]. In other words, it selects exactly one of its inputs based on sel. The index of inputs starts from zero.
sel needs to be between 0 and n - 1 (inclusive), where n is the number of inputs, otherwise the proof will fail.
Example ¶
ExampleMux gives an example on how to use mux selector.
package main import ( "fmt" "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/backend/groth16" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/frontend/cs/r1cs" "github.com/consensys/gnark/std/selector" ) // MuxCircuit is a minimal circuit using a selector mux. type MuxCircuit struct { Selector frontend.Variable In [10]frontend.Variable // we use array in witness to allocate sufficiently sized vector Expected frontend.Variable } // Define defines the arithmetic circuit. func (c *MuxCircuit) Define(api frontend.API) error { result := selector.Mux(api, c.Selector, c.In[:]...) // Note Mux takes var-arg input, need to expand the input vector api.AssertIsEqual(result, c.Expected) return nil } // ExampleMux gives an example on how to use mux selector. func main() { circuit := MuxCircuit{} witness := MuxCircuit{ Selector: 5, In: [10]frontend.Variable{0, 2, 4, 6, 8, 10, 12, 14, 16, 18}, Expected: 10, // 5-th element in vector } ccs, err := frontend.Compile(ecc.BN254.ScalarField(), r1cs.NewBuilder, &circuit) if err != nil { panic(err) } pk, vk, err := groth16.Setup(ccs) if err != nil { panic(err) } secretWitness, err := frontend.NewWitness(&witness, ecc.BN254.ScalarField()) if err != nil { panic(err) } publicWitness, err := secretWitness.Public() if err != nil { panic(err) } proof, err := groth16.Prove(ccs, pk, secretWitness) if err != nil { panic(err) } err = groth16.Verify(proof, vk, publicWitness) if err != nil { panic(err) } fmt.Println("done") }
Output: done
func Partition ¶
func Partition(api frontend.API, pivotPosition frontend.Variable, rightSide bool, input []frontend.Variable) (out []frontend.Variable)
Partition selects left or right side of the input array, with respect to the pivotPosition. More precisely when rightSide is false, for each i we have:
if i < pivotPosition out[i] = input[i] else out[i] = 0
and when rightSide is true, for each i we have:
if i >= pivotPosition out[i] = input[i] else out[i] = 0
We must have pivotPosition >= 0 and pivotPosition <= len(input), otherwise a proof cannot be generated.
Example ¶
ExamplePartition gives an example on how to use selector.Partition to make a circuit that accepts a Count and an input array In, and then calculates the sum of first Count numbers of the input array.
package main import ( "fmt" "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/backend/groth16" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/frontend/cs/r1cs" "github.com/consensys/gnark/std/selector" ) // adderCircuit adds first Count number of its input array In. type adderCircuit struct { Count frontend.Variable In [10]frontend.Variable ExpectedSum frontend.Variable } // Define defines the arithmetic circuit. func (c *adderCircuit) Define(api frontend.API) error { selectedPart := selector.Partition(api, c.Count, false, c.In[:]) sum := api.Add(selectedPart[0], selectedPart[1], selectedPart[2:]...) api.AssertIsEqual(sum, c.ExpectedSum) return nil } // ExamplePartition gives an example on how to use selector.Partition to make a circuit that accepts a Count and an // input array In, and then calculates the sum of first Count numbers of the input array. func main() { circuit := adderCircuit{} witness := adderCircuit{ Count: 6, In: [10]frontend.Variable{0, 2, 4, 6, 8, 10, 12, 14, 16, 18}, ExpectedSum: 30, } ccs, err := frontend.Compile(ecc.BN254.ScalarField(), r1cs.NewBuilder, &circuit) if err != nil { panic(err) } pk, vk, err := groth16.Setup(ccs) if err != nil { panic(err) } secretWitness, err := frontend.NewWitness(&witness, ecc.BN254.ScalarField()) if err != nil { panic(err) } publicWitness, err := secretWitness.Public() if err != nil { panic(err) } proof, err := groth16.Prove(ccs, pk, secretWitness) if err != nil { panic(err) } err = groth16.Verify(proof, vk, publicWitness) if err != nil { panic(err) } fmt.Println("done") }
Output: done
func Slice ¶
func Slice(api frontend.API, start, end frontend.Variable, input []frontend.Variable) []frontend.Variable
Slice selects a slice of the input array at indices [start, end), and zeroes the array at other indices. More precisely, for each i we have:
if i >= start and i < end out[i] = input[i] else out[i] = 0
We must have start >= 0 and end <= len(input), otherwise a proof cannot be generated.
Types ¶
This section is empty.