github.com/consensys/gnark@v0.11.0/constraint/r1cs.go (about)

     1  // Copyright 2020 ConsenSys AG
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package constraint
    16  
    17  type R1CS interface {
    18  	ConstraintSystem
    19  
    20  	// AddR1C adds a constraint to the system and returns its id
    21  	// This does not check for validity of the constraint.
    22  	AddR1C(r1c R1C, bID BlueprintID) int
    23  
    24  	// GetR1Cs return the list of R1C
    25  	// See StringBuilder for more info.
    26  	// ! this is an experimental API.
    27  	GetR1Cs() []R1C
    28  
    29  	// GetR1CIterator returns an R1CIterator to iterate on the R1C constraints of the system.
    30  	GetR1CIterator() R1CIterator
    31  }
    32  
    33  // R1CIterator facilitates iterating through R1C constraints.
    34  type R1CIterator struct {
    35  	R1C
    36  	cs *System
    37  	n  int
    38  }
    39  
    40  // Next returns the next R1C or nil if end. Caller must not store the result since the
    41  // same memory space is re-used for subsequent calls to Next.
    42  func (it *R1CIterator) Next() *R1C {
    43  	if it.n >= it.cs.GetNbInstructions() {
    44  		return nil
    45  	}
    46  	inst := it.cs.Instructions[it.n]
    47  	it.n++
    48  	blueprint := it.cs.Blueprints[inst.BlueprintID]
    49  	if bc, ok := blueprint.(BlueprintR1C); ok {
    50  		bc.DecompressR1C(&it.R1C, inst.Unpack(it.cs))
    51  		return &it.R1C
    52  	}
    53  	return it.Next()
    54  }
    55  
    56  // // IsValid perform post compilation checks on the Variables
    57  // //
    58  // // 1. checks that all user inputs are referenced in at least one constraint
    59  // // 2. checks that all hints are constrained
    60  // func (r1cs *R1CSCore) CheckUnconstrainedWires() error {
    61  // 	return nil
    62  
    63  // 	// TODO @gbotrel add unit test for that.
    64  
    65  // 	inputConstrained := make([]bool, r1cs.GetNbSecretVariables()+r1cs.GetNbPublicVariables())
    66  // 	// one wire does not need to be constrained
    67  // 	inputConstrained[0] = true
    68  // 	cptInputs := len(inputConstrained) - 1 // marking 1 wire as already constrained // TODO @gbotrel check that
    69  // 	if cptInputs == 0 {
    70  // 		return errors.New("invalid constraint system: no input defined")
    71  // 	}
    72  
    73  // 	cptHints := len(r1cs.MHints)
    74  // 	mHintsConstrained := make(map[int]bool)
    75  
    76  // 	// for each constraint, we check the linear expressions and mark our inputs / hints as constrained
    77  // 	processLinearExpression := func(l LinearExpression) {
    78  // 		for _, t := range l {
    79  // 			if t.CoeffID() == CoeffIdZero {
    80  // 				// ignore zero coefficient, as it does not constraint the Variable
    81  // 				// though, we may want to flag that IF the Variable doesn't appear else where
    82  // 				continue
    83  // 			}
    84  // 			vID := t.WireID()
    85  // 			if vID < len(inputConstrained) {
    86  // 				if !inputConstrained[vID] {
    87  // 					inputConstrained[vID] = true
    88  // 					cptInputs--
    89  // 				}
    90  // 			} else {
    91  // 				// internal variable, let's check if it's a hint
    92  // 				if _, ok := r1cs.MHints[vID]; ok {
    93  // 					if !mHintsConstrained[vID] {
    94  // 						mHintsConstrained[vID] = true
    95  // 						cptHints--
    96  // 					}
    97  // 				}
    98  // 			}
    99  
   100  // 		}
   101  // 	}
   102  // 	for _, r1c := range r1cs.Constraints {
   103  // 		processLinearExpression(r1c.L)
   104  // 		processLinearExpression(r1c.R)
   105  // 		processLinearExpression(r1c.O)
   106  
   107  // 		if cptHints|cptInputs == 0 {
   108  // 			return nil // we can stop.
   109  // 		}
   110  
   111  // 	}
   112  
   113  // 	// something is a miss, we build the error string
   114  // 	var sbb strings.Builder
   115  // 	if cptInputs != 0 {
   116  // 		sbb.WriteString(strconv.Itoa(cptInputs))
   117  // 		sbb.WriteString(" unconstrained input(s):")
   118  // 		sbb.WriteByte('\n')
   119  // 		for i := 0; i < len(inputConstrained) && cptInputs != 0; i++ {
   120  // 			if !inputConstrained[i] {
   121  // 				if i < len(r1cs.Public) {
   122  // 					sbb.WriteString(r1cs.Public[i])
   123  // 				} else {
   124  // 					sbb.WriteString(r1cs.Secret[i-len(r1cs.Public)])
   125  // 				}
   126  
   127  // 				sbb.WriteByte('\n')
   128  // 				cptInputs--
   129  // 			}
   130  // 		}
   131  // 		sbb.WriteByte('\n')
   132  // 		return errors.New(sbb.String())
   133  // 	}
   134  
   135  // 	if cptHints != 0 {
   136  // 		// TODO @gbotrel @ivokub investigate --> emulated hints seems to go in this path a lot.
   137  // 		sbb.WriteString(strconv.Itoa(cptHints))
   138  // 		sbb.WriteString(" unconstrained hints; i.e. wire created through NewHint() but doesn't not appear in the constraint system")
   139  // 		sbb.WriteByte('\n')
   140  // 		log := logger.Logger()
   141  // 		log.Warn().Err(errors.New(sbb.String())).Send()
   142  // 		return nil
   143  // 		// TODO we may add more debug info here → idea, in NewHint, take the debug stack, and store in the hint map some
   144  // 		// debugInfo to find where a hint was declared (and not constrained)
   145  // 	}
   146  // 	return errors.New(sbb.String())
   147  // }
   148  
   149  // R1C used to compute the wires
   150  type R1C struct {
   151  	L, R, O LinearExpression
   152  }
   153  
   154  // String formats a R1C as Lā‹…R == O
   155  func (r1c *R1C) String(r Resolver) string {
   156  	sbb := NewStringBuilder(r)
   157  	sbb.WriteLinearExpression(r1c.L)
   158  	sbb.WriteString(" ā‹… ")
   159  	sbb.WriteLinearExpression(r1c.R)
   160  	sbb.WriteString(" == ")
   161  	sbb.WriteLinearExpression(r1c.O)
   162  	return sbb.String()
   163  }