diff --git a/frontend/ccs.go b/frontend/ccs.go index 5196092a33..e867502a3e 100644 --- a/frontend/ccs.go +++ b/frontend/ccs.go @@ -48,4 +48,7 @@ type CompiledConstraintSystem interface { GetCounters() []compiled.Counter GetSchema() *schema.Schema + + // GetConstraints return a human readable representation of the constraints + GetConstraints() [][]string } diff --git a/internal/backend/bls12-377/cs/r1cs.go b/internal/backend/bls12-377/cs/r1cs.go index 50317ef36c..00fe977d7d 100644 --- a/internal/backend/bls12-377/cs/r1cs.go +++ b/internal/backend/bls12-377/cs/r1cs.go @@ -313,6 +313,68 @@ func sub(a, b int) int { return a - b } +func (cs *R1CS) GetConstraints() [][]string { + var r [][]string + for _, c := range cs.Constraints { + // for each constraint, we build a string representation of it's L, R and O part + // if we are worried about perf for large cs, we could do a string builder + csv format. + var line [3]string + line[0] = cs.vtoString(c.L) + line[1] = cs.vtoString(c.R) + line[2] = cs.vtoString(c.O) + r = append(r, line[:]) + } + return r +} + +func (cs *R1CS) vtoString(l compiled.Variable) string { + var sbb strings.Builder + for i := 0; i < len(l.LinExp); i++ { + cs.termToString(l.LinExp[i], &sbb) + if i+1 < len(l.LinExp) { + sbb.WriteString(" + ") + } + } + return sbb.String() +} + +func (cs *R1CS) termToString(t compiled.Term, sbb *strings.Builder) { + tID := t.CoeffID() + if tID == compiled.CoeffIdOne { + // do nothing, just print the variable + } else if tID == compiled.CoeffIdMinusOne { + // print neg sign + sbb.WriteByte('-') + } else if tID == compiled.CoeffIdZero { + sbb.WriteByte('0') + return + } else { + sbb.WriteString(cs.Coefficients[tID].String()) + sbb.WriteByte('*') + } + vID := t.WireID() + visibility := t.VariableVisibility() + + switch visibility { + case schema.Internal: + if _, isHint := cs.MHints[vID]; isHint { + sbb.WriteString(fmt.Sprintf("hv%d", vID-cs.NbPublicVariables-cs.NbSecretVariables)) + } else { + sbb.WriteString(fmt.Sprintf("v%d", vID-cs.NbPublicVariables-cs.NbSecretVariables)) + } + case schema.Public: + if vID == 0 { + sbb.WriteByte('1') // one wire + } else { + sbb.WriteString(fmt.Sprintf("p%d", vID-1)) + } + case schema.Secret: + sbb.WriteString(fmt.Sprintf("s%d", vID-cs.NbPublicVariables)) + default: + sbb.WriteString("") + } +} + func toHTML(l compiled.Variable, coeffs []fr.Element, MHints map[int]compiled.Hint) string { var sbb strings.Builder for i := 0; i < len(l.LinExp); i++ { diff --git a/internal/backend/bls12-377/cs/r1cs_sparse.go b/internal/backend/bls12-377/cs/r1cs_sparse.go index 6b676ccd0d..c8b89de4bb 100644 --- a/internal/backend/bls12-377/cs/r1cs_sparse.go +++ b/internal/backend/bls12-377/cs/r1cs_sparse.go @@ -28,6 +28,7 @@ import ( "github.com/consensys/gnark/backend" "github.com/consensys/gnark/backend/witness" + "github.com/consensys/gnark/frontend/schema" "github.com/consensys/gnark/internal/backend/compiled" "github.com/consensys/gnark/internal/backend/ioutils" @@ -254,6 +255,57 @@ func (cs *SparseR1CS) IsSolved(witness *witness.Witness, opts ...backend.ProverO return err } +func (cs *SparseR1CS) GetConstraints() [][]string { + var r [][]string + for _, c := range cs.Constraints { + // if we are worried about perf for large cs, we could do a string builder + csv format. + var line [6]string + line[0] = cs.termToString(c.L) + line[1] = cs.termToString(c.R) + line[2] = cs.termToString(c.M[0]) + line[3] = cs.termToString(c.M[1]) + line[4] = cs.termToString(c.O) + line[5] = cs.Coefficients[c.K].String() + r = append(r, line[:]) + } + return r +} + +func (cs *SparseR1CS) termToString(t compiled.Term) string { + var sbb strings.Builder + tID := t.CoeffID() + if tID == compiled.CoeffIdOne { + // do nothing, just print the variable + } else if tID == compiled.CoeffIdMinusOne { + // print neg sign + sbb.WriteByte('-') + } else if tID == compiled.CoeffIdZero { + sbb.WriteByte('0') + return sbb.String() + } else { + sbb.WriteString(cs.Coefficients[tID].String()) + sbb.WriteByte('*') + } + vID := t.WireID() + visibility := t.VariableVisibility() + + switch visibility { + case schema.Internal: + if _, isHint := cs.MHints[vID]; isHint { + sbb.WriteString(fmt.Sprintf("hv%d", vID-cs.NbPublicVariables-cs.NbSecretVariables)) + } else { + sbb.WriteString(fmt.Sprintf("v%d", vID-cs.NbPublicVariables-cs.NbSecretVariables)) + } + case schema.Public: + sbb.WriteString(fmt.Sprintf("p%d", vID)) + case schema.Secret: + sbb.WriteString(fmt.Sprintf("s%d", vID-cs.NbPublicVariables)) + default: + sbb.WriteString("") + } + return sbb.String() +} + // checkConstraint verifies that the constraint holds func (cs *SparseR1CS) checkConstraint(c compiled.SparseR1C, solution *solution) error { l := solution.computeTerm(c.L) diff --git a/internal/backend/bls12-381/cs/r1cs.go b/internal/backend/bls12-381/cs/r1cs.go index f1ba8ed9d0..66df1f795f 100644 --- a/internal/backend/bls12-381/cs/r1cs.go +++ b/internal/backend/bls12-381/cs/r1cs.go @@ -313,6 +313,68 @@ func sub(a, b int) int { return a - b } +func (cs *R1CS) GetConstraints() [][]string { + var r [][]string + for _, c := range cs.Constraints { + // for each constraint, we build a string representation of it's L, R and O part + // if we are worried about perf for large cs, we could do a string builder + csv format. + var line [3]string + line[0] = cs.vtoString(c.L) + line[1] = cs.vtoString(c.R) + line[2] = cs.vtoString(c.O) + r = append(r, line[:]) + } + return r +} + +func (cs *R1CS) vtoString(l compiled.Variable) string { + var sbb strings.Builder + for i := 0; i < len(l.LinExp); i++ { + cs.termToString(l.LinExp[i], &sbb) + if i+1 < len(l.LinExp) { + sbb.WriteString(" + ") + } + } + return sbb.String() +} + +func (cs *R1CS) termToString(t compiled.Term, sbb *strings.Builder) { + tID := t.CoeffID() + if tID == compiled.CoeffIdOne { + // do nothing, just print the variable + } else if tID == compiled.CoeffIdMinusOne { + // print neg sign + sbb.WriteByte('-') + } else if tID == compiled.CoeffIdZero { + sbb.WriteByte('0') + return + } else { + sbb.WriteString(cs.Coefficients[tID].String()) + sbb.WriteByte('*') + } + vID := t.WireID() + visibility := t.VariableVisibility() + + switch visibility { + case schema.Internal: + if _, isHint := cs.MHints[vID]; isHint { + sbb.WriteString(fmt.Sprintf("hv%d", vID-cs.NbPublicVariables-cs.NbSecretVariables)) + } else { + sbb.WriteString(fmt.Sprintf("v%d", vID-cs.NbPublicVariables-cs.NbSecretVariables)) + } + case schema.Public: + if vID == 0 { + sbb.WriteByte('1') // one wire + } else { + sbb.WriteString(fmt.Sprintf("p%d", vID-1)) + } + case schema.Secret: + sbb.WriteString(fmt.Sprintf("s%d", vID-cs.NbPublicVariables)) + default: + sbb.WriteString("") + } +} + func toHTML(l compiled.Variable, coeffs []fr.Element, MHints map[int]compiled.Hint) string { var sbb strings.Builder for i := 0; i < len(l.LinExp); i++ { diff --git a/internal/backend/bls12-381/cs/r1cs_sparse.go b/internal/backend/bls12-381/cs/r1cs_sparse.go index 9abb493f5d..b528dc3a21 100644 --- a/internal/backend/bls12-381/cs/r1cs_sparse.go +++ b/internal/backend/bls12-381/cs/r1cs_sparse.go @@ -28,6 +28,7 @@ import ( "github.com/consensys/gnark/backend" "github.com/consensys/gnark/backend/witness" + "github.com/consensys/gnark/frontend/schema" "github.com/consensys/gnark/internal/backend/compiled" "github.com/consensys/gnark/internal/backend/ioutils" @@ -254,6 +255,57 @@ func (cs *SparseR1CS) IsSolved(witness *witness.Witness, opts ...backend.ProverO return err } +func (cs *SparseR1CS) GetConstraints() [][]string { + var r [][]string + for _, c := range cs.Constraints { + // if we are worried about perf for large cs, we could do a string builder + csv format. + var line [6]string + line[0] = cs.termToString(c.L) + line[1] = cs.termToString(c.R) + line[2] = cs.termToString(c.M[0]) + line[3] = cs.termToString(c.M[1]) + line[4] = cs.termToString(c.O) + line[5] = cs.Coefficients[c.K].String() + r = append(r, line[:]) + } + return r +} + +func (cs *SparseR1CS) termToString(t compiled.Term) string { + var sbb strings.Builder + tID := t.CoeffID() + if tID == compiled.CoeffIdOne { + // do nothing, just print the variable + } else if tID == compiled.CoeffIdMinusOne { + // print neg sign + sbb.WriteByte('-') + } else if tID == compiled.CoeffIdZero { + sbb.WriteByte('0') + return sbb.String() + } else { + sbb.WriteString(cs.Coefficients[tID].String()) + sbb.WriteByte('*') + } + vID := t.WireID() + visibility := t.VariableVisibility() + + switch visibility { + case schema.Internal: + if _, isHint := cs.MHints[vID]; isHint { + sbb.WriteString(fmt.Sprintf("hv%d", vID-cs.NbPublicVariables-cs.NbSecretVariables)) + } else { + sbb.WriteString(fmt.Sprintf("v%d", vID-cs.NbPublicVariables-cs.NbSecretVariables)) + } + case schema.Public: + sbb.WriteString(fmt.Sprintf("p%d", vID)) + case schema.Secret: + sbb.WriteString(fmt.Sprintf("s%d", vID-cs.NbPublicVariables)) + default: + sbb.WriteString("") + } + return sbb.String() +} + // checkConstraint verifies that the constraint holds func (cs *SparseR1CS) checkConstraint(c compiled.SparseR1C, solution *solution) error { l := solution.computeTerm(c.L) diff --git a/internal/backend/bls24-315/cs/r1cs.go b/internal/backend/bls24-315/cs/r1cs.go index 911a0fb9ce..7bc054c6ce 100644 --- a/internal/backend/bls24-315/cs/r1cs.go +++ b/internal/backend/bls24-315/cs/r1cs.go @@ -313,6 +313,68 @@ func sub(a, b int) int { return a - b } +func (cs *R1CS) GetConstraints() [][]string { + var r [][]string + for _, c := range cs.Constraints { + // for each constraint, we build a string representation of it's L, R and O part + // if we are worried about perf for large cs, we could do a string builder + csv format. + var line [3]string + line[0] = cs.vtoString(c.L) + line[1] = cs.vtoString(c.R) + line[2] = cs.vtoString(c.O) + r = append(r, line[:]) + } + return r +} + +func (cs *R1CS) vtoString(l compiled.Variable) string { + var sbb strings.Builder + for i := 0; i < len(l.LinExp); i++ { + cs.termToString(l.LinExp[i], &sbb) + if i+1 < len(l.LinExp) { + sbb.WriteString(" + ") + } + } + return sbb.String() +} + +func (cs *R1CS) termToString(t compiled.Term, sbb *strings.Builder) { + tID := t.CoeffID() + if tID == compiled.CoeffIdOne { + // do nothing, just print the variable + } else if tID == compiled.CoeffIdMinusOne { + // print neg sign + sbb.WriteByte('-') + } else if tID == compiled.CoeffIdZero { + sbb.WriteByte('0') + return + } else { + sbb.WriteString(cs.Coefficients[tID].String()) + sbb.WriteByte('*') + } + vID := t.WireID() + visibility := t.VariableVisibility() + + switch visibility { + case schema.Internal: + if _, isHint := cs.MHints[vID]; isHint { + sbb.WriteString(fmt.Sprintf("hv%d", vID-cs.NbPublicVariables-cs.NbSecretVariables)) + } else { + sbb.WriteString(fmt.Sprintf("v%d", vID-cs.NbPublicVariables-cs.NbSecretVariables)) + } + case schema.Public: + if vID == 0 { + sbb.WriteByte('1') // one wire + } else { + sbb.WriteString(fmt.Sprintf("p%d", vID-1)) + } + case schema.Secret: + sbb.WriteString(fmt.Sprintf("s%d", vID-cs.NbPublicVariables)) + default: + sbb.WriteString("") + } +} + func toHTML(l compiled.Variable, coeffs []fr.Element, MHints map[int]compiled.Hint) string { var sbb strings.Builder for i := 0; i < len(l.LinExp); i++ { diff --git a/internal/backend/bls24-315/cs/r1cs_sparse.go b/internal/backend/bls24-315/cs/r1cs_sparse.go index ba92cd84dc..759b55c41f 100644 --- a/internal/backend/bls24-315/cs/r1cs_sparse.go +++ b/internal/backend/bls24-315/cs/r1cs_sparse.go @@ -28,6 +28,7 @@ import ( "github.com/consensys/gnark/backend" "github.com/consensys/gnark/backend/witness" + "github.com/consensys/gnark/frontend/schema" "github.com/consensys/gnark/internal/backend/compiled" "github.com/consensys/gnark/internal/backend/ioutils" @@ -254,6 +255,57 @@ func (cs *SparseR1CS) IsSolved(witness *witness.Witness, opts ...backend.ProverO return err } +func (cs *SparseR1CS) GetConstraints() [][]string { + var r [][]string + for _, c := range cs.Constraints { + // if we are worried about perf for large cs, we could do a string builder + csv format. + var line [6]string + line[0] = cs.termToString(c.L) + line[1] = cs.termToString(c.R) + line[2] = cs.termToString(c.M[0]) + line[3] = cs.termToString(c.M[1]) + line[4] = cs.termToString(c.O) + line[5] = cs.Coefficients[c.K].String() + r = append(r, line[:]) + } + return r +} + +func (cs *SparseR1CS) termToString(t compiled.Term) string { + var sbb strings.Builder + tID := t.CoeffID() + if tID == compiled.CoeffIdOne { + // do nothing, just print the variable + } else if tID == compiled.CoeffIdMinusOne { + // print neg sign + sbb.WriteByte('-') + } else if tID == compiled.CoeffIdZero { + sbb.WriteByte('0') + return sbb.String() + } else { + sbb.WriteString(cs.Coefficients[tID].String()) + sbb.WriteByte('*') + } + vID := t.WireID() + visibility := t.VariableVisibility() + + switch visibility { + case schema.Internal: + if _, isHint := cs.MHints[vID]; isHint { + sbb.WriteString(fmt.Sprintf("hv%d", vID-cs.NbPublicVariables-cs.NbSecretVariables)) + } else { + sbb.WriteString(fmt.Sprintf("v%d", vID-cs.NbPublicVariables-cs.NbSecretVariables)) + } + case schema.Public: + sbb.WriteString(fmt.Sprintf("p%d", vID)) + case schema.Secret: + sbb.WriteString(fmt.Sprintf("s%d", vID-cs.NbPublicVariables)) + default: + sbb.WriteString("") + } + return sbb.String() +} + // checkConstraint verifies that the constraint holds func (cs *SparseR1CS) checkConstraint(c compiled.SparseR1C, solution *solution) error { l := solution.computeTerm(c.L) diff --git a/internal/backend/bn254/cs/r1cs.go b/internal/backend/bn254/cs/r1cs.go index dc89c507ce..554a461f4e 100644 --- a/internal/backend/bn254/cs/r1cs.go +++ b/internal/backend/bn254/cs/r1cs.go @@ -313,6 +313,68 @@ func sub(a, b int) int { return a - b } +func (cs *R1CS) GetConstraints() [][]string { + var r [][]string + for _, c := range cs.Constraints { + // for each constraint, we build a string representation of it's L, R and O part + // if we are worried about perf for large cs, we could do a string builder + csv format. + var line [3]string + line[0] = cs.vtoString(c.L) + line[1] = cs.vtoString(c.R) + line[2] = cs.vtoString(c.O) + r = append(r, line[:]) + } + return r +} + +func (cs *R1CS) vtoString(l compiled.Variable) string { + var sbb strings.Builder + for i := 0; i < len(l.LinExp); i++ { + cs.termToString(l.LinExp[i], &sbb) + if i+1 < len(l.LinExp) { + sbb.WriteString(" + ") + } + } + return sbb.String() +} + +func (cs *R1CS) termToString(t compiled.Term, sbb *strings.Builder) { + tID := t.CoeffID() + if tID == compiled.CoeffIdOne { + // do nothing, just print the variable + } else if tID == compiled.CoeffIdMinusOne { + // print neg sign + sbb.WriteByte('-') + } else if tID == compiled.CoeffIdZero { + sbb.WriteByte('0') + return + } else { + sbb.WriteString(cs.Coefficients[tID].String()) + sbb.WriteByte('*') + } + vID := t.WireID() + visibility := t.VariableVisibility() + + switch visibility { + case schema.Internal: + if _, isHint := cs.MHints[vID]; isHint { + sbb.WriteString(fmt.Sprintf("hv%d", vID-cs.NbPublicVariables-cs.NbSecretVariables)) + } else { + sbb.WriteString(fmt.Sprintf("v%d", vID-cs.NbPublicVariables-cs.NbSecretVariables)) + } + case schema.Public: + if vID == 0 { + sbb.WriteByte('1') // one wire + } else { + sbb.WriteString(fmt.Sprintf("p%d", vID-1)) + } + case schema.Secret: + sbb.WriteString(fmt.Sprintf("s%d", vID-cs.NbPublicVariables)) + default: + sbb.WriteString("") + } +} + func toHTML(l compiled.Variable, coeffs []fr.Element, MHints map[int]compiled.Hint) string { var sbb strings.Builder for i := 0; i < len(l.LinExp); i++ { diff --git a/internal/backend/bn254/cs/r1cs_sparse.go b/internal/backend/bn254/cs/r1cs_sparse.go index bb46f520fd..cc4cbfd833 100644 --- a/internal/backend/bn254/cs/r1cs_sparse.go +++ b/internal/backend/bn254/cs/r1cs_sparse.go @@ -28,6 +28,7 @@ import ( "github.com/consensys/gnark/backend" "github.com/consensys/gnark/backend/witness" + "github.com/consensys/gnark/frontend/schema" "github.com/consensys/gnark/internal/backend/compiled" "github.com/consensys/gnark/internal/backend/ioutils" @@ -254,6 +255,57 @@ func (cs *SparseR1CS) IsSolved(witness *witness.Witness, opts ...backend.ProverO return err } +func (cs *SparseR1CS) GetConstraints() [][]string { + var r [][]string + for _, c := range cs.Constraints { + // if we are worried about perf for large cs, we could do a string builder + csv format. + var line [6]string + line[0] = cs.termToString(c.L) + line[1] = cs.termToString(c.R) + line[2] = cs.termToString(c.M[0]) + line[3] = cs.termToString(c.M[1]) + line[4] = cs.termToString(c.O) + line[5] = cs.Coefficients[c.K].String() + r = append(r, line[:]) + } + return r +} + +func (cs *SparseR1CS) termToString(t compiled.Term) string { + var sbb strings.Builder + tID := t.CoeffID() + if tID == compiled.CoeffIdOne { + // do nothing, just print the variable + } else if tID == compiled.CoeffIdMinusOne { + // print neg sign + sbb.WriteByte('-') + } else if tID == compiled.CoeffIdZero { + sbb.WriteByte('0') + return sbb.String() + } else { + sbb.WriteString(cs.Coefficients[tID].String()) + sbb.WriteByte('*') + } + vID := t.WireID() + visibility := t.VariableVisibility() + + switch visibility { + case schema.Internal: + if _, isHint := cs.MHints[vID]; isHint { + sbb.WriteString(fmt.Sprintf("hv%d", vID-cs.NbPublicVariables-cs.NbSecretVariables)) + } else { + sbb.WriteString(fmt.Sprintf("v%d", vID-cs.NbPublicVariables-cs.NbSecretVariables)) + } + case schema.Public: + sbb.WriteString(fmt.Sprintf("p%d", vID)) + case schema.Secret: + sbb.WriteString(fmt.Sprintf("s%d", vID-cs.NbPublicVariables)) + default: + sbb.WriteString("") + } + return sbb.String() +} + // checkConstraint verifies that the constraint holds func (cs *SparseR1CS) checkConstraint(c compiled.SparseR1C, solution *solution) error { l := solution.computeTerm(c.L) diff --git a/internal/backend/bw6-633/cs/r1cs.go b/internal/backend/bw6-633/cs/r1cs.go index 89dc3fb5f8..20e19fb5bd 100644 --- a/internal/backend/bw6-633/cs/r1cs.go +++ b/internal/backend/bw6-633/cs/r1cs.go @@ -313,6 +313,68 @@ func sub(a, b int) int { return a - b } +func (cs *R1CS) GetConstraints() [][]string { + var r [][]string + for _, c := range cs.Constraints { + // for each constraint, we build a string representation of it's L, R and O part + // if we are worried about perf for large cs, we could do a string builder + csv format. + var line [3]string + line[0] = cs.vtoString(c.L) + line[1] = cs.vtoString(c.R) + line[2] = cs.vtoString(c.O) + r = append(r, line[:]) + } + return r +} + +func (cs *R1CS) vtoString(l compiled.Variable) string { + var sbb strings.Builder + for i := 0; i < len(l.LinExp); i++ { + cs.termToString(l.LinExp[i], &sbb) + if i+1 < len(l.LinExp) { + sbb.WriteString(" + ") + } + } + return sbb.String() +} + +func (cs *R1CS) termToString(t compiled.Term, sbb *strings.Builder) { + tID := t.CoeffID() + if tID == compiled.CoeffIdOne { + // do nothing, just print the variable + } else if tID == compiled.CoeffIdMinusOne { + // print neg sign + sbb.WriteByte('-') + } else if tID == compiled.CoeffIdZero { + sbb.WriteByte('0') + return + } else { + sbb.WriteString(cs.Coefficients[tID].String()) + sbb.WriteByte('*') + } + vID := t.WireID() + visibility := t.VariableVisibility() + + switch visibility { + case schema.Internal: + if _, isHint := cs.MHints[vID]; isHint { + sbb.WriteString(fmt.Sprintf("hv%d", vID-cs.NbPublicVariables-cs.NbSecretVariables)) + } else { + sbb.WriteString(fmt.Sprintf("v%d", vID-cs.NbPublicVariables-cs.NbSecretVariables)) + } + case schema.Public: + if vID == 0 { + sbb.WriteByte('1') // one wire + } else { + sbb.WriteString(fmt.Sprintf("p%d", vID-1)) + } + case schema.Secret: + sbb.WriteString(fmt.Sprintf("s%d", vID-cs.NbPublicVariables)) + default: + sbb.WriteString("") + } +} + func toHTML(l compiled.Variable, coeffs []fr.Element, MHints map[int]compiled.Hint) string { var sbb strings.Builder for i := 0; i < len(l.LinExp); i++ { diff --git a/internal/backend/bw6-633/cs/r1cs_sparse.go b/internal/backend/bw6-633/cs/r1cs_sparse.go index a1301b4125..a63dc66b11 100644 --- a/internal/backend/bw6-633/cs/r1cs_sparse.go +++ b/internal/backend/bw6-633/cs/r1cs_sparse.go @@ -28,6 +28,7 @@ import ( "github.com/consensys/gnark/backend" "github.com/consensys/gnark/backend/witness" + "github.com/consensys/gnark/frontend/schema" "github.com/consensys/gnark/internal/backend/compiled" "github.com/consensys/gnark/internal/backend/ioutils" @@ -254,6 +255,57 @@ func (cs *SparseR1CS) IsSolved(witness *witness.Witness, opts ...backend.ProverO return err } +func (cs *SparseR1CS) GetConstraints() [][]string { + var r [][]string + for _, c := range cs.Constraints { + // if we are worried about perf for large cs, we could do a string builder + csv format. + var line [6]string + line[0] = cs.termToString(c.L) + line[1] = cs.termToString(c.R) + line[2] = cs.termToString(c.M[0]) + line[3] = cs.termToString(c.M[1]) + line[4] = cs.termToString(c.O) + line[5] = cs.Coefficients[c.K].String() + r = append(r, line[:]) + } + return r +} + +func (cs *SparseR1CS) termToString(t compiled.Term) string { + var sbb strings.Builder + tID := t.CoeffID() + if tID == compiled.CoeffIdOne { + // do nothing, just print the variable + } else if tID == compiled.CoeffIdMinusOne { + // print neg sign + sbb.WriteByte('-') + } else if tID == compiled.CoeffIdZero { + sbb.WriteByte('0') + return sbb.String() + } else { + sbb.WriteString(cs.Coefficients[tID].String()) + sbb.WriteByte('*') + } + vID := t.WireID() + visibility := t.VariableVisibility() + + switch visibility { + case schema.Internal: + if _, isHint := cs.MHints[vID]; isHint { + sbb.WriteString(fmt.Sprintf("hv%d", vID-cs.NbPublicVariables-cs.NbSecretVariables)) + } else { + sbb.WriteString(fmt.Sprintf("v%d", vID-cs.NbPublicVariables-cs.NbSecretVariables)) + } + case schema.Public: + sbb.WriteString(fmt.Sprintf("p%d", vID)) + case schema.Secret: + sbb.WriteString(fmt.Sprintf("s%d", vID-cs.NbPublicVariables)) + default: + sbb.WriteString("") + } + return sbb.String() +} + // checkConstraint verifies that the constraint holds func (cs *SparseR1CS) checkConstraint(c compiled.SparseR1C, solution *solution) error { l := solution.computeTerm(c.L) diff --git a/internal/backend/bw6-761/cs/r1cs.go b/internal/backend/bw6-761/cs/r1cs.go index e46112eb6f..7a857ab11e 100644 --- a/internal/backend/bw6-761/cs/r1cs.go +++ b/internal/backend/bw6-761/cs/r1cs.go @@ -313,6 +313,68 @@ func sub(a, b int) int { return a - b } +func (cs *R1CS) GetConstraints() [][]string { + var r [][]string + for _, c := range cs.Constraints { + // for each constraint, we build a string representation of it's L, R and O part + // if we are worried about perf for large cs, we could do a string builder + csv format. + var line [3]string + line[0] = cs.vtoString(c.L) + line[1] = cs.vtoString(c.R) + line[2] = cs.vtoString(c.O) + r = append(r, line[:]) + } + return r +} + +func (cs *R1CS) vtoString(l compiled.Variable) string { + var sbb strings.Builder + for i := 0; i < len(l.LinExp); i++ { + cs.termToString(l.LinExp[i], &sbb) + if i+1 < len(l.LinExp) { + sbb.WriteString(" + ") + } + } + return sbb.String() +} + +func (cs *R1CS) termToString(t compiled.Term, sbb *strings.Builder) { + tID := t.CoeffID() + if tID == compiled.CoeffIdOne { + // do nothing, just print the variable + } else if tID == compiled.CoeffIdMinusOne { + // print neg sign + sbb.WriteByte('-') + } else if tID == compiled.CoeffIdZero { + sbb.WriteByte('0') + return + } else { + sbb.WriteString(cs.Coefficients[tID].String()) + sbb.WriteByte('*') + } + vID := t.WireID() + visibility := t.VariableVisibility() + + switch visibility { + case schema.Internal: + if _, isHint := cs.MHints[vID]; isHint { + sbb.WriteString(fmt.Sprintf("hv%d", vID-cs.NbPublicVariables-cs.NbSecretVariables)) + } else { + sbb.WriteString(fmt.Sprintf("v%d", vID-cs.NbPublicVariables-cs.NbSecretVariables)) + } + case schema.Public: + if vID == 0 { + sbb.WriteByte('1') // one wire + } else { + sbb.WriteString(fmt.Sprintf("p%d", vID-1)) + } + case schema.Secret: + sbb.WriteString(fmt.Sprintf("s%d", vID-cs.NbPublicVariables)) + default: + sbb.WriteString("") + } +} + func toHTML(l compiled.Variable, coeffs []fr.Element, MHints map[int]compiled.Hint) string { var sbb strings.Builder for i := 0; i < len(l.LinExp); i++ { diff --git a/internal/backend/bw6-761/cs/r1cs_sparse.go b/internal/backend/bw6-761/cs/r1cs_sparse.go index 08f5b614d2..4ede062b48 100644 --- a/internal/backend/bw6-761/cs/r1cs_sparse.go +++ b/internal/backend/bw6-761/cs/r1cs_sparse.go @@ -28,6 +28,7 @@ import ( "github.com/consensys/gnark/backend" "github.com/consensys/gnark/backend/witness" + "github.com/consensys/gnark/frontend/schema" "github.com/consensys/gnark/internal/backend/compiled" "github.com/consensys/gnark/internal/backend/ioutils" @@ -254,6 +255,57 @@ func (cs *SparseR1CS) IsSolved(witness *witness.Witness, opts ...backend.ProverO return err } +func (cs *SparseR1CS) GetConstraints() [][]string { + var r [][]string + for _, c := range cs.Constraints { + // if we are worried about perf for large cs, we could do a string builder + csv format. + var line [6]string + line[0] = cs.termToString(c.L) + line[1] = cs.termToString(c.R) + line[2] = cs.termToString(c.M[0]) + line[3] = cs.termToString(c.M[1]) + line[4] = cs.termToString(c.O) + line[5] = cs.Coefficients[c.K].String() + r = append(r, line[:]) + } + return r +} + +func (cs *SparseR1CS) termToString(t compiled.Term) string { + var sbb strings.Builder + tID := t.CoeffID() + if tID == compiled.CoeffIdOne { + // do nothing, just print the variable + } else if tID == compiled.CoeffIdMinusOne { + // print neg sign + sbb.WriteByte('-') + } else if tID == compiled.CoeffIdZero { + sbb.WriteByte('0') + return sbb.String() + } else { + sbb.WriteString(cs.Coefficients[tID].String()) + sbb.WriteByte('*') + } + vID := t.WireID() + visibility := t.VariableVisibility() + + switch visibility { + case schema.Internal: + if _, isHint := cs.MHints[vID]; isHint { + sbb.WriteString(fmt.Sprintf("hv%d", vID-cs.NbPublicVariables-cs.NbSecretVariables)) + } else { + sbb.WriteString(fmt.Sprintf("v%d", vID-cs.NbPublicVariables-cs.NbSecretVariables)) + } + case schema.Public: + sbb.WriteString(fmt.Sprintf("p%d", vID)) + case schema.Secret: + sbb.WriteString(fmt.Sprintf("s%d", vID-cs.NbPublicVariables)) + default: + sbb.WriteString("") + } + return sbb.String() +} + // checkConstraint verifies that the constraint holds func (cs *SparseR1CS) checkConstraint(c compiled.SparseR1C, solution *solution) error { l := solution.computeTerm(c.L) diff --git a/internal/backend/compiled/cs.go b/internal/backend/compiled/cs.go index dd10a97d04..093c61eb4d 100644 --- a/internal/backend/compiled/cs.go +++ b/internal/backend/compiled/cs.go @@ -197,6 +197,8 @@ func (cs *CS) GetCounters() []Counter { return cs.Counters } func (cs *CS) GetSchema() *schema.Schema { return cs.Schema } +func (cs *CS) GetConstraints() [][]string { panic("not implemented") } + // Counter contains measurements of useful statistics between two Tag type Counter struct { From, To string diff --git a/internal/generator/backend/template/representations/r1cs.go.tmpl b/internal/generator/backend/template/representations/r1cs.go.tmpl index 10b3ac674f..88ce8c351a 100644 --- a/internal/generator/backend/template/representations/r1cs.go.tmpl +++ b/internal/generator/backend/template/representations/r1cs.go.tmpl @@ -295,6 +295,69 @@ func sub(a, b int) int { return a - b } +func (cs *R1CS) GetConstraints() [][]string { + var r [][]string + for _, c := range cs.Constraints { + // for each constraint, we build a string representation of it's L, R and O part + // if we are worried about perf for large cs, we could do a string builder + csv format. + var line [3]string + line[0] = cs.vtoString(c.L) + line[1] = cs.vtoString(c.R) + line[2] = cs.vtoString(c.O) + r = append(r, line[:]) + } + return r +} + +func (cs *R1CS) vtoString(l compiled.Variable) string { + var sbb strings.Builder + for i := 0; i < len(l.LinExp); i++ { + cs.termToString(l.LinExp[i], &sbb) + if i+1 < len(l.LinExp) { + sbb.WriteString(" + ") + } + } + return sbb.String() +} + +func (cs *R1CS) termToString(t compiled.Term, sbb *strings.Builder) { + tID := t.CoeffID() + if tID == compiled.CoeffIdOne { + // do nothing, just print the variable + } else if tID == compiled.CoeffIdMinusOne { + // print neg sign + sbb.WriteByte('-') + } else if tID == compiled.CoeffIdZero { + sbb.WriteByte('0') + return + } else { + sbb.WriteString(cs.Coefficients[tID].String()) + sbb.WriteByte('*') + } + vID := t.WireID() + visibility := t.VariableVisibility() + + switch visibility { + case schema.Internal: + if _, isHint := cs.MHints[vID]; isHint { + sbb.WriteString(fmt.Sprintf("hv%d", vID- cs.NbPublicVariables-cs.NbSecretVariables)) + } else { + sbb.WriteString(fmt.Sprintf("v%d", vID- cs.NbPublicVariables-cs.NbSecretVariables)) + } + case schema.Public: + if vID == 0 { + sbb.WriteByte('1') // one wire + } else { + sbb.WriteString(fmt.Sprintf("p%d", vID - 1)) + } + case schema.Secret: + sbb.WriteString(fmt.Sprintf("s%d", vID - cs.NbPublicVariables)) + default: + sbb.WriteString("") + } +} + + func toHTML(l compiled.Variable, coeffs []fr.Element, MHints map[int]compiled.Hint) string { var sbb strings.Builder for i := 0; i < len(l.LinExp); i++ { diff --git a/internal/generator/backend/template/representations/r1cs.sparse.go.tmpl b/internal/generator/backend/template/representations/r1cs.sparse.go.tmpl index 7ea9bbd9aa..50d5e488c4 100644 --- a/internal/generator/backend/template/representations/r1cs.sparse.go.tmpl +++ b/internal/generator/backend/template/representations/r1cs.sparse.go.tmpl @@ -11,6 +11,7 @@ import ( "github.com/consensys/gnark/internal/backend/ioutils" "github.com/consensys/gnark/internal/backend/compiled" "github.com/consensys/gnark/backend" + "github.com/consensys/gnark/frontend/schema" "github.com/consensys/gnark/backend/witness" {{ template "import_fr" . }} @@ -244,6 +245,59 @@ func (cs *SparseR1CS) IsSolved(witness *witness.Witness, opts ...backend.ProverO return err } +func (cs *SparseR1CS) GetConstraints() [][]string { + var r [][]string + for _, c := range cs.Constraints { + // if we are worried about perf for large cs, we could do a string builder + csv format. + var line [6]string + line[0] = cs.termToString(c.L) + line[1] = cs.termToString(c.R) + line[2] = cs.termToString(c.M[0]) + line[3] = cs.termToString(c.M[1]) + line[4] = cs.termToString(c.O) + line[5] = cs.Coefficients[c.K].String() + r = append(r, line[:]) + } + return r +} + + +func (cs *SparseR1CS) termToString(t compiled.Term) string { + var sbb strings.Builder + tID := t.CoeffID() + if tID == compiled.CoeffIdOne { + // do nothing, just print the variable + } else if tID == compiled.CoeffIdMinusOne { + // print neg sign + sbb.WriteByte('-') + } else if tID == compiled.CoeffIdZero { + sbb.WriteByte('0') + return sbb.String() + } else { + sbb.WriteString(cs.Coefficients[tID].String()) + sbb.WriteByte('*') + } + vID := t.WireID() + visibility := t.VariableVisibility() + + switch visibility { + case schema.Internal: + if _, isHint := cs.MHints[vID]; isHint { + sbb.WriteString(fmt.Sprintf("hv%d", vID- cs.NbPublicVariables-cs.NbSecretVariables)) + } else { + sbb.WriteString(fmt.Sprintf("v%d", vID- cs.NbPublicVariables-cs.NbSecretVariables)) + } + case schema.Public: + sbb.WriteString(fmt.Sprintf("p%d", vID)) + case schema.Secret: + sbb.WriteString(fmt.Sprintf("s%d", vID - cs.NbPublicVariables)) + default: + sbb.WriteString("") + } + return sbb.String() +} + + // checkConstraint verifies that the constraint holds func (cs *SparseR1CS) checkConstraint(c compiled.SparseR1C, solution *solution) error {