github.com/consensys/gnark@v0.11.0/internal/backend/circuits/hint.go (about) 1 package circuits 2 3 import ( 4 "fmt" 5 "math/big" 6 7 "github.com/consensys/gnark/frontend" 8 "github.com/consensys/gnark/std/math/bits" 9 ) 10 11 type hintCircuit struct { 12 A, B frontend.Variable 13 } 14 15 func (circuit *hintCircuit) Define(api frontend.API) error { 16 res, err := api.Compiler().NewHint(mulBy7, 1, circuit.A) 17 if err != nil { 18 return fmt.Errorf("mulBy7: %w", err) 19 } 20 a7 := res[0] 21 _a7 := api.Mul(circuit.A, 7) 22 23 api.AssertIsEqual(a7, _a7) 24 api.AssertIsEqual(a7, circuit.B) 25 res, err = api.Compiler().NewHint(make3, 1) 26 if err != nil { 27 return fmt.Errorf("make3: %w", err) 28 } 29 c := res[0] 30 c = api.Mul(c, c) 31 api.AssertIsEqual(c, 9) 32 33 return nil 34 } 35 36 type vectorDoubleCircuit struct { 37 A []frontend.Variable 38 B []frontend.Variable 39 } 40 41 func (c *vectorDoubleCircuit) Define(api frontend.API) error { 42 res, err := api.Compiler().NewHint(dvHint, len(c.B), c.A...) 43 if err != nil { 44 return fmt.Errorf("double newhint: %w", err) 45 } 46 if len(res) != len(c.B) { 47 return fmt.Errorf("expected len %d, got %d", len(c.B), len(res)) 48 } 49 for i := range res { 50 api.AssertIsEqual(api.Mul(2, c.A[i]), c.B[i]) 51 api.AssertIsEqual(res[i], c.B[i]) 52 } 53 return nil 54 } 55 56 type recursiveHint struct { 57 A frontend.Variable 58 } 59 60 func (circuit *recursiveHint) Define(api frontend.API) error { 61 // first hint produces wire w1 62 w1, _ := api.Compiler().NewHint(make3, 1) 63 64 // this linear expression is not recorded in a R1CS just yet 65 linearExpression := api.Add(circuit.A, w1[0]) 66 67 // api.ToBinary calls another hint (bits.NBits) with linearExpression as input 68 // however, when the solver will resolve bits[...] it will need to detect w1 as a dependency 69 // in order to compute the correct linearExpression value 70 bits := api.ToBinary(linearExpression, 6) 71 72 a := api.FromBinary(bits...) 73 74 api.AssertIsEqual(a, 45) 75 76 return nil 77 } 78 79 func init() { 80 { 81 good := []frontend.Circuit{ 82 &recursiveHint{ 83 A: 42, 84 }, 85 } 86 87 bad := []frontend.Circuit{ 88 &recursiveHint{ 89 A: 1, 90 }, 91 } 92 93 addNewEntry("recursive_hint", &recursiveHint{}, good, bad, nil, make3, bits.GetHints()[1]) 94 } 95 96 { 97 good := []frontend.Circuit{ 98 &hintCircuit{ 99 A: 42, 100 B: 42 * 7, 101 }, 102 } 103 104 bad := []frontend.Circuit{ 105 &hintCircuit{ 106 A: 42, 107 B: 42, 108 }, 109 } 110 111 addNewEntry("hint", &hintCircuit{}, good, bad, nil, mulBy7, make3) 112 } 113 114 { 115 good := []frontend.Circuit{ 116 &vectorDoubleCircuit{ 117 A: []frontend.Variable{ 118 1, 2, 3, 4, 5, 6, 7, 8, 119 }, 120 B: []frontend.Variable{ 121 2, 4, 6, 8, 10, 12, 14, 16, 122 }, 123 }, 124 } 125 126 bad := []frontend.Circuit{ 127 &vectorDoubleCircuit{ 128 A: []frontend.Variable{ 129 1, 2, 3, 4, 5, 6, 7, 8, 130 }, 131 B: []frontend.Variable{ 132 1, 2, 3, 4, 5, 6, 7, 8, 133 }, 134 }, 135 } 136 addNewEntry("multi-output-hint", &vectorDoubleCircuit{A: make([]frontend.Variable, 8), B: make([]frontend.Variable, 8)}, good, bad, nil, dvHint) 137 } 138 } 139 140 var mulBy7 = func(q *big.Int, inputs []*big.Int, result []*big.Int) error { 141 result[0].Mul(inputs[0], big.NewInt(7)).Mod(result[0], q) 142 return nil 143 } 144 145 var make3 = func(_ *big.Int, inputs []*big.Int, result []*big.Int) error { 146 result[0].SetUint64(3) 147 return nil 148 } 149 150 var dvHint = func(_ *big.Int, inputs []*big.Int, res []*big.Int) error { 151 two := big.NewInt(2) 152 for i := range inputs { 153 res[i].Mul(two, inputs[i]) 154 } 155 return nil 156 }