test/engine.go accepts two shapes that std/math/bits rejects during compilation, so a circuit can pass test.IsSolved and then fail to compile.
ToBinary guards only the negative case:
nbBits := e.FieldBitLen()
if len(n) == 1 {
nbBits = n[0]
if nbBits < 0 {
panic("invalid n")
}
}
n == 0 passes. FromBinary has no length guard at all, so a zero-length call is accepted.
Both builders route through std/math/bits, which rejects both:
FromBase needs at least 1 digit conversion.go:38
nbDigits <= 0 conversion.go:78
Observed divergence:
api.FromBinary() engine: passes r1cs: parse circuit: FromBase needs at least 1 digit
api.ToBinary(v, 0) engine: passes r1cs: parse circuit: nbDigits <= 0
api.FromBinary() diverges unconditionally. api.ToBinary(v, 0) diverges whenever the witness value of v is zero; for a nonzero v the engine panics on its own reconstruction check instead.
The stakes are much lower than #650, since both cases are compile-time rejections and nothing unsafe can ship. It is still a hole in the safety net for a gadget that computes a bit width and can legitimately reach zero, because the test engine will not tell it that the circuit is invalid.
The fix is two guards in test/engine.go. That is the same file as #1810, which is currently open, so I would rather not send a second PR against it while that is in review. Happy to fold this into #1810 if you prefer, or to send it separately once that lands. I have reproductions for both cases.
test/engine.goaccepts two shapes thatstd/math/bitsrejects during compilation, so a circuit can passtest.IsSolvedand then fail to compile.ToBinaryguards only the negative case:n == 0passes.FromBinaryhas no length guard at all, so a zero-length call is accepted.Both builders route through
std/math/bits, which rejects both:Observed divergence:
api.FromBinary()diverges unconditionally.api.ToBinary(v, 0)diverges whenever the witness value ofvis zero; for a nonzerovthe engine panics on its own reconstruction check instead.The stakes are much lower than #650, since both cases are compile-time rejections and nothing unsafe can ship. It is still a hole in the safety net for a gadget that computes a bit width and can legitimately reach zero, because the test engine will not tell it that the circuit is invalid.
The fix is two guards in
test/engine.go. That is the same file as #1810, which is currently open, so I would rather not send a second PR against it while that is in review. Happy to fold this into #1810 if you prefer, or to send it separately once that lands. I have reproductions for both cases.