github.com/consensys/gnark@v0.11.0/internal/generator/backend/template/representations/system.go.tmpl (about) 1 import ( 2 "io" 3 "time" 4 5 csolver "github.com/consensys/gnark/constraint/solver" 6 "github.com/consensys/gnark/constraint" 7 "github.com/consensys/gnark/logger" 8 "github.com/consensys/gnark/backend/witness" 9 10 "github.com/consensys/gnark-crypto/ecc" 11 12 {{ template "import_fr" . }} 13 ) 14 15 type R1CS = system 16 type SparseR1CS = system 17 18 // system is a curved-typed constraint.System with a concrete coefficient table (fr.Element) 19 type system struct { 20 constraint.System 21 CoeffTable 22 field 23 } 24 25 // NewR1CS is a constructor for R1CS. It is meant to be use by gnark frontend only, 26 // and should not be used by gnark users. See groth16.NewCS(...) instead. 27 func NewR1CS(capacity int) *R1CS { 28 return newSystem(capacity, constraint.SystemR1CS) 29 } 30 31 // NewSparseR1CS is a constructor for SparseR1CS. It is meant to be use by gnark frontend only, 32 // and should not be used by gnark users. See plonk.NewCS(...) instead. 33 func NewSparseR1CS(capacity int) *SparseR1CS { 34 return newSystem(capacity, constraint.SystemSparseR1CS) 35 } 36 37 func newSystem(capacity int, t constraint.SystemType) *system { 38 return &system{ 39 System: constraint.NewSystem(fr.Modulus(), capacity, t), 40 CoeffTable: newCoeffTable(capacity / 10), 41 } 42 } 43 44 45 // Solve solves the constraint system with provided witness. 46 // If it's a R1CS returns R1CSSolution 47 // If it's a SparseR1CS returns SparseR1CSSolution 48 func (cs *system) Solve(witness witness.Witness, opts ...csolver.Option) (any, error) { 49 log := logger.Logger().With().Int("nbConstraints", cs.GetNbConstraints()).Logger() 50 start := time.Now() 51 52 53 v := witness.Vector().(fr.Vector) 54 55 // init the solver 56 solver, err := newSolver(cs, v, opts...) 57 if err != nil { 58 log.Err(err).Send() 59 return nil, err 60 } 61 62 // reset the stateful blueprints 63 for i := range cs.Blueprints { 64 if b, ok := cs.Blueprints[i].(constraint.BlueprintStateful); ok { 65 b.Reset() 66 } 67 } 68 69 // defer log printing once all solver.values are computed 70 // (or sooner, if a constraint is not satisfied) 71 defer solver.printLogs(cs.Logs) 72 73 // run it. 74 if err := solver.run(); err != nil { 75 log.Err(err).Send() 76 return nil, err 77 } 78 79 log.Debug().Dur("took", time.Since(start)).Msg("constraint system solver done") 80 81 // format the solution 82 // TODO @gbotrel revisit post-refactor 83 if cs.Type == constraint.SystemR1CS { 84 var res R1CSSolution 85 res.W = solver.values 86 res.A = solver.a 87 res.B = solver.b 88 res.C = solver.c 89 return &res, nil 90 } else { 91 // sparse R1CS 92 var res SparseR1CSSolution 93 // query l, r, o in Lagrange basis, not blinded 94 res.L, res.R, res.O = evaluateLROSmallDomain(cs, solver.values) 95 96 return &res, nil 97 } 98 99 } 100 101 // IsSolved 102 // Deprecated: use _, err := Solve(...) instead 103 func (cs *system) IsSolved(witness witness.Witness, opts ...csolver.Option) error { 104 _, err := cs.Solve(witness, opts...) 105 return err 106 } 107 108 109 // GetR1Cs return the list of R1C 110 func (cs *system) GetR1Cs() []constraint.R1C { 111 toReturn := make([]constraint.R1C, 0, cs.GetNbConstraints()) 112 113 for _, inst := range cs.Instructions { 114 blueprint := cs.Blueprints[inst.BlueprintID] 115 if bc, ok := blueprint.(constraint.BlueprintR1C); ok { 116 var r1c constraint.R1C 117 bc.DecompressR1C(&r1c, inst.Unpack(&cs.System)) 118 toReturn = append(toReturn, r1c) 119 } 120 } 121 return toReturn 122 } 123 124 // GetNbCoefficients return the number of unique coefficients needed in the R1CS 125 func (cs *system) GetNbCoefficients() int { 126 return len(cs.Coefficients) 127 } 128 129 // CurveID returns curve ID as defined in gnark-crypto 130 func (cs *system) CurveID() ecc.ID { 131 return ecc.{{.CurveID}} 132 } 133 134 func (cs *system) GetCoefficient(i int) (r constraint.Element) { 135 copy(r[:], cs.Coefficients[i][:]) 136 return 137 } 138 139 140 // GetSparseR1Cs return the list of SparseR1C 141 func (cs *system) GetSparseR1Cs() []constraint.SparseR1C { 142 143 toReturn := make([]constraint.SparseR1C, 0, cs.GetNbConstraints()) 144 145 for _, inst := range cs.Instructions { 146 blueprint := cs.Blueprints[inst.BlueprintID] 147 if bc, ok := blueprint.(constraint.BlueprintSparseR1C); ok { 148 var sparseR1C constraint.SparseR1C 149 bc.DecompressSparseR1C(&sparseR1C, inst.Unpack(&cs.System)) 150 toReturn = append(toReturn, sparseR1C) 151 } 152 } 153 return toReturn 154 } 155 156 157 158 // evaluateLROSmallDomain extracts the solver l, r, o, and returns it in lagrange form. 159 // solver = [ public | secret | internal ] 160 // TODO @gbotrel refactor; this seems to be a small util function for plonk 161 func evaluateLROSmallDomain(cs *system, solution []fr.Element) ([]fr.Element, []fr.Element, []fr.Element) { 162 163 //s := int(pk.Domain[0].Cardinality) 164 s := cs.GetNbConstraints() + len(cs.Public) // len(spr.Public) is for the placeholder constraints 165 s = int(ecc.NextPowerOfTwo(uint64(s))) 166 167 var l, r, o []fr.Element 168 l = make([]fr.Element, s, s + 4) // +4 to leave room for the blinding in plonk 169 r = make([]fr.Element, s, s + 4) 170 o = make([]fr.Element, s, s + 4) 171 s0 := solution[0] 172 173 for i := 0; i < len(cs.Public); i++ { // placeholders 174 l[i] = solution[i] 175 r[i] = s0 176 o[i] = s0 177 } 178 offset := len(cs.Public) 179 nbConstraints := cs.GetNbConstraints() 180 181 182 var sparseR1C constraint.SparseR1C 183 j := 0 184 for _, inst := range cs.Instructions { 185 blueprint := cs.Blueprints[inst.BlueprintID] 186 if bc, ok := blueprint.(constraint.BlueprintSparseR1C); ok { 187 bc.DecompressSparseR1C(&sparseR1C, inst.Unpack(&cs.System)) 188 189 l[offset+j] = solution[sparseR1C.XA] 190 r[offset+j] = solution[sparseR1C.XB] 191 o[offset+j] = solution[sparseR1C.XC] 192 j++ 193 } 194 } 195 196 197 offset += nbConstraints 198 199 for i := 0; i < s-offset; i++ { // offset to reach 2**n constraints (where the id of l,r,o is 0, so we assign solver[0]) 200 l[offset+i] = s0 201 r[offset+i] = s0 202 o[offset+i] = s0 203 } 204 205 return l, r, o 206 207 } 208 209 210 211 // R1CSSolution represent a valid assignment to all the variables in the constraint system. 212 // The vector W such that Aw o Bw - Cw = 0 213 type R1CSSolution struct { 214 W fr.Vector 215 A, B, C fr.Vector 216 } 217 218 func (t *R1CSSolution) WriteTo(w io.Writer) (int64, error) { 219 n, err := t.W.WriteTo(w) 220 if err != nil { 221 return n, err 222 } 223 a, err := t.A.WriteTo(w) 224 n += a 225 if err != nil { 226 return n, err 227 } 228 a, err = t.B.WriteTo(w) 229 n += a 230 if err != nil { 231 return n, err 232 } 233 a, err = t.C.WriteTo(w) 234 n += a 235 return n, err 236 } 237 238 func (t *R1CSSolution) ReadFrom(r io.Reader) (int64, error) { 239 n, err := t.W.ReadFrom(r) 240 if err != nil { 241 return n, err 242 } 243 a, err := t.A.ReadFrom(r) 244 n += a 245 if err != nil { 246 return n, err 247 } 248 a, err = t.B.ReadFrom(r) 249 n += a 250 if err != nil { 251 return n, err 252 } 253 a, err = t.C.ReadFrom(r) 254 n += a 255 return n, err 256 } 257 258 259 260 // SparseR1CSSolution represent a valid assignment to all the variables in the constraint system. 261 type SparseR1CSSolution struct { 262 L, R, O fr.Vector 263 } 264 265 func (t *SparseR1CSSolution) WriteTo(w io.Writer) (int64, error) { 266 n, err := t.L.WriteTo(w) 267 if err != nil { 268 return n, err 269 } 270 a, err := t.R.WriteTo(w) 271 n += a 272 if err != nil { 273 return n, err 274 } 275 a, err = t.O.WriteTo(w) 276 n += a 277 return n, err 278 279 } 280 281 func (t *SparseR1CSSolution) ReadFrom(r io.Reader) (int64, error) { 282 n, err := t.L.ReadFrom(r) 283 if err != nil { 284 return n, err 285 } 286 a, err := t.R.ReadFrom(r) 287 n += a 288 if err != nil { 289 return n, err 290 } 291 a, err = t.O.ReadFrom(r) 292 n += a 293 return n, err 294 } 295 296 297 func (s *system) AddGkr(gkr constraint.GkrInfo) error { 298 return s.System.AddGkr(gkr) 299 }