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 }