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  }