From cfbb3931bdc41a2c9c4013091f36a9c4bb2d3ac5 Mon Sep 17 00:00:00 2001 From: Shadow <146137079+0xShadowX@users.noreply.github.com> Date: Sun, 27 Sep 2026 15:24:24 +0400 Subject: [PATCH] fix(scs): don't panic on a recorded addition with a zero coefficient reduce keeps a term whose merged coefficient is zero, so x + y - y records the addition x*1 + y*0. A later 2x + y - y finds that entry and tries to compute the output coefficient as q4/q2 = 0/0, which panics. Return "not found" there and add a new constraint instead. --- frontend/cs/scs/api_test.go | 28 ++++++++++++++++++++++++++++ frontend/cs/scs/builder.go | 4 +++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/frontend/cs/scs/api_test.go b/frontend/cs/scs/api_test.go index 07c4f69602..45b2d3ed69 100644 --- a/frontend/cs/scs/api_test.go +++ b/frontend/cs/scs/api_test.go @@ -198,6 +198,34 @@ func TestExistDiv02(t *testing.T) { assert.NoError(err) } +type IssueDiv0Circuit3 struct { + A, B frontend.Variable + Res1, Res2 frontend.Variable +} + +func (c *IssueDiv0Circuit3) Define(api frontend.API) error { + // B cancels out, which leaves a recorded addition with a zero coefficient + t1 := api.Add(c.A, c.B, api.Neg(c.B)) + t2 := api.Add(api.Mul(2, c.A), c.B, api.Neg(c.B)) + + api.AssertIsEqual(t1, c.Res1) + api.AssertIsEqual(t2, c.Res2) + return nil +} + +func TestExistDiv03(t *testing.T) { + assert := test.NewAssert(t) + ccs, err := frontend.Compile(ecc.BN254.ScalarField(), scs.NewBuilder, &IssueDiv0Circuit3{}) + assert.NoError(err) + w, err := frontend.NewWitness(&IssueDiv0Circuit3{ + A: 11, B: 21, + Res1: 11, Res2: 22, + }, ecc.BN254.ScalarField()) + assert.NoError(err) + _, err = ccs.Solve(w) + assert.NoError(err) +} + type TestZeroMulNoConstraintCircuit struct { A, B frontend.Variable } diff --git a/frontend/cs/scs/builder.go b/frontend/cs/scs/builder.go index f39d9e614a..fb67058db5 100644 --- a/frontend/cs/scs/builder.go +++ b/frontend/cs/scs/builder.go @@ -530,7 +530,9 @@ func (builder *builder[E]) addConstraintExist(a, b expr.Term[E], k E) (expr.Term // compute n, the coefficient for the output wire q2, ok = builder.cs.Inverse(q2) if !ok { - panic("div by 0") // shouldn't happen + // q2 is zero when a term cancelled out in reduce (e.g. x + y - y), + // we can't compute n from it so we just add a new constraint. + return expr.Term[E]{}, false } q2 = builder.cs.Mul(q2, q4) return expr.NewTerm(int(c.XC), q2), true