From b6f59e0c79e8e8a39ba2fc320b4ce1cb3807c6e0 Mon Sep 17 00:00:00 2001 From: MaksymMalicki Date: Fri, 13 Dec 2024 15:10:52 +0100 Subject: [PATCH 01/20] Add parsing logic for input user args --- pkg/parsers/starknet/starknet.go | 40 ++++++++++++ pkg/parsers/starknet/starknet_test.go | 93 +++++++++++++++++++++++++++ 2 files changed, 133 insertions(+) diff --git a/pkg/parsers/starknet/starknet.go b/pkg/parsers/starknet/starknet.go index 0bbc615d..6bcec83d 100644 --- a/pkg/parsers/starknet/starknet.go +++ b/pkg/parsers/starknet/starknet.go @@ -4,6 +4,8 @@ import ( "encoding/json" "fmt" "os" + "regexp" + "strings" "github.com/NethermindEth/cairo-vm-go/pkg/vm/builtins" "github.com/consensys/gnark-crypto/ecc/stark-curve/fp" @@ -95,3 +97,41 @@ func StarknetProgramFromJSON(content json.RawMessage) (*StarknetProgram, error) var starknet StarknetProgram return &starknet, json.Unmarshal(content, &starknet) } + +type CairoFuncArgs struct { + Single *fp.Element + Array []fp.Element +} + +func ParseCairoProgramArgs(input string) ([]CairoFuncArgs, error) { + re := regexp.MustCompile(`\[[^\]]*\]|\S+`) + tokens := re.FindAllString(input, -1) + var result []CairoFuncArgs + for _, token := range tokens { + if single, err := new(fp.Element).SetString(token); err == nil { + result = append(result, CairoFuncArgs{ + Single: single, + Array: nil, + }) + } else if strings.HasPrefix(token, "[") && strings.HasSuffix(token, "]") { + arrayStr := strings.Trim(token, "[]") + arrayParts := strings.Fields(arrayStr) + var array []fp.Element + for _, part := range arrayParts { + single, err := new(fp.Element).SetString(part) + if err != nil { + return nil, fmt.Errorf("invalid felt value in array: %v", err) + } + array = append(array, *single) + } + result = append(result, CairoFuncArgs{ + Single: nil, + Array: array, + }) + } else { + return nil, fmt.Errorf("invalid token: %s", token) + } + } + + return result, nil +} diff --git a/pkg/parsers/starknet/starknet_test.go b/pkg/parsers/starknet/starknet_test.go index 64842bcf..7c4fe763 100644 --- a/pkg/parsers/starknet/starknet_test.go +++ b/pkg/parsers/starknet/starknet_test.go @@ -3,6 +3,7 @@ package starknet import ( "testing" + "github.com/consensys/gnark-crypto/ecc/stark-curve/fp" "github.com/go-playground/validator/v10" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -170,3 +171,95 @@ func TestInvalidBuiltin(t *testing.T) { _, err := StarknetProgramFromJSON(testData) assert.Error(t, err) } + +func TestParseStarknetProgramArgs(t *testing.T) { + testCases := []struct { + name string + args string + expected []CairoFuncArgs + }{ + { + name: "single arg", + args: "1", + expected: []CairoFuncArgs{ + { + Single: new(fp.Element).SetUint64(1), + Array: nil, + }, + }, + }, + { + name: "single array arg", + args: "[1 2 3 4]", + expected: []CairoFuncArgs{ + { + Single: new(fp.Element), + Array: []fp.Element{ + *new(fp.Element).SetUint64(1), + *new(fp.Element).SetUint64(2), + *new(fp.Element).SetUint64(3), + *new(fp.Element).SetUint64(4), + }, + }, + }, + }, + { + name: "mixed args", + args: "1 [2 3 4] 5 [6 7 8] [1] 9 9 [12341341234 0]", + expected: []CairoFuncArgs{ + { + Single: new(fp.Element).SetUint64(1), + Array: nil, + }, + { + Single: new(fp.Element), + Array: []fp.Element{ + *new(fp.Element).SetUint64(2), + *new(fp.Element).SetUint64(3), + *new(fp.Element).SetUint64(4), + }, + }, + { + Single: new(fp.Element).SetUint64(5), + Array: nil, + }, + { + Single: new(fp.Element), + Array: []fp.Element{ + *new(fp.Element).SetUint64(6), + *new(fp.Element).SetUint64(7), + *new(fp.Element).SetUint64(8), + }, + }, + { + Single: new(fp.Element), + Array: []fp.Element{ + *new(fp.Element).SetUint64(1), + }, + }, + { + Single: new(fp.Element).SetUint64(9), + Array: nil, + }, + { + Single: new(fp.Element).SetUint64(9), + Array: nil, + }, + { + Single: new(fp.Element), + Array: []fp.Element{ + *new(fp.Element).SetUint64(12341341234), + *new(fp.Element).SetUint64(0), + }, + }, + }, + }, + } + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + args, err := ParseCairoProgramArgs(testCase.args) + require.NoError(t, err) + assert.Equal(t, testCase.expected, args) + }) + } +} From 3cce1706e347c3d4f15fdde174da08f2aebfa737 Mon Sep 17 00:00:00 2001 From: MaksymMalicki Date: Fri, 13 Dec 2024 15:11:36 +0100 Subject: [PATCH 02/20] Add flags for available gas, input user args, writing args to memory --- cmd/cli/main.go | 36 +++++++++++++++-- integration_tests/.env | 2 +- integration_tests/cairo_vm_test.go | 2 + pkg/hintrunner/core/hint.go | 28 +++++++++++++ pkg/runner/runner.go | 62 ++++++++++++++--------------- pkg/runner/runner_benchmark_test.go | 2 +- pkg/runner/runner_test.go | 38 +++++++++--------- pkg/vm/vm.go | 14 +++++-- 8 files changed, 124 insertions(+), 60 deletions(-) diff --git a/cmd/cli/main.go b/cmd/cli/main.go index 082c3643..6f08c57b 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -12,6 +12,7 @@ import ( "github.com/NethermindEth/cairo-vm-go/pkg/parsers/starknet" zero "github.com/NethermindEth/cairo-vm-go/pkg/parsers/zero" "github.com/NethermindEth/cairo-vm-go/pkg/runner" + "github.com/consensys/gnark-crypto/ecc/stark-curve/fp" "github.com/urfave/cli/v2" ) @@ -26,6 +27,8 @@ func main() { var layoutName string var airPublicInputLocation string var airPrivateInputLocation string + var args string + var availableGas uint64 app := &cli.App{ Name: "cairo-vm", Usage: "A cairo virtual machine", @@ -122,7 +125,7 @@ func main() { if proofmode { runnerMode = runner.ProofModeCairo0 } - return runVM(*program, proofmode, maxsteps, entrypointOffset, collectTrace, traceLocation, buildMemory, memoryLocation, layoutName, airPublicInputLocation, airPrivateInputLocation, hints, runnerMode) + return runVM(*program, proofmode, maxsteps, entrypointOffset, collectTrace, traceLocation, buildMemory, memoryLocation, layoutName, airPublicInputLocation, airPrivateInputLocation, hints, runnerMode, nil) }, }, { @@ -191,6 +194,18 @@ func main() { Required: false, Destination: &airPrivateInputLocation, }, + &cli.StringFlag{ + Name: "args", + Usage: "input arguments for the `main` function in the cairo progran", + Required: false, + Destination: &args, + }, + &cli.Uint64Flag{ + Name: "available_gas", + Usage: "available gas for the VM execution", + Required: false, + Destination: &availableGas, + }, }, Action: func(ctx *cli.Context) error { pathToFile := ctx.Args().Get(0) @@ -210,7 +225,19 @@ func main() { if proofmode { runnerMode = runner.ProofModeCairo1 } - return runVM(program, proofmode, maxsteps, entrypointOffset, collectTrace, traceLocation, buildMemory, memoryLocation, layoutName, airPublicInputLocation, airPrivateInputLocation, hints, runnerMode) + userArgs, err := starknet.ParseCairoProgramArgs(args) + if err != nil { + return fmt.Errorf("cannot parse args: %w", err) + } + if availableGas > 0 { + // The first argument is the available gas + availableGasArg := starknet.CairoFuncArgs{ + Single: new(fp.Element).SetUint64(availableGas), + Array: nil, + } + userArgs = append([]starknet.CairoFuncArgs{availableGasArg}, userArgs...) + } + return runVM(program, proofmode, maxsteps, entrypointOffset, collectTrace, traceLocation, buildMemory, memoryLocation, layoutName, airPublicInputLocation, airPrivateInputLocation, hints, runnerMode, userArgs) }, }, }, @@ -236,6 +263,7 @@ func runVM( airPrivateInputLocation string, hints map[uint64][]hinter.Hinter, runnerMode runner.RunnerMode, + userArgs []starknet.CairoFuncArgs, ) error { fmt.Println("Running....") runner, err := runner.NewRunner(&program, hints, runnerMode, collectTrace, maxsteps, layoutName) @@ -248,11 +276,11 @@ func runVM( // but these functions are implemented differently in both this and cairo-rs VMs // and the difference is quite subtle. if entrypointOffset == 0 { - if err := runner.Run(); err != nil { + if err := runner.Run(userArgs); err != nil { return fmt.Errorf("runtime error: %w", err) } } else { - if err := runner.RunEntryPoint(entrypointOffset); err != nil { + if err := runner.RunEntryPoint(entrypointOffset, userArgs); err != nil { return fmt.Errorf("runtime error (entrypoint=%d): %w", entrypointOffset, err) } } diff --git a/integration_tests/.env b/integration_tests/.env index 5448ff22..3ae4646f 100644 --- a/integration_tests/.env +++ b/integration_tests/.env @@ -1,2 +1,2 @@ # Set to run some specific file tests (ex. fib.cairo,alloc.cairo) -INTEGRATION_TESTS_FILTERS= \ No newline at end of file +INTEGRATION_TESTS_FILTERS=fibonacci__small.cairo \ No newline at end of file diff --git a/integration_tests/cairo_vm_test.go b/integration_tests/cairo_vm_test.go index 4c940cc1..f1c4f376 100644 --- a/integration_tests/cairo_vm_test.go +++ b/integration_tests/cairo_vm_test.go @@ -466,6 +466,8 @@ func runVm(path, layout string, zero bool) (time.Duration, string, string, strin memoryOutput, "--layout", layout, + "--available_gas", + "9999999", path, } diff --git a/pkg/hintrunner/core/hint.go b/pkg/hintrunner/core/hint.go index 3efa7a9f..d2012b6b 100644 --- a/pkg/hintrunner/core/hint.go +++ b/pkg/hintrunner/core/hint.go @@ -1929,3 +1929,31 @@ func (hint *FieldSqrt) Execute(vm *VM.VirtualMachine, _ *hinter.HintRunnerContex return vm.Memory.WriteToAddress(&sqrtAddr, &sqrtVal) } + +type ExternalWriteArgsToMemory struct{} + +func (hint *ExternalWriteArgsToMemory) String() string { + return "ExternalWriteToMemory" +} + +func (hint *ExternalWriteArgsToMemory) Execute(vm *VM.VirtualMachine, ctx *hinter.HintRunnerContext) error { + userArgs := vm.Config.UserArgs + for _, arg := range userArgs { + if arg.Single != nil { + mv := mem.MemoryValueFromFieldElement(arg.Single) + err := vm.Memory.Write(1, vm.Context.Ap-uint64(len(userArgs)), &mv) + if err != nil { + return fmt.Errorf("write single arg: %v", err) + } + } else if arg.Array != nil { + arrayBase := vm.Memory.AllocateEmptySegment() + mv := mem.MemoryValueFromMemoryAddress(&arrayBase) + err := vm.Memory.Write(1, vm.Context.Ap, &mv) + if err != nil { + return fmt.Errorf("write array base: %v", err) + } + // TODO: Implement array writing + } + } + return nil +} diff --git a/pkg/runner/runner.go b/pkg/runner/runner.go index bac6daee..39a8bf61 100644 --- a/pkg/runner/runner.go +++ b/pkg/runner/runner.go @@ -67,28 +67,31 @@ func AssembleProgram(cairoProgram *starknet.StarknetProgram) (Program, map[uint6 if err != nil { return Program{}, nil, fmt.Errorf("cannot load program: %w", err) } - entryCodeInstructions, err := GetEntryCodeInstructions(mainFunc, false, 0) + hints, err := core.GetCairoHints(cairoProgram) + if err != nil { + return Program{}, nil, fmt.Errorf("cannot get hints: %w", err) + } + entryCodeInstructions, entryCodeHints, err := GetEntryCodeInstructions(mainFunc, false) if err != nil { return Program{}, nil, fmt.Errorf("cannot load entry code instructions: %w", err) } program.Bytecode = append(entryCodeInstructions, program.Bytecode...) program.Bytecode = append(program.Bytecode, GetFooterInstructions()...) - hints, err := core.GetCairoHints(cairoProgram) - if err != nil { - return Program{}, nil, fmt.Errorf("cannot get hints: %w", err) - } offset := uint64(len(entryCodeInstructions)) shiftedHintsMap := make(map[uint64][]hinter.Hinter) for key, value := range hints { shiftedHintsMap[key+offset] = value } + for key, hint := range entryCodeHints { + shiftedHintsMap[key] = hint + } return *program, shiftedHintsMap, nil } // RunEntryPoint is like Run, but it executes the program starting from the given PC offset. // This PC offset is expected to be a start from some function inside the loaded program. -func (runner *Runner) RunEntryPoint(pc uint64) error { +func (runner *Runner) RunEntryPoint(pc uint64, userArgs []starknet.CairoFuncArgs) error { if runner.runFinished { return errors.New("cannot re-run using the same runner") } @@ -111,11 +114,7 @@ func (runner *Runner) RunEntryPoint(pc uint64) error { if runner.runnerMode == ProofModeCairo1 { cairo1FpOffset = 2 } - end, err := runner.initializeEntrypoint(pc, nil, &mvReturnFp, memory, stack, cairo1FpOffset) - if err != nil { - return err - } - err = runner.loadArguments(uint64(0), uint64(8979879877)) + end, err := runner.initializeEntrypoint(pc, nil, &mvReturnFp, memory, stack, cairo1FpOffset, userArgs) if err != nil { return err } @@ -126,20 +125,16 @@ func (runner *Runner) RunEntryPoint(pc uint64) error { return nil } -func (runner *Runner) Run() error { +func (runner *Runner) Run(userArgs []starknet.CairoFuncArgs) error { if runner.runFinished { return errors.New("cannot re-run using the same runner") } - end, err := runner.initializeMainEntrypoint() + end, err := runner.initializeMainEntrypoint(userArgs) if err != nil { return fmt.Errorf("initializing main entry point: %w", err) } - err = runner.loadArguments(uint64(0), uint64(8979879877)) - if err != nil { - return err - } err = runner.RunUntilPc(&end) if err != nil { return err @@ -167,7 +162,7 @@ func (runner *Runner) initializeSegments() (*mem.Memory, error) { return memory, nil } -func (runner *Runner) initializeMainEntrypoint() (mem.MemoryAddress, error) { +func (runner *Runner) initializeMainEntrypoint(userArgs []starknet.CairoFuncArgs) (mem.MemoryAddress, error) { memory, err := runner.initializeSegments() if err != nil { return mem.UnknownAddress, err @@ -186,9 +181,9 @@ func (runner *Runner) initializeMainEntrypoint() (mem.MemoryAddress, error) { return mem.UnknownAddress, errors.New("can't find an entrypoint for main") } if runner.runnerMode == ExecutionMode { - return runner.initializeEntrypoint(mainPCOffset, nil, &mvReturnFp, memory, stack, 0) + return runner.initializeEntrypoint(mainPCOffset, nil, &mvReturnFp, memory, stack, 0, userArgs) } else { - return runner.initializeEntrypoint(mainPCOffset, nil, &mvReturnFp, memory, stack, 2) + return runner.initializeEntrypoint(mainPCOffset, nil, &mvReturnFp, memory, stack, 2, userArgs) } case ProofModeCairo0: initialPCOffset, ok := runner.program.Labels["__start__"] @@ -211,7 +206,7 @@ func (runner *Runner) initializeMainEntrypoint() (mem.MemoryAddress, error) { if err := runner.initializeVm(&mem.MemoryAddress{ SegmentIndex: vm.ProgramSegment, Offset: initialPCOffset, - }, stack, memory, 0); err != nil { + }, stack, memory, 0, userArgs); err != nil { return mem.UnknownAddress, err } @@ -225,7 +220,7 @@ func (runner *Runner) initializeMainEntrypoint() (mem.MemoryAddress, error) { } func (runner *Runner) initializeEntrypoint( - initialPCOffset uint64, arguments []*fp.Element, returnFp *mem.MemoryValue, memory *mem.Memory, stack []mem.MemoryValue, cairo1FpOffset uint64, + initialPCOffset uint64, arguments []*fp.Element, returnFp *mem.MemoryValue, memory *mem.Memory, stack []mem.MemoryValue, cairo1FpOffset uint64, userArgs []starknet.CairoFuncArgs, ) (mem.MemoryAddress, error) { for i := range arguments { stack = append(stack, mem.MemoryValueFromFieldElement(arguments[i])) @@ -235,7 +230,7 @@ func (runner *Runner) initializeEntrypoint( return endPC, runner.initializeVm(&mem.MemoryAddress{ SegmentIndex: vm.ProgramSegment, Offset: initialPCOffset, - }, stack, memory, cairo1FpOffset) + }, stack, memory, cairo1FpOffset, userArgs) } func (runner *Runner) initializeBuiltins(memory *mem.Memory) ([]mem.MemoryValue, error) { @@ -271,7 +266,7 @@ func (runner *Runner) isProofMode() bool { } func (runner *Runner) initializeVm( - initialPC *mem.MemoryAddress, stack []mem.MemoryValue, memory *mem.Memory, cairo1FpOffset uint64, + initialPC *mem.MemoryAddress, stack []mem.MemoryValue, memory *mem.Memory, cairo1FpOffset uint64, userArgs []starknet.CairoFuncArgs, ) error { executionSegment := memory.Segments[vm.ExecutionSegment] offset := executionSegment.Len() @@ -292,16 +287,11 @@ func (runner *Runner) initializeVm( }, memory, vm.VirtualMachineConfig{ ProofMode: runner.isProofMode(), CollectTrace: runner.collectTrace, + UserArgs: userArgs, }) return err } -func (runner *Runner) loadArguments(args, initialGas uint64) error { - mv := mem.MemoryValueFromUint(initialGas) - runner.vm.Memory.Segments[vm.ExecutionSegment].Write(runner.vm.Context.Ap+1, &mv) - return nil -} - // run until the program counter equals the `pc` parameter func (runner *Runner) RunUntilPc(pc *mem.MemoryAddress) error { for !runner.vm.Context.Pc.Equal(pc) { @@ -313,6 +303,9 @@ func (runner *Runner) RunUntilPc(pc *mem.MemoryAddress) error { runner.maxsteps, ) } + if runner.vm.Context.Pc.Offset == 3 { + runner.vm.PrintMemory() + } if err := runner.vm.RunStep(&runner.hintrunner); err != nil { return fmt.Errorf("pc %s step %d: %w", runner.pc(), runner.steps(), err) } @@ -506,7 +499,7 @@ func (ctx *InlineCasmContext) AddInlineCASM(code string) { ctx.currentCodeOffset += int(total_size) } -func GetEntryCodeInstructions(function starknet.EntryPointByFunction, finalizeForProof bool, initialGas uint64) ([]*fp.Element, error) { +func GetEntryCodeInstructions(function starknet.EntryPointByFunction, finalizeForProof bool) ([]*fp.Element, map[uint64][]hinter.Hinter, error) { paramTypes := function.InputArgs apOffset := 0 builtinOffset := 3 @@ -580,10 +573,15 @@ func GetEntryCodeInstructions(function starknet.EntryPointByFunction, finalizeFo usedArgs += param.Size } } + hints := map[uint64][]hinter.Hinter{ + uint64(len(ctx.instructions)): []hinter.Hinter{ + &core.ExternalWriteArgsToMemory{}, + }, + } ctx.AddInlineCASM(fmt.Sprintf("call rel %d;", apOffset+1)) ctx.AddInlineCASM("ret;") - return ctx.instructions, nil + return ctx.instructions, hints, nil } func GetFooterInstructions() []*fp.Element { diff --git a/pkg/runner/runner_benchmark_test.go b/pkg/runner/runner_benchmark_test.go index d86be979..cca139fd 100644 --- a/pkg/runner/runner_benchmark_test.go +++ b/pkg/runner/runner_benchmark_test.go @@ -238,7 +238,7 @@ func BenchmarkRunnerWithFibonacci(b *testing.B) { panic(err) } - err = runner.Run() + err = runner.Run(nil) if err != nil { panic(err) } diff --git a/pkg/runner/runner_test.go b/pkg/runner/runner_test.go index 8c876d64..b01d22cf 100644 --- a/pkg/runner/runner_test.go +++ b/pkg/runner/runner_test.go @@ -30,7 +30,7 @@ func TestSimpleProgram(t *testing.T) { runner, err := NewRunner(program, hints, ExecutionMode, false, math.MaxUint64, "plain") require.NoError(t, err) - endPc, err := runner.initializeMainEntrypoint() + endPc, err := runner.initializeMainEntrypoint(nil) require.NoError(t, err) expectedPc := memory.MemoryAddress{SegmentIndex: 3, Offset: 0} @@ -77,7 +77,7 @@ func TestStepLimitExceeded(t *testing.T) { runner, err := NewRunner(program, hints, ExecutionMode, false, 3, "plain") require.NoError(t, err) - endPc, err := runner.initializeMainEntrypoint() + endPc, err := runner.initializeMainEntrypoint(nil) require.NoError(t, err) expectedPc := memory.MemoryAddress{SegmentIndex: 3, Offset: 0} @@ -136,7 +136,7 @@ func TestStepLimitExceededProofMode(t *testing.T) { runner, err := NewRunner(program, hints, ProofModeCairo0, false, uint64(maxstep), "plain") require.NoError(t, err) - err = runner.Run() + err = runner.Run(nil) require.ErrorContains(t, err, "step limit exceeded") executionSegment := runner.vm.Memory.Segments[vm.ExecutionSegment] @@ -188,7 +188,7 @@ func TestBitwiseBuiltin(t *testing.T) { ret; `, "starknet_with_keccak", builtins.BitwiseType) - err := runner.Run() + err := runner.Run(nil) require.NoError(t, err) bitwise, ok := runner.vm.Memory.FindSegmentWithBuiltin(builtins.BitwiseName) @@ -204,7 +204,7 @@ func TestBitwiseBuiltinError(t *testing.T) { ret; `, "starknet_with_keccak", builtins.BitwiseType) - err := runner.Run() + err := runner.Run(nil) require.ErrorContains(t, err, "cannot infer value") // inferring second write to cell @@ -212,7 +212,7 @@ func TestBitwiseBuiltinError(t *testing.T) { [ap] = [[fp - 3] + 1]; ret; `, "starknet_with_keccak", builtins.BitwiseType) - err = runner.Run() + err = runner.Run(nil) require.ErrorContains(t, err, "cannot infer value") // trying to infer without writing before @@ -221,7 +221,7 @@ func TestBitwiseBuiltinError(t *testing.T) { ret; `, "starknet_with_keccak", builtins.BitwiseType) - err = runner.Run() + err = runner.Run(nil) require.ErrorContains(t, err, "input value at offset 0 is unknown") } @@ -234,7 +234,7 @@ func TestOutputBuiltin(t *testing.T) { [ap + 1] = [[fp - 3] + 1]; ret; `, "small", builtins.OutputType) - err := runner.Run() + err := runner.Run(nil) require.NoError(t, err) output := runner.Output() @@ -264,7 +264,7 @@ func TestPedersenBuiltin(t *testing.T) { `, val1.Text(10), val2.Text(10), val3.Text(10)) runner := createRunner(code, "small", builtins.PedersenType) - err := runner.Run() + err := runner.Run(nil) require.NoError(t, err) pedersen, ok := runner.vm.Memory.FindSegmentWithBuiltin(builtins.PedersenName) @@ -277,14 +277,14 @@ func TestPedersenBuiltinError(t *testing.T) { [ap] = [[fp - 3]]; ret; `, "small", builtins.PedersenType) - err := runner.Run() + err := runner.Run(nil) require.ErrorContains(t, err, "cannot infer value") runner = createRunner(` [ap] = [[fp - 3] + 2]; ret; `, "small", builtins.PedersenType) - err = runner.Run() + err = runner.Run(nil) require.ErrorContains(t, err, "input value at offset 0 is unknown") } @@ -300,7 +300,7 @@ func TestRangeCheckBuiltin(t *testing.T) { ret; `, "small", builtins.RangeCheckType) - err := runner.Run() + err := runner.Run(nil) require.NoError(t, err) rangeCheck, ok := runner.vm.Memory.FindSegmentWithBuiltin("range_check") @@ -321,7 +321,7 @@ func TestRangeCheckBuiltinError(t *testing.T) { ret; `, "small", builtins.RangeCheckType) - err := runner.Run() + err := runner.Run(nil) require.ErrorContains(t, err, "check write: 2**128 <") // second test fails due to reading unknown value @@ -330,7 +330,7 @@ func TestRangeCheckBuiltinError(t *testing.T) { ret; `, "small", builtins.RangeCheckType) - err = runner.Run() + err = runner.Run(nil) require.ErrorContains(t, err, "cannot infer value") } @@ -346,7 +346,7 @@ func TestRangeCheck96Builtin(t *testing.T) { ret; `, "all_cairo", builtins.RangeCheck96Type) - err := runner.Run() + err := runner.Run(nil) require.NoError(t, err) rangeCheck96, ok := runner.vm.Memory.FindSegmentWithBuiltin(builtins.RangeCheck96Name) @@ -367,7 +367,7 @@ func TestRangeCheck96BuiltinError(t *testing.T) { ret; `, "all_cairo", builtins.RangeCheck96Type) - err := runner.Run() + err := runner.Run(nil) require.ErrorContains(t, err, "check write: 2**96 <") // second test fails due to reading unknown value @@ -376,7 +376,7 @@ func TestRangeCheck96BuiltinError(t *testing.T) { ret; `, "all_cairo", builtins.RangeCheck96Type) - err = runner.Run() + err = runner.Run(nil) require.ErrorContains(t, err, "cannot infer value") } @@ -406,7 +406,7 @@ func TestEcOpBuiltin(t *testing.T) { ret; `, "starknet_with_keccak", builtins.ECOPType) - err := runner.Run() + err := runner.Run(nil) require.NoError(t, err) } @@ -423,7 +423,7 @@ func TestModuloBuiltin(t *testing.T) { // ret; // `, "small", sn.AddMod, sn.MulMod) - // err := runner.Run() + // err := runner.Run(nil) // require.NoError(t, err) // modulo, ok := runner.vm.Memory.FindSegmentWithBuiltin("add_mod") diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index 65c6ab8e..3fa22644 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -6,6 +6,7 @@ import ( "math" asmb "github.com/NethermindEth/cairo-vm-go/pkg/assembler" + "github.com/NethermindEth/cairo-vm-go/pkg/parsers/starknet" "github.com/NethermindEth/cairo-vm-go/pkg/utils" mem "github.com/NethermindEth/cairo-vm-go/pkg/vm/memory" f "github.com/consensys/gnark-crypto/ecc/stark-curve/fp" @@ -76,6 +77,7 @@ type VirtualMachineConfig struct { ProofMode bool // If true, the vm collects the relocated trace at the end of execution, without finalizing segments CollectTrace bool + UserArgs []starknet.CairoFuncArgs } type VirtualMachine struct { @@ -83,7 +85,7 @@ type VirtualMachine struct { Memory *mem.Memory Step uint64 Trace []Context - config VirtualMachineConfig + Config VirtualMachineConfig // instructions cache instructions map[uint64]*asmb.Instruction // RcLimitsMin and RcLimitsMax define the range of values of instructions offsets, used for checking the number of potential range checks holes @@ -91,6 +93,12 @@ type VirtualMachine struct { RcLimitsMax uint16 } +func (vm *VirtualMachine) PrintMemory() { + for j, cell := range vm.Memory.Segments[ExecutionSegment].Data { + fmt.Printf("\tCell %d: %s\n", j, cell) + } +} + // NewVirtualMachine creates a VM from the program bytecode using a specified config. func NewVirtualMachine( initialContext Context, memory *mem.Memory, config VirtualMachineConfig, @@ -108,7 +116,7 @@ func NewVirtualMachine( Context: initialContext, Memory: memory, Trace: trace, - config: config, + Config: config, instructions: make(map[uint64]*asmb.Instruction), RcLimitsMin: math.MaxUint16, RcLimitsMax: 0, @@ -143,7 +151,7 @@ func (vm *VirtualMachine) RunStep(hintRunner HintRunner) error { } // store the trace before state change - if vm.config.ProofMode || vm.config.CollectTrace { + if vm.Config.ProofMode || vm.Config.CollectTrace { vm.Trace = append(vm.Trace, vm.Context) } From 9e2d84b2ef218625c36f34807b4b0c4e53fbdc7b Mon Sep 17 00:00:00 2001 From: MaksymMalicki Date: Fri, 13 Dec 2024 15:22:12 +0100 Subject: [PATCH 03/20] Fix unit tests for user arguments parsing --- pkg/parsers/starknet/starknet_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/parsers/starknet/starknet_test.go b/pkg/parsers/starknet/starknet_test.go index 7c4fe763..81bb5524 100644 --- a/pkg/parsers/starknet/starknet_test.go +++ b/pkg/parsers/starknet/starknet_test.go @@ -193,7 +193,7 @@ func TestParseStarknetProgramArgs(t *testing.T) { args: "[1 2 3 4]", expected: []CairoFuncArgs{ { - Single: new(fp.Element), + Single: nil, Array: []fp.Element{ *new(fp.Element).SetUint64(1), *new(fp.Element).SetUint64(2), @@ -212,7 +212,7 @@ func TestParseStarknetProgramArgs(t *testing.T) { Array: nil, }, { - Single: new(fp.Element), + Single: nil, Array: []fp.Element{ *new(fp.Element).SetUint64(2), *new(fp.Element).SetUint64(3), @@ -224,7 +224,7 @@ func TestParseStarknetProgramArgs(t *testing.T) { Array: nil, }, { - Single: new(fp.Element), + Single: nil, Array: []fp.Element{ *new(fp.Element).SetUint64(6), *new(fp.Element).SetUint64(7), @@ -232,7 +232,7 @@ func TestParseStarknetProgramArgs(t *testing.T) { }, }, { - Single: new(fp.Element), + Single: nil, Array: []fp.Element{ *new(fp.Element).SetUint64(1), }, @@ -246,7 +246,7 @@ func TestParseStarknetProgramArgs(t *testing.T) { Array: nil, }, { - Single: new(fp.Element), + Single: nil, Array: []fp.Element{ *new(fp.Element).SetUint64(12341341234), *new(fp.Element).SetUint64(0), From 316a872ad56bb7b5960eaee4b9cfca419065a14e Mon Sep 17 00:00:00 2001 From: MaksymMalicki Date: Fri, 13 Dec 2024 15:28:11 +0100 Subject: [PATCH 04/20] Lint the PR --- pkg/runner/runner.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/runner/runner.go b/pkg/runner/runner.go index 39a8bf61..3bb0e464 100644 --- a/pkg/runner/runner.go +++ b/pkg/runner/runner.go @@ -574,7 +574,7 @@ func GetEntryCodeInstructions(function starknet.EntryPointByFunction, finalizeFo } } hints := map[uint64][]hinter.Hinter{ - uint64(len(ctx.instructions)): []hinter.Hinter{ + uint64(len(ctx.instructions)): { &core.ExternalWriteArgsToMemory{}, }, } From 7753686f1250956314c52b7beac8d903a59f82ee Mon Sep 17 00:00:00 2001 From: MaksymMalicki Date: Mon, 16 Dec 2024 14:36:12 +0100 Subject: [PATCH 05/20] Add user args to hint context --- cmd/cli/main.go | 6 ++-- integration_tests/.env | 2 +- pkg/hintrunner/core/hint.go | 10 ++++++- pkg/hintrunner/hintrunner.go | 11 +++++-- pkg/hintrunner/hintrunner_test.go | 4 +-- pkg/runner/runner.go | 27 ++++++++--------- pkg/runner/runner_benchmark_test.go | 4 +-- pkg/runner/runner_test.go | 46 ++++++++++++++--------------- pkg/vm/vm.go | 2 -- 9 files changed, 61 insertions(+), 51 deletions(-) diff --git a/cmd/cli/main.go b/cmd/cli/main.go index 6f08c57b..151b4a28 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -266,7 +266,7 @@ func runVM( userArgs []starknet.CairoFuncArgs, ) error { fmt.Println("Running....") - runner, err := runner.NewRunner(&program, hints, runnerMode, collectTrace, maxsteps, layoutName) + runner, err := runner.NewRunner(&program, hints, runnerMode, collectTrace, maxsteps, layoutName, userArgs) if err != nil { return fmt.Errorf("cannot create runner: %w", err) } @@ -276,11 +276,11 @@ func runVM( // but these functions are implemented differently in both this and cairo-rs VMs // and the difference is quite subtle. if entrypointOffset == 0 { - if err := runner.Run(userArgs); err != nil { + if err := runner.Run(); err != nil { return fmt.Errorf("runtime error: %w", err) } } else { - if err := runner.RunEntryPoint(entrypointOffset, userArgs); err != nil { + if err := runner.RunEntryPoint(entrypointOffset); err != nil { return fmt.Errorf("runtime error (entrypoint=%d): %w", entrypointOffset, err) } } diff --git a/integration_tests/.env b/integration_tests/.env index 3ae4646f..5448ff22 100644 --- a/integration_tests/.env +++ b/integration_tests/.env @@ -1,2 +1,2 @@ # Set to run some specific file tests (ex. fib.cairo,alloc.cairo) -INTEGRATION_TESTS_FILTERS=fibonacci__small.cairo \ No newline at end of file +INTEGRATION_TESTS_FILTERS= \ No newline at end of file diff --git a/pkg/hintrunner/core/hint.go b/pkg/hintrunner/core/hint.go index d2012b6b..c91b0b7f 100644 --- a/pkg/hintrunner/core/hint.go +++ b/pkg/hintrunner/core/hint.go @@ -1937,7 +1937,14 @@ func (hint *ExternalWriteArgsToMemory) String() string { } func (hint *ExternalWriteArgsToMemory) Execute(vm *VM.VirtualMachine, ctx *hinter.HintRunnerContext) error { - userArgs := vm.Config.UserArgs + userArgsVar, err := ctx.ScopeManager.GetVariableValue("userArgs") + if err != nil { + return fmt.Errorf("get user args: %v", err) + } + userArgs, ok := userArgsVar.([]starknet.CairoFuncArgs) + if !ok { + return fmt.Errorf("expected user args to be a list of CairoFuncArgs") + } for _, arg := range userArgs { if arg.Single != nil { mv := mem.MemoryValueFromFieldElement(arg.Single) @@ -1955,5 +1962,6 @@ func (hint *ExternalWriteArgsToMemory) Execute(vm *VM.VirtualMachine, ctx *hinte // TODO: Implement array writing } } + ctx.ScopeManager.ExitScope() return nil } diff --git a/pkg/hintrunner/hintrunner.go b/pkg/hintrunner/hintrunner.go index f8e54ebb..bb2ddf23 100644 --- a/pkg/hintrunner/hintrunner.go +++ b/pkg/hintrunner/hintrunner.go @@ -4,6 +4,7 @@ import ( "fmt" h "github.com/NethermindEth/cairo-vm-go/pkg/hintrunner/hinter" + "github.com/NethermindEth/cairo-vm-go/pkg/parsers/starknet" VM "github.com/NethermindEth/cairo-vm-go/pkg/vm" ) @@ -14,12 +15,16 @@ type HintRunner struct { hints map[uint64][]h.Hinter } -func NewHintRunner(hints map[uint64][]h.Hinter) HintRunner { +func NewHintRunner(hints map[uint64][]h.Hinter, userArgs []starknet.CairoFuncArgs) HintRunner { return HintRunner{ // Context for certain hints that require it. Each manager is // initialized only when required by the hint - context: *h.InitializeDefaultContext(), - hints: hints, + context: *h.SetContextWithScope( + map[string]any{ + "userArgs": userArgs, + }, + ), + hints: hints, } } diff --git a/pkg/hintrunner/hintrunner_test.go b/pkg/hintrunner/hintrunner_test.go index 0e057de9..c3cbbd5e 100644 --- a/pkg/hintrunner/hintrunner_test.go +++ b/pkg/hintrunner/hintrunner_test.go @@ -20,7 +20,7 @@ func TestExistingHint(t *testing.T) { hr := NewHintRunner(map[uint64][]hinter.Hinter{ 10: {&allocHint}, - }) + }, nil) vm.Context.Pc = memory.MemoryAddress{ SegmentIndex: 0, @@ -44,7 +44,7 @@ func TestNoHint(t *testing.T) { hr := NewHintRunner(map[uint64][]hinter.Hinter{ 10: {&allocHint}, - }) + }, nil) vm.Context.Pc = memory.MemoryAddress{ SegmentIndex: 0, diff --git a/pkg/runner/runner.go b/pkg/runner/runner.go index 3bb0e464..87189706 100644 --- a/pkg/runner/runner.go +++ b/pkg/runner/runner.go @@ -42,8 +42,8 @@ type Runner struct { type CairoRunner struct{} // Creates a new Runner of a Cairo Zero program -func NewRunner(program *Program, hints map[uint64][]hinter.Hinter, runnerMode RunnerMode, collectTrace bool, maxsteps uint64, layoutName string) (Runner, error) { - hintrunner := hintrunner.NewHintRunner(hints) +func NewRunner(program *Program, hints map[uint64][]hinter.Hinter, runnerMode RunnerMode, collectTrace bool, maxsteps uint64, layoutName string, userArgs []starknet.CairoFuncArgs) (Runner, error) { + hintrunner := hintrunner.NewHintRunner(hints, userArgs) layout, err := builtins.GetLayout(layoutName) if err != nil { return Runner{}, err @@ -91,7 +91,7 @@ func AssembleProgram(cairoProgram *starknet.StarknetProgram) (Program, map[uint6 // RunEntryPoint is like Run, but it executes the program starting from the given PC offset. // This PC offset is expected to be a start from some function inside the loaded program. -func (runner *Runner) RunEntryPoint(pc uint64, userArgs []starknet.CairoFuncArgs) error { +func (runner *Runner) RunEntryPoint(pc uint64) error { if runner.runFinished { return errors.New("cannot re-run using the same runner") } @@ -114,7 +114,7 @@ func (runner *Runner) RunEntryPoint(pc uint64, userArgs []starknet.CairoFuncArgs if runner.runnerMode == ProofModeCairo1 { cairo1FpOffset = 2 } - end, err := runner.initializeEntrypoint(pc, nil, &mvReturnFp, memory, stack, cairo1FpOffset, userArgs) + end, err := runner.initializeEntrypoint(pc, nil, &mvReturnFp, memory, stack, cairo1FpOffset) if err != nil { return err } @@ -125,12 +125,12 @@ func (runner *Runner) RunEntryPoint(pc uint64, userArgs []starknet.CairoFuncArgs return nil } -func (runner *Runner) Run(userArgs []starknet.CairoFuncArgs) error { +func (runner *Runner) Run() error { if runner.runFinished { return errors.New("cannot re-run using the same runner") } - end, err := runner.initializeMainEntrypoint(userArgs) + end, err := runner.initializeMainEntrypoint() if err != nil { return fmt.Errorf("initializing main entry point: %w", err) } @@ -162,7 +162,7 @@ func (runner *Runner) initializeSegments() (*mem.Memory, error) { return memory, nil } -func (runner *Runner) initializeMainEntrypoint(userArgs []starknet.CairoFuncArgs) (mem.MemoryAddress, error) { +func (runner *Runner) initializeMainEntrypoint() (mem.MemoryAddress, error) { memory, err := runner.initializeSegments() if err != nil { return mem.UnknownAddress, err @@ -181,9 +181,9 @@ func (runner *Runner) initializeMainEntrypoint(userArgs []starknet.CairoFuncArgs return mem.UnknownAddress, errors.New("can't find an entrypoint for main") } if runner.runnerMode == ExecutionMode { - return runner.initializeEntrypoint(mainPCOffset, nil, &mvReturnFp, memory, stack, 0, userArgs) + return runner.initializeEntrypoint(mainPCOffset, nil, &mvReturnFp, memory, stack, 0) } else { - return runner.initializeEntrypoint(mainPCOffset, nil, &mvReturnFp, memory, stack, 2, userArgs) + return runner.initializeEntrypoint(mainPCOffset, nil, &mvReturnFp, memory, stack, 2) } case ProofModeCairo0: initialPCOffset, ok := runner.program.Labels["__start__"] @@ -206,7 +206,7 @@ func (runner *Runner) initializeMainEntrypoint(userArgs []starknet.CairoFuncArgs if err := runner.initializeVm(&mem.MemoryAddress{ SegmentIndex: vm.ProgramSegment, Offset: initialPCOffset, - }, stack, memory, 0, userArgs); err != nil { + }, stack, memory, 0); err != nil { return mem.UnknownAddress, err } @@ -220,7 +220,7 @@ func (runner *Runner) initializeMainEntrypoint(userArgs []starknet.CairoFuncArgs } func (runner *Runner) initializeEntrypoint( - initialPCOffset uint64, arguments []*fp.Element, returnFp *mem.MemoryValue, memory *mem.Memory, stack []mem.MemoryValue, cairo1FpOffset uint64, userArgs []starknet.CairoFuncArgs, + initialPCOffset uint64, arguments []*fp.Element, returnFp *mem.MemoryValue, memory *mem.Memory, stack []mem.MemoryValue, cairo1FpOffset uint64, ) (mem.MemoryAddress, error) { for i := range arguments { stack = append(stack, mem.MemoryValueFromFieldElement(arguments[i])) @@ -230,7 +230,7 @@ func (runner *Runner) initializeEntrypoint( return endPC, runner.initializeVm(&mem.MemoryAddress{ SegmentIndex: vm.ProgramSegment, Offset: initialPCOffset, - }, stack, memory, cairo1FpOffset, userArgs) + }, stack, memory, cairo1FpOffset) } func (runner *Runner) initializeBuiltins(memory *mem.Memory) ([]mem.MemoryValue, error) { @@ -266,7 +266,7 @@ func (runner *Runner) isProofMode() bool { } func (runner *Runner) initializeVm( - initialPC *mem.MemoryAddress, stack []mem.MemoryValue, memory *mem.Memory, cairo1FpOffset uint64, userArgs []starknet.CairoFuncArgs, + initialPC *mem.MemoryAddress, stack []mem.MemoryValue, memory *mem.Memory, cairo1FpOffset uint64, ) error { executionSegment := memory.Segments[vm.ExecutionSegment] offset := executionSegment.Len() @@ -287,7 +287,6 @@ func (runner *Runner) initializeVm( }, memory, vm.VirtualMachineConfig{ ProofMode: runner.isProofMode(), CollectTrace: runner.collectTrace, - UserArgs: userArgs, }) return err } diff --git a/pkg/runner/runner_benchmark_test.go b/pkg/runner/runner_benchmark_test.go index cca139fd..64bc7b87 100644 --- a/pkg/runner/runner_benchmark_test.go +++ b/pkg/runner/runner_benchmark_test.go @@ -233,12 +233,12 @@ func BenchmarkRunnerWithFibonacci(b *testing.B) { panic(err) } - runner, err := NewRunner(program, hints, ProofModeCairo0, false, math.MaxUint64, "plain") + runner, err := NewRunner(program, hints, ProofModeCairo0, false, math.MaxUint64, "plain", nil) if err != nil { panic(err) } - err = runner.Run(nil) + err = runner.Run() if err != nil { panic(err) } diff --git a/pkg/runner/runner_test.go b/pkg/runner/runner_test.go index b01d22cf..f94834cf 100644 --- a/pkg/runner/runner_test.go +++ b/pkg/runner/runner_test.go @@ -27,10 +27,10 @@ func TestSimpleProgram(t *testing.T) { `) hints := make(map[uint64][]hinter.Hinter) - runner, err := NewRunner(program, hints, ExecutionMode, false, math.MaxUint64, "plain") + runner, err := NewRunner(program, hints, ExecutionMode, false, math.MaxUint64, "plain", nil) require.NoError(t, err) - endPc, err := runner.initializeMainEntrypoint(nil) + endPc, err := runner.initializeMainEntrypoint() require.NoError(t, err) expectedPc := memory.MemoryAddress{SegmentIndex: 3, Offset: 0} @@ -74,10 +74,10 @@ func TestStepLimitExceeded(t *testing.T) { `) hints := make(map[uint64][]hinter.Hinter) - runner, err := NewRunner(program, hints, ExecutionMode, false, 3, "plain") + runner, err := NewRunner(program, hints, ExecutionMode, false, 3, "plain", nil) require.NoError(t, err) - endPc, err := runner.initializeMainEntrypoint(nil) + endPc, err := runner.initializeMainEntrypoint() require.NoError(t, err) expectedPc := memory.MemoryAddress{SegmentIndex: 3, Offset: 0} @@ -133,10 +133,10 @@ func TestStepLimitExceededProofMode(t *testing.T) { // when maxstep = 6, it fails executing the extra step required by proof mode // when maxstep = 7, it fails trying to get the trace to be a power of 2 hints := make(map[uint64][]hinter.Hinter) - runner, err := NewRunner(program, hints, ProofModeCairo0, false, uint64(maxstep), "plain") + runner, err := NewRunner(program, hints, ProofModeCairo0, false, uint64(maxstep), "plain", nil) require.NoError(t, err) - err = runner.Run(nil) + err = runner.Run() require.ErrorContains(t, err, "step limit exceeded") executionSegment := runner.vm.Memory.Segments[vm.ExecutionSegment] @@ -188,7 +188,7 @@ func TestBitwiseBuiltin(t *testing.T) { ret; `, "starknet_with_keccak", builtins.BitwiseType) - err := runner.Run(nil) + err := runner.Run() require.NoError(t, err) bitwise, ok := runner.vm.Memory.FindSegmentWithBuiltin(builtins.BitwiseName) @@ -204,7 +204,7 @@ func TestBitwiseBuiltinError(t *testing.T) { ret; `, "starknet_with_keccak", builtins.BitwiseType) - err := runner.Run(nil) + err := runner.Run() require.ErrorContains(t, err, "cannot infer value") // inferring second write to cell @@ -212,7 +212,7 @@ func TestBitwiseBuiltinError(t *testing.T) { [ap] = [[fp - 3] + 1]; ret; `, "starknet_with_keccak", builtins.BitwiseType) - err = runner.Run(nil) + err = runner.Run() require.ErrorContains(t, err, "cannot infer value") // trying to infer without writing before @@ -221,7 +221,7 @@ func TestBitwiseBuiltinError(t *testing.T) { ret; `, "starknet_with_keccak", builtins.BitwiseType) - err = runner.Run(nil) + err = runner.Run() require.ErrorContains(t, err, "input value at offset 0 is unknown") } @@ -234,7 +234,7 @@ func TestOutputBuiltin(t *testing.T) { [ap + 1] = [[fp - 3] + 1]; ret; `, "small", builtins.OutputType) - err := runner.Run(nil) + err := runner.Run() require.NoError(t, err) output := runner.Output() @@ -264,7 +264,7 @@ func TestPedersenBuiltin(t *testing.T) { `, val1.Text(10), val2.Text(10), val3.Text(10)) runner := createRunner(code, "small", builtins.PedersenType) - err := runner.Run(nil) + err := runner.Run() require.NoError(t, err) pedersen, ok := runner.vm.Memory.FindSegmentWithBuiltin(builtins.PedersenName) @@ -277,14 +277,14 @@ func TestPedersenBuiltinError(t *testing.T) { [ap] = [[fp - 3]]; ret; `, "small", builtins.PedersenType) - err := runner.Run(nil) + err := runner.Run() require.ErrorContains(t, err, "cannot infer value") runner = createRunner(` [ap] = [[fp - 3] + 2]; ret; `, "small", builtins.PedersenType) - err = runner.Run(nil) + err = runner.Run() require.ErrorContains(t, err, "input value at offset 0 is unknown") } @@ -300,7 +300,7 @@ func TestRangeCheckBuiltin(t *testing.T) { ret; `, "small", builtins.RangeCheckType) - err := runner.Run(nil) + err := runner.Run() require.NoError(t, err) rangeCheck, ok := runner.vm.Memory.FindSegmentWithBuiltin("range_check") @@ -321,7 +321,7 @@ func TestRangeCheckBuiltinError(t *testing.T) { ret; `, "small", builtins.RangeCheckType) - err := runner.Run(nil) + err := runner.Run() require.ErrorContains(t, err, "check write: 2**128 <") // second test fails due to reading unknown value @@ -330,7 +330,7 @@ func TestRangeCheckBuiltinError(t *testing.T) { ret; `, "small", builtins.RangeCheckType) - err = runner.Run(nil) + err = runner.Run() require.ErrorContains(t, err, "cannot infer value") } @@ -346,7 +346,7 @@ func TestRangeCheck96Builtin(t *testing.T) { ret; `, "all_cairo", builtins.RangeCheck96Type) - err := runner.Run(nil) + err := runner.Run() require.NoError(t, err) rangeCheck96, ok := runner.vm.Memory.FindSegmentWithBuiltin(builtins.RangeCheck96Name) @@ -367,7 +367,7 @@ func TestRangeCheck96BuiltinError(t *testing.T) { ret; `, "all_cairo", builtins.RangeCheck96Type) - err := runner.Run(nil) + err := runner.Run() require.ErrorContains(t, err, "check write: 2**96 <") // second test fails due to reading unknown value @@ -376,7 +376,7 @@ func TestRangeCheck96BuiltinError(t *testing.T) { ret; `, "all_cairo", builtins.RangeCheck96Type) - err = runner.Run(nil) + err = runner.Run() require.ErrorContains(t, err, "cannot infer value") } @@ -406,7 +406,7 @@ func TestEcOpBuiltin(t *testing.T) { ret; `, "starknet_with_keccak", builtins.ECOPType) - err := runner.Run(nil) + err := runner.Run() require.NoError(t, err) } @@ -423,7 +423,7 @@ func TestModuloBuiltin(t *testing.T) { // ret; // `, "small", sn.AddMod, sn.MulMod) - // err := runner.Run(nil) + // err := runner.Run() // require.NoError(t, err) // modulo, ok := runner.vm.Memory.FindSegmentWithBuiltin("add_mod") @@ -436,7 +436,7 @@ func createRunner(code string, layoutName string, builtins ...builtins.BuiltinTy program := createProgramWithBuiltins(code, builtins...) hints := make(map[uint64][]hinter.Hinter) - runner, err := NewRunner(program, hints, ExecutionMode, false, math.MaxUint64, layoutName) + runner, err := NewRunner(program, hints, ExecutionMode, false, math.MaxUint64, layoutName, nil) if err != nil { panic(err) } diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index 3fa22644..d13eb1e3 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -6,7 +6,6 @@ import ( "math" asmb "github.com/NethermindEth/cairo-vm-go/pkg/assembler" - "github.com/NethermindEth/cairo-vm-go/pkg/parsers/starknet" "github.com/NethermindEth/cairo-vm-go/pkg/utils" mem "github.com/NethermindEth/cairo-vm-go/pkg/vm/memory" f "github.com/consensys/gnark-crypto/ecc/stark-curve/fp" @@ -77,7 +76,6 @@ type VirtualMachineConfig struct { ProofMode bool // If true, the vm collects the relocated trace at the end of execution, without finalizing segments CollectTrace bool - UserArgs []starknet.CairoFuncArgs } type VirtualMachine struct { From 9c4fb3433003d140ae9ba7b33606fda1dea76cb7 Mon Sep 17 00:00:00 2001 From: MaksymMalicki Date: Mon, 16 Dec 2024 14:40:56 +0100 Subject: [PATCH 06/20] Refactor the code --- pkg/hintrunner/hintrunner.go | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/pkg/hintrunner/hintrunner.go b/pkg/hintrunner/hintrunner.go index bb2ddf23..fe54c340 100644 --- a/pkg/hintrunner/hintrunner.go +++ b/pkg/hintrunner/hintrunner.go @@ -16,15 +16,19 @@ type HintRunner struct { } func NewHintRunner(hints map[uint64][]h.Hinter, userArgs []starknet.CairoFuncArgs) HintRunner { - return HintRunner{ - // Context for certain hints that require it. Each manager is - // initialized only when required by the hint - context: *h.SetContextWithScope( + context := *h.InitializeDefaultContext() + if userArgs != nil { + context = *h.SetContextWithScope( map[string]any{ "userArgs": userArgs, }, - ), - hints: hints, + ) + } + return HintRunner{ + // Context for certain hints that require it. Each manager is + // initialized only when required by the hint + context: context, + hints: hints, } } From 5869a28a282f67eb66e00805708551920c4d883a Mon Sep 17 00:00:00 2001 From: MaksymMalicki Date: Mon, 16 Dec 2024 23:21:14 +0100 Subject: [PATCH 07/20] Fix unconditional append of ExternalWriteArgsToMemory, bug fixes in integration tests --- .../{sample.cairo => sample__small.cairo} | 0 integration_tests/cairo_vm_test.go | 11 ++++++----- pkg/runner/runner.go | 19 ++++++++++--------- 3 files changed, 16 insertions(+), 14 deletions(-) rename integration_tests/cairo_1_programs/{sample.cairo => sample__small.cairo} (100%) diff --git a/integration_tests/cairo_1_programs/sample.cairo b/integration_tests/cairo_1_programs/sample__small.cairo similarity index 100% rename from integration_tests/cairo_1_programs/sample.cairo rename to integration_tests/cairo_1_programs/sample__small.cairo diff --git a/integration_tests/cairo_vm_test.go b/integration_tests/cairo_vm_test.go index f1c4f376..2490da31 100644 --- a/integration_tests/cairo_vm_test.go +++ b/integration_tests/cairo_vm_test.go @@ -459,18 +459,19 @@ func runVm(path, layout string, zero bool) (time.Duration, string, string, strin } args := []string{ cliCommand, - "--proofmode", + // "--proofmode", "--tracefile", traceOutput, "--memoryfile", memoryOutput, "--layout", layout, - "--available_gas", - "9999999", - path, } - + if !zero { + args = append(args, "--available_gas", "9999999") + } + args = append(args, path) + fmt.Println(args) cmd := exec.Command( "../bin/cairo-vm", args..., diff --git a/pkg/runner/runner.go b/pkg/runner/runner.go index 87189706..7174ad77 100644 --- a/pkg/runner/runner.go +++ b/pkg/runner/runner.go @@ -302,9 +302,6 @@ func (runner *Runner) RunUntilPc(pc *mem.MemoryAddress) error { runner.maxsteps, ) } - if runner.vm.Context.Pc.Offset == 3 { - runner.vm.PrintMemory() - } if err := runner.vm.RunStep(&runner.hintrunner); err != nil { return fmt.Errorf("pc %s step %d: %w", runner.pc(), runner.steps(), err) } @@ -531,7 +528,7 @@ func GetEntryCodeInstructions(function starknet.EntryPointByFunction, finalizeFo } apOffset += paramsSize usedArgs := 0 - + writeArgsHint := false for _, builtin := range function.Builtins { if offset, isBuiltin := builtinsOffsetsMap[builtin]; isBuiltin { ctx.AddInlineCASM( @@ -571,11 +568,15 @@ func GetEntryCodeInstructions(function starknet.EntryPointByFunction, finalizeFo apOffset += param.Size usedArgs += param.Size } - } - hints := map[uint64][]hinter.Hinter{ - uint64(len(ctx.instructions)): { - &core.ExternalWriteArgsToMemory{}, - }, + writeArgsHint = true + } + var hints map[uint64][]hinter.Hinter + if writeArgsHint { + hints = map[uint64][]hinter.Hinter{ + uint64(len(ctx.instructions)): { + &core.ExternalWriteArgsToMemory{}, + }, + } } ctx.AddInlineCASM(fmt.Sprintf("call rel %d;", apOffset+1)) ctx.AddInlineCASM("ret;") From 29488246e6d0fb3c3009dcc2e4196ee5aa859662 Mon Sep 17 00:00:00 2001 From: MaksymMalicki Date: Tue, 17 Dec 2024 14:37:00 +0100 Subject: [PATCH 08/20] Add fixes of the call size calculation and include ExternalWriteArgsToMemory hint when gas present --- pkg/runner/runner.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pkg/runner/runner.go b/pkg/runner/runner.go index 7174ad77..3d6f29ad 100644 --- a/pkg/runner/runner.go +++ b/pkg/runner/runner.go @@ -557,6 +557,7 @@ func GetEntryCodeInstructions(function starknet.EntryPointByFunction, finalizeFo ) apOffset += 1 usedArgs += 1 + writeArgsHint = true } } for _, param := range paramTypes { @@ -578,9 +579,7 @@ func GetEntryCodeInstructions(function starknet.EntryPointByFunction, finalizeFo }, } } - ctx.AddInlineCASM(fmt.Sprintf("call rel %d;", apOffset+1)) - ctx.AddInlineCASM("ret;") - + ctx.AddInlineCASM(fmt.Sprintf("call rel %d; ret;", ctx.currentCodeOffset)) return ctx.instructions, hints, nil } From 1adfe243e9f504b21005c439210ad25ddb275e18 Mon Sep 17 00:00:00 2001 From: MaksymMalicki Date: Tue, 17 Dec 2024 14:37:25 +0100 Subject: [PATCH 09/20] Add layouts for integration tests --- .../{dict_with_struct.cairo => dict_with_struct__small.cairo} | 0 .../{dictionaries.cairo => dictionaries__small.cairo} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename integration_tests/cairo_1_programs/{dict_with_struct.cairo => dict_with_struct__small.cairo} (100%) rename integration_tests/cairo_1_programs/{dictionaries.cairo => dictionaries__small.cairo} (100%) diff --git a/integration_tests/cairo_1_programs/dict_with_struct.cairo b/integration_tests/cairo_1_programs/dict_with_struct__small.cairo similarity index 100% rename from integration_tests/cairo_1_programs/dict_with_struct.cairo rename to integration_tests/cairo_1_programs/dict_with_struct__small.cairo diff --git a/integration_tests/cairo_1_programs/dictionaries.cairo b/integration_tests/cairo_1_programs/dictionaries__small.cairo similarity index 100% rename from integration_tests/cairo_1_programs/dictionaries.cairo rename to integration_tests/cairo_1_programs/dictionaries__small.cairo From 73480e30db16d6ac35e8e9fdff868f4492a34848 Mon Sep 17 00:00:00 2001 From: MaksymMalicki Date: Tue, 17 Dec 2024 14:49:56 +0100 Subject: [PATCH 10/20] Add error handling --- pkg/hintrunner/core/hint.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pkg/hintrunner/core/hint.go b/pkg/hintrunner/core/hint.go index c91b0b7f..172d20a6 100644 --- a/pkg/hintrunner/core/hint.go +++ b/pkg/hintrunner/core/hint.go @@ -1962,6 +1962,9 @@ func (hint *ExternalWriteArgsToMemory) Execute(vm *VM.VirtualMachine, ctx *hinte // TODO: Implement array writing } } - ctx.ScopeManager.ExitScope() + err = ctx.ScopeManager.ExitScope() + if err != nil { + return fmt.Errorf("exit scope: %v", err) + } return nil } From 747e93d64da320744ee22f6c93adaa93f44e5d11 Mon Sep 17 00:00:00 2001 From: MaksymMalicki Date: Tue, 17 Dec 2024 20:21:02 +0100 Subject: [PATCH 11/20] Fixes in entry code generation --- integration_tests/cairo_vm_test.go | 1 - pkg/hintrunner/core/hint.go | 4 ---- pkg/runner/runner.go | 8 +++++++- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/integration_tests/cairo_vm_test.go b/integration_tests/cairo_vm_test.go index 2490da31..c38a14cf 100644 --- a/integration_tests/cairo_vm_test.go +++ b/integration_tests/cairo_vm_test.go @@ -471,7 +471,6 @@ func runVm(path, layout string, zero bool) (time.Duration, string, string, strin args = append(args, "--available_gas", "9999999") } args = append(args, path) - fmt.Println(args) cmd := exec.Command( "../bin/cairo-vm", args..., diff --git a/pkg/hintrunner/core/hint.go b/pkg/hintrunner/core/hint.go index 172d20a6..05c1650b 100644 --- a/pkg/hintrunner/core/hint.go +++ b/pkg/hintrunner/core/hint.go @@ -1962,9 +1962,5 @@ func (hint *ExternalWriteArgsToMemory) Execute(vm *VM.VirtualMachine, ctx *hinte // TODO: Implement array writing } } - err = ctx.ScopeManager.ExitScope() - if err != nil { - return fmt.Errorf("exit scope: %v", err) - } return nil } diff --git a/pkg/runner/runner.go b/pkg/runner/runner.go index 3d6f29ad..899f74c5 100644 --- a/pkg/runner/runner.go +++ b/pkg/runner/runner.go @@ -579,7 +579,13 @@ func GetEntryCodeInstructions(function starknet.EntryPointByFunction, finalizeFo }, } } - ctx.AddInlineCASM(fmt.Sprintf("call rel %d; ret;", ctx.currentCodeOffset)) + _, endInstructionsSize, err := assembler.CasmToBytecode("call rel 0; ret;") + if err != nil { + return nil, nil, err + } + totalSize := uint64(endInstructionsSize) + uint64(ctx.currentCodeOffset) + //TODO: This will always result in 3 in the current form, but lets keep the calculation dynamic for the moment + ctx.AddInlineCASM(fmt.Sprintf("call rel %d; ret;", int(totalSize)-ctx.currentCodeOffset)) return ctx.instructions, hints, nil } From 3d3a53766755e421818ec7170db0ac283c71eca2 Mon Sep 17 00:00:00 2001 From: MaksymMalicki Date: Fri, 20 Dec 2024 14:01:28 +0100 Subject: [PATCH 12/20] Address changes mentioned in a discussion --- pkg/hintrunner/hintrunner.go | 6 +- pkg/parsers/starknet/args.go | 56 +++++++++++++ pkg/parsers/starknet/args_test.go | 115 ++++++++++++++++++++++++++ pkg/parsers/starknet/starknet.go | 40 --------- pkg/parsers/starknet/starknet_test.go | 93 --------------------- 5 files changed, 172 insertions(+), 138 deletions(-) create mode 100644 pkg/parsers/starknet/args.go create mode 100644 pkg/parsers/starknet/args_test.go diff --git a/pkg/hintrunner/hintrunner.go b/pkg/hintrunner/hintrunner.go index fe54c340..c60dfc5e 100644 --- a/pkg/hintrunner/hintrunner.go +++ b/pkg/hintrunner/hintrunner.go @@ -18,11 +18,7 @@ type HintRunner struct { func NewHintRunner(hints map[uint64][]h.Hinter, userArgs []starknet.CairoFuncArgs) HintRunner { context := *h.InitializeDefaultContext() if userArgs != nil { - context = *h.SetContextWithScope( - map[string]any{ - "userArgs": userArgs, - }, - ) + context.ScopeManager.AssignVariable("userArgs", userArgs) } return HintRunner{ // Context for certain hints that require it. Each manager is diff --git a/pkg/parsers/starknet/args.go b/pkg/parsers/starknet/args.go new file mode 100644 index 00000000..0e7d2845 --- /dev/null +++ b/pkg/parsers/starknet/args.go @@ -0,0 +1,56 @@ +package starknet + +import ( + "fmt" + "regexp" + "strings" + + "github.com/consensys/gnark-crypto/ecc/stark-curve/fp" +) + +type CairoFuncArgs struct { + Single *fp.Element + Array []fp.Element +} + +func ParseCairoProgramArgs(input string) ([]CairoFuncArgs, error) { + re := regexp.MustCompile(`\[[^\]]*\]|\S+`) + tokens := re.FindAllString(input, -1) + var result []CairoFuncArgs + + parseValueToFelt := func(token string) (*fp.Element, error) { + felt, err := new(fp.Element).SetString(token) + if err != nil { + return nil, fmt.Errorf("invalid felt value: %v", err) + } + return felt, nil + } + + for _, token := range tokens { + if single, err := parseValueToFelt(token); err == nil { + result = append(result, CairoFuncArgs{ + Single: single, + Array: nil, + }) + } else if strings.HasPrefix(token, "[") && strings.HasSuffix(token, "]") { + arrayStr := strings.Trim(token, "[]") + arrayElements := strings.Fields(arrayStr) + array := make([]fp.Element, len(arrayElements)) + for i, element := range arrayElements { + single, err := parseValueToFelt(element) + if err != nil { + return nil, fmt.Errorf("invalid felt value in array: %v", err) + } + array[i] = *single + } + result = append(result, CairoFuncArgs{ + Single: nil, + Array: array, + }) + } else { + return nil, fmt.Errorf("invalid token: %s", token) + } + } + + return result, nil +} diff --git a/pkg/parsers/starknet/args_test.go b/pkg/parsers/starknet/args_test.go new file mode 100644 index 00000000..f6bdd6f9 --- /dev/null +++ b/pkg/parsers/starknet/args_test.go @@ -0,0 +1,115 @@ +package starknet + +import ( + "fmt" + "testing" + + "github.com/consensys/gnark-crypto/ecc/stark-curve/fp" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestParseStarknetProgramArgs(t *testing.T) { + testCases := []struct { + name string + args string + expected []CairoFuncArgs + err error + }{ + { + name: "single arg", + args: "1", + expected: []CairoFuncArgs{ + { + Single: new(fp.Element).SetUint64(1), + Array: nil, + }, + }, + err: nil, + }, + { + name: "single array arg", + args: "[1 2 3 4]", + expected: []CairoFuncArgs{ + { + Single: nil, + Array: []fp.Element{ + *new(fp.Element).SetUint64(1), + *new(fp.Element).SetUint64(2), + *new(fp.Element).SetUint64(3), + *new(fp.Element).SetUint64(4), + }, + }, + }, + }, + { + name: "nested array arg", + args: "[1 [2 3 4]]", + expected: nil, + err: fmt.Errorf("invalid felt value in array: invalid felt value: Element.SetString failed -> can't parse number into a big.Int [2"), + }, + { + name: "mixed args", + args: "1 [2 3 4] 5 [6 7 8] [1] 9 9 [12341341234 0]", + expected: []CairoFuncArgs{ + { + Single: new(fp.Element).SetUint64(1), + Array: nil, + }, + { + Single: nil, + Array: []fp.Element{ + *new(fp.Element).SetUint64(2), + *new(fp.Element).SetUint64(3), + *new(fp.Element).SetUint64(4), + }, + }, + { + Single: new(fp.Element).SetUint64(5), + Array: nil, + }, + { + Single: nil, + Array: []fp.Element{ + *new(fp.Element).SetUint64(6), + *new(fp.Element).SetUint64(7), + *new(fp.Element).SetUint64(8), + }, + }, + { + Single: nil, + Array: []fp.Element{ + *new(fp.Element).SetUint64(1), + }, + }, + { + Single: new(fp.Element).SetUint64(9), + Array: nil, + }, + { + Single: new(fp.Element).SetUint64(9), + Array: nil, + }, + { + Single: nil, + Array: []fp.Element{ + *new(fp.Element).SetUint64(12341341234), + *new(fp.Element).SetUint64(0), + }, + }, + }, + }, + } + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + args, err := ParseCairoProgramArgs(testCase.args) + if testCase.err != nil { + require.Error(t, err) + assert.Equal(t, testCase.err.Error(), err.Error()) + return + } + require.NoError(t, err) + assert.Equal(t, testCase.expected, args) + }) + } +} diff --git a/pkg/parsers/starknet/starknet.go b/pkg/parsers/starknet/starknet.go index 6bcec83d..0bbc615d 100644 --- a/pkg/parsers/starknet/starknet.go +++ b/pkg/parsers/starknet/starknet.go @@ -4,8 +4,6 @@ import ( "encoding/json" "fmt" "os" - "regexp" - "strings" "github.com/NethermindEth/cairo-vm-go/pkg/vm/builtins" "github.com/consensys/gnark-crypto/ecc/stark-curve/fp" @@ -97,41 +95,3 @@ func StarknetProgramFromJSON(content json.RawMessage) (*StarknetProgram, error) var starknet StarknetProgram return &starknet, json.Unmarshal(content, &starknet) } - -type CairoFuncArgs struct { - Single *fp.Element - Array []fp.Element -} - -func ParseCairoProgramArgs(input string) ([]CairoFuncArgs, error) { - re := regexp.MustCompile(`\[[^\]]*\]|\S+`) - tokens := re.FindAllString(input, -1) - var result []CairoFuncArgs - for _, token := range tokens { - if single, err := new(fp.Element).SetString(token); err == nil { - result = append(result, CairoFuncArgs{ - Single: single, - Array: nil, - }) - } else if strings.HasPrefix(token, "[") && strings.HasSuffix(token, "]") { - arrayStr := strings.Trim(token, "[]") - arrayParts := strings.Fields(arrayStr) - var array []fp.Element - for _, part := range arrayParts { - single, err := new(fp.Element).SetString(part) - if err != nil { - return nil, fmt.Errorf("invalid felt value in array: %v", err) - } - array = append(array, *single) - } - result = append(result, CairoFuncArgs{ - Single: nil, - Array: array, - }) - } else { - return nil, fmt.Errorf("invalid token: %s", token) - } - } - - return result, nil -} diff --git a/pkg/parsers/starknet/starknet_test.go b/pkg/parsers/starknet/starknet_test.go index 81bb5524..64842bcf 100644 --- a/pkg/parsers/starknet/starknet_test.go +++ b/pkg/parsers/starknet/starknet_test.go @@ -3,7 +3,6 @@ package starknet import ( "testing" - "github.com/consensys/gnark-crypto/ecc/stark-curve/fp" "github.com/go-playground/validator/v10" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -171,95 +170,3 @@ func TestInvalidBuiltin(t *testing.T) { _, err := StarknetProgramFromJSON(testData) assert.Error(t, err) } - -func TestParseStarknetProgramArgs(t *testing.T) { - testCases := []struct { - name string - args string - expected []CairoFuncArgs - }{ - { - name: "single arg", - args: "1", - expected: []CairoFuncArgs{ - { - Single: new(fp.Element).SetUint64(1), - Array: nil, - }, - }, - }, - { - name: "single array arg", - args: "[1 2 3 4]", - expected: []CairoFuncArgs{ - { - Single: nil, - Array: []fp.Element{ - *new(fp.Element).SetUint64(1), - *new(fp.Element).SetUint64(2), - *new(fp.Element).SetUint64(3), - *new(fp.Element).SetUint64(4), - }, - }, - }, - }, - { - name: "mixed args", - args: "1 [2 3 4] 5 [6 7 8] [1] 9 9 [12341341234 0]", - expected: []CairoFuncArgs{ - { - Single: new(fp.Element).SetUint64(1), - Array: nil, - }, - { - Single: nil, - Array: []fp.Element{ - *new(fp.Element).SetUint64(2), - *new(fp.Element).SetUint64(3), - *new(fp.Element).SetUint64(4), - }, - }, - { - Single: new(fp.Element).SetUint64(5), - Array: nil, - }, - { - Single: nil, - Array: []fp.Element{ - *new(fp.Element).SetUint64(6), - *new(fp.Element).SetUint64(7), - *new(fp.Element).SetUint64(8), - }, - }, - { - Single: nil, - Array: []fp.Element{ - *new(fp.Element).SetUint64(1), - }, - }, - { - Single: new(fp.Element).SetUint64(9), - Array: nil, - }, - { - Single: new(fp.Element).SetUint64(9), - Array: nil, - }, - { - Single: nil, - Array: []fp.Element{ - *new(fp.Element).SetUint64(12341341234), - *new(fp.Element).SetUint64(0), - }, - }, - }, - }, - } - for _, testCase := range testCases { - t.Run(testCase.name, func(t *testing.T) { - args, err := ParseCairoProgramArgs(testCase.args) - require.NoError(t, err) - assert.Equal(t, testCase.expected, args) - }) - } -} From 5ff587407ec966ad66ac77a6be4234197bc812cd Mon Sep 17 00:00:00 2001 From: MaksymMalicki Date: Fri, 20 Dec 2024 14:02:56 +0100 Subject: [PATCH 13/20] Add comment regarding writing to memory in a hint for the future reference in the integration tests with args --- pkg/hintrunner/core/hint.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/hintrunner/core/hint.go b/pkg/hintrunner/core/hint.go index 05c1650b..7f73c38a 100644 --- a/pkg/hintrunner/core/hint.go +++ b/pkg/hintrunner/core/hint.go @@ -1948,6 +1948,7 @@ func (hint *ExternalWriteArgsToMemory) Execute(vm *VM.VirtualMachine, ctx *hinte for _, arg := range userArgs { if arg.Single != nil { mv := mem.MemoryValueFromFieldElement(arg.Single) + //TODO: Temporary solution, AP based, to be replaced with a proper memory allocation err := vm.Memory.Write(1, vm.Context.Ap-uint64(len(userArgs)), &mv) if err != nil { return fmt.Errorf("write single arg: %v", err) From 77d8516d3ea80be9b56a9b8f09f0ba6b2a9e1eb4 Mon Sep 17 00:00:00 2001 From: MaksymMalicki Date: Wed, 25 Dec 2024 12:54:58 +0100 Subject: [PATCH 14/20] Changes in calculations of the initial PC offset, CALL opcode offset incremented by mainFuncOffset, writing user args to the AP in the hint --- pkg/hintrunner/core/hint.go | 3 +-- pkg/runner/runner.go | 47 +++++++++++++++++-------------------- pkg/vm/vm.go | 6 +++-- 3 files changed, 27 insertions(+), 29 deletions(-) diff --git a/pkg/hintrunner/core/hint.go b/pkg/hintrunner/core/hint.go index 7f73c38a..202c6891 100644 --- a/pkg/hintrunner/core/hint.go +++ b/pkg/hintrunner/core/hint.go @@ -1948,8 +1948,7 @@ func (hint *ExternalWriteArgsToMemory) Execute(vm *VM.VirtualMachine, ctx *hinte for _, arg := range userArgs { if arg.Single != nil { mv := mem.MemoryValueFromFieldElement(arg.Single) - //TODO: Temporary solution, AP based, to be replaced with a proper memory allocation - err := vm.Memory.Write(1, vm.Context.Ap-uint64(len(userArgs)), &mv) + err := vm.Memory.Write(1, vm.Context.Ap, &mv) if err != nil { return fmt.Errorf("write single arg: %v", err) } diff --git a/pkg/runner/runner.go b/pkg/runner/runner.go index 899f74c5..f3228b17 100644 --- a/pkg/runner/runner.go +++ b/pkg/runner/runner.go @@ -176,10 +176,8 @@ func (runner *Runner) initializeMainEntrypoint() (mem.MemoryAddress, error) { case ExecutionMode, ProofModeCairo1: returnFp := memory.AllocateEmptySegment() mvReturnFp := mem.MemoryValueFromMemoryAddress(&returnFp) - mainPCOffset, ok := runner.program.Entrypoints["main"] - if !ok { - return mem.UnknownAddress, errors.New("can't find an entrypoint for main") - } + // In Cairo mainPCOffset is equal to the offset of program segment base + mainPCOffset := uint64(0) if runner.runnerMode == ExecutionMode { return runner.initializeEntrypoint(mainPCOffset, nil, &mvReturnFp, memory, stack, 0) } else { @@ -276,7 +274,16 @@ func (runner *Runner) initializeVm( return err } } - + // Write builtins costs segment address to the end of the program segment + if runner.runnerMode == ProofModeCairo1 || runner.runnerMode == ExecutionMode { + builtinsCostSegmentAddress := memory.AllocateEmptySegment() + mv := mem.MemoryValueFromMemoryAddress(&builtinsCostSegmentAddress) + programSegment := memory.Segments[vm.ProgramSegment] + err := memory.Write(0, programSegment.Len(), &mv) + if err != nil { + return err + } + } initialFp := offset + uint64(len(stack)) + cairo1FpOffset var err error // initialize vm @@ -499,7 +506,7 @@ func GetEntryCodeInstructions(function starknet.EntryPointByFunction, finalizeFo paramTypes := function.InputArgs apOffset := 0 builtinOffset := 3 - + codeOffset := uint64(function.Offset) builtinsOffsetsMap := map[builtins.BuiltinType]int{} emulatedBuiltins := map[builtins.BuiltinType]struct{}{ builtins.SystemType: {}, @@ -528,7 +535,7 @@ func GetEntryCodeInstructions(function starknet.EntryPointByFunction, finalizeFo } apOffset += paramsSize usedArgs := 0 - writeArgsHint := false + var hints map[uint64][]hinter.Hinter for _, builtin := range function.Builtins { if offset, isBuiltin := builtinsOffsetsMap[builtin]; isBuiltin { ctx.AddInlineCASM( @@ -550,14 +557,14 @@ func GetEntryCodeInstructions(function starknet.EntryPointByFunction, finalizeFo ) apOffset += 1 } else if builtin == builtins.GasBuiltinType { - ctx.AddInlineCASM( - ` - ap += 1; - `, - ) + hints = map[uint64][]hinter.Hinter{ + uint64(ctx.currentCodeOffset): { + &core.ExternalWriteArgsToMemory{}, + }, + } + ctx.AddInlineCASM("ap += 1;") apOffset += 1 usedArgs += 1 - writeArgsHint = true } } for _, param := range paramTypes { @@ -569,23 +576,13 @@ func GetEntryCodeInstructions(function starknet.EntryPointByFunction, finalizeFo apOffset += param.Size usedArgs += param.Size } - writeArgsHint = true - } - var hints map[uint64][]hinter.Hinter - if writeArgsHint { - hints = map[uint64][]hinter.Hinter{ - uint64(len(ctx.instructions)): { - &core.ExternalWriteArgsToMemory{}, - }, - } } _, endInstructionsSize, err := assembler.CasmToBytecode("call rel 0; ret;") if err != nil { return nil, nil, err } - totalSize := uint64(endInstructionsSize) + uint64(ctx.currentCodeOffset) - //TODO: This will always result in 3 in the current form, but lets keep the calculation dynamic for the moment - ctx.AddInlineCASM(fmt.Sprintf("call rel %d; ret;", int(totalSize)-ctx.currentCodeOffset)) + totalSize := uint64(endInstructionsSize) + uint64(codeOffset) + ctx.AddInlineCASM(fmt.Sprintf("call rel %d; ret;", int(totalSize))) return ctx.instructions, hints, nil } diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index d13eb1e3..958b6633 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -92,8 +92,10 @@ type VirtualMachine struct { } func (vm *VirtualMachine) PrintMemory() { - for j, cell := range vm.Memory.Segments[ExecutionSegment].Data { - fmt.Printf("\tCell %d: %s\n", j, cell) + for i, _ := range vm.Memory.Segments { + for j, cell := range vm.Memory.Segments[i].Data { + fmt.Printf("%d:%d %s\n", i, j, cell) + } } } From e8b96154ca3ea38ca527f9dde147ee9715e4be13 Mon Sep 17 00:00:00 2001 From: MaksymMalicki Date: Wed, 25 Dec 2024 13:26:33 +0100 Subject: [PATCH 15/20] Turn back VM config to private field --- pkg/vm/vm.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index 958b6633..e11245da 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -83,7 +83,7 @@ type VirtualMachine struct { Memory *mem.Memory Step uint64 Trace []Context - Config VirtualMachineConfig + config VirtualMachineConfig // instructions cache instructions map[uint64]*asmb.Instruction // RcLimitsMin and RcLimitsMax define the range of values of instructions offsets, used for checking the number of potential range checks holes @@ -116,7 +116,7 @@ func NewVirtualMachine( Context: initialContext, Memory: memory, Trace: trace, - Config: config, + config: config, instructions: make(map[uint64]*asmb.Instruction), RcLimitsMin: math.MaxUint16, RcLimitsMax: 0, @@ -151,7 +151,7 @@ func (vm *VirtualMachine) RunStep(hintRunner HintRunner) error { } // store the trace before state change - if vm.Config.ProofMode || vm.Config.CollectTrace { + if vm.config.ProofMode || vm.config.CollectTrace { vm.Trace = append(vm.Trace, vm.Context) } From dc209dcd226ab2162844c699e7160f68f7a5f387 Mon Sep 17 00:00:00 2001 From: MaksymMalicki Date: Wed, 25 Dec 2024 16:10:11 +0100 Subject: [PATCH 16/20] Add error handling on assign of `userArgs` to the initial scope --- pkg/hintrunner/hintrunner.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pkg/hintrunner/hintrunner.go b/pkg/hintrunner/hintrunner.go index c60dfc5e..c1a209e7 100644 --- a/pkg/hintrunner/hintrunner.go +++ b/pkg/hintrunner/hintrunner.go @@ -18,7 +18,10 @@ type HintRunner struct { func NewHintRunner(hints map[uint64][]h.Hinter, userArgs []starknet.CairoFuncArgs) HintRunner { context := *h.InitializeDefaultContext() if userArgs != nil { - context.ScopeManager.AssignVariable("userArgs", userArgs) + err := context.ScopeManager.AssignVariable("userArgs", userArgs) + if err != nil { + panic(fmt.Errorf("assign userArgs: %v", err)) + } } return HintRunner{ // Context for certain hints that require it. Each manager is From d8b6e6489048f1e134d9a21a59c225c7971a113d Mon Sep 17 00:00:00 2001 From: MaksymMalicki Date: Wed, 25 Dec 2024 16:12:27 +0100 Subject: [PATCH 17/20] Lint project --- pkg/vm/vm.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index e11245da..7673f6ac 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -92,7 +92,7 @@ type VirtualMachine struct { } func (vm *VirtualMachine) PrintMemory() { - for i, _ := range vm.Memory.Segments { + for i := range vm.Memory.Segments { for j, cell := range vm.Memory.Segments[i].Data { fmt.Printf("%d:%d %s\n", i, j, cell) } From 4941781979749d009be8064463f1956119e770dc Mon Sep 17 00:00:00 2001 From: MaksymMalicki <81577596+MaksymMalicki@users.noreply.github.com> Date: Wed, 25 Dec 2024 10:13:26 -0500 Subject: [PATCH 18/20] Bump go version from 1.20 -> 1.21 (#678) * Bump go version from 1.20 -> 1.21 * Update golangci-lint --- .github/workflows/lint.yml | 4 ++-- .github/workflows/unit-test.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 55839c55..1f41bfa3 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -19,7 +19,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-go@v4 with: - go-version: '1.20' + go-version: '1.21' cache: false - name: golangci-lint uses: golangci/golangci-lint-action@v3 @@ -27,7 +27,7 @@ jobs: # Require: The version of golangci-lint to use. # When `install-mode` is `binary` (default) the value can be v1.2 or v1.2.3 or `latest` to use the latest version. # When `install-mode` is `goinstall` the value can be v1.2.3, `latest`, or the hash of a commit. - version: v1.53.3 + version: v1.55.2 # Optional: working directory, useful for monorepos # working-directory: somedir diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index 1ef9abe7..a8e009f9 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -15,7 +15,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v4 with: - go-version: "1.20" + go-version: "1.21" - name: Build run: make build From 03f3f22c1c631526081bb2abee0130ccda68f8fb Mon Sep 17 00:00:00 2001 From: MaksymMalicki Date: Wed, 25 Dec 2024 17:53:10 +0100 Subject: [PATCH 19/20] Simplify the Makefile --- Makefile | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/Makefile b/Makefile index 79269ac4..98b5e094 100644 --- a/Makefile +++ b/Makefile @@ -41,31 +41,29 @@ integration: @echo "Running integration tests..." @$(MAKE) build @if [ $$? -eq 0 ]; then \ - if [ ! -d ./rust_vm_bin ]; then \ - mkdir -p ./rust_vm_bin; \ + if [ ! -d rust_vm_bin ]; then \ + mkdir -p rust_vm_bin; \ fi; \ - if [ ! -d ./rust_vm_bin/cairo ]; then \ - mkdir -p ./rust_vm_bin/cairo; \ + if [ ! -d rust_vm_bin/cairo ]; then \ + mkdir -p rust_vm_bin/cairo-lang; \ fi; \ - if [ ! -f ./rust_vm_bin/cairo/cairo-compile ] || [ ! -f ./rust_vm_bin/cairo/sierra-compile-json ] || [ ! -d ./rust_vm_bin/corelib ]; then \ - cd ./rust_vm_bin/cairo; \ + if [ ! -f rust_vm_bin/cairo/cairo-compile ] || [ ! -f rust_vm_bin/cairo/sierra-compile-json ] || [ ! -d rust_vm_bin/corelib ]; then \ + cd rust_vm_bin; \ git clone --single-branch --branch feat/main-casm-json --depth=1 https://github.com/zmalatrax/cairo.git; \ - mv cairo/corelib ../../rust_vm_bin/; \ - cd cairo/crates/bin && \ - cargo build --release --bin cairo-compile --bin sierra-compile-json && \ - cd ../../../; \ - mv cairo/target/release/cairo-compile cairo/target/release/sierra-compile-json ../cairo/ && \ - rm -rf ./cairo; \ + mv cairo/corelib .; \ + cd cairo/crates/bin && cargo build --release --bin cairo-compile --bin sierra-compile-json && cd ../../../; \ + mv cairo/target/release/cairo-compile cairo/target/release/sierra-compile-json cairo-lang; \ + rm -rf cairo; \ + cd ../; \ fi; \ if [ ! -f ./rust_vm_bin/cairo/cairo1-run ] || [ ! -f ./rust_vm_bin/cairo-vm-cli ]; then \ - cd ./rust_vm_bin; \ - git clone https://github.com/lambdaclass/cairo-vm.git && \ - cd cairo-vm/; \ - cargo build --release --bin cairo-vm-cli --bin cairo1-run; \ - mv cairo-vm/target/release/cairo1-run ./cairo/ && \ - mv cairo-vm/target/release/cairo-vm-cli ../rust_vm_bin/ && \ + cd rust_vm_bin; \ + git clone https://github.com/lambdaclass/cairo-vm.git; \ + cd cairo-vm && cargo build --release --bin cairo-vm-cli --bin cairo1-run && cd ../; \ + mv cairo-vm/target/release/cairo1-run cairo;\ + mv cairo-vm/target/release/cairo-vm-cli . ; \ rm -rf cairo-vm; \ - cd ../../; \ + cd ../; \ fi; \ go test ./integration_tests/... -v; \ else \ From f6e723e07dd63d27f61c3d6b86d4d2cf3bf16bf7 Mon Sep 17 00:00:00 2001 From: MaksymMalicki Date: Wed, 25 Dec 2024 18:08:20 +0100 Subject: [PATCH 20/20] Correction in the makefile --- Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 98b5e094..d3c129ac 100644 --- a/Makefile +++ b/Makefile @@ -47,7 +47,7 @@ integration: if [ ! -d rust_vm_bin/cairo ]; then \ mkdir -p rust_vm_bin/cairo-lang; \ fi; \ - if [ ! -f rust_vm_bin/cairo/cairo-compile ] || [ ! -f rust_vm_bin/cairo/sierra-compile-json ] || [ ! -d rust_vm_bin/corelib ]; then \ + if [ ! -f ./rust_vm_bin/cairo-lang/cairo-compile ] || [ ! -f ./rust_vm_bin/cairo-lang/sierra-compile-json ] || [ ! -d rust_vm_bin/corelib ]; then \ cd rust_vm_bin; \ git clone --single-branch --branch feat/main-casm-json --depth=1 https://github.com/zmalatrax/cairo.git; \ mv cairo/corelib .; \ @@ -56,11 +56,11 @@ integration: rm -rf cairo; \ cd ../; \ fi; \ - if [ ! -f ./rust_vm_bin/cairo/cairo1-run ] || [ ! -f ./rust_vm_bin/cairo-vm-cli ]; then \ + if [ ! -f ./rust_vm_bin/cairo-lang/cairo1-run ] || [ ! -f ./rust_vm_bin/cairo-vm-cli ]; then \ cd rust_vm_bin; \ git clone https://github.com/lambdaclass/cairo-vm.git; \ cd cairo-vm && cargo build --release --bin cairo-vm-cli --bin cairo1-run && cd ../; \ - mv cairo-vm/target/release/cairo1-run cairo;\ + mv cairo-vm/target/release/cairo1-run cairo-lang;\ mv cairo-vm/target/release/cairo-vm-cli . ; \ rm -rf cairo-vm; \ cd ../; \