diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml index 81cf92190..59c73cf74 100644 --- a/.github/workflows/integration-test.yml +++ b/.github/workflows/integration-test.yml @@ -18,7 +18,7 @@ jobs: python-version: "3.9" - name: Install cairo-lang - run: pip install cairo-lang==0.13.1 + run: pip install cairo-lang==0.13.2 - name: Install sympy run: pip install sympy==1.11.1 diff --git a/pkg/hintrunner/core/cairo_hintparser.go b/pkg/hintrunner/core/cairo_hintparser.go index 51bda93de..86168d73f 100644 --- a/pkg/hintrunner/core/cairo_hintparser.go +++ b/pkg/hintrunner/core/cairo_hintparser.go @@ -95,6 +95,14 @@ func GetHintByName(hint starknet.Hint) (hinter.Hinter, error) { return &AllocSegment{ Dst: parseCellRefer(args.Dst), }, nil + case starknet.EvalCircuitName: + args := hint.Args.(*starknet.EvalCircuit) + return &EvalCircuit{ + AddModN: parseResOperand(args.NAddMods), + AddModPtr: parseResOperand(args.AddModPtr), + MulModN: parseResOperand(args.NMulMods), + MulModPtr: parseResOperand(args.MulModPtr), + }, nil case starknet.TestLessThanName: args := hint.Args.(*starknet.TestLessThan) return &TestLessThan{ diff --git a/pkg/hintrunner/core/hint.go b/pkg/hintrunner/core/hint.go index 23b426b38..8e98ad233 100644 --- a/pkg/hintrunner/core/hint.go +++ b/pkg/hintrunner/core/hint.go @@ -12,6 +12,7 @@ import ( "github.com/NethermindEth/cairo-vm-go/pkg/parsers/starknet" "github.com/NethermindEth/cairo-vm-go/pkg/utils" VM "github.com/NethermindEth/cairo-vm-go/pkg/vm" + "github.com/NethermindEth/cairo-vm-go/pkg/vm/builtins" mem "github.com/NethermindEth/cairo-vm-go/pkg/vm/memory" f "github.com/consensys/gnark-crypto/ecc/stark-curve/fp" ) @@ -56,6 +57,46 @@ func (hint *AllocSegment) Execute(vm *VM.VirtualMachine, _ *hinter.HintRunnerCon return nil } +type EvalCircuit struct { + AddModN hinter.Reference + AddModPtr hinter.Reference + MulModN hinter.Reference + MulModPtr hinter.Reference +} + +func (hint *EvalCircuit) String() string { + return "EvalCircuit" +} + +func (hint *EvalCircuit) Execute(vm *VM.VirtualMachine, _ *hinter.HintRunnerContext) error { + addModInputAddress, err := hinter.ResolveAsAddress(vm, hint.AddModPtr) + if err != nil { + return fmt.Errorf("resolve addModBuiltin pointer: %w", err) + } + nAddMods, err := hint.AddModN.Resolve(vm) + if err != nil { + return fmt.Errorf("resolve nAddMods operand %s: %v", hint.AddModN, err) + } + nAddModsFelt, err := nAddMods.Uint64() + if err != nil { + return err + } + mulModInputAddress, err := hinter.ResolveAsAddress(vm, hint.MulModPtr) + if err != nil { + return fmt.Errorf("resolve mulModBuiltin pointer: %w", err) + } + nMulMods, err := hint.MulModN.Resolve(vm) + if err != nil { + return fmt.Errorf("resolve nMulMods operand %s: %v", hint.MulModN, err) + } + nMulModsFelt, err := nMulMods.Uint64() + if err != nil { + return err + } + + return builtins.FillMemory(vm.Memory, *addModInputAddress, nAddModsFelt, *mulModInputAddress, nMulModsFelt) +} + type TestLessThan struct { dst hinter.Reference lhs hinter.Reference diff --git a/pkg/hintrunner/core/hint_test.go b/pkg/hintrunner/core/hint_test.go index 82bf52ffb..39dbe0796 100644 --- a/pkg/hintrunner/core/hint_test.go +++ b/pkg/hintrunner/core/hint_test.go @@ -455,6 +455,577 @@ func TestDivModDivisionByZeroError(t *testing.T) { require.ErrorContains(t, err, "cannot be divided by zero, rhs: 0") } +func TestEvalCircuit(t *testing.T) { + t.Run("test mod_builtin_runner (1)", func(t *testing.T) { + vm := VM.DefaultVirtualMachine() + + vm.Context.Ap = 0 + vm.Context.Fp = 0 + + // Test : p = 2^96 + 1 + // Note that these calculations are performed based on the offsets that we provide + // x1 = 17 (4 memory cells) + // nil (4 memory cells) (should become equal to 6) + // x2 = 23 (4 memory cells) + // res = nil (4 memory cells) (multiplication of the above two numbers should then equal 138) + + // Values Array + // x1 = UInt384(17,0,0,0) + utils.WriteTo(vm, VM.ExecutionSegment, 0, mem.MemoryValueFromInt(17)) + utils.WriteTo(vm, VM.ExecutionSegment, 1, mem.MemoryValueFromInt(0)) + utils.WriteTo(vm, VM.ExecutionSegment, 2, mem.MemoryValueFromInt(0)) + utils.WriteTo(vm, VM.ExecutionSegment, 3, mem.MemoryValueFromInt(0)) + + // 4 unallocated memory cells + + // x2 = UInt384(23,0,0,0) + utils.WriteTo(vm, VM.ExecutionSegment, 8, mem.MemoryValueFromInt(23)) + utils.WriteTo(vm, VM.ExecutionSegment, 9, mem.MemoryValueFromInt(0)) + utils.WriteTo(vm, VM.ExecutionSegment, 10, mem.MemoryValueFromInt(0)) + utils.WriteTo(vm, VM.ExecutionSegment, 11, mem.MemoryValueFromInt(0)) + + // 4 unallocated memory cells for res + + // AddMod Offsets Array + utils.WriteTo(vm, VM.ExecutionSegment, 16, mem.MemoryValueFromInt(0)) + utils.WriteTo(vm, VM.ExecutionSegment, 17, mem.MemoryValueFromInt(4)) + utils.WriteTo(vm, VM.ExecutionSegment, 18, mem.MemoryValueFromInt(8)) + + // MulMod Offsets Array + utils.WriteTo(vm, VM.ExecutionSegment, 19, mem.MemoryValueFromInt(4)) + utils.WriteTo(vm, VM.ExecutionSegment, 20, mem.MemoryValueFromInt(8)) + utils.WriteTo(vm, VM.ExecutionSegment, 21, mem.MemoryValueFromInt(12)) + + AddModBuiltin := vm.Memory.AllocateBuiltinSegment(builtins.NewModBuiltin(1, 96, 1, builtins.Add)) + MulModBuiltin := vm.Memory.AllocateBuiltinSegment(builtins.NewModBuiltin(1, 96, 1, builtins.Mul)) + + /* + The Add and Mul Mod builtin structure are defined as: + struct ModBuiltin { + p: UInt384, // The modulus. + values_ptr: UInt384*, // A pointer to input values, the intermediate results and the output. + offsets_ptr: felt*, // A pointer to offsets inside the values array, defining the circuit. + // The offsets array should contain 3 * n elements. + n: felt, // The number of operations to perform. + } + */ + + // add_mod_ptr + // p = UInt384(1,1,0,0) + utils.WriteTo(vm, AddModBuiltin.SegmentIndex, 0, mem.MemoryValueFromInt(1)) + utils.WriteTo(vm, AddModBuiltin.SegmentIndex, 1, mem.MemoryValueFromInt(1)) + utils.WriteTo(vm, AddModBuiltin.SegmentIndex, 2, mem.MemoryValueFromInt(0)) + utils.WriteTo(vm, AddModBuiltin.SegmentIndex, 3, mem.MemoryValueFromInt(0)) + + // values_ptr + utils.WriteTo(vm, AddModBuiltin.SegmentIndex, 4, mem.MemoryValueFromMemoryAddress(&mem.MemoryAddress{SegmentIndex: VM.ExecutionSegment, Offset: 0})) + + // offsets_ptr + utils.WriteTo(vm, AddModBuiltin.SegmentIndex, 5, mem.MemoryValueFromMemoryAddress(&mem.MemoryAddress{SegmentIndex: VM.ExecutionSegment, Offset: 16})) + + // n + utils.WriteTo(vm, AddModBuiltin.SegmentIndex, 6, mem.MemoryValueFromInt(1)) + + // mul_mod_ptr + // p = UInt384(1,1,0,0) + utils.WriteTo(vm, MulModBuiltin.SegmentIndex, 0, mem.MemoryValueFromInt(1)) + utils.WriteTo(vm, MulModBuiltin.SegmentIndex, 1, mem.MemoryValueFromInt(1)) + utils.WriteTo(vm, MulModBuiltin.SegmentIndex, 2, mem.MemoryValueFromInt(0)) + utils.WriteTo(vm, MulModBuiltin.SegmentIndex, 3, mem.MemoryValueFromInt(0)) + + // values_ptr + utils.WriteTo(vm, MulModBuiltin.SegmentIndex, 4, mem.MemoryValueFromMemoryAddress(&mem.MemoryAddress{SegmentIndex: VM.ExecutionSegment, Offset: 0})) + + // offsets_ptr + utils.WriteTo(vm, MulModBuiltin.SegmentIndex, 5, mem.MemoryValueFromMemoryAddress(&mem.MemoryAddress{SegmentIndex: VM.ExecutionSegment, Offset: 19})) + + // n + utils.WriteTo(vm, MulModBuiltin.SegmentIndex, 6, mem.MemoryValueFromInt(1)) + + // To get the address of mul_mod_ptr and add_mod_ptr + utils.WriteTo(vm, VM.ExecutionSegment, 22, mem.MemoryValueFromSegmentAndOffset(AddModBuiltin.SegmentIndex, 0)) + utils.WriteTo(vm, VM.ExecutionSegment, 23, mem.MemoryValueFromSegmentAndOffset(MulModBuiltin.SegmentIndex, 0)) + + var addRef hinter.ApCellRef = 22 + var mulRef hinter.ApCellRef = 23 + + nAddMods := hinter.Immediate(f.NewElement(1)) + nMulMods := hinter.Immediate(f.NewElement(1)) + addModPtrAddr := hinter.Deref{Deref: addRef} + mulModPtrAddr := hinter.Deref{Deref: mulRef} + + hint := EvalCircuit{ + AddModN: nAddMods, + AddModPtr: addModPtrAddr, + MulModN: nMulMods, + MulModPtr: mulModPtrAddr, + } + + err := hint.Execute(vm, nil) + require.Nil(t, err) + + res1 := &f.Element{} + res1.SetInt64(138) + + require.Equal( + t, + mem.MemoryValueFromFieldElement(res1), + utils.ReadFrom(vm, VM.ExecutionSegment, 12), + ) + + res2 := &f.Element{} + res2.SetInt64(0) + + require.Equal( + t, + mem.MemoryValueFromFieldElement(res2), + utils.ReadFrom(vm, VM.ExecutionSegment, 13), + ) + + res3 := &f.Element{} + res3.SetInt64(0) + + require.Equal( + t, + mem.MemoryValueFromFieldElement(res2), + utils.ReadFrom(vm, VM.ExecutionSegment, 14), + ) + + res4 := &f.Element{} + res4.SetInt64(0) + + require.Equal( + t, + mem.MemoryValueFromFieldElement(res2), + utils.ReadFrom(vm, VM.ExecutionSegment, 15), + ) + }) + + t.Run("test mod_builtin_runner (2)", func(t *testing.T) { + vm := VM.DefaultVirtualMachine() + + vm.Context.Ap = 0 + vm.Context.Fp = 0 + + // Test : p = 2^96 + 1 + // Note that these calculations are performed based on the offsets that we provide + // x1 = 1 (4 memory cells) + // nil (4 memory cells) (should become equal to 0) + // x2 = 2^96 + 2 (4 memory cells) + // res = nil (4 memory cells) (multiplication of the above two numbers should then equal 0) + + // Values Array + // x1 = UInt384(1,0,0,0) + utils.WriteTo(vm, VM.ExecutionSegment, 0, mem.MemoryValueFromInt(1)) + utils.WriteTo(vm, VM.ExecutionSegment, 1, mem.MemoryValueFromInt(0)) + utils.WriteTo(vm, VM.ExecutionSegment, 2, mem.MemoryValueFromInt(0)) + utils.WriteTo(vm, VM.ExecutionSegment, 3, mem.MemoryValueFromInt(0)) + + // 4 unallocated memory cells + + // x2 = UInt384(2,1,0,0) + utils.WriteTo(vm, VM.ExecutionSegment, 8, mem.MemoryValueFromInt(2)) + utils.WriteTo(vm, VM.ExecutionSegment, 9, mem.MemoryValueFromInt(1)) + utils.WriteTo(vm, VM.ExecutionSegment, 10, mem.MemoryValueFromInt(0)) + utils.WriteTo(vm, VM.ExecutionSegment, 11, mem.MemoryValueFromInt(0)) + + // 4 unallocated memory cells for res + + // AddMod Offsets Array + utils.WriteTo(vm, VM.ExecutionSegment, 16, mem.MemoryValueFromInt(0)) + utils.WriteTo(vm, VM.ExecutionSegment, 17, mem.MemoryValueFromInt(4)) + utils.WriteTo(vm, VM.ExecutionSegment, 18, mem.MemoryValueFromInt(8)) + + // MulMod Offsets Array + utils.WriteTo(vm, VM.ExecutionSegment, 19, mem.MemoryValueFromInt(4)) + utils.WriteTo(vm, VM.ExecutionSegment, 20, mem.MemoryValueFromInt(8)) + utils.WriteTo(vm, VM.ExecutionSegment, 21, mem.MemoryValueFromInt(12)) + + AddModBuiltin := vm.Memory.AllocateBuiltinSegment(builtins.NewModBuiltin(1, 96, 1, builtins.Add)) + MulModBuiltin := vm.Memory.AllocateBuiltinSegment(builtins.NewModBuiltin(1, 96, 1, builtins.Mul)) + + /* + The Add and Mul Mod builtin structure are defined as: + struct ModBuiltin { + p: UInt384, // The modulus. + values_ptr: UInt384*, // A pointer to input values, the intermediate results and the output. + offsets_ptr: felt*, // A pointer to offsets inside the values array, defining the circuit. + // The offsets array should contain 3 * n elements. + n: felt, // The number of operations to perform. + } + */ + + // add_mod_ptr + // p = UInt384(1,1,0,0) + utils.WriteTo(vm, AddModBuiltin.SegmentIndex, 0, mem.MemoryValueFromInt(1)) + utils.WriteTo(vm, AddModBuiltin.SegmentIndex, 1, mem.MemoryValueFromInt(1)) + utils.WriteTo(vm, AddModBuiltin.SegmentIndex, 2, mem.MemoryValueFromInt(0)) + utils.WriteTo(vm, AddModBuiltin.SegmentIndex, 3, mem.MemoryValueFromInt(0)) + + // values_ptr + utils.WriteTo(vm, AddModBuiltin.SegmentIndex, 4, mem.MemoryValueFromMemoryAddress(&mem.MemoryAddress{SegmentIndex: VM.ExecutionSegment, Offset: 0})) + + // offsets_ptr + utils.WriteTo(vm, AddModBuiltin.SegmentIndex, 5, mem.MemoryValueFromMemoryAddress(&mem.MemoryAddress{SegmentIndex: VM.ExecutionSegment, Offset: 16})) + + // n + utils.WriteTo(vm, AddModBuiltin.SegmentIndex, 6, mem.MemoryValueFromInt(1)) + + // mul_mod_ptr + // p = UInt384(1,1,0,0) + utils.WriteTo(vm, MulModBuiltin.SegmentIndex, 0, mem.MemoryValueFromInt(1)) + utils.WriteTo(vm, MulModBuiltin.SegmentIndex, 1, mem.MemoryValueFromInt(1)) + utils.WriteTo(vm, MulModBuiltin.SegmentIndex, 2, mem.MemoryValueFromInt(0)) + utils.WriteTo(vm, MulModBuiltin.SegmentIndex, 3, mem.MemoryValueFromInt(0)) + + // values_ptr + utils.WriteTo(vm, MulModBuiltin.SegmentIndex, 4, mem.MemoryValueFromMemoryAddress(&mem.MemoryAddress{SegmentIndex: VM.ExecutionSegment, Offset: 0})) + + // offsets_ptr + utils.WriteTo(vm, MulModBuiltin.SegmentIndex, 5, mem.MemoryValueFromMemoryAddress(&mem.MemoryAddress{SegmentIndex: VM.ExecutionSegment, Offset: 19})) + + // n + utils.WriteTo(vm, MulModBuiltin.SegmentIndex, 6, mem.MemoryValueFromInt(1)) + + // To get the address of mul_mod_ptr and add_mod_ptr + utils.WriteTo(vm, VM.ExecutionSegment, 22, mem.MemoryValueFromSegmentAndOffset(AddModBuiltin.SegmentIndex, 0)) + utils.WriteTo(vm, VM.ExecutionSegment, 23, mem.MemoryValueFromSegmentAndOffset(MulModBuiltin.SegmentIndex, 0)) + + var addRef hinter.ApCellRef = 22 + var mulRef hinter.ApCellRef = 23 + + nAddMods := hinter.Immediate(f.NewElement(1)) + nMulMods := hinter.Immediate(f.NewElement(1)) + addModPtrAddr := hinter.Deref{Deref: addRef} + mulModPtrAddr := hinter.Deref{Deref: mulRef} + + hint := EvalCircuit{ + AddModN: nAddMods, + AddModPtr: addModPtrAddr, + MulModN: nMulMods, + MulModPtr: mulModPtrAddr, + } + + err := hint.Execute(vm, nil) + require.Nil(t, err) + + res1 := &f.Element{} + res1.SetInt64(0) + + require.Equal( + t, + mem.MemoryValueFromFieldElement(res1), + utils.ReadFrom(vm, VM.ExecutionSegment, 12), + ) + + res2 := &f.Element{} + res2.SetInt64(0) + + require.Equal( + t, + mem.MemoryValueFromFieldElement(res2), + utils.ReadFrom(vm, VM.ExecutionSegment, 13), + ) + + res3 := &f.Element{} + res3.SetInt64(0) + + require.Equal( + t, + mem.MemoryValueFromFieldElement(res2), + utils.ReadFrom(vm, VM.ExecutionSegment, 14), + ) + + res4 := &f.Element{} + res4.SetInt64(0) + + require.Equal( + t, + mem.MemoryValueFromFieldElement(res2), + utils.ReadFrom(vm, VM.ExecutionSegment, 15), + ) + }) + + t.Run("test mod_builtin_runner (3)", func(t *testing.T) { + vm := VM.DefaultVirtualMachine() + + vm.Context.Ap = 0 + vm.Context.Fp = 0 + + // Test : p = 2^3 + 1 + // Note that the calculations are performed based on the offsets that we provide + // x1 = 1 + // x2 = 2^3 + 2 + // x3 = 2 + + // Values Array + // x1 = UInt384(1,0,0,0) + utils.WriteTo(vm, VM.ExecutionSegment, 0, mem.MemoryValueFromInt(1)) + utils.WriteTo(vm, VM.ExecutionSegment, 1, mem.MemoryValueFromInt(0)) + utils.WriteTo(vm, VM.ExecutionSegment, 2, mem.MemoryValueFromInt(0)) + utils.WriteTo(vm, VM.ExecutionSegment, 3, mem.MemoryValueFromInt(0)) + + // x2 = UInt384(2,1,0,0) + utils.WriteTo(vm, VM.ExecutionSegment, 4, mem.MemoryValueFromInt(2)) + utils.WriteTo(vm, VM.ExecutionSegment, 5, mem.MemoryValueFromInt(1)) + utils.WriteTo(vm, VM.ExecutionSegment, 6, mem.MemoryValueFromInt(0)) + utils.WriteTo(vm, VM.ExecutionSegment, 7, mem.MemoryValueFromInt(0)) + + // x3 = UInt384(2,0,0,0) + utils.WriteTo(vm, VM.ExecutionSegment, 8, mem.MemoryValueFromInt(2)) + utils.WriteTo(vm, VM.ExecutionSegment, 9, mem.MemoryValueFromInt(0)) + utils.WriteTo(vm, VM.ExecutionSegment, 10, mem.MemoryValueFromInt(0)) + utils.WriteTo(vm, VM.ExecutionSegment, 11, mem.MemoryValueFromInt(0)) + + // 20 unallocated memory cells for res and other calculations + + // AddMod Offsets Array + utils.WriteTo(vm, VM.ExecutionSegment, 32, mem.MemoryValueFromInt(0)) // x1 + utils.WriteTo(vm, VM.ExecutionSegment, 33, mem.MemoryValueFromInt(12)) // x2 - x1 + utils.WriteTo(vm, VM.ExecutionSegment, 34, mem.MemoryValueFromInt(4)) // x2 + utils.WriteTo(vm, VM.ExecutionSegment, 35, mem.MemoryValueFromInt(16)) // (x2 - x1) / x3 + utils.WriteTo(vm, VM.ExecutionSegment, 36, mem.MemoryValueFromInt(20)) // x1 / x3 + utils.WriteTo(vm, VM.ExecutionSegment, 37, mem.MemoryValueFromInt(24)) // (x2 - x1) / x3 + x1 / x3 + + // MulMod Offsets Array + utils.WriteTo(vm, VM.ExecutionSegment, 38, mem.MemoryValueFromInt(8)) // x3 + utils.WriteTo(vm, VM.ExecutionSegment, 39, mem.MemoryValueFromInt(16)) // (x2 - x1) / x3 + utils.WriteTo(vm, VM.ExecutionSegment, 40, mem.MemoryValueFromInt(12)) // (x2 - x1) + utils.WriteTo(vm, VM.ExecutionSegment, 41, mem.MemoryValueFromInt(8)) // x3 + utils.WriteTo(vm, VM.ExecutionSegment, 42, mem.MemoryValueFromInt(20)) // x1 / x3 + utils.WriteTo(vm, VM.ExecutionSegment, 43, mem.MemoryValueFromInt(0)) // x1 + utils.WriteTo(vm, VM.ExecutionSegment, 44, mem.MemoryValueFromInt(8)) // x3 + utils.WriteTo(vm, VM.ExecutionSegment, 45, mem.MemoryValueFromInt(24)) // ((x2 - x1) / x3 + x1 / x3) + utils.WriteTo(vm, VM.ExecutionSegment, 46, mem.MemoryValueFromInt(28)) // ((x2 - x1) / x3 + x1 / x3) * x3 + + AddModBuiltin := vm.Memory.AllocateBuiltinSegment(builtins.NewModBuiltin(1, 3, 1, builtins.Add)) + MulModBuiltin := vm.Memory.AllocateBuiltinSegment(builtins.NewModBuiltin(1, 3, 1, builtins.Mul)) + + /* + The Add and Mul Mod builtin structure are defined as: + struct ModBuiltin { + p: UInt384, // The modulus. + values_ptr: UInt384*, // A pointer to input values, the intermediate results and the output. + offsets_ptr: felt*, // A pointer to offsets inside the values array, defining the circuit. + // The offsets array should contain 3 * n elements. + n: felt, // The number of operations to perform. + } + */ + + // add_mod_ptr + // p = UInt384(1,1,0,0) + utils.WriteTo(vm, AddModBuiltin.SegmentIndex, 0, mem.MemoryValueFromInt(1)) + utils.WriteTo(vm, AddModBuiltin.SegmentIndex, 1, mem.MemoryValueFromInt(1)) + utils.WriteTo(vm, AddModBuiltin.SegmentIndex, 2, mem.MemoryValueFromInt(0)) + utils.WriteTo(vm, AddModBuiltin.SegmentIndex, 3, mem.MemoryValueFromInt(0)) + + // values_ptr + utils.WriteTo(vm, AddModBuiltin.SegmentIndex, 4, mem.MemoryValueFromMemoryAddress(&mem.MemoryAddress{SegmentIndex: VM.ExecutionSegment, Offset: 0})) + + // offsets_ptr + utils.WriteTo(vm, AddModBuiltin.SegmentIndex, 5, mem.MemoryValueFromMemoryAddress(&mem.MemoryAddress{SegmentIndex: VM.ExecutionSegment, Offset: 32})) + + // n + utils.WriteTo(vm, AddModBuiltin.SegmentIndex, 6, mem.MemoryValueFromInt(2)) + + // mul_mod_ptr + // p = UInt384(1,1,0,0) + utils.WriteTo(vm, MulModBuiltin.SegmentIndex, 0, mem.MemoryValueFromInt(1)) + utils.WriteTo(vm, MulModBuiltin.SegmentIndex, 1, mem.MemoryValueFromInt(1)) + utils.WriteTo(vm, MulModBuiltin.SegmentIndex, 2, mem.MemoryValueFromInt(0)) + utils.WriteTo(vm, MulModBuiltin.SegmentIndex, 3, mem.MemoryValueFromInt(0)) + + // values_ptr + utils.WriteTo(vm, MulModBuiltin.SegmentIndex, 4, mem.MemoryValueFromMemoryAddress(&mem.MemoryAddress{SegmentIndex: VM.ExecutionSegment, Offset: 0})) + + // offsets_ptr + utils.WriteTo(vm, MulModBuiltin.SegmentIndex, 5, mem.MemoryValueFromMemoryAddress(&mem.MemoryAddress{SegmentIndex: VM.ExecutionSegment, Offset: 38})) + + // n + utils.WriteTo(vm, MulModBuiltin.SegmentIndex, 6, mem.MemoryValueFromInt(3)) + + // To get the address of mul_mod_ptr and add_mod_ptr + utils.WriteTo(vm, VM.ExecutionSegment, 47, mem.MemoryValueFromSegmentAndOffset(AddModBuiltin.SegmentIndex, 0)) + utils.WriteTo(vm, VM.ExecutionSegment, 48, mem.MemoryValueFromSegmentAndOffset(MulModBuiltin.SegmentIndex, 0)) + + var addRef hinter.ApCellRef = 47 + var mulRef hinter.ApCellRef = 48 + + nAddMods := hinter.Immediate(f.NewElement(2)) + nMulMods := hinter.Immediate(f.NewElement(3)) + addModPtrAddr := hinter.Deref{Deref: addRef} + mulModPtrAddr := hinter.Deref{Deref: mulRef} + + hint := EvalCircuit{ + AddModN: nAddMods, + AddModPtr: addModPtrAddr, + MulModN: nMulMods, + MulModPtr: mulModPtrAddr, + } + + err := hint.Execute(vm, nil) + require.Nil(t, err) + + res1 := &f.Element{} + res1.SetInt64(1) + + require.Equal( + t, + mem.MemoryValueFromFieldElement(res1), + utils.ReadFrom(vm, VM.ExecutionSegment, 28), + ) + + res2 := &f.Element{} + res2.SetInt64(0) + + require.Equal( + t, + mem.MemoryValueFromFieldElement(res2), + utils.ReadFrom(vm, VM.ExecutionSegment, 29), + ) + + res3 := &f.Element{} + res3.SetInt64(0) + + require.Equal( + t, + mem.MemoryValueFromFieldElement(res2), + utils.ReadFrom(vm, VM.ExecutionSegment, 30), + ) + + res4 := &f.Element{} + res4.SetInt64(0) + + require.Equal( + t, + mem.MemoryValueFromFieldElement(res2), + utils.ReadFrom(vm, VM.ExecutionSegment, 31), + ) + }) + + t.Run("test mod_builtin_runner (4)", func(t *testing.T) { + vm := VM.DefaultVirtualMachine() + + vm.Context.Ap = 0 + vm.Context.Fp = 0 + + // Test : p = 2^3 + 1 + // Note that the calculations are performed based on the offsets that we provide + // x1 = 8 + // x2 = 2^3 + 2 + // x3 = 2 + + // Values Array + // x1 = UInt384(8,0,0,0) + utils.WriteTo(vm, VM.ExecutionSegment, 0, mem.MemoryValueFromInt(8)) + utils.WriteTo(vm, VM.ExecutionSegment, 1, mem.MemoryValueFromInt(0)) + utils.WriteTo(vm, VM.ExecutionSegment, 2, mem.MemoryValueFromInt(0)) + utils.WriteTo(vm, VM.ExecutionSegment, 3, mem.MemoryValueFromInt(0)) + + // x2 = UInt384(2,1,0,0) + utils.WriteTo(vm, VM.ExecutionSegment, 4, mem.MemoryValueFromInt(2)) + utils.WriteTo(vm, VM.ExecutionSegment, 5, mem.MemoryValueFromInt(1)) + utils.WriteTo(vm, VM.ExecutionSegment, 6, mem.MemoryValueFromInt(0)) + utils.WriteTo(vm, VM.ExecutionSegment, 7, mem.MemoryValueFromInt(0)) + + // x3 = UInt384(2,0,0,0) + utils.WriteTo(vm, VM.ExecutionSegment, 8, mem.MemoryValueFromInt(2)) + utils.WriteTo(vm, VM.ExecutionSegment, 9, mem.MemoryValueFromInt(0)) + utils.WriteTo(vm, VM.ExecutionSegment, 10, mem.MemoryValueFromInt(0)) + utils.WriteTo(vm, VM.ExecutionSegment, 11, mem.MemoryValueFromInt(0)) + + // 20 unallocated memory cells for res and other calculations + + // AddMod Offsets Array + utils.WriteTo(vm, VM.ExecutionSegment, 32, mem.MemoryValueFromInt(0)) // x1 + utils.WriteTo(vm, VM.ExecutionSegment, 33, mem.MemoryValueFromInt(12)) // x2 - x1 + utils.WriteTo(vm, VM.ExecutionSegment, 34, mem.MemoryValueFromInt(4)) // x2 + utils.WriteTo(vm, VM.ExecutionSegment, 35, mem.MemoryValueFromInt(16)) // (x2 - x1) / x3 + utils.WriteTo(vm, VM.ExecutionSegment, 36, mem.MemoryValueFromInt(20)) // x1 / x3 + utils.WriteTo(vm, VM.ExecutionSegment, 37, mem.MemoryValueFromInt(24)) // (x2 - x1) / x3 + x1 / x3 + + // MulMod Offsets Array + utils.WriteTo(vm, VM.ExecutionSegment, 38, mem.MemoryValueFromInt(8)) // x3 + utils.WriteTo(vm, VM.ExecutionSegment, 39, mem.MemoryValueFromInt(16)) // (x2 - x1) / x3 + utils.WriteTo(vm, VM.ExecutionSegment, 40, mem.MemoryValueFromInt(12)) // (x2 - x1) + utils.WriteTo(vm, VM.ExecutionSegment, 41, mem.MemoryValueFromInt(8)) // x3 + utils.WriteTo(vm, VM.ExecutionSegment, 42, mem.MemoryValueFromInt(20)) // x1 / x3 + utils.WriteTo(vm, VM.ExecutionSegment, 43, mem.MemoryValueFromInt(0)) // x1 + utils.WriteTo(vm, VM.ExecutionSegment, 44, mem.MemoryValueFromInt(8)) // x3 + utils.WriteTo(vm, VM.ExecutionSegment, 45, mem.MemoryValueFromInt(24)) // ((x2 - x1) / x3 + x1 / x3) + utils.WriteTo(vm, VM.ExecutionSegment, 46, mem.MemoryValueFromInt(28)) // ((x2 - x1) / x3 + x1 / x3) * x3 + + AddModBuiltin := vm.Memory.AllocateBuiltinSegment(builtins.NewModBuiltin(1, 3, 1, builtins.Add)) + MulModBuiltin := vm.Memory.AllocateBuiltinSegment(builtins.NewModBuiltin(1, 3, 1, builtins.Mul)) + + /* + The Add and Mul Mod builtin structure are defined as: + struct ModBuiltin { + p: UInt384, // The modulus. + values_ptr: UInt384*, // A pointer to input values, the intermediate results and the output. + offsets_ptr: felt*, // A pointer to offsets inside the values array, defining the circuit. + // The offsets array should contain 3 * n elements. + n: felt, // The number of operations to perform. + } + */ + + // add_mod_ptr + // p = UInt384(1,1,0,0) + utils.WriteTo(vm, AddModBuiltin.SegmentIndex, 0, mem.MemoryValueFromInt(1)) + utils.WriteTo(vm, AddModBuiltin.SegmentIndex, 1, mem.MemoryValueFromInt(1)) + utils.WriteTo(vm, AddModBuiltin.SegmentIndex, 2, mem.MemoryValueFromInt(0)) + utils.WriteTo(vm, AddModBuiltin.SegmentIndex, 3, mem.MemoryValueFromInt(0)) + + // values_ptr + utils.WriteTo(vm, AddModBuiltin.SegmentIndex, 4, mem.MemoryValueFromMemoryAddress(&mem.MemoryAddress{SegmentIndex: VM.ExecutionSegment, Offset: 0})) + + // offsets_ptr + utils.WriteTo(vm, AddModBuiltin.SegmentIndex, 5, mem.MemoryValueFromMemoryAddress(&mem.MemoryAddress{SegmentIndex: VM.ExecutionSegment, Offset: 32})) + + // n + utils.WriteTo(vm, AddModBuiltin.SegmentIndex, 6, mem.MemoryValueFromInt(2)) + + // mul_mod_ptr + // p = UInt384(1,1,0,0) + utils.WriteTo(vm, MulModBuiltin.SegmentIndex, 0, mem.MemoryValueFromInt(1)) + utils.WriteTo(vm, MulModBuiltin.SegmentIndex, 1, mem.MemoryValueFromInt(1)) + utils.WriteTo(vm, MulModBuiltin.SegmentIndex, 2, mem.MemoryValueFromInt(0)) + utils.WriteTo(vm, MulModBuiltin.SegmentIndex, 3, mem.MemoryValueFromInt(0)) + + // values_ptr + utils.WriteTo(vm, MulModBuiltin.SegmentIndex, 4, mem.MemoryValueFromMemoryAddress(&mem.MemoryAddress{SegmentIndex: VM.ExecutionSegment, Offset: 0})) + + // offsets_ptr + utils.WriteTo(vm, MulModBuiltin.SegmentIndex, 5, mem.MemoryValueFromMemoryAddress(&mem.MemoryAddress{SegmentIndex: VM.ExecutionSegment, Offset: 38})) + + // n + utils.WriteTo(vm, MulModBuiltin.SegmentIndex, 6, mem.MemoryValueFromInt(3)) + + // To get the address of mul_mod_ptr and add_mod_ptr + utils.WriteTo(vm, VM.ExecutionSegment, 47, mem.MemoryValueFromSegmentAndOffset(AddModBuiltin.SegmentIndex, 0)) + utils.WriteTo(vm, VM.ExecutionSegment, 48, mem.MemoryValueFromSegmentAndOffset(MulModBuiltin.SegmentIndex, 0)) + + var addRef hinter.ApCellRef = 47 + var mulRef hinter.ApCellRef = 48 + + nAddMods := hinter.Immediate(f.NewElement(2)) + nMulMods := hinter.Immediate(f.NewElement(3)) + addModPtrAddr := hinter.Deref{Deref: addRef} + mulModPtrAddr := hinter.Deref{Deref: mulRef} + + hint := EvalCircuit{ + AddModN: nAddMods, + AddModPtr: addModPtrAddr, + MulModN: nMulMods, + MulModPtr: mulModPtrAddr, + } + + err := hint.Execute(vm, nil) + require.ErrorContains(t, err, "expected integer at address") + }) + +} + func TestU256InvModN(t *testing.T) { t.Run("test u256InvModN (n == 1)", func(t *testing.T) { vm := VM.DefaultVirtualMachine() diff --git a/pkg/hintrunner/zero/hintcode.go b/pkg/hintrunner/zero/hintcode.go index 1fa40ffca..d79146b38 100755 --- a/pkg/hintrunner/zero/hintcode.go +++ b/pkg/hintrunner/zero/hintcode.go @@ -173,6 +173,7 @@ const ( // ------ Other hints related code ------ allocSegmentCode string = "memory[ap] = segments.add()" + evalCircuitCode string = "from starkware.cairo.lang.builtins.modulo.mod_builtin_runner import ModBuiltinRunner\nassert builtin_runners[\"add_mod_builtin\"].instance_def.batch_size == 1\nassert builtin_runners[\"mul_mod_builtin\"].instance_def.batch_size == 1\n\nModBuiltinRunner.fill_memory(\n memory=memory,\n add_mod=(ids.add_mod_ptr.address_, builtin_runners[\"add_mod_builtin\"], ids.add_mod_n),\n mul_mod=(ids.mul_mod_ptr.address_, builtin_runners[\"mul_mod_builtin\"], ids.mul_mod_n),\n)" memcpyContinueCopyingCode string = "n -= 1\nids.continue_copying = 1 if n > 0 else 0" memsetContinueLoopCode string = "n -= 1\nids.continue_loop = 1 if n > 0 else 0" memcpyEnterScopeCode string = "vm_enter_scope({'n': ids.len})" diff --git a/pkg/hintrunner/zero/zerohint.go b/pkg/hintrunner/zero/zerohint.go index ccc105b26..b241a77be 100755 --- a/pkg/hintrunner/zero/zerohint.go +++ b/pkg/hintrunner/zero/zerohint.go @@ -319,6 +319,8 @@ func GetHintFromCode(program *zero.ZeroProgram, rawHint zero.Hint) (hinter.Hinte // Other hints case allocSegmentCode: return createAllocSegmentHinter() + case evalCircuitCode: + return createEvalCircuitHinter(resolver) case memcpyContinueCopyingCode: return createMemContinueHinter(resolver, false) case memsetContinueLoopCode: diff --git a/pkg/hintrunner/zero/zerohint_others.go b/pkg/hintrunner/zero/zerohint_others.go index efd061576..a44b13acb 100644 --- a/pkg/hintrunner/zero/zerohint_others.go +++ b/pkg/hintrunner/zero/zerohint_others.go @@ -81,6 +81,35 @@ func createAllocSegmentHinter() (hinter.Hinter, error) { return &core.AllocSegment{Dst: hinter.ApCellRef(0)}, nil } +func createEvalCircuitHinter(resolver hintReferenceResolver) (hinter.Hinter, error) { + addModPtr, err := resolver.GetReference("add_mod_ptr") + if err != nil { + return nil, err + } + + nAddMods, err := resolver.GetReference("add_mod_n") + if err != nil { + return nil, err + } + + mulModPtr, err := resolver.GetReference("mul_mod_ptr") + if err != nil { + return nil, err + } + + nMulMods, err := resolver.GetReference("mul_mod_n") + if err != nil { + return nil, err + } + + return &core.EvalCircuit{ + AddModN: nAddMods, + AddModPtr: addModPtr, + MulModN: nMulMods, + MulModPtr: mulModPtr, + }, nil +} + // VMEnterScope hint enters a new scope in the Cairo VM func createVMEnterScopeHinter() (hinter.Hinter, error) { return &GenericZeroHinter{ diff --git a/pkg/parsers/starknet/hint.go b/pkg/parsers/starknet/hint.go index 768b20d65..01bc19ac8 100644 --- a/pkg/parsers/starknet/hint.go +++ b/pkg/parsers/starknet/hint.go @@ -18,6 +18,7 @@ const ( CheatcodeName HintName = "Cheatcode" // Core hints AllocSegmentName HintName = "AllocSegment" + EvalCircuitName HintName = "EvalCircuit" TestLessThanName HintName = "TestLessThan" TestLessThanOrEqualName HintName = "TestLessThanOrEqual" TestLessThanOrEqualAddressName HintName = "TestLessThanOrEqualAddress" @@ -75,6 +76,13 @@ type AllocSegment struct { Dst CellRef `json:"dst" validate:"required"` } +type EvalCircuit struct { + NAddMods ResOperand `json:"add_mod_n" validate:"required"` + AddModPtr ResOperand `json:"add_mod_ptr" validate:"required"` + NMulMods ResOperand `json:"mul_mod_n" validate:"required"` + MulModPtr ResOperand `json:"mul_mod_ptr" validate:"required"` +} + type TestLessThan struct { Lhs ResOperand `json:"lhs" validate:"required"` Rhs ResOperand `json:"rhs" validate:"required"` @@ -489,6 +497,8 @@ func (h *Hint) UnmarshalJSON(data []byte) error { // Core hints case AllocSegmentName: args = &AllocSegment{} + case EvalCircuitName: + args = &EvalCircuit{} case TestLessThanName: args = &TestLessThan{} case TestLessThanOrEqualName: diff --git a/pkg/vm/builtins/modulo.go b/pkg/vm/builtins/modulo.go index 4a3a97f87..450330724 100644 --- a/pkg/vm/builtins/modulo.go +++ b/pkg/vm/builtins/modulo.go @@ -3,6 +3,7 @@ package builtins import ( "fmt" "math/big" + "strings" "github.com/NethermindEth/cairo-vm-go/pkg/utils" @@ -317,30 +318,38 @@ func (m *ModBuiltin) writeNWordsValue(mem *memory.Memory, addr memory.MemoryAddr } // Fills a value in the values table, if exactly one value is missing. -// Returns true on success or if all values are already known. +// Returns 1 on success or if all values are already known. +// Returns 0 if there is an error or is the value cannot be filled +// Returns 2 when the mulModBuiltin has a zero divisor. // Given known, res, p fillValue tries to compute the minimal integer operand x which // satisfies the equation op(x,known) = res + k*p for some k in {0,1,...,self.k_bound-1}. -func (m *ModBuiltin) fillValue(mem *memory.Memory, inputs ModBuiltinInputs, index int, op ModBuiltinType) (bool, error) { +func (m *ModBuiltin) fillValue(mem *memory.Memory, inputs ModBuiltinInputs, index int, op ModBuiltinType) (int, error) { addresses := make([]memory.MemoryAddress, 0, 3) values := make([]*big.Int, 0, 3) for i := 0; i < 3; i++ { addr, err := inputs.offsetsPtr.AddOffset(int16(3*index + i)) if err != nil { - return false, err + return 0, err } offsetFelt, err := mem.ReadAsElement(addr.SegmentIndex, addr.Offset) if err != nil { - return false, err + return 0, err } offset := offsetFelt.Uint64() addr, err = inputs.valuesPtr.AddOffset(int16(offset)) if err != nil { - return false, err + return 0, err } addresses = append(addresses, addr) - // do not check for error, as the value might not be in memory - _, value, _ := m.readNWordsValue(mem, addr) + // do not check for all errors, as the value might not be in memory + // only check for the error when the value in memory exceeds 2**wordBitLen + _, value, err := m.readNWordsValue(mem, addr) + if err != nil { + if strings.Contains(err.Error(), "expected integer at address") { + return 0, err + } + } values = append(values, value) } @@ -352,7 +361,6 @@ func (m *ModBuiltin) fillValue(mem *memory.Memory, inputs ModBuiltinInputs, inde if kBound == nil { kBound = new(big.Int).Set(intLim) } - switch { case a != nil && b != nil && c == nil: var value big.Int @@ -363,7 +371,7 @@ func (m *ModBuiltin) fillValue(mem *memory.Memory, inputs ModBuiltinInputs, inde } // value - (kBound - 1) * p <= intLim - 1 if new(big.Int).Sub(&value, new(big.Int).Mul((new(big.Int).Sub(kBound, big.NewInt(1))), &inputs.p)).Cmp(new(big.Int).Sub(intLim, big.NewInt(1))) == 1 { - return false, fmt.Errorf("%s builtin: Expected a %s b - %d * p <= %d", m.String(), m.modBuiltinType, kBound.Sub(kBound, big.NewInt(1)), intLim.Sub(intLim, big.NewInt(1))) + return 0, fmt.Errorf("%s builtin: Expected a %s b - %d * p <= %d", m.String(), m.modBuiltinType, kBound.Sub(kBound, big.NewInt(1)), intLim.Sub(intLim, big.NewInt(1))) } if value.Cmp(new(big.Int).Mul(kBound, &inputs.p)) < 0 { value.Mod(&value, &inputs.p) @@ -371,16 +379,17 @@ func (m *ModBuiltin) fillValue(mem *memory.Memory, inputs ModBuiltinInputs, inde value.Sub(&value, new(big.Int).Mul(new(big.Int).Sub(kBound, big.NewInt(1)), &inputs.p)) } if err := m.writeNWordsValue(mem, addresses[2], value); err != nil { - return false, err + return 0, err } - return true, nil + return 1, nil case a != nil && b == nil && c != nil: + zeroDivisor := false var value big.Int if op == Add { // Right now only k = 2 is an option, hence as we stated above that x + known can only take values // from res to res + (k - 1) * p, hence known <= res + p if a.Cmp(new(big.Int).Add(c, &inputs.p)) > 0 { - return false, fmt.Errorf("%s builtin: addend greater than sum + p: %d > %d + %d", m.String(), a, c, &inputs.p) + return 0, fmt.Errorf("%s builtin: addend greater than sum + p: %d > %d + %d", m.String(), a, c, &inputs.p) } else { if a.Cmp(c) <= 0 { value = *new(big.Int).Sub(c, a) @@ -392,16 +401,17 @@ func (m *ModBuiltin) fillValue(mem *memory.Memory, inputs ModBuiltinInputs, inde x, _, gcd := utils.Igcdex(a, &inputs.p) // if gcd != 1, the known value is 0, in which case the res must be 0 if gcd.Cmp(big.NewInt(1)) != 0 { + zeroDivisor = true value = *new(big.Int).Div(&inputs.p, &gcd) } else { value = *new(big.Int).Mul(c, &x) value = *value.Mod(&value, &inputs.p) tmpK, err := utils.SafeDiv(new(big.Int).Sub(new(big.Int).Mul(a, &value), c), &inputs.p) if err != nil { - return false, err + return 0, err } if tmpK.Cmp(kBound) >= 0 { - return false, fmt.Errorf("%s builtin: ((%d * q) - %d) / %d > %d for any q > 0, such that %d * q = %d (mod %d) ", m.String(), a, c, &inputs.p, kBound, a, c, &inputs.p) + return 0, fmt.Errorf("%s builtin: ((%d * q) - %d) / %d > %d for any q > 0, such that %d * q = %d (mod %d) ", m.String(), a, c, &inputs.p, kBound, a, c, &inputs.p) } if tmpK.Cmp(big.NewInt(0)) < 0 { value = *value.Add(&value, new(big.Int).Mul(&inputs.p, new(big.Int).Div(new(big.Int).Sub(a, new(big.Int).Sub(&tmpK, big.NewInt(1))), a))) @@ -409,16 +419,20 @@ func (m *ModBuiltin) fillValue(mem *memory.Memory, inputs ModBuiltinInputs, inde } } if err := m.writeNWordsValue(mem, addresses[1], value); err != nil { - return false, err + return 0, err } - return true, nil + if zeroDivisor { + return 2, nil + } + return 1, nil case a == nil && b != nil && c != nil: + zeroDivisor := false var value big.Int if op == Add { // Right now only k = 2 is an option, hence as we stated above that x + known can only take values // from res to res + (k - 1) * p, hence known <= res + p if b.Cmp(new(big.Int).Add(c, &inputs.p)) > 0 { - return false, fmt.Errorf("%s builtin: addend greater than sum + p: %d > %d + %d", m.String(), b, c, &inputs.p) + return 0, fmt.Errorf("%s builtin: addend greater than sum + p: %d > %d + %d", m.String(), b, c, &inputs.p) } else { if b.Cmp(c) <= 0 { value = *new(big.Int).Sub(c, b) @@ -430,16 +444,17 @@ func (m *ModBuiltin) fillValue(mem *memory.Memory, inputs ModBuiltinInputs, inde x, _, gcd := utils.Igcdex(b, &inputs.p) // if gcd != 1, the known value is 0, in which case the res must be 0 if gcd.Cmp(big.NewInt(1)) != 0 { + zeroDivisor = true value = *new(big.Int).Div(&inputs.p, &gcd) } else { value = *new(big.Int).Mul(c, &x) value = *value.Mod(&value, &inputs.p) tmpK, err := utils.SafeDiv(new(big.Int).Sub(new(big.Int).Mul(b, &value), c), &inputs.p) if err != nil { - return false, err + return 0, err } if tmpK.Cmp(kBound) >= 0 { - return false, fmt.Errorf("%s builtin: ((%d * q) - %d) / %d > %d for any q > 0, such that %d * q = %d (mod %d) ", m.String(), b, c, &inputs.p, kBound, b, c, &inputs.p) + return 0, fmt.Errorf("%s builtin: ((%d * q) - %d) / %d > %d for any q > 0, such that %d * q = %d (mod %d) ", m.String(), b, c, &inputs.p, kBound, b, c, &inputs.p) } if tmpK.Cmp(big.NewInt(0)) < 0 { value = *value.Add(&value, new(big.Int).Mul(&inputs.p, new(big.Int).Div(new(big.Int).Sub(b, new(big.Int).Sub(&tmpK, big.NewInt(1))), b))) @@ -447,13 +462,16 @@ func (m *ModBuiltin) fillValue(mem *memory.Memory, inputs ModBuiltinInputs, inde } } if err := m.writeNWordsValue(mem, addresses[0], value); err != nil { - return false, err + return 0, err + } + if zeroDivisor { + return 2, nil } - return true, nil + return 1, nil case a != nil && b != nil && c != nil: - return true, nil + return 1, nil default: - return false, nil + return 0, nil } } @@ -464,78 +482,118 @@ func (m *ModBuiltin) fillValue(mem *memory.Memory, inputs ModBuiltinInputs, inde // The number of operations written to the input of the first instance n should be at // least n and a multiple of batch_size. Previous offsets are copied to the end of the // offsets table to make its length 3n'. -func FillMemory(mem *memory.Memory, addModBuiltinAddr memory.MemoryAddress, nAddModsIndex uint64, mulModBuiltinAddr memory.MemoryAddress, nMulModsIndex uint64) error { - if nAddModsIndex > MAX_N { +func FillMemory(mem *memory.Memory, addModInputAddress memory.MemoryAddress, nAddMods uint64, mulModInputAddress memory.MemoryAddress, nMulMods uint64) error { + if nAddMods > MAX_N { return fmt.Errorf("AddMod builtin: n must be <= {MAX_N}") } - if nMulModsIndex > MAX_N { + if nMulMods > MAX_N { return fmt.Errorf("MulMod builtin: n must be <= {MAX_N}") } - addModBuiltinSegment, ok := mem.FindSegmentWithBuiltin("AddMod") - if !ok { - return fmt.Errorf("AddMod builtin segment doesn't exist") - } - mulModBuiltinSegment, ok := mem.FindSegmentWithBuiltin("MulMod") - if !ok { - return fmt.Errorf("MulMod builtin segment doesn't exist") - } - addModBuiltinRunner, ok := addModBuiltinSegment.BuiltinRunner.(*ModBuiltin) - if !ok { - return fmt.Errorf("addModBuiltinRunner is not a ModBuiltin") - } - mulModBuiltinRunner, ok := mulModBuiltinSegment.BuiltinRunner.(*ModBuiltin) - if !ok { - return fmt.Errorf("mulModBuiltinRunner is not a ModBuiltin") - } + var addModBuiltinRunner *ModBuiltin + var mulModBuiltinRunner *ModBuiltin + var addModBuiltinInputs, mulModBuiltinInputs ModBuiltinInputs + var err error - if addModBuiltinRunner.wordBitLen != mulModBuiltinRunner.wordBitLen { - return fmt.Errorf("AddMod and MulMod wordBitLen mismatch") - } + if nAddMods != 0 { + addModBuiltinSegment, ok := mem.FindSegmentWithBuiltin("AddMod") + if !ok { + return fmt.Errorf("AddMod builtin segment doesn't exist") + } + addModBuiltinRunner, ok = addModBuiltinSegment.BuiltinRunner.(*ModBuiltin) + if !ok { + return fmt.Errorf("addModBuiltinRunner is not a ModBuiltin") + } - addModBuiltinInputs, err := addModBuiltinRunner.readInputs(mem, addModBuiltinAddr, true) - if err != nil { - return err - } - if err := addModBuiltinRunner.fillInputs(mem, addModBuiltinAddr, addModBuiltinInputs); err != nil { - return err - } - if err := addModBuiltinRunner.fillOffsets(mem, addModBuiltinInputs.offsetsPtr, nAddModsIndex, addModBuiltinInputs.n-nAddModsIndex); err != nil { - return err + addModBuiltinInputs, err = addModBuiltinRunner.readInputs(mem, addModInputAddress, true) + if err != nil { + return err + } + if err := addModBuiltinRunner.fillInputs(mem, addModInputAddress, addModBuiltinInputs); err != nil { + return err + } + if err := addModBuiltinRunner.fillOffsets(mem, addModBuiltinInputs.offsetsPtr, nAddMods, addModBuiltinInputs.n-nAddMods); err != nil { + return err + } + } else { + addModBuiltinRunner = nil } - mulModBuiltinInputs, err := mulModBuiltinRunner.readInputs(mem, mulModBuiltinAddr, true) - if err != nil { - return err - } - if err := mulModBuiltinRunner.fillInputs(mem, mulModBuiltinAddr, mulModBuiltinInputs); err != nil { - return err - } - if err := mulModBuiltinRunner.fillOffsets(mem, mulModBuiltinInputs.offsetsPtr, nMulModsIndex, mulModBuiltinInputs.n-nMulModsIndex); err != nil { - return err - } + if nMulMods != 0 { + mulModBuiltinSegment, ok := mem.FindSegmentWithBuiltin("MulMod") + if !ok { + return fmt.Errorf("MulMod builtin segment doesn't exist") + } + mulModBuiltinRunner, ok = mulModBuiltinSegment.BuiltinRunner.(*ModBuiltin) + if !ok { + return fmt.Errorf("mulModBuiltinRunner is not a ModBuiltin") + } - addModIndex, mulModIndex := uint64(0), uint64(0) - for addModIndex < nAddModsIndex { - ok, err := addModBuiltinRunner.fillValue(mem, addModBuiltinInputs, int(addModIndex), Add) + mulModBuiltinInputs, err = mulModBuiltinRunner.readInputs(mem, mulModInputAddress, true) if err != nil { return err } - if ok { - addModIndex++ + } else { + mulModBuiltinRunner = nil + } + + addModIndex, mulModIndex := uint64(0), uint64(0) + nComputedMulGates := uint64(0) + for addModIndex < nAddMods || mulModIndex < nMulMods { + if addModIndex < nAddMods && addModBuiltinRunner != nil { + res, err := addModBuiltinRunner.fillValue(mem, addModBuiltinInputs, int(addModIndex), Add) + if err != nil { + return err + } + if res == 1 { + addModIndex++ + } + } + + if mulModIndex < nMulMods && mulModBuiltinRunner != nil { + res, err := mulModBuiltinRunner.fillValue(mem, mulModBuiltinInputs, int(mulModIndex), Mul) + if err != nil { + return err + } + if res == 0 { + return fmt.Errorf("MulMod builtin: Could not fill the values table") + } + if res == 2 && nComputedMulGates == 0 { + nComputedMulGates = mulModIndex + } + mulModIndex++ } } - for mulModIndex < nMulModsIndex { - ok, err = mulModBuiltinRunner.fillValue(mem, mulModBuiltinInputs, int(mulModIndex), Mul) + // TODO: Investigate tests that fail when nComputedMulGates is not implemented + if mulModBuiltinRunner != nil { + if nComputedMulGates == 0 { + nComputedMulGates = mulModBuiltinInputs.n + if nComputedMulGates == 0 { + nComputedMulGates = nMulMods + } + mulModBuiltinInputs.n = nComputedMulGates + if err := mulModBuiltinRunner.fillOffsets(mem, mulModBuiltinInputs.offsetsPtr, nMulMods, nComputedMulGates-nMulMods); err != nil { + return err + } + } else { + if mulModBuiltinRunner.batchSize != 1 { + return fmt.Errorf("MulMod builtin: Inverse failure is supported only at batch_size == 1") + } + } + mulModBuiltinInputs.n = nComputedMulGates + mulModInputNAddr, err := mulModInputAddress.AddOffset(int16(N_OFFSET)) if err != nil { return err } - if ok { - mulModIndex++ + mv := memory.MemoryValueFromFieldElement(new(fp.Element).SetUint64(nComputedMulGates)) + if err := mem.WriteToAddress(&mulModInputNAddr, &mv); err != nil { + return err } - } - // POTENTIALY: add n_computed_mul_gates features in the future + if err := mulModBuiltinRunner.fillInputs(mem, mulModInputAddress, mulModBuiltinInputs); err != nil { + return err + } + } return nil } diff --git a/pkg/vm/builtins/modulo_test.go b/pkg/vm/builtins/modulo_test.go index 722817b72..875adcdb2 100644 --- a/pkg/vm/builtins/modulo_test.go +++ b/pkg/vm/builtins/modulo_test.go @@ -12,7 +12,7 @@ import ( /* Tests whether runner completes a trio a, b, c as the input implies: -If inverse is False it tests whether a = x1, b=x2, c = None will be completed with c = res. +If inverse is False it tests whether a = x1, b = x2, c = None will be completed with c = res. If inverse is True it tests whether c = x1, b = x2, a = None will be completed with a = res. */ func checkResult(runner ModBuiltin, inverse bool, p, x1, x2 big.Int) (*big.Int, error) {