Non-obvious issue testing a circuit containing a slice of variables
I'm testing a circuit right now of the form:
type Circuit struct {
Slice: []frontend.Variable
}
I went to test it like this:
func TestCircuit(t *testing.T) {
assert := test.NewAssert(t)
var circuit Circuit
assert.ProverSucceeded(&circuit, &Circuit{
Slice: []frontend.Variable{1, 2, 3},
})
}
However, this fails with the message:
schema is inconsistent with Witness
Along with the log message:
ignoring uninitialized slice: Slice []frontend.Variable
Because the slice in &circuit is, indeed, uninitialized, and gets ignored during circuit parsing.
Working around this is easy, I'm just using a statically-sized array instead of a slice, but if I wanted to use a slice, I'd have to instantiate circuit with some dummy values, and it's really nice syntactically to not have to do that, as is the case in your documentation.
Maybe mention this in the docs somehow?
no need for dummy values;
var circuit Circuit
circuit.Slice = make([]frontend.Variable, 3)
assert.ProverSucceeded(&circuit, &Circuit{
Slice: []frontend.Variable{1, 2, 3},
})
which doc are you referring to?
ah, true - but it's kinda clean being able to "not touch" circuit as in https://docs.gnark.consensys.net/HowTo/debug_test#test:
// assert object wrapping testing.T
assert := test.NewAssert(t)
// declare the circuit
var cubicCircuit Circuit
assert.ProverFailed(&cubicCircuit, &Circuit{
PreImage: 42,
Hash: 42,
})
assert.ProverSucceeded(&cubicCircuit, &Circuit{
PreImage: 35,
Hash: "16130099170765464552823636852555369511329944820189892919423002775646948828469",
}, test.WithCurves(ecc.BN254))
can just do:
var circuit Circuit
// ...
assert.ProverSucceeded(&circuit, &Circuit{...})
but i won't sit here and nit-pick over syntax, it was just unclear to me at first what was meant by "uninitialized slice"
maybe just cause i'm not great at Go lol
Another option is to use arrays instead of slices. Then gnark knows how many variables to expect and everything just works:
type Circuit struct {
Vals [5]frontend.Variable
}
func (c *Circuit) Define(api frontend.API) error {
// do whatever we want with vals
}
func Test(t *testing.T) {
var circuit Circuit
witness := Circuit{Vals: [5]int{1,2,3,4,5}}
assert := test.NewAssert(t)
assert.ProverSucceeded(&circuit, &witness)
}
And Go now has ways to nicely cast from slice to arrays a la https://go.dev/play/p/bseAeVqRL2Q.
yup, this is what i ended up doing! anyway, this is definitely a minute point, but if you agree a note should be made of it in the docs somewhere, i'm happy to put up a PR making such a note/example. if not, which i also understand, we can just close this issue
no need for dummy values;
var circuit Circuit circuit.Slice = make([]frontend.Variable, 3) assert.ProverSucceeded(&circuit, &Circuit{ Slice: []frontend.Variable{1, 2, 3}, })which doc are you referring to?
A litter confused about this, if the variable is slice, then are slice length two and length three the same circuit? Also, is their on-chain verification contract the same?
// length 2
var circuit2 Circuit
circuit2.Slice = make([]frontend.Variable, 2)
// length 3
var circuit3 Circuit
circuit3.Slice = make([]frontend.Variable, 3)
Nope, they are different and incompatible circuits.
Usually if you want to have a variable-length input, then you have to implement a circuit which has a maximum-length input and then using a mask omit the results of unnecessary computation.