github.com/consensys/gnark@v0.11.0/test/engine.go (about)

     1  /*
     2  Copyright © 2021 ConsenSys Software Inc.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package test
    18  
    19  import (
    20  	"fmt"
    21  	"math/big"
    22  	"path/filepath"
    23  	"reflect"
    24  	"runtime"
    25  	"strconv"
    26  	"strings"
    27  	"sync/atomic"
    28  
    29  	"github.com/bits-and-blooms/bitset"
    30  	"github.com/consensys/gnark/constraint"
    31  
    32  	"github.com/consensys/gnark/constraint/solver"
    33  	"github.com/consensys/gnark/debug"
    34  	"github.com/consensys/gnark/frontend/schema"
    35  	"github.com/consensys/gnark/logger"
    36  	"golang.org/x/crypto/sha3"
    37  
    38  	"github.com/consensys/gnark-crypto/ecc"
    39  	"github.com/consensys/gnark-crypto/field/pool"
    40  	"github.com/consensys/gnark/backend"
    41  	"github.com/consensys/gnark/frontend"
    42  	"github.com/consensys/gnark/internal/circuitdefer"
    43  	"github.com/consensys/gnark/internal/kvstore"
    44  	"github.com/consensys/gnark/internal/utils"
    45  )
    46  
    47  // engine implements frontend.API
    48  //
    49  // it is used for a faster verification of witness in tests
    50  // and more importantly, for fuzzing purposes
    51  //
    52  // it converts the inputs to the API to big.Int (after a mod reduce using the curve base field)
    53  type engine struct {
    54  	curveID ecc.ID
    55  	q       *big.Int
    56  	opt     backend.ProverConfig
    57  	// mHintsFunctions map[hint.ID]hintFunction
    58  	constVars bool
    59  	kvstore.Store
    60  	blueprints        []constraint.Blueprint
    61  	internalVariables []*big.Int
    62  }
    63  
    64  // TestEngineOption defines an option for the test engine.
    65  type TestEngineOption func(e *engine) error
    66  
    67  // SetAllVariablesAsConstants is a test engine option which makes the calls to
    68  // IsConstant() and ConstantValue() always return true. If this test engine
    69  // option is not set, then all variables are considered as non-constant,
    70  // regardless if it is constructed by a call to ConstantValue().
    71  func SetAllVariablesAsConstants() TestEngineOption {
    72  	return func(e *engine) error {
    73  		e.constVars = true
    74  		return nil
    75  	}
    76  }
    77  
    78  // WithBackendProverOptions is a test engine option which allows to define
    79  // prover options. If not set, then default prover configuration is used.
    80  func WithBackendProverOptions(opts ...backend.ProverOption) TestEngineOption {
    81  	return func(e *engine) error {
    82  		cfg, err := backend.NewProverConfig(opts...)
    83  		if err != nil {
    84  			return fmt.Errorf("new prover config: %w", err)
    85  		}
    86  		e.opt = cfg
    87  		return nil
    88  	}
    89  }
    90  
    91  // IsSolved returns an error if the test execution engine failed to execute the given circuit
    92  // with provided witness as input.
    93  //
    94  // The test execution engine implements frontend.API using big.Int operations.
    95  //
    96  // This is an experimental feature.
    97  func IsSolved(circuit, witness frontend.Circuit, field *big.Int, opts ...TestEngineOption) (err error) {
    98  	e := &engine{
    99  		curveID:   utils.FieldToCurve(field),
   100  		q:         new(big.Int).Set(field),
   101  		constVars: false,
   102  		Store:     kvstore.New(),
   103  	}
   104  	for _, opt := range opts {
   105  		if err := opt(e); err != nil {
   106  			return fmt.Errorf("apply option: %w", err)
   107  		}
   108  	}
   109  
   110  	// TODO handle opt.LoggerOut ?
   111  
   112  	// we clone the circuit, in case the circuit has some attributes it uses in its Define function
   113  	// set by the user.
   114  	// then, we set all the variables values to the ones from the witness
   115  
   116  	// clone the circuit
   117  	c := shallowClone(circuit)
   118  
   119  	// set the witness values
   120  	copyWitness(c, witness)
   121  
   122  	defer func() {
   123  		if r := recover(); r != nil {
   124  			err = fmt.Errorf("%v\n%s", r, string(debug.Stack()))
   125  		}
   126  	}()
   127  
   128  	log := logger.Logger()
   129  	log.Debug().Msg("running circuit in test engine")
   130  	cptAdd, cptMul, cptSub, cptToBinary, cptFromBinary, cptAssertIsEqual = 0, 0, 0, 0, 0, 0
   131  
   132  	// first we reset the stateful blueprints
   133  	for i := range e.blueprints {
   134  		if b, ok := e.blueprints[i].(constraint.BlueprintStateful); ok {
   135  			b.Reset()
   136  		}
   137  	}
   138  
   139  	if err = c.Define(e); err != nil {
   140  		return fmt.Errorf("define: %w", err)
   141  	}
   142  	if err = callDeferred(e); err != nil {
   143  		return fmt.Errorf("deferred: %w", err)
   144  	}
   145  
   146  	log.Debug().Uint64("add", cptAdd).
   147  		Uint64("sub", cptSub).
   148  		Uint64("mul", cptMul).
   149  		Uint64("equals", cptAssertIsEqual).
   150  		Uint64("toBinary", cptToBinary).
   151  		Uint64("fromBinary", cptFromBinary).Msg("counters")
   152  
   153  	return
   154  }
   155  
   156  func callDeferred(builder *engine) error {
   157  	for i := 0; i < len(circuitdefer.GetAll[func(frontend.API) error](builder)); i++ {
   158  		if err := circuitdefer.GetAll[func(frontend.API) error](builder)[i](builder); err != nil {
   159  			return fmt.Errorf("defer fn %d: %w", i, err)
   160  		}
   161  	}
   162  	return nil
   163  }
   164  
   165  var cptAdd, cptMul, cptSub, cptToBinary, cptFromBinary, cptAssertIsEqual uint64
   166  
   167  func (e *engine) Add(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable {
   168  	atomic.AddUint64(&cptAdd, 1)
   169  	res := new(big.Int)
   170  	res.Add(e.toBigInt(i1), e.toBigInt(i2))
   171  	for i := 0; i < len(in); i++ {
   172  		atomic.AddUint64(&cptAdd, 1)
   173  		res.Add(res, e.toBigInt(in[i]))
   174  	}
   175  	res.Mod(res, e.modulus())
   176  	return res
   177  }
   178  
   179  func (e *engine) MulAcc(a, b, c frontend.Variable) frontend.Variable {
   180  	bc := pool.BigInt.Get()
   181  	bc.Mul(e.toBigInt(b), e.toBigInt(c))
   182  
   183  	res := new(big.Int)
   184  	_a := e.toBigInt(a)
   185  	res.Add(_a, bc).Mod(res, e.modulus())
   186  
   187  	pool.BigInt.Put(bc)
   188  	return res
   189  }
   190  
   191  func (e *engine) Sub(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable {
   192  	atomic.AddUint64(&cptSub, 1)
   193  	res := new(big.Int)
   194  	res.Sub(e.toBigInt(i1), e.toBigInt(i2))
   195  	for i := 0; i < len(in); i++ {
   196  		atomic.AddUint64(&cptSub, 1)
   197  		res.Sub(res, e.toBigInt(in[i]))
   198  	}
   199  	res.Mod(res, e.modulus())
   200  	return res
   201  }
   202  
   203  func (e *engine) Neg(i1 frontend.Variable) frontend.Variable {
   204  	res := new(big.Int)
   205  	res.Neg(e.toBigInt(i1))
   206  	res.Mod(res, e.modulus())
   207  	return res
   208  }
   209  
   210  func (e *engine) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable {
   211  	atomic.AddUint64(&cptMul, 1)
   212  	b2 := e.toBigInt(i2)
   213  	if len(in) == 0 && b2.IsUint64() && b2.Uint64() <= 1 {
   214  		// special path to avoid useless allocations
   215  		if b2.Uint64() == 0 {
   216  			return 0
   217  		}
   218  		return i1
   219  	}
   220  	b1 := e.toBigInt(i1)
   221  	res := new(big.Int)
   222  	res.Mul(b1, b2)
   223  	res.Mod(res, e.modulus())
   224  	for i := 0; i < len(in); i++ {
   225  		atomic.AddUint64(&cptMul, 1)
   226  		res.Mul(res, e.toBigInt(in[i]))
   227  		res.Mod(res, e.modulus())
   228  	}
   229  	return res
   230  }
   231  
   232  func (e *engine) Div(i1, i2 frontend.Variable) frontend.Variable {
   233  	res := new(big.Int)
   234  	if res.ModInverse(e.toBigInt(i2), e.modulus()) == nil {
   235  		panic("no inverse")
   236  	}
   237  	res.Mul(res, e.toBigInt(i1))
   238  	res.Mod(res, e.modulus())
   239  	return res
   240  }
   241  
   242  func (e *engine) DivUnchecked(i1, i2 frontend.Variable) frontend.Variable {
   243  	res := new(big.Int)
   244  	b1, b2 := e.toBigInt(i1), e.toBigInt(i2)
   245  	if b1.IsUint64() && b2.IsUint64() && b1.Uint64() == 0 && b2.Uint64() == 0 {
   246  		return 0
   247  	}
   248  	if res.ModInverse(b2, e.modulus()) == nil {
   249  		panic("no inverse")
   250  	}
   251  	res.Mul(res, b1)
   252  	res.Mod(res, e.modulus())
   253  	return res
   254  }
   255  
   256  func (e *engine) Inverse(i1 frontend.Variable) frontend.Variable {
   257  	res := new(big.Int)
   258  	if res.ModInverse(e.toBigInt(i1), e.modulus()) == nil {
   259  		panic("no inverse")
   260  	}
   261  	return res
   262  }
   263  
   264  func (e *engine) BatchInvert(in []frontend.Variable) []frontend.Variable {
   265  	// having a batch invert saves a lot of ops in the test engine (ModInverse is terribly inefficient)
   266  	_in := make([]*big.Int, len(in))
   267  	for i := 0; i < len(_in); i++ {
   268  		_in[i] = e.toBigInt(in[i])
   269  	}
   270  
   271  	_out := e.batchInvert(_in)
   272  
   273  	res := make([]frontend.Variable, len(in))
   274  	for i := 0; i < len(in); i++ {
   275  		res[i] = _out[i]
   276  	}
   277  	return res
   278  }
   279  
   280  func (e *engine) batchInvert(a []*big.Int) []*big.Int {
   281  	res := make([]*big.Int, len(a))
   282  	for i := range res {
   283  		res[i] = new(big.Int)
   284  	}
   285  	if len(a) == 0 {
   286  		return res
   287  	}
   288  
   289  	zeroes := bitset.New(uint(len(a)))
   290  	accumulator := new(big.Int).SetUint64(1)
   291  
   292  	for i := 0; i < len(a); i++ {
   293  		if a[i].Sign() == 0 {
   294  			zeroes.Set(uint(i))
   295  			continue
   296  		}
   297  		res[i].Set(accumulator)
   298  
   299  		accumulator.Mul(accumulator, a[i])
   300  		accumulator.Mod(accumulator, e.modulus())
   301  	}
   302  
   303  	accumulator.ModInverse(accumulator, e.modulus())
   304  
   305  	for i := len(a) - 1; i >= 0; i-- {
   306  		if zeroes.Test(uint(i)) {
   307  			continue
   308  		}
   309  		res[i].Mul(res[i], accumulator)
   310  		res[i].Mod(res[i], e.modulus())
   311  		accumulator.Mul(accumulator, a[i])
   312  		accumulator.Mod(accumulator, e.modulus())
   313  	}
   314  
   315  	return res
   316  }
   317  
   318  func (e *engine) ToBinary(i1 frontend.Variable, n ...int) []frontend.Variable {
   319  	atomic.AddUint64(&cptToBinary, 1)
   320  	nbBits := e.FieldBitLen()
   321  	if len(n) == 1 {
   322  		nbBits = n[0]
   323  		if nbBits < 0 {
   324  			panic("invalid n")
   325  		}
   326  	}
   327  
   328  	b1 := e.toBigInt(i1)
   329  
   330  	if b1.BitLen() > nbBits {
   331  		panic(fmt.Sprintf("[ToBinary] decomposing %s (bitLen == %d) with %d bits", b1.String(), b1.BitLen(), nbBits))
   332  	}
   333  
   334  	r := make([]frontend.Variable, nbBits)
   335  	ri := make([]frontend.Variable, nbBits)
   336  	for i := 0; i < len(r); i++ {
   337  		r[i] = (b1.Bit(i))
   338  		ri[i] = r[i]
   339  	}
   340  
   341  	// this is a sanity check, it should never happen
   342  	value := e.toBigInt(e.FromBinary(ri...))
   343  	if value.Cmp(b1) != 0 {
   344  
   345  		panic(fmt.Sprintf("[ToBinary] decomposing %s (bitLen == %d) with %d bits reconstructs into %s", b1.String(), b1.BitLen(), nbBits, value.String()))
   346  	}
   347  	return r
   348  }
   349  
   350  func (e *engine) FromBinary(v ...frontend.Variable) frontend.Variable {
   351  	atomic.AddUint64(&cptFromBinary, 1)
   352  	bits := make([]bool, len(v))
   353  	for i := 0; i < len(v); i++ {
   354  		be := e.toBigInt(v[i])
   355  		e.mustBeBoolean(be)
   356  		bits[i] = be.Uint64() == 1
   357  
   358  	}
   359  
   360  	// Σ (2**i * bits[i]) == r
   361  	c := new(big.Int)
   362  	r := new(big.Int)
   363  	c.SetUint64(1)
   364  
   365  	for i := 0; i < len(bits); i++ {
   366  		if bits[i] {
   367  			r.Add(r, c)
   368  		}
   369  		c.Lsh(c, 1)
   370  	}
   371  	r.Mod(r, e.modulus())
   372  
   373  	return r
   374  }
   375  
   376  func (e *engine) Xor(i1, i2 frontend.Variable) frontend.Variable {
   377  	b1, b2 := e.toBigInt(i1), e.toBigInt(i2)
   378  	e.mustBeBoolean(b1)
   379  	e.mustBeBoolean(b2)
   380  	res := new(big.Int)
   381  	res.Xor(b1, b2)
   382  	return res
   383  }
   384  
   385  func (e *engine) Or(i1, i2 frontend.Variable) frontend.Variable {
   386  	b1, b2 := e.toBigInt(i1), e.toBigInt(i2)
   387  	e.mustBeBoolean(b1)
   388  	e.mustBeBoolean(b2)
   389  	res := new(big.Int)
   390  	res.Or(b1, b2)
   391  	return res
   392  }
   393  
   394  func (e *engine) And(i1, i2 frontend.Variable) frontend.Variable {
   395  	b1, b2 := e.toBigInt(i1), e.toBigInt(i2)
   396  	e.mustBeBoolean(b1)
   397  	e.mustBeBoolean(b2)
   398  	res := new(big.Int)
   399  	res.And(b1, b2)
   400  	return res
   401  }
   402  
   403  // Select if b is true, yields i1 else yields i2
   404  func (e *engine) Select(b frontend.Variable, i1, i2 frontend.Variable) frontend.Variable {
   405  	b1 := e.toBigInt(b)
   406  	e.mustBeBoolean(b1)
   407  
   408  	if b1.Uint64() == 1 {
   409  		return e.toBigInt(i1)
   410  	}
   411  	return (e.toBigInt(i2))
   412  }
   413  
   414  // Lookup2 performs a 2-bit lookup between i1, i2, i3, i4 based on bits b0
   415  // and b1. Returns i0 if b0=b1=0, i1 if b0=1 and b1=0, i2 if b0=0 and b1=1
   416  // and i3 if b0=b1=1.
   417  func (e *engine) Lookup2(b0, b1 frontend.Variable, i0, i1, i2, i3 frontend.Variable) frontend.Variable {
   418  	s0 := e.toBigInt(b0)
   419  	s1 := e.toBigInt(b1)
   420  	e.mustBeBoolean(s0)
   421  	e.mustBeBoolean(s1)
   422  	lookup := new(big.Int).Lsh(s1, 1)
   423  	lookup.Or(lookup, s0)
   424  	return e.toBigInt([]frontend.Variable{i0, i1, i2, i3}[lookup.Uint64()])
   425  }
   426  
   427  // IsZero returns 1 if a is zero, 0 otherwise
   428  func (e *engine) IsZero(i1 frontend.Variable) frontend.Variable {
   429  	b1 := e.toBigInt(i1)
   430  
   431  	if b1.IsUint64() && b1.Uint64() == 0 {
   432  		return big.NewInt(1)
   433  	}
   434  
   435  	return big.NewInt(0)
   436  }
   437  
   438  // Cmp returns 1 if i1>i2, 0 if i1==i2, -1 if i1<i2
   439  func (e *engine) Cmp(i1, i2 frontend.Variable) frontend.Variable {
   440  	b1 := e.toBigInt(i1)
   441  	b2 := e.toBigInt(i2)
   442  	res := big.NewInt(int64(b1.Cmp(b2)))
   443  	res.Mod(res, e.modulus())
   444  	return res
   445  }
   446  
   447  func (e *engine) AssertIsEqual(i1, i2 frontend.Variable) {
   448  	atomic.AddUint64(&cptAssertIsEqual, 1)
   449  	b1, b2 := e.toBigInt(i1), e.toBigInt(i2)
   450  	if b1.Cmp(b2) != 0 {
   451  		panic(fmt.Sprintf("[assertIsEqual] %s == %s", b1.String(), b2.String()))
   452  	}
   453  }
   454  
   455  func (e *engine) AssertIsDifferent(i1, i2 frontend.Variable) {
   456  	b1, b2 := e.toBigInt(i1), e.toBigInt(i2)
   457  	if b1.Cmp(b2) == 0 {
   458  		panic(fmt.Sprintf("[assertIsDifferent] %s != %s", b1.String(), b2.String()))
   459  	}
   460  }
   461  
   462  func (e *engine) AssertIsBoolean(i1 frontend.Variable) {
   463  	b1 := e.toBigInt(i1)
   464  	e.mustBeBoolean(b1)
   465  }
   466  
   467  func (e *engine) AssertIsCrumb(i1 frontend.Variable) {
   468  	i1 = e.MulAcc(e.Mul(-3, i1), i1, i1)
   469  	i1 = e.MulAcc(e.Mul(2, i1), i1, i1)
   470  	e.AssertIsEqual(i1, 0)
   471  }
   472  
   473  func (e *engine) AssertIsLessOrEqual(v frontend.Variable, bound frontend.Variable) {
   474  
   475  	bValue := e.toBigInt(bound)
   476  
   477  	if bValue.Sign() == -1 {
   478  		panic(fmt.Sprintf("[assertIsLessOrEqual] bound (%s) must be positive", bValue.String()))
   479  	}
   480  
   481  	b1 := e.toBigInt(v)
   482  	if b1.Cmp(bValue) == 1 {
   483  		panic(fmt.Sprintf("[assertIsLessOrEqual] %s > %s", b1.String(), bValue.String()))
   484  	}
   485  }
   486  
   487  func (e *engine) Println(a ...frontend.Variable) {
   488  	var sbb strings.Builder
   489  	sbb.WriteString("(test.engine) ")
   490  
   491  	// prefix log line with file.go:line
   492  	if _, file, line, ok := runtime.Caller(1); ok {
   493  		sbb.WriteString(filepath.Base(file))
   494  		sbb.WriteByte(':')
   495  		sbb.WriteString(strconv.Itoa(line))
   496  		sbb.WriteByte(' ')
   497  	}
   498  
   499  	for i := 0; i < len(a); i++ {
   500  		e.print(&sbb, a[i])
   501  		sbb.WriteByte(' ')
   502  	}
   503  	fmt.Println(sbb.String())
   504  }
   505  
   506  func (e *engine) print(sbb *strings.Builder, x interface{}) {
   507  	switch v := x.(type) {
   508  	case string:
   509  		sbb.WriteString(v)
   510  	case []frontend.Variable:
   511  		sbb.WriteRune('[')
   512  		for i := range v {
   513  			e.print(sbb, v[i])
   514  			if i+1 != len(v) {
   515  				sbb.WriteRune(',')
   516  			}
   517  		}
   518  		sbb.WriteRune(']')
   519  	default:
   520  		i := e.toBigInt(v)
   521  		var iAsNeg big.Int
   522  		iAsNeg.Sub(i, e.q)
   523  		if iAsNeg.IsInt64() {
   524  			sbb.WriteString(strconv.FormatInt(iAsNeg.Int64(), 10))
   525  		} else {
   526  			sbb.WriteString(i.String())
   527  		}
   528  	}
   529  }
   530  
   531  func (e *engine) NewHint(f solver.Hint, nbOutputs int, inputs ...frontend.Variable) ([]frontend.Variable, error) {
   532  
   533  	if nbOutputs <= 0 {
   534  		return nil, fmt.Errorf("hint function must return at least one output")
   535  	}
   536  
   537  	in := make([]*big.Int, len(inputs))
   538  
   539  	for i := 0; i < len(inputs); i++ {
   540  		in[i] = e.toBigInt(inputs[i])
   541  	}
   542  	res := make([]*big.Int, nbOutputs)
   543  	for i := range res {
   544  		res[i] = new(big.Int)
   545  	}
   546  
   547  	err := f(e.Field(), in, res)
   548  
   549  	if err != nil {
   550  		panic("NewHint: " + err.Error())
   551  	}
   552  
   553  	out := make([]frontend.Variable, len(res))
   554  	for i := range res {
   555  		res[i].Mod(res[i], e.q)
   556  		out[i] = res[i]
   557  	}
   558  
   559  	return out, nil
   560  }
   561  
   562  func (e *engine) NewHintForId(id solver.HintID, nbOutputs int, inputs ...frontend.Variable) ([]frontend.Variable, error) {
   563  	if f := solver.GetRegisteredHint(id); f != nil {
   564  		return e.NewHint(f, nbOutputs, inputs...)
   565  	}
   566  
   567  	return nil, fmt.Errorf("no hint registered with id #%d. Use solver.RegisterHint or solver.RegisterNamedHint", id)
   568  }
   569  
   570  // IsConstant returns true if v is a constant known at compile time
   571  func (e *engine) IsConstant(v frontend.Variable) bool {
   572  	return e.constVars
   573  }
   574  
   575  // ConstantValue returns the big.Int value of v
   576  func (e *engine) ConstantValue(v frontend.Variable) (*big.Int, bool) {
   577  	r := e.toBigInt(v)
   578  	return r, e.constVars
   579  }
   580  
   581  func (e *engine) IsBoolean(v frontend.Variable) bool {
   582  	r := e.toBigInt(v)
   583  	return r.IsUint64() && r.Uint64() <= 1
   584  }
   585  
   586  func (e *engine) MarkBoolean(v frontend.Variable) {
   587  	if !e.IsBoolean(v) {
   588  		panic("mark boolean a non-boolean value")
   589  	}
   590  }
   591  
   592  func (e *engine) toBigInt(i1 frontend.Variable) *big.Int {
   593  	switch vv := i1.(type) {
   594  	case *big.Int:
   595  		return vv
   596  	case big.Int:
   597  		return &vv
   598  	default:
   599  		b := utils.FromInterface(i1)
   600  		b.Mod(&b, e.modulus())
   601  		return &b
   602  	}
   603  }
   604  
   605  // FieldBitLen returns the number of bits needed to represent a fr.Element
   606  func (e *engine) FieldBitLen() int {
   607  	return e.q.BitLen()
   608  }
   609  
   610  func (e *engine) mustBeBoolean(b *big.Int) {
   611  	if !b.IsUint64() || !(b.Uint64() == 0 || b.Uint64() == 1) {
   612  		panic(fmt.Sprintf("[assertIsBoolean] %s", b.String()))
   613  	}
   614  }
   615  
   616  func (e *engine) modulus() *big.Int {
   617  	return e.q
   618  }
   619  
   620  // shallowClone clones given circuit
   621  // this is actually a shallow copy → if the circuits contains maps or slices
   622  // only the reference is copied.
   623  func shallowClone(circuit frontend.Circuit) frontend.Circuit {
   624  
   625  	cValue := reflect.ValueOf(circuit).Elem()
   626  	newCircuit := reflect.New(cValue.Type())
   627  	newCircuit.Elem().Set(cValue)
   628  
   629  	circuitCopy, ok := newCircuit.Interface().(frontend.Circuit)
   630  	if !ok {
   631  		panic("couldn't clone the circuit")
   632  	}
   633  
   634  	if !reflect.DeepEqual(circuitCopy, circuit) {
   635  		panic("clone failed")
   636  	}
   637  
   638  	return circuitCopy
   639  }
   640  
   641  func copyWitness(to, from frontend.Circuit) {
   642  	var wValues []reflect.Value
   643  
   644  	collectHandler := func(f schema.LeafInfo, tInput reflect.Value) error {
   645  		if tInput.IsNil() {
   646  			// TODO @gbotrel test for missing assignment
   647  			return fmt.Errorf("when parsing variable %s: missing assignment", f.FullName())
   648  		}
   649  		wValues = append(wValues, tInput)
   650  		return nil
   651  	}
   652  	if _, err := schema.Walk(from, tVariable, collectHandler); err != nil {
   653  		panic(err)
   654  	}
   655  
   656  	i := 0
   657  	setHandler := func(f schema.LeafInfo, tInput reflect.Value) error {
   658  		tInput.Set(wValues[i])
   659  		i++
   660  		return nil
   661  	}
   662  	// this can't error.
   663  	_, _ = schema.Walk(to, tVariable, setHandler)
   664  
   665  }
   666  
   667  func (e *engine) Field() *big.Int {
   668  	return e.q
   669  }
   670  
   671  func (e *engine) Compiler() frontend.Compiler {
   672  	return e
   673  }
   674  
   675  func (e *engine) Commit(v ...frontend.Variable) (frontend.Variable, error) {
   676  	nb := (e.FieldBitLen() + 7) / 8
   677  	buf := make([]byte, nb)
   678  	hasher := sha3.NewCShake128(nil, []byte("gnark test engine"))
   679  	for i := range v {
   680  		vs := e.toBigInt(v[i])
   681  		bs := vs.FillBytes(buf)
   682  		hasher.Write(bs)
   683  	}
   684  	hasher.Read(buf)
   685  	res := new(big.Int).SetBytes(buf)
   686  	res.Mod(res, e.modulus())
   687  	if res.Sign() == 0 {
   688  		// a commit == 0 is unlikely; happens quite often in tests
   689  		// with tinyfield
   690  		res.SetUint64(1)
   691  	}
   692  	return res, nil
   693  }
   694  
   695  func (e *engine) Defer(cb func(frontend.API) error) {
   696  	circuitdefer.Put(e, cb)
   697  }
   698  
   699  // AddInstruction is used to add custom instructions to the constraint system.
   700  // In constraint system, this is asynchronous. In here, we do it synchronously.
   701  func (e *engine) AddInstruction(bID constraint.BlueprintID, calldata []uint32) []uint32 {
   702  	blueprint := e.blueprints[bID].(constraint.BlueprintSolvable)
   703  
   704  	// create a dummy instruction
   705  	inst := constraint.Instruction{
   706  		Calldata:   calldata,
   707  		WireOffset: uint32(len(e.internalVariables)),
   708  	}
   709  
   710  	// blueprint declared nbOutputs; add as many internal variables
   711  	// and return their indices
   712  	nbOutputs := blueprint.NbOutputs(inst)
   713  	var r []uint32
   714  	for i := 0; i < nbOutputs; i++ {
   715  		r = append(r, uint32(len(e.internalVariables)))
   716  		e.internalVariables = append(e.internalVariables, new(big.Int))
   717  	}
   718  
   719  	// solve the blueprint synchronously
   720  	s := blueprintSolver{
   721  		internalVariables: e.internalVariables,
   722  		q:                 e.q,
   723  	}
   724  	if err := blueprint.Solve(&s, inst); err != nil {
   725  		panic(err)
   726  	}
   727  
   728  	return r
   729  }
   730  
   731  // AddBlueprint adds a custom blueprint to the constraint system.
   732  func (e *engine) AddBlueprint(b constraint.Blueprint) constraint.BlueprintID {
   733  	if _, ok := b.(constraint.BlueprintSolvable); !ok {
   734  		panic("unsupported blueprint in test engine")
   735  	}
   736  	e.blueprints = append(e.blueprints, b)
   737  	return constraint.BlueprintID(len(e.blueprints) - 1)
   738  }
   739  
   740  // InternalVariable returns the value of an internal variable. This is used in custom blueprints.
   741  // The variableID is the index of the variable in the internalVariables slice, as
   742  // filled by AddInstruction.
   743  func (e *engine) InternalVariable(vID uint32) frontend.Variable {
   744  	if vID >= uint32(len(e.internalVariables)) {
   745  		panic("internal variable not found")
   746  	}
   747  	return new(big.Int).Set(e.internalVariables[vID])
   748  }
   749  
   750  // ToCanonicalVariable converts a frontend.Variable to a frontend.CanonicalVariable
   751  // this is used in custom blueprints to return a variable than can be encoded in blueprints
   752  func (e *engine) ToCanonicalVariable(v frontend.Variable) frontend.CanonicalVariable {
   753  	r := e.toBigInt(v)
   754  	return wrappedBigInt{r}
   755  }
   756  
   757  func (e *engine) SetGkrInfo(info constraint.GkrInfo) error {
   758  	return nil
   759  }
   760  
   761  // MustBeLessOrEqCst implements method comparing value given by its bits aBits
   762  // to a bound.
   763  func (e *engine) MustBeLessOrEqCst(aBits []frontend.Variable, bound *big.Int, aForDebug frontend.Variable) {
   764  	v := new(big.Int)
   765  	for i, b := range aBits {
   766  		bb, ok := b.(*big.Int)
   767  		if !ok {
   768  			panic("not big.Int bit")
   769  		}
   770  		if !bb.IsUint64() {
   771  			panic("given bit large")
   772  		}
   773  		bbu := uint(bb.Uint64())
   774  		if bbu > 1 {
   775  			fmt.Println(bbu)
   776  			panic("given bit is not a bit")
   777  		}
   778  		v.SetBit(v, i, bbu)
   779  	}
   780  	if v.Cmp(bound) > 0 {
   781  		panic(fmt.Sprintf("%d > %d", v, bound))
   782  	}
   783  }